ソースを参照

resource manager

feature/replace-renderer
Martin Dørum 3年前
コミット
5706c3e399

+ 1
- 0
libcygnet/CMakeLists.txt ファイルの表示

src/Context.cc src/Context.cc
src/GlWrappers.cc src/GlWrappers.cc
src/Renderer.cc src/Renderer.cc
src/ResourceManager.cc
src/shaders.cc src/shaders.cc
src/TileAtlas.cc src/TileAtlas.cc
src/util.cc src/util.cc

+ 1
- 1
libcygnet/include/cygnet/GlWrappers.h ファイルの表示

}; };


GlShader(Type type, const char *source); GlShader(Type type, const char *source);
~GlShader();
virtual ~GlShader();


GLuint id() const { return id_; } GLuint id() const { return id_; }



+ 7
- 3
libcygnet/include/cygnet/Renderer.h ファイルの表示

int frameCount; int frameCount;
}; };


struct RenderTile {
uint16_t id;
};

struct RenderCamera { struct RenderCamera {
SwanCommon::Vec2 pos; SwanCommon::Vec2 pos;
SwanCommon::Vec2i size; SwanCommon::Vec2i size;


void draw(const RenderCamera &cam); void draw(const RenderCamera &cam);


void registerTileTexture(TileID tileId, const void *data, size_t len);
void uploadTileTexture();
void uploadTileAtlas(const void *data, int width, int height);
void modifyTile(TileID id, const void *data);


RenderChunk createChunk( RenderChunk createChunk(
TileID tiles[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]); TileID tiles[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]);
void modifyChunk(RenderChunk chunk, SwanCommon::Vec2i pos, TileID id); void modifyChunk(RenderChunk chunk, SwanCommon::Vec2i pos, TileID id);
void destroyChunk(RenderChunk chunk); void destroyChunk(RenderChunk chunk);


RenderSprite createSprite(void *data, int width, int height);
RenderSprite createSprite(void *data, int width, int height, int fh); RenderSprite createSprite(void *data, int width, int height, int fh);
RenderSprite createSprite(void *data, int width, int height);
void destroySprite(RenderSprite sprite); void destroySprite(RenderSprite sprite);


private: private:

+ 91
- 0
libcygnet/include/cygnet/ResourceManager.h ファイルの表示

#pragma once

#include <unordered_map>
#include <optional>
#include <memory>
#include <stdint.h>
#include <string.h>
#include <swan-common/constants.h>

#include "Renderer.h"
#include "TileAtlas.h"

namespace Cygnet {

struct ResourceTileAnimation {
uint16_t id;
int frames;
int index;
std::unique_ptr<unsigned char[]> data;
};

class ResourceManager;

class ResourceBuilder {
public:
ResourceBuilder(Renderer &rnd): rnd_(rnd) {}

RenderSprite addSprite(std::string name, void *data, int width, int height, int fh);
RenderSprite addSprite(std::string name, void *data, int width, int height);
void addTile(Renderer::TileID id, void *data, int frames = 1);
void addTile(Renderer::TileID id, std::unique_ptr<unsigned char[]> data, int frames = 1);

private:
Renderer &rnd_;
std::unordered_map<std::string, RenderSprite> sprites_;
std::vector<ResourceTileAnimation> tile_anims_;
TileAtlas atlas_;

friend ResourceManager;
};

class ResourceManager {
public:
ResourceManager(ResourceBuilder &&builder);
~ResourceManager();

RenderSprite getSprite(std::string name) { return sprites_.at(std::move(name)); }

void tick();

private:
Renderer &rnd_;
std::unordered_map<std::string, RenderSprite> sprites_;
std::unordered_map<std::string, RenderTile> tiles_;
std::vector<ResourceTileAnimation> tile_anims_;
};

inline RenderSprite ResourceBuilder::addSprite(
std::string name, void *data, int width, int height, int fh) {
return sprites_[std::move(name)] = rnd_.createSprite(data, width, height, fh);
}

inline RenderSprite ResourceBuilder::addSprite(
std::string name, void *data, int width, int height) {
return sprites_[std::move(name)] = rnd_.createSprite(data, width, height);
}

inline void ResourceBuilder::addTile(uint16_t id, void *data, int frames) {
if (frames == 0) {
atlas_.addTile(id, data);
} else {
auto ptr = std::make_unique<unsigned char[]>(
SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE * 4 * frames);
memcpy(ptr.get(), data, SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE * 4 * frames);
addTile(id, std::move(ptr), frames);
}
}

inline void ResourceBuilder::addTile(Renderer::TileID id, std::unique_ptr<unsigned char[]> data, int frames) {
atlas_.addTile(id, data.get());
if (frames > 1) {
tile_anims_.push_back({
.id = id,
.frames = frames,
.index = 0,
.data = std::move(data),
});
}
}

}

+ 1
- 1
libcygnet/include/cygnet/TileAtlas.h ファイルの表示

TileAtlas(); TileAtlas();
~TileAtlas(); ~TileAtlas();


void addTile(size_t tileId, const void *data, size_t len);
void addTile(size_t tileId, const void *data);
const unsigned char *getImage(size_t *w, size_t *h); const unsigned char *getImage(size_t *w, size_t *h);


private: private:

+ 31
- 14
libcygnet/src/Renderer.cc ファイルの表示

SpriteProg spriteProg{spriteVx, spriteFr}; SpriteProg spriteProg{spriteVx, spriteFr};
ChunkProg chunkProg{chunkVx, chunkFr}; ChunkProg chunkProg{chunkVx, chunkFr};


TileAtlas atlas;
GlTexture atlasTex;
GLuint atlasTex;
SwanCommon::Vec2 atlasTexSize;
}; };


