Browse Source

resource manager

feature/replace-renderer
Martin Dørum 3 years ago
parent
commit
5706c3e399

+ 1
- 0
libcygnet/CMakeLists.txt View File

@@ -2,6 +2,7 @@ add_library(libcygnet SHARED
src/Context.cc
src/GlWrappers.cc
src/Renderer.cc
src/ResourceManager.cc
src/shaders.cc
src/TileAtlas.cc
src/util.cc

+ 1
- 1
libcygnet/include/cygnet/GlWrappers.h View File

@@ -21,7 +21,7 @@ public:
};

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

GLuint id() const { return id_; }


+ 7
- 3
libcygnet/include/cygnet/Renderer.h View File

@@ -23,6 +23,10 @@ struct RenderSprite {
int frameCount;
};

struct RenderTile {
uint16_t id;
};

struct RenderCamera {
SwanCommon::Vec2 pos;
SwanCommon::Vec2i size;
@@ -43,16 +47,16 @@ public:

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(
TileID tiles[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]);
void modifyChunk(RenderChunk chunk, SwanCommon::Vec2i pos, TileID id);
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);
void destroySprite(RenderSprite sprite);

private:

+ 91
- 0
libcygnet/include/cygnet/ResourceManager.h View File

@@ -0,0 +1,91 @@
#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 View File

@@ -11,7 +11,7 @@ public:
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);

private:

+ 31
- 14
libcygnet/src/Renderer.cc View File

@@ -134,11 +134,14 @@ struct RendererState {
SpriteProg spriteProg{spriteVx, spriteFr};
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;

@@ -159,13 +162,11 @@ void Renderer::draw(const RenderCamera &cam) {
glUniformMatrix3fv(chunkProg.camera, 1, GL_TRUE, camMat.data());
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();

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

glActiveTexture(GL_TEXTURE1);
@@ -200,15 +201,31 @@ void Renderer::draw(const RenderCamera &cam) {
}
}

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(

+ 29
- 0
libcygnet/src/ResourceManager.cc View File

@@ -0,0 +1,29 @@
#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 View File

@@ -27,8 +27,7 @@ TileAtlas::TileAtlas(): state_(std::make_unique<AtlasState>()) {

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;
size_t x = tileId % state_->tilesPerLine;
size_t y = tileId / state_->tilesPerLine;
@@ -46,7 +45,7 @@ void TileAtlas::addTile(size_t tileId, const void *data, size_t len) {
state_->height * SwanCommon::TILE_SIZE * 4;
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;
unsigned char *dest = state_->data.data() +
(y * SwanCommon::TILE_SIZE + ty) * state_->tilesPerLine * SwanCommon::TILE_SIZE * 4 +

+ 3
- 1
libcygnet/src/Window.cc View File

@@ -32,7 +32,9 @@ Window::Window(const char *name, int w, int h):
onResize(w, h);
}

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

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

+ 40
- 18
src/cygnet-test.cc View File

@@ -1,6 +1,7 @@
#include <cygnet/Context.h>
#include <cygnet/Window.h>
#include <cygnet/Renderer.h>
#include <cygnet/ResourceManager.h>
#include <swan-common/constants.h>

#include <time.h>
@@ -15,23 +16,23 @@ double getTime() {
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;
SDL_Surface *surf = IMG_Load(path);
rnd.registerTileTexture(id++, surf->pixels, surf->pitch * surf->h);
builder.addTile(id++, surf->pixels);
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);
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);
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);
auto sprite = rnd.createSprite(surf->pixels, surf->w, surf->h);
auto sprite = builder.addSprite(path, surf->pixels, surf->w, surf->h);
SDL_FreeSurface(surf);
return sprite;
}
@@ -41,6 +42,7 @@ int main() {
IMG_Init(IMG_INIT_PNG);
Cygnet::Window win("Cygnet Test", 680, 680);
Cygnet::Renderer rnd;
Cygnet::ResourceBuilder rbuilder(rnd);

for (auto path: {
"core.mod/assets/tile/dirt.png",
@@ -49,10 +51,26 @@ int main() {
"core.mod/assets/tile/stone.png",
"core.mod/assets/tile/torch.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;
{
@@ -61,6 +79,7 @@ int main() {
tiles[0] = 1;
tiles[1] = 2;
tiles[2] = 3;
tiles[10] = 10;
chunk = rnd.createChunk(tiles);
}

@@ -74,7 +93,8 @@ int main() {

bool keys[512] = { 0 };

double acc = 0;
double tileAnimAcc = 0;
double fpsAcc = 0;
double prevTime = getTime() - 1/60.0;
int frames = 0;
float x = 0, y = 0;
@@ -83,15 +103,22 @@ int main() {
double currTime = getTime();
double dt = currTime - prevTime;
prevTime = currTime;
acc += dt;
lol += dt;

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

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

SDL_Event evt;
while (SDL_PollEvent(&evt)) {
switch (evt.type) {
@@ -134,11 +161,6 @@ int main() {
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.drawSprite(playerSprite, { x, y }, (int)lol % 2);

Loading…
Cancel
Save