public: | public: | ||||
using RelPos = TilePos; | using RelPos = TilePos; | ||||
static constexpr size_t DATA_SIZE = | |||||
CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID) + // Tiles | |||||
CHUNK_WIDTH * CHUNK_HEIGHT; // Light levels | |||||
// What does this chunk want the world gen to do after a tick? | // What does this chunk want the world gen to do after a tick? | ||||
enum class TickAction { | enum class TickAction { | ||||
DEACTIVATE, | DEACTIVATE, | ||||
}; | }; | ||||
Chunk(ChunkPos pos): pos_(pos) { | Chunk(ChunkPos pos): pos_(pos) { | ||||
data_.reset(new uint8_t[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]); | |||||
data_.reset(new uint8_t[DATA_SIZE]); | |||||
memset(getLightData(), 255, CHUNK_WIDTH * CHUNK_HEIGHT); | |||||
} | } | ||||
Tile::ID *getTileData() { | Tile::ID *getTileData() { | ||||
return (Tile::ID *)data_.get(); | return (Tile::ID *)data_.get(); | ||||
} | } | ||||
uint8_t *getLightData() { | |||||
assert(isActive()); | |||||
return data_.get() + CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID); | |||||
} | |||||
Tile::ID getTileID(RelPos pos) { | Tile::ID getTileID(RelPos pos) { | ||||
return getTileData()[pos.y * CHUNK_WIDTH + pos.x]; | return getTileData()[pos.y * CHUNK_WIDTH + pos.x]; | ||||
} | } | ||||
need_render_ = true; | need_render_ = true; | ||||
} | } | ||||
uint8_t getLightLevel(RelPos pos) { | |||||
return getLightData()[pos.y * CHUNK_WIDTH + pos.x]; | |||||
} | |||||
void setLightLevel(RelPos pos, uint8_t level, SDL_Renderer *rnd); | |||||
void setLightData(RelPos pos, uint8_t level) { | |||||
getLightData()[pos.y * CHUNK_WIDTH + pos.x] = level; | |||||
need_render_ = true; | |||||
} | |||||
void render(const Context &ctx, SDL_Renderer *rnd); | void render(const Context &ctx, SDL_Renderer *rnd); | ||||
void compress(); | void compress(); | ||||
bool is_modified_ = false; | bool is_modified_ = false; | ||||
CPtr<SDL_Texture, SDL_DestroyTexture> texture_; | CPtr<SDL_Texture, SDL_DestroyTexture> texture_; | ||||
CPtr<SDL_Texture, SDL_DestroyTexture> light_texture_; | |||||
}; | }; | ||||
} | } |
warn << "RenderCopyEx failed: " << SDL_GetError(); | warn << "RenderCopyEx failed: " << SDL_GetError(); | ||||
} | } | ||||
void showTexture(const Vec2 &pos, SDL_Texture *tex, SDL_Rect *srcrect, | |||||
SDL_Rect *dest, ShowTextureArgs args) { | |||||
SDL_Point *center = args.center ? &*args.center : nullptr; | |||||
SDL_Rect destrect = createDestRect(pos, Vec2(dest->w * args.hscale, dest->h * args.hscale)); | |||||
if (SDL_RenderCopyEx(renderer_, tex, srcrect, &destrect, args.angle, center, args.flip) < 0) | |||||
warn << "RenderCopyEx failed: " << SDL_GetError(); | |||||
} | |||||
// We want an overload which uses RenderCopy instead of RenderCopyEx, | // We want an overload which uses RenderCopy instead of RenderCopyEx, | ||||
// because RenderCopy might be faster | // because RenderCopy might be faster | ||||
void showTexture(const Vec2 &pos, SDL_Texture *tex, SDL_Rect *srcrect) { | void showTexture(const Vec2 &pos, SDL_Texture *tex, SDL_Rect *srcrect) { | ||||
warn << "RenderCopy failed: " << SDL_GetError(); | warn << "RenderCopy failed: " << SDL_GetError(); | ||||
} | } | ||||
// Another overload without RenderCopyEx | |||||
void showTexture(const Vec2 &pos, SDL_Texture *tex, SDL_Rect *srcrect, | |||||
SDL_Rect *dest) { | |||||
SDL_Rect destrect = createDestRect(pos, Vec2(dest->w, dest->h)); | |||||
if (SDL_RenderCopy(renderer_, tex, srcrect, &destrect) < 0) | |||||
warn << "RenderCopy failed: " << SDL_GetError(); | |||||
} | |||||
void drawRect(const Vec2 &pos, const Vec2 &size) { | void drawRect(const Vec2 &pos, const Vec2 &size) { | ||||
SDL_Rect destrect = createDestRect(pos, size * TILE_SIZE); | SDL_Rect destrect = createDestRect(pos, size * TILE_SIZE); | ||||
if (SDL_RenderDrawRect(renderer_, &destrect) < 0) | if (SDL_RenderDrawRect(renderer_, &destrect) < 0) |
SDL_SetRenderDrawColor(rnd_, r, g, b, a); | SDL_SetRenderDrawColor(rnd_, r, g, b, a); | ||||
} | } | ||||
void change(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255) { | |||||
SDL_SetRenderDrawColor(rnd_, r, g, b, a); | |||||
} | |||||
~RenderDrawColor() { | ~RenderDrawColor() { | ||||
SDL_SetRenderDrawColor(rnd_, r_, g_, b_, a_); | SDL_SetRenderDrawColor(rnd_, r_, g_, b_, a_); | ||||
} | } |
uLongf destlen = sizeof(dest); | uLongf destlen = sizeof(dest); | ||||
int ret = compress2( | int ret = compress2( | ||||
(Bytef *)dest, &destlen, | (Bytef *)dest, &destlen, | ||||
(Bytef *)data_.get(), CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID), | |||||
(Bytef *)data_.get(), DATA_SIZE, | |||||
Z_BEST_COMPRESSION); | Z_BEST_COMPRESSION); | ||||
if (ret == Z_OK) { | if (ret == Z_OK) { | ||||
info | info | ||||
<< "Compressed chunk " << pos_ << " from " | << "Compressed chunk " << pos_ << " from " | ||||
<< CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID) << " bytes " | |||||
<< DATA_SIZE << " bytes " | |||||
<< "to " << destlen << " bytes"; | << "to " << destlen << " bytes"; | ||||
} else if (ret == Z_BUF_ERROR) { | } else if (ret == Z_BUF_ERROR) { | ||||
info | info | ||||
if (!isCompressed()) | if (!isCompressed()) | ||||
return; | return; | ||||
auto dest = std::make_unique<uint8_t[]>(CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)); | |||||
uLongf destlen = CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID); | |||||
auto dest = std::make_unique<uint8_t[]>(DATA_SIZE); | |||||
uLongf destlen = DATA_SIZE; | |||||
int ret = uncompress( | int ret = uncompress( | ||||
dest.get(), &destlen, | dest.get(), &destlen, | ||||
(Bytef *)data_.get(), compressed_size_); | (Bytef *)data_.get(), compressed_size_); | ||||
info | info | ||||
<< "Decompressed chunk " << pos_ << " from " | << "Decompressed chunk " << pos_ << " from " | ||||
<< compressed_size_ << " bytes to " | << compressed_size_ << " bytes to " | ||||
<< CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID) << " bytes."; | |||||
<< DATA_SIZE << " bytes."; | |||||
compressed_size_ = -1; | compressed_size_ = -1; | ||||
} | } | ||||
target.emplace(rnd, texture_.get()); | target.emplace(rnd, texture_.get()); | ||||
} | } | ||||
// Same with light texture | |||||
if (!light_texture_) { | |||||
light_texture_.reset(SDL_CreateTexture( | |||||
ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, | |||||
CHUNK_WIDTH, CHUNK_HEIGHT)); | |||||
SDL_SetTextureBlendMode(light_texture_.get(), SDL_BLENDMODE_BLEND); | |||||
} | |||||
// We're caching tiles so we don't have to world.getTileByID() every time | // We're caching tiles so we don't have to world.getTileByID() every time | ||||
Tile::ID prevID = Tile::INVALID_ID; | Tile::ID prevID = Tile::INVALID_ID; | ||||
Tile *tile = ctx.game.invalid_tile_.get(); | Tile *tile = ctx.game.invalid_tile_.get(); | ||||
// Fill tile texture | |||||
for (int y = 0; y < CHUNK_HEIGHT; ++y) { | for (int y = 0; y < CHUNK_HEIGHT; ++y) { | ||||
for (int x = 0; x < CHUNK_WIDTH; ++x) { | for (int x = 0; x < CHUNK_WIDTH; ++x) { | ||||
Tile::ID id = getTileID(RelPos(x, y)); | Tile::ID id = getTileID(RelPos(x, y)); | ||||
} | } | ||||
} | } | ||||
// Fill light texture | |||||
target.emplace(rnd, light_texture_.get()); | |||||
RenderBlendMode mode(rnd, SDL_BLENDMODE_NONE); | |||||
RenderDrawColor color(rnd, 0, 0, 0, 0); | |||||
for (int y = 0; y < CHUNK_HEIGHT; ++y) { | |||||
for (int x = 0; x < CHUNK_WIDTH; ++x) { | |||||
color.change(0, 0, 0, 255 - getLightLevel({ x, y })); | |||||
SDL_Rect rect{ x, y, 1, 1 }; | |||||
SDL_RenderFillRect(rnd, &rect); | |||||
} | |||||
} | |||||
need_render_ = false; | need_render_ = false; | ||||
} | } | ||||
draw_list_.clear(); | draw_list_.clear(); | ||||
} | } | ||||
auto chunkpos = pos_ * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT); | |||||
SDL_Rect rect{ 0, 0, CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE }; | SDL_Rect rect{ 0, 0, CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE }; | ||||
win.showTexture( | |||||
pos_ * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT), | |||||
texture_.get(), &rect); | |||||
win.showTexture(chunkpos, texture_.get(), &rect); | |||||
SDL_Rect texrect{ 0, 0, CHUNK_WIDTH, CHUNK_HEIGHT }; | |||||
win.showTexture(chunkpos, light_texture_.get(), &texrect, &rect); | |||||
} | } | ||||
Chunk::TickAction Chunk::tick(float dt) { | Chunk::TickAction Chunk::tick(float dt) { |