Renderer::Renderer(): state_(std::make_unique<RendererState>()) {}
Renderer::Renderer(): state_(std::make_unique<RendererState>()) {
glGenTextures(1, &state_->atlasTex);
glCheck();
}


Renderer::~Renderer() = default; Renderer::~Renderer() = default;


glUniformMatrix3fv(chunkProg.camera, 1, GL_TRUE, camMat.data()); glUniformMatrix3fv(chunkProg.camera, 1, GL_TRUE, camMat.data());
glCheck(); glCheck();


glUniform2f(chunkProg.tileAtlasSize,
(float)(int)(state_->atlasTex.width() / SwanCommon::TILE_SIZE),
(float)(int)(state_->atlasTex.height() / SwanCommon::TILE_SIZE));
glUniform2f(chunkProg.tileAtlasSize, state_->atlasTexSize.x, state_->atlasTexSize.y);
glCheck(); glCheck();


glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, state_->atlasTex.id());
glBindTexture(GL_TEXTURE_2D, state_->atlasTex);
glCheck(); glCheck();


glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
} }
} }


void Renderer::registerTileTexture(TileID tileId, const void *data, size_t len) {
state_->atlas.addTile(tileId, data, len);
void Renderer::uploadTileAtlas(const void *data, int width, int height) {
glBindTexture(GL_TEXTURE_2D, state_->atlasTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCheck();

state_->atlasTexSize = {
(float)(int)(width / SwanCommon::TILE_SIZE),
(float)(int)(height / SwanCommon::TILE_SIZE) };
} }


void Renderer::uploadTileTexture() {
size_t w, h;
const unsigned char *data = state_->atlas.getImage(&w, &h);
state_->atlasTex.upload(w, h, (void *)data, GL_RGBA, GL_UNSIGNED_BYTE);
std::cerr << "Uploaded image of size " << w << 'x' << h << '\n';
void Renderer::modifyTile(TileID id, const void *data) {
int w = (int)state_->atlasTexSize.x;
int x = id % w;
int y = id / w;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, state_->atlasTex);
glTexSubImage2D(
GL_TEXTURE_2D, 0, x * SwanCommon::TILE_SIZE, y * SwanCommon::TILE_SIZE,
SwanCommon::TILE_SIZE, SwanCommon::TILE_SIZE,
GL_RGBA, GL_UNSIGNED_BYTE, data);
glCheck();
} }


