RenderChunk Renderer::createChunk( | RenderChunk Renderer::createChunk( | ||||
TileID tiles[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]) { | TileID tiles[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]) { | ||||
// TODO: Maybe don't do this here? Maybe instead store the buffer and | |||||
// upload the texture in the draw method? | |||||
// The current approach needs createChunk to be called on the graphics thread. | |||||
RenderChunk chunk; | RenderChunk chunk; | ||||
glGenTextures(1, &chunk.tex); | glGenTextures(1, &chunk.tex); | ||||
glCheck(); | glCheck(); | ||||
glBindTexture(GL_TEXTURE_2D, chunk.tex); | glBindTexture(GL_TEXTURE_2D, chunk.tex); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 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_MAG_FILTER, GL_NEAREST); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |||||
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(); | glCheck(); | ||||
static_assert( | static_assert( |
#include <string.h> | #include <string.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <memory> | #include <memory> | ||||
#include <cygnet/Renderer.h> | |||||
#include "util.h" | #include "util.h" | ||||
#include "common.h" | #include "common.h" | ||||
void setTileID(RelPos pos, Tile::ID id) { | void setTileID(RelPos pos, Tile::ID id) { | ||||
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | ||||
drawList_.push_back({pos, id}); | |||||
changeList_.emplace_back(pos, id); | |||||
isModified_ = true; | |||||
} | } | ||||
void setTileData(RelPos pos, Tile::ID id) { | void setTileData(RelPos pos, Tile::ID id) { | ||||
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | ||||
needRender_ = true; | |||||
} | } | ||||
uint8_t getLightLevel(RelPos pos) { | uint8_t getLightLevel(RelPos pos) { | ||||
needLightRender_ = true; | needLightRender_ = true; | ||||
} | } | ||||
void compress(); | |||||
void generateDone(); | |||||
void keepActive(); | |||||
void decompress(); | void decompress(); | ||||
void compress(Cygnet::Renderer &rnd); | |||||
void destroy(Cygnet::Renderer &rnd) { rnd.destroyChunk(renderChunk_); } | |||||
void draw(const Context &ctx, Cygnet::Renderer &rnd); | void draw(const Context &ctx, Cygnet::Renderer &rnd); | ||||
TickAction tick(float dt); | TickAction tick(float dt); | ||||
bool isActive() { return deactivateTimer_ > 0; } | bool isActive() { return deactivateTimer_ > 0; } | ||||
void keepActive(); | |||||
void markModified() { isModified_ = true; } | |||||
ChunkPos pos_; | ChunkPos pos_; | ||||
bool isCompressed() { return compressedSize_ != -1; } | bool isCompressed() { return compressedSize_ != -1; } | ||||
std::unique_ptr<uint8_t[]> data_; | std::unique_ptr<uint8_t[]> data_; | ||||
std::vector<std::pair<RelPos, Tile::ID>> drawList_; | |||||
std::vector<std::pair<RelPos, Tile::ID>> changeList_; | |||||
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 | ||||
bool needRender_ = false; | |||||
Cygnet::RenderChunk renderChunk_; | |||||
bool needChunkRender_ = true; | |||||
bool needLightRender_ = false; | bool needLightRender_ = false; | ||||
float deactivateTimer_ = DEACTIVATE_INTERVAL; | float deactivateTimer_ = DEACTIVATE_INTERVAL; | ||||
bool isModified_ = false; | bool isModified_ = false; |
namespace Swan { | namespace Swan { | ||||
void Chunk::compress() { | |||||
void Chunk::compress(Cygnet::Renderer &rnd) { | |||||
if (isCompressed()) | if (isCompressed()) | ||||
return; | return; | ||||
warn << "Chunk compression error: " << ret << " (Out of memory?)"; | warn << "Chunk compression error: " << ret << " (Out of memory?)"; | ||||
} | } | ||||
// TODO: Delete renderChunk_ | |||||
rnd.destroyChunk(renderChunk_); | |||||
} | } | ||||
void Chunk::decompress() { | void Chunk::decompress() { | ||||
} | } | ||||
data_ = std::move(dest); | data_ = std::move(dest); | ||||
needRender_ = true; | |||||
info | info | ||||
<< "Decompressed chunk " << pos_ << " from " | << "Decompressed chunk " << pos_ << " from " | ||||
<< DATA_SIZE << " bytes."; | << DATA_SIZE << " bytes."; | ||||
compressedSize_ = -1; | compressedSize_ = -1; | ||||
// TODO: Create renderChunk_ | |||||
needChunkRender_ = true; | |||||
} | } | ||||
void Chunk::draw(const Context &ctx, Cygnet::Renderer &rnd) { | void Chunk::draw(const Context &ctx, Cygnet::Renderer &rnd) { | ||||
if (isCompressed()) | if (isCompressed()) | ||||
return; | return; | ||||
// The world plane is responsible for managing initial renders | |||||
if (needRender_) | |||||
return; | |||||
if (drawList_.size() > 0) { | |||||
//renderList(win.renderer_); TODO | |||||
drawList_.clear(); | |||||
if (needChunkRender_) { | |||||
renderChunk_ = rnd.createChunk((Tile::ID *)data_.get()); | |||||
} else { | |||||
for (auto &change: changeList_) { | |||||
rnd.modifyChunk(renderChunk_, change.first, change.second); | |||||
} | |||||
} | } | ||||
// rnd.drawChunk(renderChunk_, (Vec2)pos_ * Vec2{CHUNK_WIDTH, CHUNK_HEIGHT}); TODO | |||||
rnd.drawChunk(renderChunk_, (Vec2)pos_ * Vec2{CHUNK_WIDTH, CHUNK_HEIGHT}); | |||||
} | } | ||||
Chunk::TickAction Chunk::tick(float dt) { | Chunk::TickAction Chunk::tick(float dt) { |
void Game::update(float dt) { | void Game::update(float dt) { | ||||
// Zoom the window using the scroll wheel | // Zoom the window using the scroll wheel | ||||
cam_.zoom += (float)wasWheelScrolled() * 0.1f * cam_.zoom; | cam_.zoom += (float)wasWheelScrolled() * 0.1f * cam_.zoom; | ||||
if (cam_.zoom > 3) | |||||
cam_.zoom = 3; | |||||
else if (cam_.zoom < 0.3) | |||||
cam_.zoom = 0.3; | |||||
if (cam_.zoom > 1) | |||||
cam_.zoom = 1; | |||||
else if (cam_.zoom < 0.05) | |||||
cam_.zoom = 0.05; | |||||
world_->update(dt); | world_->update(dt); | ||||
} | } | ||||
Chunk &chunk = slowGetChunk(pos); | Chunk &chunk = slowGetChunk(pos); | ||||
tickChunks_.push_back({ pos, &chunk }); | |||||
tickChunks_.push_back({pos, &chunk}); | |||||
return chunk; | return chunk; | ||||
} | } | ||||
lc.blocks[y * CHUNK_HEIGHT + x] = true; | lc.blocks[y * CHUNK_HEIGHT + x] = true; | ||||
} | } | ||||
if (tile.lightLevel > 0) { | if (tile.lightLevel > 0) { | ||||
lc.lightSources[{ x, y }] = tile.lightLevel; | |||||
lc.lightSources[{x, y}] = tile.lightLevel; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
Tile &newTile = world_->getTileByID(id); | Tile &newTile = world_->getTileByID(id); | ||||
Tile &oldTile = world_->getTileByID(old); | Tile &oldTile = world_->getTileByID(old); | ||||
chunk.setTileID(rp, id); | chunk.setTileID(rp, id); | ||||
chunk.markModified(); | |||||
if (!oldTile.isSolid && newTile.isSolid) { | if (!oldTile.isSolid && newTile.isSolid) { | ||||
lighting_->onSolidBlockAdded(pos); | lighting_->onSolidBlockAdded(pos); | ||||
switch (action) { | switch (action) { | ||||
case Chunk::TickAction::DEACTIVATE: | case Chunk::TickAction::DEACTIVATE: | ||||
info << "Compressing inactive modified chunk " << chunk->pos_; | info << "Compressing inactive modified chunk " << chunk->pos_; | ||||
chunk->compress(); | |||||
chunk->compress(world_->game_->renderer_); | |||||
iter = activeChunks_.erase(iter); | iter = activeChunks_.erase(iter); | ||||
last = activeChunks_.end(); | last = activeChunks_.end(); | ||||
break; | break; | ||||
case Chunk::TickAction::DELETE: | case Chunk::TickAction::DELETE: | ||||
info << "Deleting inactive unmodified chunk " << chunk->pos_; | info << "Deleting inactive unmodified chunk " << chunk->pos_; | ||||
chunk->destroy(world_->game_->renderer_); | |||||
chunks_.erase(chunk->pos_); | chunks_.erase(chunk->pos_); | ||||
iter = activeChunks_.erase(iter); | iter = activeChunks_.erase(iter); | ||||
last = activeChunks_.end(); | last = activeChunks_.end(); |