mat.translate({-0.5, 0}).scale({-1, 1}).translate({0.5, 0}); | mat.translate({-0.5, 0}).scale({-1, 1}).translate({0.5, 0}); | ||||
} | } | ||||
anims_[(int)state_].draw(rnd, mat.translate(body_.pos)); | |||||
anims_[(int)state_].draw(rnd, mat.translate( | |||||
body_.pos - Swan::Vec2{0.2, 0.1})); | |||||
rnd.drawRect(mouseTile_, {1, 1}); | |||||
rnd.drawRect(body_.pos, body_.size); | |||||
} | } | ||||
void PlayerEntity::update(const Swan::Context &ctx, float dt) { | void PlayerEntity::update(const Swan::Context &ctx, float dt) { |
int frameCount; | int frameCount; | ||||
}; | }; | ||||
struct RenderTile { | |||||
uint16_t id; | |||||
}; | |||||
struct RenderCamera { | struct RenderCamera { | ||||
SwanCommon::Vec2 pos = {0, 0}; | SwanCommon::Vec2 pos = {0, 0}; | ||||
SwanCommon::Vec2i size = {1, 1}; | SwanCommon::Vec2i size = {1, 1}; | ||||
void drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos); | void drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos); | ||||
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 draw(const RenderCamera &cam); | void draw(const RenderCamera &cam); | ||||
RenderSprite sprite; | RenderSprite sprite; | ||||
}; | }; | ||||
struct DrawRect { | |||||
SwanCommon::Vec2 pos; | |||||
SwanCommon::Vec2 size; | |||||
}; | |||||
SwanCommon::Vec2 winScale_ = {1, 1}; | SwanCommon::Vec2 winScale_ = {1, 1}; | ||||
std::unique_ptr<RendererState> state_; | std::unique_ptr<RendererState> state_; | ||||
std::vector<DrawChunk> drawChunks_; | std::vector<DrawChunk> drawChunks_; | ||||
std::vector<DrawSprite> drawSprites_; | std::vector<DrawSprite> drawSprites_; | ||||
std::vector<DrawRect> drawRects_; | |||||
}; | }; | ||||
inline void Renderer::drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos) { | inline void Renderer::drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos) { | ||||
drawSprites_.push_back({mat, frame, sprite}); | drawSprites_.push_back({mat, frame, sprite}); | ||||
} | } | ||||
inline void Renderer::drawRect(SwanCommon::Vec2 pos, SwanCommon::Vec2 size) { | |||||
drawRects_.push_back({pos, size}); | |||||
} | |||||
} | } |
Renderer &rnd_; | Renderer &rnd_; | ||||
std::unordered_map<std::string, RenderSprite> sprites_; | std::unordered_map<std::string, RenderSprite> sprites_; | ||||
std::unordered_map<std::string, RenderTile> tiles_; | |||||
std::unordered_map<std::string, Renderer::TileID> tiles_; | |||||
std::vector<ResourceTileAnimation> tileAnims_; | std::vector<ResourceTileAnimation> tileAnims_; | ||||
}; | }; | ||||
namespace Cygnet::Shaders { | namespace Cygnet::Shaders { | ||||
extern const char *chunkVx; | |||||
extern const char *chunkFr; | |||||
extern const char *spriteVx; | extern const char *spriteVx; | ||||
extern const char *spriteFr; | extern const char *spriteFr; | ||||
extern const char *chunkVx; | |||||
extern const char *chunkFr; | |||||
extern const char *rectVx; | |||||
extern const char *rectFr; | |||||
} | } |
} | } | ||||
}; | }; | ||||
struct RectProg: public GlProgram { | |||||
template<typename... T> | |||||
RectProg(const T &... shaders): GlProgram(shaders...) { init(); } | |||||
~RectProg() { deinit(); } | |||||
GLint camera = uniformLoc("camera"); | |||||
GLint pos = uniformLoc("pos"); | |||||
GLint size = uniformLoc("size"); | |||||
GLint vertex = attribLoc("vertex"); | |||||
GLuint vbo; | |||||
static constexpr GLfloat vertexes[] = { | |||||
0.0f, 0.0f, // pos 0: top left | |||||
0.0f, 1.0f, // pos 1: bottom left | |||||
1.0f, 1.0f, // pos 2: bottom right | |||||
1.0f, 1.0f, // pos 2: bottom right | |||||
1.0f, 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(); | |||||
} | |||||
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 SpriteProg: public GlProgram { | struct SpriteProg: public GlProgram { | ||||
template<typename... T> | template<typename... T> | ||||
SpriteProg(const T &... shaders): GlProgram(shaders...) { init(); } | SpriteProg(const T &... shaders): GlProgram(shaders...) { init(); } | ||||
GlFrShader spriteFr{Shaders::spriteFr}; | GlFrShader spriteFr{Shaders::spriteFr}; | ||||
GlVxShader chunkVx{Shaders::chunkVx}; | GlVxShader chunkVx{Shaders::chunkVx}; | ||||
GlFrShader chunkFr{Shaders::chunkFr}; | GlFrShader chunkFr{Shaders::chunkFr}; | ||||
GlVxShader rectVx{Shaders::rectVx}; | |||||
GlFrShader rectFr{Shaders::rectFr}; | |||||
SpriteProg spriteProg{spriteVx, spriteFr}; | SpriteProg spriteProg{spriteVx, spriteFr}; | ||||
ChunkProg chunkProg{chunkVx, chunkFr}; | ChunkProg chunkProg{chunkVx, chunkFr}; | ||||
RectProg rectProg{rectVx, rectFr}; | |||||
GLuint atlasTex; | GLuint atlasTex; | ||||
SwanCommon::Vec2 atlasTexSize; | SwanCommon::Vec2 atlasTexSize; | ||||
auto &chunkProg = state_->chunkProg; | auto &chunkProg = state_->chunkProg; | ||||
auto &spriteProg = state_->spriteProg; | auto &spriteProg = state_->spriteProg; | ||||
auto &rectProg = state_->rectProg; | |||||
{ | { | ||||
chunkProg.enable(); | chunkProg.enable(); | ||||
drawSprites_.clear(); | drawSprites_.clear(); | ||||
spriteProg.disable(); | spriteProg.disable(); | ||||
} | } | ||||
{ | |||||
rectProg.enable(); | |||||
glUniformMatrix3fv(rectProg.camera, 1, GL_TRUE, camMat.data()); | |||||
glCheck(); | |||||
for (auto [pos, size]: drawRects_) { | |||||
glUniform2f(rectProg.pos, pos.x, pos.y); | |||||
glUniform2f(rectProg.size, size.x, size.y); | |||||
glDrawArrays(GL_TRIANGLES, 0, 6); | |||||
glCheck(); | |||||
} | |||||
drawRects_.clear(); | |||||
rectProg.disable(); | |||||
} | |||||
} | } | ||||
void Renderer::uploadTileAtlas(const void *data, int width, int height) { | void Renderer::uploadTileAtlas(const void *data, int width, int height) { |
namespace Cygnet::Shaders { | namespace Cygnet::Shaders { | ||||
const char *chunkVx = R"glsl( | |||||
uniform mat3 camera; | |||||
uniform vec2 pos; | |||||
attribute vec2 vertex; | |||||
varying vec2 v_tileCoord; | |||||
void main() { | |||||
vec3 pos = camera * vec3(pos + vertex, 1); | |||||
gl_Position = vec4(pos.xy, 0, 1); | |||||
v_tileCoord = vertex; | |||||
} | |||||
)glsl"; | |||||
const char *chunkFr = R"glsl( | |||||
precision mediump float; | |||||
#define TILE_SIZE 32.0 | |||||
#define CHUNK_WIDTH 64 | |||||
#define CHUNK_HEIGHT 64 | |||||
varying vec2 v_tileCoord; | |||||
uniform sampler2D tileAtlas; | |||||
uniform vec2 tileAtlasSize; | |||||
uniform sampler2D tiles; | |||||
void main() { | |||||
vec2 tilePos = floor(vec2(v_tileCoord.x, v_tileCoord.y)); | |||||
vec4 tileColor = texture2D(tiles, tilePos / vec2(CHUNK_WIDTH, CHUNK_HEIGHT)); | |||||
float tileID = floor((tileColor.r * 256.0 + tileColor.a) * 256.0); | |||||
// 1/(TILE_SIZE*16) plays the same role here as in the sprite vertex shader. | |||||
vec2 offset = v_tileCoord - tilePos; | |||||
vec2 pixoffset = (1.0 - offset * 2.0) / (TILE_SIZE * 16.0); | |||||
vec2 atlasPos = vec2( | |||||
pixoffset.x + tileID + offset.x, | |||||
pixoffset.y + floor(tileID / tileAtlasSize.x) + offset.y); | |||||
gl_FragColor = texture2D(tileAtlas, atlasPos / tileAtlasSize); | |||||
} | |||||
)glsl"; | |||||
const char *spriteVx = R"glsl( | const char *spriteVx = R"glsl( | ||||
#define TILE_SIZE 32.0 | #define TILE_SIZE 32.0 | ||||
} | } | ||||
)glsl"; | )glsl"; | ||||
const char *chunkVx = R"glsl( | |||||
const char *rectVx = R"glsl( | |||||
uniform mat3 camera; | uniform mat3 camera; | ||||
uniform vec2 pos; | uniform vec2 pos; | ||||
uniform vec2 size; | |||||
attribute vec2 vertex; | attribute vec2 vertex; | ||||
varying vec2 v_tileCoord; | |||||
varying vec2 v_coord; | |||||
void main() { | void main() { | ||||
vec3 pos = camera * vec3(pos + vertex, 1); | |||||
vec3 pos = camera * vec3(pos + vertex * size, 1); | |||||
gl_Position = vec4(pos.xy, 0, 1); | gl_Position = vec4(pos.xy, 0, 1); | ||||
v_tileCoord = vec2(vertex.x, vertex.y); | |||||
v_coord = vertex * size; | |||||
} | } | ||||
)glsl"; | )glsl"; | ||||
const char *chunkFr = R"glsl( | |||||
const char *rectFr = R"glsl( | |||||
precision mediump float; | precision mediump float; | ||||
#define TILE_SIZE 32.0 | |||||
#define CHUNK_WIDTH 64 | |||||
#define CHUNK_HEIGHT 64 | |||||
#define THICKNESS 0.02 | |||||
varying vec2 v_tileCoord; | |||||
uniform sampler2D tileAtlas; | |||||
uniform vec2 tileAtlasSize; | |||||
uniform sampler2D tiles; | |||||
varying vec2 v_coord; | |||||
uniform vec2 size; | |||||
void main() { | void main() { | ||||
vec2 tilePos = floor(vec2(v_tileCoord.x, v_tileCoord.y)); | |||||
vec4 tileColor = texture2D(tiles, tilePos / vec2(CHUNK_WIDTH, CHUNK_HEIGHT)); | |||||
float tileID = floor((tileColor.r * 256.0 + tileColor.a) * 256.0); | |||||
// 1/(TILE_SIZE*16) plays the same role here as in the sprite vertex shader. | |||||
vec2 offset = v_tileCoord - tilePos; | |||||
vec2 pixoffset = (1.0 - offset * 2.0) / (TILE_SIZE * 16.0); | |||||
vec2 atlasPos = vec2( | |||||
pixoffset.x + tileID + offset.x, | |||||
pixoffset.y + floor(tileID / tileAtlasSize.x) + offset.y); | |||||
gl_FragColor = texture2D(tileAtlas, atlasPos / tileAtlasSize); | |||||
// TODO: This probably shouldn't be an if? | |||||
if ( | |||||
v_coord.x < THICKNESS || v_coord.x > size.x - THICKNESS || | |||||
v_coord.y < THICKNESS || v_coord.y > size.y - THICKNESS) { | |||||
gl_FragColor = vec4(0.6, 0.6, 0.6, 0.8); | |||||
} else { | |||||
gl_FragColor = vec4(0, 0, 0, 0); | |||||
} | |||||
} | } | ||||
)glsl"; | )glsl"; | ||||
for (auto &plane: planes_) | for (auto &plane: planes_) | ||||
plane->update(dt); | plane->update(dt); | ||||
game_->cam_.pos = player_->pos + Vec2{0.5, 0.5}; | |||||
game_->cam_.pos = player_->pos + player_->size / 2; | |||||
} | } | ||||
void World::tick(float dt) { | void World::tick(float dt) { |