RenderChunk Renderer::createChunk( RenderChunk Renderer::createChunk(

+ 29
- 0
libcygnet/src/ResourceManager.cc ファイルの表示

#include "ResourceManager.h"

namespace Cygnet {

ResourceManager::ResourceManager(ResourceBuilder &&builder):
rnd_(builder.rnd_), sprites_(std::move(builder.sprites_)),
tile_anims_(std::move(builder.tile_anims_)) {
size_t width, height;
const unsigned char *data = builder.atlas_.getImage(&width, &height);
rnd_.uploadTileAtlas(data, width, height);
}

ResourceManager::~ResourceManager() {
for (auto &[name, sprite]: sprites_) {
rnd_.destroySprite(sprite);
}
}

void ResourceManager::tick() {
// TODO: Maybe do a GPU->GPU copy instead of an upload from the CPU?
for (auto &anim: tile_anims_) {
anim.index = (anim.index + 1) % anim.frames;
unsigned char *data = anim.data.get() +
SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE * 4 * anim.index;
rnd_.modifyTile(anim.id, data);
}
}

}

+ 2
- 3
libcygnet/src/TileAtlas.cc ファイルの表示



TileAtlas::~TileAtlas() = default; TileAtlas::~TileAtlas() = default;


void TileAtlas::addTile(size_t tileId, const void *data, size_t len) {
size_t rows = len / (SwanCommon::TILE_SIZE * 4);
void TileAtlas::addTile(size_t tileId, const void *data) {
const unsigned char *bytes = (const unsigned char *)data; const unsigned char *bytes = (const unsigned char *)data;
size_t x = tileId % state_->tilesPerLine; size_t x = tileId % state_->tilesPerLine;
size_t y = tileId / state_->tilesPerLine; size_t y = tileId / state_->tilesPerLine;
state_->height * SwanCommon::TILE_SIZE * 4; state_->height * SwanCommon::TILE_SIZE * 4;
state_->data.resize(requiredSize); state_->data.resize(requiredSize);


for (size_t ty = 0; ty < rows; ++ty) {
for (size_t ty = 0; ty < SwanCommon::TILE_SIZE; ++ty) {
const unsigned char *src = bytes + ty * SwanCommon::TILE_SIZE * 4; const unsigned char *src = bytes + ty * SwanCommon::TILE_SIZE * 4;
unsigned char *dest = state_->data.data() + unsigned char *dest = state_->data.data() +
(y * SwanCommon::TILE_SIZE + ty) * state_->tilesPerLine * SwanCommon::TILE_SIZE * 4 + (y * SwanCommon::TILE_SIZE + ty) * state_->tilesPerLine * SwanCommon::TILE_SIZE * 4 +

+ 3
- 1
libcygnet/src/Window.cc ファイルの表示

onResize(w, h); onResize(w, h);
} }


Window::~Window() = default;
Window::~Window() {
SDL_DestroyWindow(state_->window);
}


void Window::makeCurrent() { void Window::makeCurrent() {
SDL_GL_MakeCurrent(state_->window, state_->glctx); SDL_GL_MakeCurrent(state_->window, state_->glctx);

+ 40
- 18
src/cygnet-test.cc ファイルの表示

#include <cygnet/Context.h> #include <cygnet/Context.h>
#include <cygnet/Window.h> #include <cygnet/Window.h>
#include <cygnet/Renderer.h> #include <cygnet/Renderer.h>
#include <cygnet/ResourceManager.h>
#include <swan-common/constants.h> #include <swan-common/constants.h>


#include <time.h> #include <time.h>
return tv.tv_sec + tv.tv_nsec / 1000000000.0; return tv.tv_sec + tv.tv_nsec / 1000000000.0;
} }


void addTile(Cygnet::Renderer &rnd, const char *path) {
void addTile(Cygnet::ResourceBuilder &builder, const char *path) {
static size_t id = 0; static size_t id = 0;
SDL_Surface *surf = IMG_Load(path); SDL_Surface *surf = IMG_Load(path);
rnd.registerTileTexture(id++, surf->pixels, surf->pitch * surf->h);
builder.addTile(id++, surf->pixels);
SDL_FreeSurface(surf); SDL_FreeSurface(surf);
} }


Cygnet::RenderSprite loadSprite(Cygnet::Renderer &rnd, const char *path, int fh) {
Cygnet::RenderSprite loadSprite(Cygnet::ResourceBuilder &builder, const char *path, int fh) {
SDL_Surface *surf = IMG_Load(path); SDL_Surface *surf = IMG_Load(path);
auto sprite = rnd.createSprite(surf->pixels, surf->w, surf->h, fh);
auto sprite = builder.addSprite(path, surf->pixels, surf->w, surf->h, fh);
SDL_FreeSurface(surf); SDL_FreeSurface(surf);
return sprite; return sprite;
} }


Cygnet::RenderSprite loadSprite(Cygnet::Renderer &rnd, const char *path) {
Cygnet::RenderSprite loadSprite(Cygnet::ResourceBuilder &builder, const char *path) {
SDL_Surface *surf = IMG_Load(path); SDL_Surface *surf = IMG_Load(path);
auto sprite = rnd.createSprite(surf->pixels, surf->w, surf->h);
auto sprite = builder.addSprite(path, surf->pixels, surf->w, surf->h);
SDL_FreeSurface(surf); SDL_FreeSurface(surf);
return sprite; return sprite;
} }
IMG_Init(IMG_INIT_PNG); IMG_Init(IMG_INIT_PNG);
Cygnet::Window win("Cygnet Test", 680, 680); Cygnet::Window win("Cygnet Test", 680, 680);
Cygnet::Renderer rnd; Cygnet::Renderer rnd;
Cygnet::ResourceBuilder rbuilder(rnd);


for (auto path: { for (auto path: {
"core.mod/assets/tile/dirt.png", "core.mod/assets/tile/dirt.png",
"core.mod/assets/tile/stone.png", "core.mod/assets/tile/stone.png",
"core.mod/assets/tile/torch.png", "core.mod/assets/tile/torch.png",
"core.mod/assets/tile/tree-trunk.png", "core.mod/assets/tile/tree-trunk.png",
}) addTile(rnd, path);
rnd.uploadTileTexture();
}) addTile(rbuilder, path);

unsigned char lolTexture[32*32*4*3];
for (size_t i = 0; i < 3; ++i) {
int col = 100 * i + 50;;
for (size_t y = 0; y < 32; ++y) {
for (size_t x = 0; x < 32; ++x) {
lolTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 0] = col;
lolTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 1] = col;
lolTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 2] = col;
lolTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 3] = 255;
}
}
}
rbuilder.addTile(10, lolTexture, 3);

