@@ -17,6 +17,10 @@ struct RenderChunk { | |||
GLuint tex; | |||
}; | |||
struct RenderChunkShadow { | |||
GLuint tex; | |||
}; | |||
struct RenderSprite { | |||
GLuint tex; | |||
SwanCommon::Vec2 scale; | |||
@@ -37,6 +41,7 @@ public: | |||
~Renderer(); | |||
void drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos); | |||
void drawChunkShadow(RenderChunkShadow shadow, SwanCommon::Vec2 pos); | |||
void drawTile(TileID id, Mat3gf mat); | |||
void drawSprite(RenderSprite sprite, Mat3gf mat, int y = 0); | |||
void drawRect(SwanCommon::Vec2 pos, SwanCommon::Vec2 size); | |||
@@ -51,6 +56,13 @@ public: | |||
void modifyChunk(RenderChunk chunk, SwanCommon::Vec2i pos, TileID id); | |||
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); | |||
void destroySprite(RenderSprite sprite); | |||
@@ -63,6 +75,11 @@ private: | |||
RenderChunk chunk; | |||
}; | |||
struct DrawShadow { | |||
SwanCommon::Vec2 pos; | |||
RenderChunkShadow shadow; | |||
}; | |||
struct DrawTile { | |||
Mat3gf transform; | |||
TileID id; | |||
@@ -84,6 +101,7 @@ private: | |||
std::unique_ptr<RendererState> state_; | |||
std::vector<DrawChunk> drawChunks_; | |||
std::vector<DrawShadow> drawChunkShadows_; | |||
std::vector<DrawTile> drawTiles_; | |||
std::vector<DrawSprite> drawSprites_; | |||
std::vector<DrawRect> drawRects_; | |||
@@ -93,6 +111,10 @@ inline void Renderer::drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos) { | |||
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) { | |||
drawTiles_.push_back({mat, id}); | |||
} |
@@ -5,6 +5,9 @@ namespace Cygnet::Shaders { | |||
extern const char *chunkVx; | |||
extern const char *chunkFr; | |||
extern const char *chunkShadowVx; | |||
extern const char *chunkShadowFr; | |||
extern const char *tileVx; | |||
extern const char *tileFr; | |||
@@ -73,6 +73,59 @@ struct ChunkProg: public GlProgram { | |||
} | |||
}; | |||
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 { | |||
template<typename... T> | |||
TileProg(const T &... shaders): GlProgram(shaders...) { init(); } | |||
@@ -231,6 +284,8 @@ struct RectProg: public GlProgram { | |||
struct RendererState { | |||
GlVxShader chunkVx{Shaders::chunkVx}; | |||
GlFrShader chunkFr{Shaders::chunkFr}; | |||
GlVxShader chunkShadowVx{Shaders::chunkShadowVx}; | |||
GlFrShader chunkShadowFr{Shaders::chunkShadowFr}; | |||
GlVxShader tileVx{Shaders::tileVx}; | |||
GlFrShader tileFr{Shaders::tileFr}; | |||
GlVxShader spriteVx{Shaders::spriteVx}; | |||
@@ -239,6 +294,7 @@ struct RendererState { | |||
GlFrShader rectFr{Shaders::rectFr}; | |||
ChunkProg chunkProg{chunkVx, chunkFr}; | |||
ChunkShadowProg chunkShadowProg{chunkShadowVx, chunkShadowFr}; | |||
TileProg tileProg{tileVx, tileFr}; | |||
SpriteProg spriteProg{spriteVx, spriteFr}; | |||
RectProg rectProg{rectVx, rectFr}; | |||
@@ -270,6 +326,7 @@ void Renderer::draw(const RenderCamera &cam) { | |||
} | |||
auto &chunkProg = state_->chunkProg; | |||
auto &chunkShadowProg = state_->chunkShadowProg; | |||
auto &tileProg = state_->tileProg; | |||
auto &spriteProg = state_->spriteProg; | |||
auto &rectProg = state_->rectProg; | |||
@@ -356,6 +413,23 @@ void Renderer::draw(const RenderCamera &cam) { | |||
drawRects_.clear(); | |||
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) { | |||
@@ -459,6 +533,47 @@ void Renderer::destroyChunk(RenderChunk chunk) { | |||
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 sprite; | |||
sprite.scale = { |
@@ -43,6 +43,35 @@ const char *chunkFr = R"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( | |||
precision mediump float; | |||
uniform mat3 camera; |
@@ -89,6 +89,7 @@ private: | |||
ssize_t compressedSize_ = -1; // -1 if not compressed, a positive number if compressed | |||
Cygnet::RenderChunk renderChunk_; | |||
Cygnet::RenderChunkShadow renderChunkShadow_; | |||
bool needChunkRender_ = true; | |||
bool needLightRender_ = false; | |||
float deactivateTimer_ = DEACTIVATE_INTERVAL; |
@@ -46,6 +46,7 @@ void Chunk::compress(Cygnet::Renderer &rnd) { | |||
} | |||
rnd.destroyChunk(renderChunk_); | |||
rnd.destroyChunkShadow(renderChunkShadow_); | |||
} | |||
void Chunk::decompress() { | |||
@@ -79,15 +80,24 @@ void Chunk::draw(const Context &ctx, Cygnet::Renderer &rnd) { | |||
return; | |||
if (needChunkRender_) { | |||
renderChunk_ = rnd.createChunk((Tile::ID *)data_.get()); | |||
renderChunk_ = rnd.createChunk(getTileData()); | |||
renderChunkShadow_ = rnd.createChunkShadow(getLightData()); | |||
needChunkRender_ = false; | |||
needLightRender_ = false; | |||
} else { | |||
for (auto &change: changeList_) { | |||
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) { |