GLuint tex; | GLuint tex; | ||||
}; | }; | ||||
struct RenderChunkShadow { | |||||
GLuint tex; | |||||
}; | |||||
struct RenderSprite { | struct RenderSprite { | ||||
GLuint tex; | GLuint tex; | ||||
SwanCommon::Vec2 scale; | SwanCommon::Vec2 scale; | ||||
~Renderer(); | ~Renderer(); | ||||
void drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos); | void drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos); | ||||
void drawChunkShadow(RenderChunkShadow shadow, SwanCommon::Vec2 pos); | |||||
void drawTile(TileID id, Mat3gf mat); | void drawTile(TileID id, Mat3gf mat); | ||||
void drawSprite(RenderSprite sprite, Mat3gf mat, int y = 0); | void drawSprite(RenderSprite sprite, Mat3gf mat, int y = 0); | ||||
void drawRect(SwanCommon::Vec2 pos, SwanCommon::Vec2 size); | void drawRect(SwanCommon::Vec2 pos, SwanCommon::Vec2 size); | ||||
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); | ||||
RenderChunkShadow createChunkShadow( | |||||
uint8_t data[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]); | |||||
void modifyChunkShadow( | |||||
RenderChunkShadow shadow, | |||||
uint8_t data[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]); | |||||
void destroyChunkShadow(RenderChunkShadow chunk); | |||||
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); | RenderSprite createSprite(void *data, int width, int height); | ||||
void destroySprite(RenderSprite sprite); | void destroySprite(RenderSprite sprite); | ||||
RenderChunk chunk; | RenderChunk chunk; | ||||
}; | }; | ||||
struct DrawShadow { | |||||
SwanCommon::Vec2 pos; | |||||
RenderChunkShadow shadow; | |||||
}; | |||||
struct DrawTile { | struct DrawTile { | ||||
Mat3gf transform; | Mat3gf transform; | ||||
TileID id; | TileID id; | ||||
std::unique_ptr<RendererState> state_; | std::unique_ptr<RendererState> state_; | ||||
std::vector<DrawChunk> drawChunks_; | std::vector<DrawChunk> drawChunks_; | ||||
std::vector<DrawShadow> drawChunkShadows_; | |||||
std::vector<DrawTile> drawTiles_; | std::vector<DrawTile> drawTiles_; | ||||
std::vector<DrawSprite> drawSprites_; | std::vector<DrawSprite> drawSprites_; | ||||
std::vector<DrawRect> drawRects_; | std::vector<DrawRect> drawRects_; | ||||
drawChunks_.push_back({pos, chunk}); | drawChunks_.push_back({pos, chunk}); | ||||
} | } | ||||
inline void Renderer::drawChunkShadow(RenderChunkShadow shadow, SwanCommon::Vec2 pos) { | |||||
drawChunkShadows_.push_back({pos, shadow}); | |||||
} | |||||
inline void Renderer::drawTile(TileID id, Mat3gf mat) { | inline void Renderer::drawTile(TileID id, Mat3gf mat) { | ||||
drawTiles_.push_back({mat, id}); | drawTiles_.push_back({mat, id}); | ||||
} | } |
extern const char *chunkVx; | extern const char *chunkVx; | ||||
extern const char *chunkFr; | extern const char *chunkFr; | ||||
extern const char *chunkShadowVx; | |||||
extern const char *chunkShadowFr; | |||||
extern const char *tileVx; | extern const char *tileVx; | ||||
extern const char *tileFr; | extern const char *tileFr; | ||||
} | } | ||||
}; | }; | ||||
struct ChunkShadowProg: public GlProgram { | |||||
template<typename... T> | |||||
ChunkShadowProg(const T &... shaders): GlProgram(shaders...) { init(); } | |||||
~ChunkShadowProg() { deinit(); } | |||||
GLint camera = uniformLoc("camera"); | |||||
GLint pos = uniformLoc("pos"); | |||||
GLint vertex = attribLoc("vertex"); | |||||
GLint tex = uniformLoc("tex"); | |||||
GLuint vbo; | |||||
static constexpr float ch = (float)SwanCommon::CHUNK_HEIGHT; | |||||
static constexpr float cw = (float)SwanCommon::CHUNK_WIDTH; | |||||
static constexpr GLfloat vertexes[] = { | |||||
0.0f, 0.0f, // pos 0: top left | |||||
0.0f, ch , // pos 1: bottom left | |||||
cw, ch, // pos 2: bottom right | |||||
cw, ch, // pos 2: bottom right | |||||
cw, 0.0f, // pos 3: top right | |||||
0.0f, 0.0f, // pos 0: top left | |||||
}; | |||||
void enable() { | |||||
glUseProgram(id()); | |||||
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |||||
glVertexAttribPointer(vertex, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); | |||||
glEnableVertexAttribArray(vertex); | |||||
glCheck(); | |||||
glUniform1i(tex, 0); | |||||
} | |||||
void disable() { | |||||
glDisableVertexAttribArray(vertex); | |||||
glCheck(); | |||||
} | |||||
void init() { | |||||
glGenBuffers(1, &vbo); | |||||
glCheck(); | |||||
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW); | |||||
glCheck(); | |||||
} | |||||
void deinit() { | |||||
glDeleteBuffers(1, &vbo); | |||||
glCheck(); | |||||
} | |||||
}; | |||||
struct TileProg: public GlProgram { | struct TileProg: public GlProgram { | ||||
template<typename... T> | template<typename... T> | ||||
TileProg(const T &... shaders): GlProgram(shaders...) { init(); } | TileProg(const T &... shaders): GlProgram(shaders...) { init(); } | ||||
struct RendererState { | struct RendererState { | ||||
GlVxShader chunkVx{Shaders::chunkVx}; | GlVxShader chunkVx{Shaders::chunkVx}; | ||||
GlFrShader chunkFr{Shaders::chunkFr}; | GlFrShader chunkFr{Shaders::chunkFr}; | ||||
GlVxShader chunkShadowVx{Shaders::chunkShadowVx}; | |||||
GlFrShader chunkShadowFr{Shaders::chunkShadowFr}; | |||||
GlVxShader tileVx{Shaders::tileVx}; | GlVxShader tileVx{Shaders::tileVx}; | ||||
GlFrShader tileFr{Shaders::tileFr}; | GlFrShader tileFr{Shaders::tileFr}; | ||||
GlVxShader spriteVx{Shaders::spriteVx}; | GlVxShader spriteVx{Shaders::spriteVx}; | ||||
GlFrShader rectFr{Shaders::rectFr}; | GlFrShader rectFr{Shaders::rectFr}; | ||||
ChunkProg chunkProg{chunkVx, chunkFr}; | ChunkProg chunkProg{chunkVx, chunkFr}; | ||||
ChunkShadowProg chunkShadowProg{chunkShadowVx, chunkShadowFr}; | |||||
TileProg tileProg{tileVx, tileFr}; | TileProg tileProg{tileVx, tileFr}; | ||||
SpriteProg spriteProg{spriteVx, spriteFr}; | SpriteProg spriteProg{spriteVx, spriteFr}; | ||||
RectProg rectProg{rectVx, rectFr}; | RectProg rectProg{rectVx, rectFr}; | ||||
} | } | ||||
auto &chunkProg = state_->chunkProg; | auto &chunkProg = state_->chunkProg; | ||||
auto &chunkShadowProg = state_->chunkShadowProg; | |||||
auto &tileProg = state_->tileProg; | auto &tileProg = state_->tileProg; | ||||
auto &spriteProg = state_->spriteProg; | auto &spriteProg = state_->spriteProg; | ||||
auto &rectProg = state_->rectProg; | auto &rectProg = state_->rectProg; | ||||
drawRects_.clear(); | drawRects_.clear(); | ||||
rectProg.disable(); | rectProg.disable(); | ||||
} | } | ||||
{ | |||||
chunkShadowProg.enable(); | |||||
glUniformMatrix3fv(chunkShadowProg.camera, 1, GL_TRUE, camMat.data()); | |||||
glCheck(); | |||||
glActiveTexture(GL_TEXTURE0); | |||||
for (auto [pos, shadow]: drawChunkShadows_) { | |||||
glUniform2f(chunkShadowProg.pos, pos.x, pos.y); | |||||
glBindTexture(GL_TEXTURE_2D, shadow.tex); | |||||
glDrawArrays(GL_TRIANGLES, 0, 6); | |||||
glCheck(); | |||||
} | |||||
drawChunkShadows_.clear(); | |||||
chunkShadowProg.disable(); | |||||
} | |||||
} | } | ||||
void Renderer::uploadTileAtlas(const void *data, int width, int height) { | void Renderer::uploadTileAtlas(const void *data, int width, int height) { | ||||
glCheck(); | glCheck(); | ||||
} | } | ||||
RenderChunkShadow Renderer::createChunkShadow( | |||||
uint8_t data[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]) { | |||||
RenderChunkShadow shadow; | |||||
glGenTextures(1, &shadow.tex); | |||||
glCheck(); | |||||
glActiveTexture(GL_TEXTURE0); | |||||
glBindTexture(GL_TEXTURE_2D, shadow.tex); | |||||
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(); | |||||
glTexImage2D( | |||||
GL_TEXTURE_2D, 0, GL_LUMINANCE, | |||||
SwanCommon::CHUNK_WIDTH, SwanCommon::CHUNK_HEIGHT, | |||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||||
glCheck(); | |||||
return shadow; | |||||
} | |||||
void Renderer::modifyChunkShadow( | |||||
RenderChunkShadow shadow, | |||||
uint8_t data[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]) { | |||||
glActiveTexture(GL_TEXTURE0); | |||||
glBindTexture(GL_TEXTURE_2D, shadow.tex); | |||||
glTexImage2D( | |||||
GL_TEXTURE_2D, 0, GL_LUMINANCE, | |||||
SwanCommon::CHUNK_WIDTH, SwanCommon::CHUNK_HEIGHT, | |||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||||
glCheck(); | |||||
} | |||||
void Renderer::destroyChunkShadow(RenderChunkShadow shadow) { | |||||
glDeleteTextures(1, &shadow.tex); | |||||
glCheck(); | |||||
} | |||||
RenderSprite Renderer::createSprite(void *data, int width, int height, int fh) { | RenderSprite Renderer::createSprite(void *data, int width, int height, int fh) { | ||||
RenderSprite sprite; | RenderSprite sprite; | ||||
sprite.scale = { | sprite.scale = { |
} | } | ||||
)glsl"; | )glsl"; | ||||
const char *chunkShadowVx = R"glsl( | |||||
precision mediump float; | |||||
#define CHUNK_WIDTH 64 | |||||
#define CHUNK_HEIGHT 64 | |||||
uniform mat3 camera; | |||||
uniform vec2 pos; | |||||
attribute vec2 vertex; | |||||
varying vec2 v_texCoord; | |||||
void main() { | |||||
vec3 pos = camera * vec3(pos + vertex, 1); | |||||
gl_Position = vec4(pos.xy, 0, 1); | |||||
v_texCoord = vertex / vec2(CHUNK_WIDTH, CHUNK_HEIGHT); | |||||
} | |||||
)glsl"; | |||||
const char *chunkShadowFr = R"glsl( | |||||
precision mediump float; | |||||
varying vec2 v_texCoord; | |||||
uniform sampler2D tex; | |||||
void main() { | |||||
vec4 color = texture2D(tex, v_texCoord); | |||||
gl_FragColor = vec4(0, 0, 0, 1.0 - color.r); | |||||
} | |||||
)glsl"; | |||||
const char *tileVx = R"glsl( | const char *tileVx = R"glsl( | ||||
precision mediump float; | precision mediump float; | ||||
uniform mat3 camera; | uniform mat3 camera; |
ssize_t compressedSize_ = -1; // -1 if not compressed, a positive number if compressed | ssize_t compressedSize_ = -1; // -1 if not compressed, a positive number if compressed | ||||
Cygnet::RenderChunk renderChunk_; | Cygnet::RenderChunk renderChunk_; | ||||
Cygnet::RenderChunkShadow renderChunkShadow_; | |||||
bool needChunkRender_ = true; | bool needChunkRender_ = true; | ||||
bool needLightRender_ = false; | bool needLightRender_ = false; | ||||
float deactivateTimer_ = DEACTIVATE_INTERVAL; | float deactivateTimer_ = DEACTIVATE_INTERVAL; |
} | } | ||||
rnd.destroyChunk(renderChunk_); | rnd.destroyChunk(renderChunk_); | ||||
rnd.destroyChunkShadow(renderChunkShadow_); | |||||
} | } | ||||
void Chunk::decompress() { | void Chunk::decompress() { | ||||
return; | return; | ||||
if (needChunkRender_) { | if (needChunkRender_) { | ||||
renderChunk_ = rnd.createChunk((Tile::ID *)data_.get()); | |||||
renderChunk_ = rnd.createChunk(getTileData()); | |||||
renderChunkShadow_ = rnd.createChunkShadow(getLightData()); | |||||
needChunkRender_ = false; | needChunkRender_ = false; | ||||
needLightRender_ = false; | |||||
} else { | } else { | ||||
for (auto &change: changeList_) { | for (auto &change: changeList_) { | ||||
rnd.modifyChunk(renderChunk_, change.first, change.second); | rnd.modifyChunk(renderChunk_, change.first, change.second); | ||||
} | } | ||||
} | } | ||||
rnd.drawChunk(renderChunk_, (Vec2)pos_ * Vec2{CHUNK_WIDTH, CHUNK_HEIGHT}); | |||||
if (needLightRender_) { | |||||
rnd.modifyChunkShadow(renderChunkShadow_, getLightData()); | |||||
needLightRender_ = false; | |||||
} | |||||
Vec2 pos = (Vec2)pos_ * Vec2{CHUNK_WIDTH, CHUNK_HEIGHT}; | |||||
rnd.drawChunk(renderChunk_, pos); | |||||
rnd.drawChunkShadow(renderChunkShadow_, pos); | |||||
} | } | ||||
Chunk::TickAction Chunk::tick(float dt) { | Chunk::TickAction Chunk::tick(float dt) { |