Cygnet::RenderSprite playerSprite = loadSprite(
rbuilder, "core.mod/assets/entity/player-still.png", 64);


Cygnet::RenderSprite playerSprite = loadSprite(rnd, "core.mod/assets/entity/player-still.png", 64);
Cygnet::ResourceManager resources(std::move(rbuilder));


Cygnet::RenderChunk chunk; Cygnet::RenderChunk chunk;
{ {
tiles[0] = 1; tiles[0] = 1;
tiles[1] = 2; tiles[1] = 2;
tiles[2] = 3; tiles[2] = 3;
tiles[10] = 10;
chunk = rnd.createChunk(tiles); chunk = rnd.createChunk(tiles);
} }




bool keys[512] = { 0 }; bool keys[512] = { 0 };


double acc = 0;
double tileAnimAcc = 0;
double fpsAcc = 0;
double prevTime = getTime() - 1/60.0; double prevTime = getTime() - 1/60.0;
int frames = 0; int frames = 0;
float x = 0, y = 0; float x = 0, y = 0;
double currTime = getTime(); double currTime = getTime();
double dt = currTime - prevTime; double dt = currTime - prevTime;
prevTime = currTime; prevTime = currTime;
acc += dt;
lol += dt;


fpsAcc += dt;
frames += 1; frames += 1;
if (acc >= 2) {
if (fpsAcc >= 2) {
std::cerr << "FPS: " << (frames / 2.0) << '\n'; std::cerr << "FPS: " << (frames / 2.0) << '\n';
acc -= 2;
fpsAcc -= 2;
frames = 0; frames = 0;
} }


tileAnimAcc += dt;
if (tileAnimAcc >= 0.5) {
resources.tick();
tileAnimAcc -= 0.5;
}

SDL_Event evt; SDL_Event evt;
while (SDL_PollEvent(&evt)) { while (SDL_PollEvent(&evt)) {
switch (evt.type) { switch (evt.type) {
y += 1 * dt; y += 1 * dt;
} }


lol += 1 * dt;
rnd.modifyChunk(chunk, { 0, 0 }, (int)lol % 6);
rnd.modifyChunk(chunk, { 4, 4 }, ((int)(lol / 2) + 3) % 6);
rnd.modifyChunk(chunk, { 3, 2 }, ((int)(lol * 1.5) + 7) % 6);

rnd.drawChunk(chunk, { 0, 0 }); rnd.drawChunk(chunk, { 0, 0 });


rnd.drawSprite(playerSprite, { x, y }, (int)lol % 2); rnd.drawSprite(playerSprite, { x, y }, (int)lol % 2);

読み込み中…
キャンセル
保存