class TileAtlas { | class TileAtlas { | ||||
public: | public: | ||||
TileAtlas(); | TileAtlas(); | ||||
TileAtlas(TileAtlas &&); | |||||
~TileAtlas(); | ~TileAtlas(); | ||||
void addTile(size_t tileId, const void *data); | void addTile(size_t tileId, const void *data); |
state_->tilesPerLine = std::min(size / SwanCommon::TILE_SIZE, 256); | state_->tilesPerLine = std::min(size / SwanCommon::TILE_SIZE, 256); | ||||
} | } | ||||
TileAtlas::TileAtlas(TileAtlas &&) = default; | |||||
TileAtlas::~TileAtlas() = default; | TileAtlas::~TileAtlas() = default; | ||||
void TileAtlas::addTile(size_t tileId, const void *data) { | void TileAtlas::addTile(size_t tileId, const void *data) { |
namespace Cygnet::Shaders { | namespace Cygnet::Shaders { | ||||
const char *spriteVx = R"glsl( | const char *spriteVx = R"glsl( | ||||
#define TILE_SIZE 32.0 | |||||
uniform mat3 camera; | uniform mat3 camera; | ||||
uniform mat3 transform; | uniform mat3 transform; | ||||
uniform vec3 frameInfo; // frame height, frame count, frame index | uniform vec3 frameInfo; // frame height, frame count, frame index | ||||
varying vec2 v_texCoord; | varying vec2 v_texCoord; | ||||
void main() { | void main() { | ||||
// Here, I'm basically treating 1/(TILE_SIZE*16) as half the size of a "pixel". | |||||
// It's just an arbitrary small number, but it works as an offset to make sure | |||||
// neighbouring parts of the atlas don't bleed into the frame we actually | |||||
// want to draw due to (nearest neighbour) interpolation. | |||||
float pixoffset = (1.0 - vertex.y * 2.0) / (frameInfo.x * TILE_SIZE * 16.0); | |||||
v_texCoord = vec2( | v_texCoord = vec2( | ||||
vertex.x, | vertex.x, | ||||
(frameInfo.x * frameInfo.z + (frameInfo.x * vertex.y)) / | (frameInfo.x * frameInfo.z + (frameInfo.x * vertex.y)) / | ||||
(frameInfo.x * frameInfo.y)); | |||||
(frameInfo.x * frameInfo.y) + pixoffset); | |||||
vec3 pos = camera * transform * vec3(vertex, 1); | vec3 pos = camera * transform * vec3(vertex, 1); | ||||
gl_Position = vec4(pos.xy, 0, 1); | gl_Position = vec4(pos.xy, 0, 1); | ||||
vec4 tileColor = texture2D(tiles, tilePos / vec2(CHUNK_WIDTH, CHUNK_HEIGHT)); | vec4 tileColor = texture2D(tiles, tilePos / vec2(CHUNK_WIDTH, CHUNK_HEIGHT)); | ||||
float tileID = floor((tileColor.r * 256.0 + tileColor.a) * 256.0); | 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( | vec2 atlasPos = vec2( | ||||
tileID + v_tileCoord.x - tilePos.x, | |||||
floor(tileID / tileAtlasSize.x) + v_tileCoord.y - tilePos.y); | |||||
pixoffset.x + tileID + offset.x, | |||||
pixoffset.y + floor(tileID / tileAtlasSize.x) + offset.y); | |||||
gl_FragColor = texture2D(tileAtlas, atlasPos / tileAtlasSize); | gl_FragColor = texture2D(tileAtlas, atlasPos / tileAtlasSize); | ||||
} | } |
"core.mod/assets/tile/tree-trunk.png", | "core.mod/assets/tile/tree-trunk.png", | ||||
}) addTile(rbuilder, path); | }) addTile(rbuilder, path); | ||||
unsigned char lolTexture[32*32*4*3]; | |||||
unsigned char animTexture[32*32*4*3]; | |||||
for (size_t i = 0; i < 3; ++i) { | for (size_t i = 0; i < 3; ++i) { | ||||
int col = 100 * i + 50;; | int col = 100 * i + 50;; | ||||
for (size_t y = 0; y < 32; ++y) { | for (size_t y = 0; y < 32; ++y) { | ||||
for (size_t x = 0; x < 32; ++x) { | 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; | |||||
animTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 0] = col; | |||||
animTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 1] = col; | |||||
animTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 2] = col; | |||||
animTexture[i * 32 * 32 * 4 + y * 32 * 4 + x * 4 + 3] = 255; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
rbuilder.addTile(10, lolTexture, 3); | |||||
rbuilder.addTile(10, animTexture, 3); | |||||
Cygnet::RenderSprite playerSprite = loadSprite( | Cygnet::RenderSprite playerSprite = loadSprite( | ||||
rbuilder, "core.mod/assets/entity/player-still.png", 64); | rbuilder, "core.mod/assets/entity/player-still.png", 64); | ||||
tiles[0] = 1; | tiles[0] = 1; | ||||
tiles[1] = 2; | tiles[1] = 2; | ||||
tiles[2] = 3; | tiles[2] = 3; | ||||
for (int y = 0; y < 8; ++y) { | |||||
for (int x = 0; x < 8; ++x) { | |||||
tiles[y * SwanCommon::CHUNK_WIDTH + x] = 10; | |||||
} | |||||
} | |||||
tiles[10] = 10; | tiles[10] = 10; | ||||
chunk = rnd.createChunk(tiles); | chunk = rnd.createChunk(tiles); | ||||
} | } | ||||
.zoom = 1, | .zoom = 1, | ||||
}; | }; | ||||
float lol = 0; | |||||
float animAcc = 0; | |||||
bool keys[512] = { 0 }; | bool keys[512] = { 0 }; | ||||
double currTime = getTime(); | double currTime = getTime(); | ||||
double dt = currTime - prevTime; | double dt = currTime - prevTime; | ||||
prevTime = currTime; | prevTime = currTime; | ||||
lol += dt; | |||||
animAcc += dt; | |||||
fpsAcc += dt; | fpsAcc += dt; | ||||
frames += 1; | frames += 1; | ||||
rnd.drawChunk(chunk, { 0, 0 }); | rnd.drawChunk(chunk, { 0, 0 }); | ||||
rnd.drawSprite(playerSprite, { x, y }, (int)lol % 2); | |||||
cam.pos = { x, y }; | |||||
rnd.drawSprite(playerSprite, { x, y }, (int)animAcc % 2); | |||||
cam.pos = { x + 0.5f, y + 0.5f }; | |||||
win.clear(); | win.clear(); | ||||
rnd.draw(cam); | rnd.draw(cam); |