Browse Source

make style consistent

fix/style
Martin Dørum 3 years ago
parent
commit
0db704582d

+ 6
- 5
conventions.md View File



``` C++ ``` C++
void someLongFunctionName( void someLongFunctionName(
int first_argument,
int second_argument) {
int firstArgument,
int secondArgument) {
whatever; whatever;
} }
``` ```


* Classes and structs are PascalCase. * Classes and structs are PascalCase.
* Methods and free functions are camelCase. * Methods and free functions are camelCase.
* Local variables are snake_case.
* Class member variables are snake_case_ (with the trailing underscore).
* Struct member variables are snake_case (without the trailing underscore).
* Local variables are camelCase.
* Class member variables are camelCase\_ (with the trailing underscore).
* Struct member variables are camelCase (without the trailing underscore).
* Constants are UPPER\_SNAKE\_CASE (no trailing underscore) everywhere.


## Structure ## Structure



+ 9
- 9
core.mod/src/DefaultWorldGen.cc View File



#include "entities/PlayerEntity.h" #include "entities/PlayerEntity.h"


static int grassLevel(const siv::PerlinNoise &perlin, int x) {
static int getGrassLevel(const siv::PerlinNoise &perlin, int x) {
return (int)(perlin.noise(x / 50.0, 0) * 13); return (int)(perlin.noise(x / 50.0, 0) * 13);
} }


static int stoneLevel(const siv::PerlinNoise &perlin, int x) {
static int getStoneLevel(const siv::PerlinNoise &perlin, int x) {
return (int)(perlin.noise(x / 50.0, 10) * 10) + 10; return (int)(perlin.noise(x / 50.0, 10) * 10) + 10;
} }


} }


Swan::Tile::ID DefaultWorldGen::genTile(Swan::TilePos pos) { Swan::Tile::ID DefaultWorldGen::genTile(Swan::TilePos pos) {
int grass_level = grassLevel(perlin_, pos.x);
int stone_level = stoneLevel(perlin_, pos.x);
int grassLevel = getGrassLevel(perlin_, pos.x);
int stoneLevel = getStoneLevel(perlin_, pos.x);


// Caves // Caves
if (pos.y > grass_level + 7 && perlin_.noise(pos.x / 43.37, pos.y / 16.37) > 0.2)
if (pos.y > grassLevel + 7 && perlin_.noise(pos.x / 43.37, pos.y / 16.37) > 0.2)
return tAir_; return tAir_;


if (pos.y > stone_level)
if (pos.y > stoneLevel)
return tStone_; return tStone_;
else if (pos.y > grass_level)
else if (pos.y > grassLevel)
return tDirt_; return tDirt_;
else if (pos.y == grass_level)
else if (pos.y == grassLevel)
return tGrass_; return tGrass_;
else else
return tAir_; return tAir_;
Swan::EntityRef DefaultWorldGen::spawnPlayer(const Swan::Context &ctx) { Swan::EntityRef DefaultWorldGen::spawnPlayer(const Swan::Context &ctx) {
int x = 0; int x = 0;
return ctx.plane.spawnEntity<PlayerEntity>( return ctx.plane.spawnEntity<PlayerEntity>(
ctx, Swan::Vec2{ (float)x, (float)grassLevel(perlin_, x) - 4 });
ctx, Swan::Vec2{ (float)x, (float)getGrassLevel(perlin_, x) - 4 });
} }

+ 2
- 2
core.mod/src/entities/ItemStackEntity.cc View File

} }


void ItemStackEntity::tick(const Swan::Context &ctx, float dt) { void ItemStackEntity::tick(const Swan::Context &ctx, float dt) {
despawn_timer_ -= dt;
if (despawn_timer_ <= 0)
despawnTimer_ -= dt;
if (despawnTimer_ <= 0)
despawn(ctx); despawn(ctx);
} }



+ 1
- 1
core.mod/src/entities/ItemStackEntity.h View File



ItemStackEntity(): PhysicsEntity(SIZE) {} ItemStackEntity(): PhysicsEntity(SIZE) {}


float despawn_timer_ = DESPAWN_TIME;
float despawnTimer_ = DESPAWN_TIME;
Swan::Item *item_ = NULL; Swan::Item *item_ = NULL;
}; };

+ 17
- 17
core.mod/src/entities/PlayerEntity.cc View File

State oldState = state_; State oldState = state_;
state_ = State::IDLE; state_ = State::IDLE;


mouse_tile_ = ctx.game.getMouseTile();
ctx.plane.debugBox(mouse_tile_);
jump_timer_.tick(dt);
place_timer_.tick(dt);
mouseTile_ = ctx.game.getMouseTile();
ctx.plane.debugBox(mouseTile_);
jumpTimer_.tick(dt);
placeTimer_.tick(dt);


// Break block // Break block
if (ctx.game.isMousePressed(SDL_BUTTON_LEFT)) if (ctx.game.isMousePressed(SDL_BUTTON_LEFT))
ctx.plane.breakTile(mouse_tile_);
ctx.plane.breakTile(mouseTile_);


// Place block // Place block
if (ctx.game.isMousePressed(SDL_BUTTON_RIGHT) && place_timer_.periodic(0.50)) {
if (ctx.plane.getTileID(mouse_tile_) == ctx.world.getTileID("@::air")) {
ctx.plane.setTile(mouse_tile_, "core::torch");
if (ctx.game.isMousePressed(SDL_BUTTON_RIGHT) && placeTimer_.periodic(0.50)) {
if (ctx.plane.getTileID(mouseTile_) == ctx.world.getTileID("@::air")) {
ctx.plane.setTile(mouseTile_, "core::torch");
} }
} }


state_ = State::RUNNING_R; state_ = State::RUNNING_R;
} }


bool jump_pressed = ctx.game.isKeyPressed(SDL_SCANCODE_SPACE);
bool jumpPressed = ctx.game.isKeyPressed(SDL_SCANCODE_SPACE);


// Jump // Jump
if (physics_.on_ground && jump_pressed && jump_timer_.periodic(0.5)) {
if (physics_.onGround && jumpPressed && jumpTimer_.periodic(0.5)) {
physics_.vel.y = -JUMP_VEL; physics_.vel.y = -JUMP_VEL;
} }


// Fall down faster than we went up // Fall down faster than we went up
if (!physics_.on_ground && (!jump_pressed || physics_.vel.y > 0))
if (!physics_.on_ground && (!jumpPressed || physics_.vel.y > 0))
physics_.force += Swan::Vec2(0, DOWN_FORCE); physics_.force += Swan::Vec2(0, DOWN_FORCE);


if (state_ != oldState) if (state_ != oldState)
// Do this after moving so that it's not behind // Do this after moving so that it's not behind
Swan::Vec2 headPos = body_.topMid() + Swan::Vec2(0, 0.5); Swan::Vec2 headPos = body_.topMid() + Swan::Vec2(0, 0.5);
Swan::TilePos tilePos = Swan::Vec2i(floor(headPos.x), floor(headPos.y)); Swan::TilePos tilePos = Swan::Vec2i(floor(headPos.x), floor(headPos.y));
if (!placed_light_) {
if (!placedLight_) {
ctx.plane.addLight(tilePos, LIGHT_LEVEL); ctx.plane.addLight(tilePos, LIGHT_LEVEL);
placed_light_ = true;
light_tile_ = tilePos;
} else if (tilePos != light_tile_) {
ctx.plane.removeLight(light_tile_, LIGHT_LEVEL);
placedLight_ = true;
lightTile_ = tilePos;
} else if (tilePos != lightTile_) {
ctx.plane.removeLight(lightTile_, LIGHT_LEVEL);
ctx.plane.addLight(tilePos, LIGHT_LEVEL); ctx.plane.addLight(tilePos, LIGHT_LEVEL);
light_tile_ = tilePos;
lightTile_ = tilePos;
} }
} }



+ 5
- 5
core.mod/src/entities/PlayerEntity.h View File

State state_ = State::IDLE; State state_ = State::IDLE;
std::array<Swan::Animation, (int)State::COUNT> anims_; std::array<Swan::Animation, (int)State::COUNT> anims_;


Swan::Clock jump_timer_;
Swan::Clock place_timer_;
Swan::TilePos mouse_tile_;
Swan::Clock jumpTimer_;
Swan::Clock placeTimer_;
Swan::TilePos mouseTile_;


Swan::TilePos light_tile_;
bool placed_light_ = false;
Swan::TilePos lightTile_;
bool placedLight_ = false;


BasicInventory inventory_{INVENTORY_SIZE}; BasicInventory inventory_{INVENTORY_SIZE};
}; };

+ 4
- 4
core.mod/src/main.cc View File

class CoreMod: public Swan::Mod { class CoreMod: public Swan::Mod {
public: public:
CoreMod(Swan::World &world): Swan::Mod("core") { CoreMod(Swan::World &world): Swan::Mod("core") {
break_listener_ = world.evt_tile_break_.subscribe(
breakListener_ = world.evtTileBreak_.subscribe(
std::bind_front(&CoreMod::onTileBreak, this)); std::bind_front(&CoreMod::onTileBreak, this));


registerImage("tile/stone"); registerImage("tile/stone");
} }


void onTileBreak(const Swan::Context &ctx, Swan::TilePos pos, Swan::Tile &tile) { void onTileBreak(const Swan::Context &ctx, Swan::TilePos pos, Swan::Tile &tile) {
if (tile.dropped_item_) {
if (tile.droppedItem_) {
ctx.plane.spawnEntity<ItemStackEntity>( ctx.plane.spawnEntity<ItemStackEntity>(
ctx, (Swan::Vec2)pos + Swan::Vec2{0.5, 0.5}, *tile.dropped_item_);
ctx, (Swan::Vec2)pos + Swan::Vec2{0.5, 0.5}, *tile.droppedItem_);
} }
} }


Swan::EventListener break_listener_;
Swan::EventListener breakListener_;
}; };


extern "C" Swan::Mod *mod_create(Swan::World &world) { extern "C" Swan::Mod *mod_create(Swan::World &world) {

+ 6
- 6
libcygnet/include/cygnet/Renderer.h View File



std::unique_ptr<RendererState> state_; std::unique_ptr<RendererState> state_;


std::vector<DrawChunk> draw_chunks_;
std::vector<DrawSprite> draw_sprites_;
std::vector<DrawChunk> drawChunks_;
std::vector<DrawSprite> drawSprites_;
}; };


inline void Renderer::drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos) { inline void Renderer::drawChunk(RenderChunk chunk, SwanCommon::Vec2 pos) {
draw_chunks_.emplace_back(pos, chunk);
drawChunks_.emplace_back(pos, chunk);
} }


inline void Renderer::drawSprite(RenderSprite sprite, Mat3gf mat, int frame) { inline void Renderer::drawSprite(RenderSprite sprite, Mat3gf mat, int frame) {
draw_sprites_.emplace_back(mat, frame, sprite);
drawSprites_.emplace_back(mat, frame, sprite);
} }


inline void Renderer::drawSprite(RenderSprite sprite, SwanCommon::Vec2 pos, int frame) { inline void Renderer::drawSprite(RenderSprite sprite, SwanCommon::Vec2 pos, int frame) {
draw_sprites_.emplace_back(Mat3gf{}.translate(pos), frame, sprite);
drawSprites_.emplace_back(Mat3gf{}.translate(pos), frame, sprite);
} }


inline void Renderer::drawSpriteFlipped(RenderSprite sprite, SwanCommon::Vec2 pos, int frame) { inline void Renderer::drawSpriteFlipped(RenderSprite sprite, SwanCommon::Vec2 pos, int frame) {
draw_sprites_.emplace_back(Mat3gf{}.translate(pos).scale({ -1, 1 }), frame, sprite);
drawSprites_.emplace_back(Mat3gf{}.translate(pos).scale({ -1, 1 }), frame, sprite);
} }


} }

+ 3
- 3
libcygnet/include/cygnet/ResourceManager.h View File

private: private:
Renderer &rnd_; Renderer &rnd_;
std::unordered_map<std::string, RenderSprite> sprites_; std::unordered_map<std::string, RenderSprite> sprites_;
std::vector<ResourceTileAnimation> tile_anims_;
std::vector<ResourceTileAnimation> tileAnims_;
TileAtlas atlas_; TileAtlas atlas_;


friend ResourceManager; friend ResourceManager;
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, RenderTile> tiles_;
std::vector<ResourceTileAnimation> tile_anims_;
std::vector<ResourceTileAnimation> tileAnims_;
}; };


inline RenderSprite ResourceBuilder::addSprite( inline RenderSprite ResourceBuilder::addSprite(
inline void ResourceBuilder::addTile(Renderer::TileID id, std::unique_ptr<unsigned char[]> data, int frames) { inline void ResourceBuilder::addTile(Renderer::TileID id, std::unique_ptr<unsigned char[]> data, int frames) {
atlas_.addTile(id, data.get()); atlas_.addTile(id, data.get());
if (frames > 1) { if (frames > 1) {
tile_anims_.push_back({
tileAnims_.push_back({
.id = id, .id = id,
.frames = frames, .frames = frames,
.index = 0, .index = 0,

+ 4
- 4
libcygnet/src/Renderer.cc View File

glCheck(); glCheck();


glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
for (auto [pos, chunk]: draw_chunks_) {
for (auto [pos, chunk]: drawChunks_) {
glUniform2f(chunkProg.pos, pos.x, pos.y); glUniform2f(chunkProg.pos, pos.x, pos.y);
glBindTexture(GL_TEXTURE_2D, chunk.tex); glBindTexture(GL_TEXTURE_2D, chunk.tex);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
glCheck(); glCheck();
} }


draw_chunks_.clear();
drawChunks_.clear();
chunkProg.disable(); chunkProg.disable();
} }


glCheck(); glCheck();


glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
for (auto [mat, frame, sprite]: draw_sprites_) {
for (auto [mat, frame, sprite]: drawSprites_) {
mat.scale(sprite.scale); mat.scale(sprite.scale);
glUniformMatrix3fv(spriteProg.transform, 1, GL_TRUE, mat.data()); glUniformMatrix3fv(spriteProg.transform, 1, GL_TRUE, mat.data());
glUniform3f(spriteProg.frameInfo, sprite.scale.y, sprite.frameCount, frame); glUniform3f(spriteProg.frameInfo, sprite.scale.y, sprite.frameCount, frame);
glCheck(); glCheck();
} }


draw_sprites_.clear();
drawSprites_.clear();
spriteProg.disable(); spriteProg.disable();
} }
} }

+ 2
- 2
libcygnet/src/ResourceManager.cc View File



ResourceManager::ResourceManager(ResourceBuilder &&builder): ResourceManager::ResourceManager(ResourceBuilder &&builder):
rnd_(builder.rnd_), sprites_(std::move(builder.sprites_)), rnd_(builder.rnd_), sprites_(std::move(builder.sprites_)),
tile_anims_(std::move(builder.tile_anims_)) {
tileAnims_(std::move(builder.tileAnims_)) {
size_t width, height; size_t width, height;
const unsigned char *data = builder.atlas_.getImage(&width, &height); const unsigned char *data = builder.atlas_.getImage(&width, &height);
rnd_.uploadTileAtlas(data, width, height); rnd_.uploadTileAtlas(data, width, height);


void ResourceManager::tick() { void ResourceManager::tick() {
// TODO: Maybe do a GPU->GPU copy instead of an upload from the CPU? // TODO: Maybe do a GPU->GPU copy instead of an upload from the CPU?
for (auto &anim: tile_anims_) {
for (auto &anim: tileAnims_) {
anim.index = (anim.index + 1) % anim.frames; anim.index = (anim.index + 1) % anim.frames;
unsigned char *data = anim.data.get() + unsigned char *data = anim.data.get() +
SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE * 4 * anim.index; SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE * 4 * anim.index;

+ 13
- 13
libswan/include/swan/Chunk.h View File



void setTileID(RelPos pos, Tile::ID id, SDL_Texture *tex) { void setTileID(RelPos pos, Tile::ID id, SDL_Texture *tex) {
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id;
draw_list_.push_back({ pos, tex });
drawList_.push_back({ pos, tex });
} }


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;
need_render_ = true;
needRender_ = true;
} }


uint8_t getLightLevel(RelPos pos) { uint8_t getLightLevel(RelPos pos) {


void setLightData(const uint8_t *data) { void setLightData(const uint8_t *data) {
memcpy(getLightData(), data, CHUNK_WIDTH * CHUNK_HEIGHT); memcpy(getLightData(), data, CHUNK_WIDTH * CHUNK_HEIGHT);
need_light_render_ = true;
needLightRender_ = true;
} }


void render(const Context &ctx, SDL_Renderer *rnd); void render(const Context &ctx, SDL_Renderer *rnd);
void draw(const Context &ctx, Win &win); void draw(const Context &ctx, Win &win);
TickAction tick(float dt); TickAction tick(float dt);


bool isActive() { return deactivate_timer_ > 0; }
bool isActive() { return deactivateTimer_ > 0; }
void keepActive(); void keepActive();
void markModified() { is_modified_ = true; }
void markModified() { isModified_ = true; }


ChunkPos pos_; ChunkPos pos_;




void renderList(SDL_Renderer *rnd); void renderList(SDL_Renderer *rnd);


bool isCompressed() { return compressed_size_ != -1; }
bool isCompressed() { return compressedSize_ != -1; }


std::unique_ptr<uint8_t[]> data_; std::unique_ptr<uint8_t[]> data_;
std::vector<std::pair<RelPos, SDL_Texture *>> draw_list_;
std::vector<std::pair<RelPos, SDL_Texture *>> drawList_;


ssize_t compressed_size_ = -1; // -1 if not compressed, a positive number if compressed
bool need_render_ = false;
bool need_light_render_ = false;
float deactivate_timer_ = DEACTIVATE_INTERVAL;
bool is_modified_ = false;
ssize_t compressedSize_ = -1; // -1 if not compressed, a positive number if compressed
bool needRender_ = false;
bool needLightRender_ = false;
float deactivateTimer_ = DEACTIVATE_INTERVAL;
bool isModified_ = false;


CPtr<SDL_Texture, SDL_DestroyTexture> texture_; CPtr<SDL_Texture, SDL_DestroyTexture> texture_;
CPtr<SDL_Texture, SDL_DestroyTexture> light_texture_;
CPtr<SDL_Texture, SDL_DestroyTexture> lightTexture_;
}; };


} }

+ 1
- 1
libswan/include/swan/Collection.h View File



template<typename Ent> template<typename Ent>
inline Entity *EntityCollectionImpl<Ent>::get(size_t idx, size_t generation) { inline Entity *EntityCollectionImpl<Ent>::get(size_t idx, size_t generation) {
if (idx >=entities_.size())
if (idx >= entities_.size())
return nullptr; return nullptr;


auto &e = entities_[idx]; auto &e = entities_[idx];

+ 32
- 32
libswan/include/swan/Game.h View File

public: public:
Game(Win &win): Game(Win &win):
win_(win), win_(win),
mouse_pos_(0, 0) {}
mousePos_(0, 0) {}


std::optional<ModWrapper> loadMod(std::string path, World &world); std::optional<ModWrapper> loadMod(std::string path, World &world);
void createWorld(const std::string &worldgen, const std::vector<std::string> &mods); void createWorld(const std::string &worldgen, const std::vector<std::string> &mods);


void onKeyDown(SDL_Keysym sym) { void onKeyDown(SDL_Keysym sym) {
pressed_keys_[sym.scancode] = true;
did_press_keys_[sym.scancode] = true;
pressedKeys_[sym.scancode] = true;
didPressKeys_[sym.scancode] = true;
} }


void onKeyUp(SDL_Keysym sym) { void onKeyUp(SDL_Keysym sym) {
pressed_keys_[sym.scancode] = false;
did_release_keys_[sym.scancode] = true;
pressedKeys_[sym.scancode] = false;
didReleaseKeys_[sym.scancode] = true;
} }


void onMouseMove(Sint32 x, Sint32 y) { void onMouseMove(Sint32 x, Sint32 y) {
mouse_pos_ = { x, y };
mousePos_ = { x, y };
} }


void onMouseDown(Sint32 x, Sint32 y, Uint8 button) { void onMouseDown(Sint32 x, Sint32 y, Uint8 button) {
mouse_pos_ = { x, y };
pressed_buttons_[button] = true;
did_press_buttons_[button] = true;
mousePos_ = { x, y };
pressedButtons_[button] = true;
didPressButtons_[button] = true;


} }
void onMouseUp(Sint32 x, Sint32 y, Uint8 button) { void onMouseUp(Sint32 x, Sint32 y, Uint8 button) {
mouse_pos_ = { x, y };
pressed_buttons_[button] = false;
did_release_buttons_[button] = true;
mousePos_ = { x, y };
pressedButtons_[button] = false;
didReleaseButtons_[button] = true;
} }


void onScrollWheel(Sint32 y) { void onScrollWheel(Sint32 y) {
did_scroll_ = (y > 0 ? 1 : -1 );
didScroll_ = (y > 0 ? 1 : -1 );
} }


bool isKeyPressed(SDL_Scancode code) { return pressed_keys_[code]; }
bool wasKeyPressed(SDL_Scancode code) { return did_press_keys_[code]; }
bool wasKeyReleased(SDL_Scancode code) { return did_release_keys_[code]; }
Vec2i getMousePos() { return mouse_pos_; }
bool isMousePressed(Uint8 button) { return pressed_buttons_[button]; }
bool wasMousePressed(Uint8 button) { return did_press_buttons_[button]; }
bool wasMouseReleased(Uint8 button) { return did_release_buttons_[button]; }
int wasWheelScrolled() { return did_scroll_; }
bool isKeyPressed(SDL_Scancode code) { return pressedKeys_[code]; }
bool wasKeyPressed(SDL_Scancode code) { return didPressKeys_[code]; }
bool wasKeyReleased(SDL_Scancode code) { return didReleaseKeys_[code]; }
Vec2i getMousePos() { return mousePos_; }
bool isMousePressed(Uint8 button) { return pressedButtons_[button]; }
bool wasMousePressed(Uint8 button) { return didPressButtons_[button]; }
bool wasMouseReleased(Uint8 button) { return didReleaseButtons_[button]; }
int wasWheelScrolled() { return didScroll_; }


TilePos getMouseTile(); TilePos getMouseTile();


void tick(float dt); void tick(float dt);


std::unique_ptr<World> world_ = NULL; std::unique_ptr<World> world_ = NULL;
std::unique_ptr<ImageResource> invalid_image_ = NULL;
std::unique_ptr<Tile> invalid_tile_ = NULL;
std::unique_ptr<Item> invalid_item_ = NULL;
std::unique_ptr<ImageResource> invalidImage_ = NULL;
std::unique_ptr<Tile> invalidTile_ = NULL;
std::unique_ptr<Item> invalidItem_ = NULL;
Win &win_; Win &win_;


private: private:
std::bitset<SDL_NUM_SCANCODES> pressed_keys_;
std::bitset<SDL_NUM_SCANCODES> did_press_keys_;
std::bitset<SDL_NUM_SCANCODES> did_release_keys_;
std::bitset<SDL_NUM_SCANCODES> pressedKeys_;
std::bitset<SDL_NUM_SCANCODES> didPressKeys_;
std::bitset<SDL_NUM_SCANCODES> didReleaseKeys_;


Vec2i mouse_pos_;
std::bitset<SDL_BUTTON_X2> pressed_buttons_;
std::bitset<SDL_BUTTON_X2> did_press_buttons_;
std::bitset<SDL_BUTTON_X2> did_release_buttons_;
Vec2i mousePos_;
std::bitset<SDL_BUTTON_X2> pressedButtons_;
std::bitset<SDL_BUTTON_X2> didPressButtons_;
std::bitset<SDL_BUTTON_X2> didReleaseButtons_;


int did_scroll_ = 0;
int didScroll_ = 0;
}; };


} }

+ 4
- 4
libswan/include/swan/Item.h View File

struct Builder { struct Builder {
std::string name; std::string name;
std::string image; std::string image;
int max_stack = 64;
int maxStack = 64;
}; };


Item(const ResourceManager &resources, const Builder &builder): Item(const ResourceManager &resources, const Builder &builder):
name_(builder.name), image_(resources.getImage(builder.image)), name_(builder.name), image_(resources.getImage(builder.image)),
max_stack_(builder.max_stack) {}
maxStack_(builder.maxStack) {}


const std::string name_; const std::string name_;
const ImageResource &image_; const ImageResource &image_;
const int max_stack_;
const int maxStack_;


static std::unique_ptr<Item> createInvalid(Context &ctx); static std::unique_ptr<Item> createInvalid(Context &ctx);


protected: protected:
Item(const Builder &builder): Item(const Builder &builder):
name_(builder.name), image_(*(ImageResource *)this), name_(builder.name), image_(*(ImageResource *)this),
max_stack_(builder.max_stack) {}
maxStack_(builder.maxStack) {}
}; };


} }

+ 13
- 13
libswan/include/swan/LightServer.h View File



struct NewLightChunk { struct NewLightChunk {
std::bitset<CHUNK_WIDTH * CHUNK_HEIGHT> blocks; std::bitset<CHUNK_WIDTH * CHUNK_HEIGHT> blocks;
std::map<std::pair<int, int>, float> light_sources;
std::map<std::pair<int, int>, float> lightSources;
}; };


struct LightChunk { struct LightChunk {
LightChunk(NewLightChunk &&ch); LightChunk(NewLightChunk &&ch);


std::bitset<CHUNK_WIDTH * CHUNK_HEIGHT> blocks; std::bitset<CHUNK_WIDTH * CHUNK_HEIGHT> blocks;
uint8_t light_levels[CHUNK_WIDTH * CHUNK_HEIGHT] = { 0 };
float light_buffers[CHUNK_WIDTH * CHUNK_HEIGHT * 2] = { 0 };
uint8_t lightLevels[CHUNK_WIDTH * CHUNK_HEIGHT] = { 0 };
float lightBuffers[CHUNK_WIDTH * CHUNK_HEIGHT * 2] = { 0 };
int buffer = 0; int buffer = 0;
uint8_t blocks_line[CHUNK_WIDTH] = { 0 };
std::map<std::pair<int, int>, float> light_sources;
uint8_t blocksLine[CHUNK_WIDTH] = { 0 };
std::map<std::pair<int, int>, float> lightSources;
std::vector<std::pair<TilePos, float>> bounces; std::vector<std::pair<TilePos, float>> bounces;


float *light_buffer() { return light_buffers + CHUNK_WIDTH * CHUNK_HEIGHT * buffer; }
float *lightBuffer() { return lightBuffers + CHUNK_WIDTH * CHUNK_HEIGHT * buffer; }


bool was_updated = false;
bool wasUpdated = false;
}; };


class LightCallback { class LightCallback {
LightCallback &cb_; LightCallback &cb_;
bool running_ = true; bool running_ = true;
std::map<std::pair<int, int>, LightChunk> chunks_; std::map<std::pair<int, int>, LightChunk> chunks_;
std::set<std::pair<int, int>> updated_chunks_;
LightChunk *cached_chunk_ = nullptr;
Vec2i cached_chunk_pos_;
std::set<std::pair<int, int>> updatedChunks_;
LightChunk *cachedChunk_ = nullptr;
Vec2i cachedChunkPos_;


int buffer_ = 0; int buffer_ = 0;
std::vector<Event> buffers_[2] = { {}, {} }; std::vector<Event> buffers_[2] = { {}, {} };
std::vector<NewLightChunk> new_chunk_buffers_[2] = { {}, {} };
std::vector<NewLightChunk> newChunkBuffers_[2] = { {}, {} };
std::thread thread_; std::thread thread_;
std::condition_variable cond_; std::condition_variable cond_;
std::mutex mut_; std::mutex mut_;
inline void LightServer::onChunkAdded(Vec2i pos, NewLightChunk &&chunk) { inline void LightServer::onChunkAdded(Vec2i pos, NewLightChunk &&chunk) {
std::lock_guard<std::mutex> lock(mut_); std::lock_guard<std::mutex> lock(mut_);
buffers_[buffer_].push_back({ Event::Tag::CHUNK_ADDED, pos, buffers_[buffer_].push_back({ Event::Tag::CHUNK_ADDED, pos,
{ .i = (int)new_chunk_buffers_[buffer_].size() } });
new_chunk_buffers_[buffer_].push_back(std::move(chunk));
{ .i = (int)newChunkBuffers_[buffer_].size() } });
newChunkBuffers_[buffer_].push_back(std::move(chunk));
cond_.notify_one(); cond_.notify_one();
} }



+ 8
- 8
libswan/include/swan/Tile.h View File

struct Builder { struct Builder {
std::string name; std::string name;
std::string image; std::string image;
bool is_solid = true;
float light_level = 0;
std::optional<std::string> dropped_item = std::nullopt;
bool isSolid = true;
float lightLevel = 0;
std::optional<std::string> droppedItem = std::nullopt;
}; };


Tile(const ResourceManager &resources, const Builder &builder): Tile(const ResourceManager &resources, const Builder &builder):
name_(builder.name), image_(resources.getImage(builder.image)), name_(builder.name), image_(resources.getImage(builder.image)),
is_solid_(builder.is_solid), light_level_(builder.light_level),
dropped_item_(builder.dropped_item) {}
isSolid_(builder.isSolid), lightLevel_(builder.lightLevel),
droppedItem_(builder.droppedItem) {}


const std::string name_; const std::string name_;
const ImageResource &image_; const ImageResource &image_;
const bool is_solid_;
const float light_level_;
const std::optional<std::string> dropped_item_;
const bool isSolid_;
const float lightLevel_;
const std::optional<std::string> droppedItem_;


static std::unique_ptr<Tile> createInvalid(const ResourceManager &ctx); static std::unique_ptr<Tile> createInvalid(const ResourceManager &ctx);
static std::unique_ptr<Tile> createAir(const ResourceManager &ctx); static std::unique_ptr<Tile> createAir(const ResourceManager &ctx);

+ 8
- 8
libswan/include/swan/World.h View File



void setCurrentPlane(WorldPlane &plane); void setCurrentPlane(WorldPlane &plane);
WorldPlane &addPlane(const std::string &gen); WorldPlane &addPlane(const std::string &gen);
WorldPlane &addPlane() { return addPlane(default_world_gen_); }
WorldPlane &addPlane() { return addPlane(defaultWorldGen_); }


Tile &getTileByID(Tile::ID id) { return *tiles_[id]; } Tile &getTileByID(Tile::ID id) { return *tiles_[id]; }
Tile::ID getTileID(const std::string &name); Tile::ID getTileID(const std::string &name);


// Event emitters // Event emitters
EventEmitter<const Context &, TilePos, Tile &> EventEmitter<const Context &, TilePos, Tile &>
evt_tile_break_;
evtTileBreak_;


// World owns all mods // World owns all mods
std::vector<ModWrapper> mods_; std::vector<ModWrapper> mods_;


// World owns tiles and items, the mod just has Builder objects // World owns tiles and items, the mod just has Builder objects
std::vector<std::unique_ptr<Tile>> tiles_; std::vector<std::unique_ptr<Tile>> tiles_;
std::unordered_map<std::string, Tile::ID> tiles_map_;
std::unordered_map<std::string, Tile::ID> tilesMap_;
std::unordered_map<std::string, std::unique_ptr<Item>> items_; std::unordered_map<std::string, std::unique_ptr<Item>> items_;


// Mods give us factories to create new world gens and new entity collections // Mods give us factories to create new world gens and new entity collections
std::unordered_map<std::string, WorldGen::Factory> worldgen_factories_;
std::vector<EntityCollection::Factory> ent_coll_factories_;
std::unordered_map<std::string, WorldGen::Factory> worldgenFactories_;
std::vector<EntityCollection::Factory> entCollFactories_;


BodyTrait::Body *player_; BodyTrait::Body *player_;
Game *game_; Game *game_;
void tick(WorldPlane &plane, ChunkPos abspos); void tick(WorldPlane &plane, ChunkPos abspos);
}; };


ChunkRenderer chunk_renderer_;
WorldPlane::ID current_plane_;
ChunkRenderer chunkRenderer_;
WorldPlane::ID currentPlane_;
std::vector<std::unique_ptr<WorldPlane>> planes_; std::vector<std::unique_ptr<WorldPlane>> planes_;
std::string default_world_gen_;
std::string defaultWorldGen_;
}; };


} }

+ 11
- 11
libswan/include/swan/WorldPlane.h View File

std::unique_ptr<LightServer> lighting_; std::unique_ptr<LightServer> lighting_;


std::map<std::pair<int, int>, Chunk> chunks_; std::map<std::pair<int, int>, Chunk> chunks_;
std::vector<Chunk *> active_chunks_;
std::vector<std::pair<ChunkPos, Chunk *>> tick_chunks_;
std::vector<std::unique_ptr<EntityCollection>> ent_colls_;
std::unordered_map<std::type_index, EntityCollection *> ent_colls_by_type_;
std::unordered_map<std::string, EntityCollection *> ent_colls_by_name_;
std::deque<Chunk *> chunk_init_list_;
std::vector<TilePos> debug_boxes_;
std::vector<Chunk *> activeChunks_;
std::vector<std::pair<ChunkPos, Chunk *>> tickChunks_;
std::vector<std::unique_ptr<EntityCollection>> entColls_;
std::unordered_map<std::type_index, EntityCollection *> entCollsByType_;
std::unordered_map<std::string, EntityCollection *> entCollsByName_;
std::deque<Chunk *> chunkInitList_;
std::vector<TilePos> debugBoxes_;
}; };


/* /*


template<typename Ent> template<typename Ent>
inline EntityCollection &WorldPlane::getCollectionOf() { inline EntityCollection &WorldPlane::getCollectionOf() {
return *ent_colls_by_type_.at(typeid(Ent));
return *entCollsByType_.at(typeid(Ent));
} }


inline EntityCollection &WorldPlane::getCollectionOf(std::string name) { inline EntityCollection &WorldPlane::getCollectionOf(std::string name) {
return *ent_colls_by_name_.at(name);
return *entCollsByName_.at(name);
} }


inline EntityCollection &WorldPlane::getCollectionOf(std::type_index type) { inline EntityCollection &WorldPlane::getCollectionOf(std::type_index type) {
return *ent_colls_by_type_.at(type);
return *entCollsByType_.at(type);
} }


} }

+ 3
- 3
libswan/include/swan/gfxutil.h View File

class RenderTarget: NonCopyable { class RenderTarget: NonCopyable {
public: public:
RenderTarget(SDL_Renderer *rnd, SDL_Texture *tex): rnd_(rnd) { RenderTarget(SDL_Renderer *rnd, SDL_Texture *tex): rnd_(rnd) {
prev_target_ = SDL_GetRenderTarget(rnd_);
prevTarget_ = SDL_GetRenderTarget(rnd_);
SDL_SetRenderTarget(rnd_, tex); SDL_SetRenderTarget(rnd_, tex);
} }


~RenderTarget() { ~RenderTarget() {
SDL_SetRenderTarget(rnd_, prev_target_);
SDL_SetRenderTarget(rnd_, prevTarget_);
} }


private: private:
SDL_Renderer *rnd_; SDL_Renderer *rnd_;
SDL_Texture *prev_target_;
SDL_Texture *prevTarget_;
}; };


class TexLock: NonCopyable { class TexLock: NonCopyable {

+ 3
- 3
libswan/include/swan/log.h View File

}; };


Logger(std::ostream &os, std::string name, bool use_color = false, std::string color = ""): Logger(std::ostream &os, std::string name, bool use_color = false, std::string color = ""):
os_(os), name_(std::move(name)), use_color_(use_color), color_(std::move(color)) {}
os_(os), name_(std::move(name)), useColor_(use_color), color_(std::move(color)) {}


template<typename T> template<typename T>
NewlineStream operator<<(const T &val) { NewlineStream operator<<(const T &val) {
if (use_color_)
if (useColor_)
os_ << color_ << name_ << "\033[0m: " << val; os_ << color_ << name_ << "\033[0m: " << val;
else else
os_ << name_ << ": " << val; os_ << name_ << ": " << val;
private: private:
std::ostream &os_; std::ostream &os_;
std::string name_; std::string name_;
bool use_color_;
bool useColor_;
std::string color_; std::string color_;
}; };



+ 1
- 1
libswan/include/swan/traits/PhysicsTrait.h View File

struct Physics { struct Physics {
Vec2 vel{}; Vec2 vel{};
Vec2 force{}; Vec2 force{};
bool on_ground = false;
bool onGround = false;


void friction(Vec2 coef = Vec2(400, 50)); void friction(Vec2 coef = Vec2(400, 50));
void gravity(float mass, Vec2 g = Vec2(0, 20)); void gravity(float mass, Vec2 g = Vec2(0, 20));

+ 22
- 22
libswan/src/Chunk.cc View File

memcpy(data_.get(), dest, destlen); memcpy(data_.get(), dest, destlen);


texture_.reset(); texture_.reset();
compressed_size_ = destlen;
compressedSize_ = destlen;


info info
<< "Compressed chunk " << pos_ << " from " << "Compressed chunk " << pos_ << " from "
uLongf destlen = 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(), compressedSize_);


if (ret != Z_OK) { if (ret != Z_OK) {
panic << "Decompressing chunk failed: " << ret; panic << "Decompressing chunk failed: " << ret;
} }


data_ = std::move(dest); data_ = std::move(dest);
need_render_ = true;
needRender_ = true;


info info
<< "Decompressed chunk " << pos_ << " from " << "Decompressed chunk " << pos_ << " from "
<< compressed_size_ << " bytes to "
<< compressedSize_ << " bytes to "
<< DATA_SIZE << " bytes."; << DATA_SIZE << " bytes.";
compressed_size_ = -1;
compressedSize_ = -1;
} }


void Chunk::renderLight(const Context &ctx, SDL_Renderer *rnd) { void Chunk::renderLight(const Context &ctx, SDL_Renderer *rnd) {
std::optional<RenderTarget> target; std::optional<RenderTarget> target;


// The texture might not be created yet // The texture might not be created yet
if (!light_texture_) {
light_texture_.reset(SDL_CreateTexture(
if (!lightTexture_) {
lightTexture_.reset(SDL_CreateTexture(
ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
CHUNK_WIDTH, CHUNK_HEIGHT)); CHUNK_WIDTH, CHUNK_HEIGHT));
SDL_SetTextureBlendMode(light_texture_.get(), SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(lightTexture_.get(), SDL_BLENDMODE_BLEND);


target.emplace(rnd, texture_.get()); target.emplace(rnd, texture_.get());
} else { } else {
} }


// Fill light texture // Fill light texture
target.emplace(rnd, light_texture_.get());
target.emplace(rnd, lightTexture_.get());
RenderBlendMode mode(rnd, SDL_BLENDMODE_NONE); RenderBlendMode mode(rnd, SDL_BLENDMODE_NONE);
RenderDrawColor color(rnd, 0, 0, 0, 0); RenderDrawColor color(rnd, 0, 0, 0, 0);
for (int y = 0; y < CHUNK_HEIGHT; ++y) { for (int y = 0; y < CHUNK_HEIGHT; ++y) {
} }
} }


need_light_render_ = false;
needLightRender_ = false;
} }


void Chunk::render(const Context &ctx, SDL_Renderer *rnd) { void Chunk::render(const Context &ctx, SDL_Renderer *rnd) {


// 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.invalidTile_.get();


// Fill tile texture // Fill tile texture
for (int y = 0; y < CHUNK_HEIGHT; ++y) { for (int y = 0; y < CHUNK_HEIGHT; ++y) {
} }
} }


need_render_ = false;
needRender_ = false;


renderLight(ctx, rnd); renderLight(ctx, rnd);
} }
// When we FillRect, we must fill transparency. // When we FillRect, we must fill transparency.
RenderDrawColor color(rnd, 0, 0, 0, 0); RenderDrawColor color(rnd, 0, 0, 0, 0);


for (auto &[pos, tex]: draw_list_) {
for (auto &[pos, tex]: drawList_) {
SDL_Rect dest{pos.x * TILE_SIZE, pos.y * TILE_SIZE, TILE_SIZE, TILE_SIZE}; SDL_Rect dest{pos.x * TILE_SIZE, pos.y * TILE_SIZE, TILE_SIZE, TILE_SIZE};
SDL_RenderFillRect(rnd, &dest); SDL_RenderFillRect(rnd, &dest);
SDL_RenderCopy(rnd, tex, nullptr, &dest); SDL_RenderCopy(rnd, tex, nullptr, &dest);
return; return;


// The world plane is responsible for managing initial renders // The world plane is responsible for managing initial renders
if (need_render_)
if (needRender_)
return; return;


// We're responsible for the light level rendering though // We're responsible for the light level rendering though
if (need_light_render_)
if (needLightRender_)
renderLight(ctx, win.renderer_); renderLight(ctx, win.renderer_);


if (draw_list_.size() > 0) {
if (drawList_.size() > 0) {
renderList(win.renderer_); renderList(win.renderer_);
draw_list_.clear();
drawList_.clear();
} }


auto chunkpos = pos_ * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT); auto chunkpos = pos_ * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT);
win.showTexture(chunkpos, texture_.get(), &rect); win.showTexture(chunkpos, texture_.get(), &rect);


SDL_Rect texrect{ 0, 0, CHUNK_WIDTH, CHUNK_HEIGHT }; SDL_Rect texrect{ 0, 0, CHUNK_WIDTH, CHUNK_HEIGHT };
win.showTexture(chunkpos, light_texture_.get(), &texrect, &rect);
win.showTexture(chunkpos, lightTexture_.get(), &texrect, &rect);
} }


Chunk::TickAction Chunk::tick(float dt) { Chunk::TickAction Chunk::tick(float dt) {
assert(isActive()); assert(isActive());


deactivate_timer_ -= dt;
if (deactivate_timer_ <= 0) {
if (is_modified_)
deactivateTimer_ -= dt;
if (deactivateTimer_ <= 0) {
if (isModified_)
return TickAction::DEACTIVATE; return TickAction::DEACTIVATE;
else else
return TickAction::DELETE; return TickAction::DELETE;
} }


void Chunk::keepActive() { void Chunk::keepActive() {
deactivate_timer_ = DEACTIVATE_INTERVAL;
deactivateTimer_ = DEACTIVATE_INTERVAL;
decompress(); decompress();
} }



+ 5
- 5
libswan/src/Game.cc View File

else if (win_.zoom_ < 0.3) else if (win_.zoom_ < 0.3)
win_.zoom_ = 0.3; win_.zoom_ = 0.3;


did_scroll_ = 0;
did_press_keys_.reset();
did_release_keys_.reset();
did_press_buttons_.reset();
did_release_buttons_.reset();
didScroll_ = 0;
didPressKeys_.reset();
didReleaseKeys_.reset();
didPressButtons_.reset();
didReleaseButtons_.reset();
} }


void Game::tick(float dt) { void Game::tick(float dt) {

+ 3
- 3
libswan/src/ItemStack.cc View File



// Merge // Merge
count_ += st.count_; count_ += st.count_;
if (count_ > item_->max_stack_) {
st.count_ = count_ - item_->max_stack_;
count_ = item_->max_stack_;
if (count_ > item_->maxStack_) {
st.count_ = count_ - item_->maxStack_;
count_ = item_->maxStack_;
} else { } else {
st.count_ = 0; st.count_ = 0;
st.item_ = nullptr; st.item_ = nullptr;

+ 58
- 58
libswan/src/LightServer.cc View File

} }


LightChunk::LightChunk(NewLightChunk &&ch): LightChunk::LightChunk(NewLightChunk &&ch):
blocks(std::move(ch.blocks)), light_sources(std::move(ch.light_sources)) {
blocks(std::move(ch.blocks)), lightSources(std::move(ch.lightSources)) {
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) {
if (blocks[y * CHUNK_WIDTH + x]) { if (blocks[y * CHUNK_WIDTH + x]) {
blocks_line[x] += 1;
blocksLine[x] += 1;
} }
} }
} }
} }


LightChunk *LightServer::getChunk(ChunkPos cpos) { LightChunk *LightServer::getChunk(ChunkPos cpos) {
if (cached_chunk_ && cached_chunk_pos_ == cpos) {
return cached_chunk_;
if (cachedChunk_ && cachedChunkPos_ == cpos) {
return cachedChunk_;
} }


auto it = chunks_.find(cpos); auto it = chunks_.find(cpos);
if (it != chunks_.end()) { if (it != chunks_.end()) {
cached_chunk_ = &it->second;
cached_chunk_pos_ = cpos;
cachedChunk_ = &it->second;
cachedChunkPos_ = cpos;
return &it->second; return &it->second;
} }


auto markAdjacentChunksModified = [&](ChunkPos cpos) { auto markAdjacentChunksModified = [&](ChunkPos cpos) {
for (int y = -1; y <= 1; ++y) { for (int y = -1; y <= 1; ++y) {
for (int x = -1; x <= 1; ++x) { for (int x = -1; x <= 1; ++x) {
updated_chunks_.insert(cpos + Vec2i(x, y));
updatedChunks_.insert(cpos + Vec2i(x, y));
} }
} }
}; };


auto markChunks = [&](ChunkPos cpos, Vec2i rpos, bool l, bool r, bool t, bool b) { auto markChunks = [&](ChunkPos cpos, Vec2i rpos, bool l, bool r, bool t, bool b) {
updated_chunks_.insert(cpos);
if (l) updated_chunks_.insert(cpos + Vec2i(-1, 0));
if (r) updated_chunks_.insert(cpos + Vec2i(1, 0));
if (t) updated_chunks_.insert(cpos + Vec2i(0, -1));
if (b) updated_chunks_.insert(cpos + Vec2i(0, 1));
if (l && t) updated_chunks_.insert(cpos + Vec2i(-1, -1));
if (r && t) updated_chunks_.insert(cpos + Vec2i(1, -1));
if (l && b) updated_chunks_.insert(cpos + Vec2i(-1, 1));
if (r && b) updated_chunks_.insert(cpos + Vec2i(1, 1));
updatedChunks_.insert(cpos);
if (l) updatedChunks_.insert(cpos + Vec2i(-1, 0));
if (r) updatedChunks_.insert(cpos + Vec2i(1, 0));
if (t) updatedChunks_.insert(cpos + Vec2i(0, -1));
if (b) updatedChunks_.insert(cpos + Vec2i(0, 1));
if (l && t) updatedChunks_.insert(cpos + Vec2i(-1, -1));
if (r && t) updatedChunks_.insert(cpos + Vec2i(1, -1));
if (l && b) updatedChunks_.insert(cpos + Vec2i(-1, 1));
if (r && b) updatedChunks_.insert(cpos + Vec2i(1, 1));
}; };


auto markChunksModified = [&](ChunkPos cpos, Vec2i rpos, float light) { auto markChunksModified = [&](ChunkPos cpos, Vec2i rpos, float light) {
switch (evt.tag) { switch (evt.tag) {
case Event::Tag::BLOCK_ADDED: case Event::Tag::BLOCK_ADDED:
ch->blocks.set(rpos.y * CHUNK_WIDTH + rpos.x, true); ch->blocks.set(rpos.y * CHUNK_WIDTH + rpos.x, true);
ch->blocks_line[rpos.x] += 1;
ch->blocksLine[rpos.x] += 1;
markChunksModifiedRange(cpos, rpos, LIGHT_CUTOFF_DIST); markChunksModifiedRange(cpos, rpos, LIGHT_CUTOFF_DIST);
break; break;


case Event::Tag::BLOCK_REMOVED: case Event::Tag::BLOCK_REMOVED:
ch->blocks.set(rpos.y * CHUNK_WIDTH + rpos.x, false); ch->blocks.set(rpos.y * CHUNK_WIDTH + rpos.x, false);
ch->blocks_line[rpos.x] -= 1;
ch->blocksLine[rpos.x] -= 1;
markChunksModifiedRange(cpos, rpos, LIGHT_CUTOFF_DIST); markChunksModifiedRange(cpos, rpos, LIGHT_CUTOFF_DIST);
break; break;


case Event::Tag::LIGHT_ADDED: case Event::Tag::LIGHT_ADDED:
info << cpos << ": Add " << evt.f << " light to " << rpos; info << cpos << ": Add " << evt.f << " light to " << rpos;
ch->light_sources[rpos] += evt.f;
markChunksModified(cpos, rpos, ch->light_sources[rpos]);
ch->lightSources[rpos] += evt.f;
markChunksModified(cpos, rpos, ch->lightSources[rpos]);
break; break;


case Event::Tag::LIGHT_REMOVED: case Event::Tag::LIGHT_REMOVED:
info << cpos << ": Remove " << evt.f << " light to " << rpos; info << cpos << ": Remove " << evt.f << " light to " << rpos;
markChunksModified(cpos, rpos, ch->light_sources[rpos]);
ch->light_sources[rpos] -= evt.f;
if (ch->light_sources[rpos] < LIGHT_CUTOFF) {
ch->light_sources.erase(rpos);
markChunksModified(cpos, rpos, ch->lightSources[rpos]);
ch->lightSources[rpos] -= evt.f;
if (ch->lightSources[rpos] < LIGHT_CUTOFF) {
ch->lightSources.erase(rpos);
} }
break; break;


} }


for (int rx = 0; rx < CHUNK_WIDTH; ++rx) { for (int rx = 0; rx < CHUNK_WIDTH; ++rx) {
bool lit = light > 0 && tc && tc->blocks_line[rx] == 0 && !line[rx];
bool lit = light > 0 && tc && tc->blocksLine[rx] == 0 && !line[rx];
if (lit) { if (lit) {
chunk.light_buffer()[ry * CHUNK_WIDTH + rx] = light;
chunk.lightBuffer()[ry * CHUNK_WIDTH + rx] = light;
if (chunk.blocks[ry * CHUNK_WIDTH + rx]) { if (chunk.blocks[ry * CHUNK_WIDTH + rx]) {
line[rx] = true; line[rx] = true;
} }
} else { } else {
chunk.light_buffer()[ry * CHUNK_WIDTH + rx] = 0;
chunk.lightBuffer()[ry * CHUNK_WIDTH + rx] = 0;
} }
} }
} }
TilePos base = cpos * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT); TilePos base = cpos * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT);
std::vector<std::pair<TilePos, float>> lights; std::vector<std::pair<TilePos, float>> lights;


for (auto &[pos, level]: chunk.light_sources) {
for (auto &[pos, level]: chunk.lightSources) {
lights.emplace_back(Vec2i(pos) + base, level); lights.emplace_back(Vec2i(pos) + base, level);
} }


} }


TilePos b = base + Vec2i(dx * CHUNK_WIDTH, dy * CHUNK_HEIGHT); TilePos b = base + Vec2i(dx * CHUNK_WIDTH, dy * CHUNK_HEIGHT);
for (auto &[pos, level]: chunk->light_sources) {
for (auto &[pos, level]: chunk->lightSources) {
lights.emplace_back(TilePos(pos) + b, level); lights.emplace_back(TilePos(pos) + b, level);
} }
}; };
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) {
float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights); float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights);
chunk.light_buffer()[y * CHUNK_WIDTH + x] += light;
chunk.lightBuffer()[y * CHUNK_WIDTH + x] += light;


if (light > 0 && chunk.blocks[y * CHUNK_WIDTH + x]) { if (light > 0 && chunk.blocks[y * CHUNK_WIDTH + x]) {
chunk.bounces.emplace_back(base + Vec2i(x, y), light * 0.1); chunk.bounces.emplace_back(base + Vec2i(x, y), light * 0.1);
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) {
float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights); float light = recalcTile(chunk, cpos, Vec2i(x, y), base, lights);
float sum = chunk.light_buffer()[y * CHUNK_WIDTH + x] + light;
chunk.light_buffer()[y * CHUNK_WIDTH + x] = sum;
float sum = chunk.lightBuffer()[y * CHUNK_WIDTH + x] + light;
chunk.lightBuffer()[y * CHUNK_WIDTH + x] = sum;
} }
} }
} }
LightChunk *rc = getChunk(cpos + Vec2i(1, 0)); LightChunk *rc = getChunk(cpos + Vec2i(1, 0));


auto getLight = [&](LightChunk &chunk, int x, int y) { auto getLight = [&](LightChunk &chunk, int x, int y) {
return chunk.light_buffer()[y * CHUNK_WIDTH + x];
return chunk.lightBuffer()[y * CHUNK_WIDTH + x];
}; };


auto calc = [&](int x1, int x2, int y1, int y2, auto tf, auto bf, auto lf, auto rf) { auto calc = [&](int x1, int x2, int y1, int y2, auto tf, auto bf, auto lf, auto rf) {
float *dest = chunk.light_buffers + CHUNK_WIDTH * CHUNK_HEIGHT * ((chunk.buffer + 1) % 2);
float *dest = chunk.lightBuffers + CHUNK_WIDTH * CHUNK_HEIGHT * ((chunk.buffer + 1) % 2);
for (int y = y1; y < y2; ++y) { for (int y = y1; y < y2; ++y) {
for (int x = x1; x < x2; ++x) { for (int x = x1; x < x2; ++x) {
float t = tf(x, y); float t = tf(x, y);
float b = bf(x, y); float b = bf(x, y);
float l = lf(x, y); float l = lf(x, y);
float r = rf(x, y); float r = rf(x, y);
float light = chunk.light_buffer()[y * CHUNK_WIDTH + x];
float light = chunk.lightBuffer()[y * CHUNK_WIDTH + x];
int count = 1; int count = 1;
if (t > light) { light += t; count += 1; } if (t > light) { light += t; count += 1; }
if (b > light) { light += b; count += 1; } if (b > light) { light += b; count += 1; }


if (tc) { if (tc) {
calc(1, CHUNK_WIDTH - 1, 0, 1, calc(1, CHUNK_WIDTH - 1, 0, 1,
[&](int x, int y) { return tc->light_buffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
[&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
[&](int x, int y) { return getLight(chunk, x, y + 1); }, [&](int x, int y) { return getLight(chunk, x, y + 1); },
[&](int x, int y) { return getLight(chunk, x - 1, y); }, [&](int x, int y) { return getLight(chunk, x - 1, y); },
[&](int x, int y) { return getLight(chunk, x + 1, y); }); [&](int x, int y) { return getLight(chunk, x + 1, y); });
if (bc) { if (bc) {
calc(1, CHUNK_WIDTH - 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT, calc(1, CHUNK_WIDTH - 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
[&](int x, int y) { return getLight(chunk, x, y - 1); }, [&](int x, int y) { return getLight(chunk, x, y - 1); },
[&](int x, int y) { return bc->light_buffer()[x]; },
[&](int x, int y) { return bc->lightBuffer()[x]; },
[&](int x, int y) { return getLight(chunk, x - 1, y); }, [&](int x, int y) { return getLight(chunk, x - 1, y); },
[&](int x, int y) { return getLight(chunk, x + 1, y); }); [&](int x, int y) { return getLight(chunk, x + 1, y); });
} }
calc(0, 1, 1, CHUNK_HEIGHT - 1, calc(0, 1, 1, CHUNK_HEIGHT - 1,
[&](int x, int y) { return getLight(chunk, x, y - 1); }, [&](int x, int y) { return getLight(chunk, x, y - 1); },
[&](int x, int y) { return getLight(chunk, x, y + 1); }, [&](int x, int y) { return getLight(chunk, x, y + 1); },
[&](int x, int y) { return lc->light_buffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
[&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
[&](int x, int y) { return getLight(chunk, x + 1, y); }); [&](int x, int y) { return getLight(chunk, x + 1, y); });
} }


[&](int x, int y) { return getLight(chunk, x, y - 1); }, [&](int x, int y) { return getLight(chunk, x, y - 1); },
[&](int x, int y) { return getLight(chunk, x, y + 1); }, [&](int x, int y) { return getLight(chunk, x, y + 1); },
[&](int x, int y) { return getLight(chunk, x - 1, y); }, [&](int x, int y) { return getLight(chunk, x - 1, y); },
[&](int x, int y) { return rc->light_buffer()[y * CHUNK_WIDTH]; });
[&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
} }


if (tc && lc) { if (tc && lc) {
calc(0, 1, 0, 1, calc(0, 1, 0, 1,
[&](int x, int y) { return tc->light_buffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
[&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
[&](int x, int y) { return getLight(chunk, x, y + 1); }, [&](int x, int y) { return getLight(chunk, x, y + 1); },
[&](int x, int y) { return lc->light_buffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
[&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
[&](int x, int y) { return getLight(chunk, x + 1, y); }); [&](int x, int y) { return getLight(chunk, x + 1, y); });
} }


if (tc && rc) { if (tc && rc) {
calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, 0, 1, calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, 0, 1,
[&](int x, int y) { return tc->light_buffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
[&](int x, int y) { return tc->lightBuffer()[(CHUNK_HEIGHT - 1) * CHUNK_WIDTH + x]; },
[&](int x, int y) { return getLight(chunk, x, y + 1); }, [&](int x, int y) { return getLight(chunk, x, y + 1); },
[&](int x, int y) { return getLight(chunk, x - 1, y); }, [&](int x, int y) { return getLight(chunk, x - 1, y); },
[&](int x, int y) { return rc->light_buffer()[y * CHUNK_WIDTH]; });
[&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
} }


if (bc && lc) { if (bc && lc) {
calc(0, 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT, calc(0, 1, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
[&](int x, int y) { return getLight(chunk, x, y - 1); }, [&](int x, int y) { return getLight(chunk, x, y - 1); },
[&](int x, int y) { return bc->light_buffer()[x]; },
[&](int x, int y) { return lc->light_buffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
[&](int x, int y) { return bc->lightBuffer()[x]; },
[&](int x, int y) { return lc->lightBuffer()[y * CHUNK_WIDTH + CHUNK_WIDTH - 1]; },
[&](int x, int y) { return getLight(chunk, x + 1, y); }); [&](int x, int y) { return getLight(chunk, x + 1, y); });
} }


if (bc && rc) { if (bc && rc) {
calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, CHUNK_HEIGHT - 1, CHUNK_HEIGHT, calc(CHUNK_WIDTH - 1, CHUNK_WIDTH, CHUNK_HEIGHT - 1, CHUNK_HEIGHT,
[&](int x, int y) { return getLight(chunk, x, y - 1); }, [&](int x, int y) { return getLight(chunk, x, y - 1); },
[&](int x, int y) { return bc->light_buffer()[x]; },
[&](int x, int y) { return bc->lightBuffer()[x]; },
[&](int x, int y) { return getLight(chunk, x - 1, y); }, [&](int x, int y) { return getLight(chunk, x - 1, y); },
[&](int x, int y) { return rc->light_buffer()[y * CHUNK_WIDTH]; });
[&](int x, int y) { return rc->lightBuffer()[y * CHUNK_WIDTH]; });
} }
} }


void LightServer::finalizeChunk(LightChunk &chunk) { void LightServer::finalizeChunk(LightChunk &chunk) {
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) {
chunk.light_levels[y * CHUNK_WIDTH + x] =
linToSRGB(chunk.light_buffer()[y * CHUNK_HEIGHT + x]);
chunk.lightLevels[y * CHUNK_WIDTH + x] =
linToSRGB(chunk.lightBuffer()[y * CHUNK_HEIGHT + x]);
} }
} }
} }
cond_.wait(lock, [&] { return buffers_[buffer_].size() > 0 || !running_; }); cond_.wait(lock, [&] { return buffers_[buffer_].size() > 0 || !running_; });


std::vector<Event> &buf = buffers_[buffer_]; std::vector<Event> &buf = buffers_[buffer_];
std::vector<NewLightChunk> &newChunks = new_chunk_buffers_[buffer_];
std::vector<NewLightChunk> &newChunks = newChunkBuffers_[buffer_];
buffer_ = (buffer_ + 1) % 2; buffer_ = (buffer_ + 1) % 2;
lock.unlock(); lock.unlock();


updated_chunks_.clear();
updatedChunks_.clear();
for (auto &evt: buf) { for (auto &evt: buf) {
processEvent(evt, newChunks); processEvent(evt, newChunks);
} }


auto start = std::chrono::steady_clock::now(); auto start = std::chrono::steady_clock::now();


for (auto &pos: updated_chunks_) {
for (auto &pos: updatedChunks_) {
auto ch = chunks_.find(pos); auto ch = chunks_.find(pos);
if (ch != chunks_.end()) { if (ch != chunks_.end()) {
processChunkSun(ch->second, ChunkPos(pos.first, pos.second)); processChunkSun(ch->second, ChunkPos(pos.first, pos.second));
} }
} }


for (auto &pos: updated_chunks_) {
for (auto &pos: updatedChunks_) {
auto ch = chunks_.find(pos); auto ch = chunks_.find(pos);
if (ch != chunks_.end()) { if (ch != chunks_.end()) {
processChunkLights(ch->second, ChunkPos(pos.first, pos.second)); processChunkLights(ch->second, ChunkPos(pos.first, pos.second));
} }
} }


for (auto &pos: updated_chunks_) {
for (auto &pos: updatedChunks_) {
auto ch = chunks_.find(pos); auto ch = chunks_.find(pos);
if (ch != chunks_.end()) { if (ch != chunks_.end()) {
processChunkBounces(ch->second, ChunkPos(pos.first, pos.second)); processChunkBounces(ch->second, ChunkPos(pos.first, pos.second));
} }


for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
for (auto &pos: updated_chunks_) {
for (auto &pos: updatedChunks_) {
auto ch = chunks_.find(pos); auto ch = chunks_.find(pos);
if (ch != chunks_.end()) { if (ch != chunks_.end()) {
processChunkSmoothing(ch->second, ChunkPos(pos.first, pos.second)); processChunkSmoothing(ch->second, ChunkPos(pos.first, pos.second));
} }
} }


for (auto &pos: updated_chunks_) {
for (auto &pos: updatedChunks_) {
auto ch = chunks_.find(pos); auto ch = chunks_.find(pos);
if (ch != chunks_.end()) { if (ch != chunks_.end()) {
finalizeChunk(ch->second); finalizeChunk(ch->second);


auto end = std::chrono::steady_clock::now(); auto end = std::chrono::steady_clock::now();
auto dur = std::chrono::duration<double, std::milli>(end - start); auto dur = std::chrono::duration<double, std::milli>(end - start);
info << "Generating light for " << updated_chunks_.size()
info << "Generating light for " << updatedChunks_.size()
<< " chunks took " << dur.count() << "ms"; << " chunks took " << dur.count() << "ms";


for (auto &pos: updated_chunks_) {
for (auto &pos: updatedChunks_) {
auto ch = chunks_.find(pos); auto ch = chunks_.find(pos);
if (ch != chunks_.end()) { if (ch != chunks_.end()) {
cb_.onLightChunkUpdated(ch->second, pos); cb_.onLightChunkUpdated(ch->second, pos);

+ 1
- 1
libswan/src/Tile.cc View File

return std::make_unique<Tile>(resources, Builder{ return std::make_unique<Tile>(resources, Builder{
.name = "@::air", .name = "@::air",
.image = "@::air", .image = "@::air",
.is_solid = false,
.isSolid = false,
}); });
} }



+ 21
- 22
libswan/src/World.cc View File

World::World(Game *game, unsigned long rand_seed): World::World(Game *game, unsigned long rand_seed):
game_(game), random_(rand_seed), resources_(game->win_) { game_(game), random_(rand_seed), resources_(game->win_) {


std::unique_ptr<Tile> invalid_tile = Tile::createInvalid(resources_);
tiles_map_[invalid_tile->name_] = 0;
std::unique_ptr<Tile> invalidTile = Tile::createInvalid(resources_);
tilesMap_[invalidTile->name_] = 0;


// tiles_ is empty, so pushing back now will ensure invalid_tile // tiles_ is empty, so pushing back now will ensure invalid_tile
// ends up at location 0 // ends up at location 0
tiles_.push_back(std::move(invalid_tile));
tiles_.push_back(std::move(invalidTile));


// We're also going to need an air tile at location 1 // We're also going to need an air tile at location 1
tiles_.push_back(Tile::createAir(resources_)); tiles_.push_back(Tile::createAir(resources_));
tiles_map_["@::air"] = 1;

tilesMap_["@::air"] = 1;
} }


void World::ChunkRenderer::tick(WorldPlane &plane, ChunkPos abspos) { void World::ChunkRenderer::tick(WorldPlane &plane, ChunkPos abspos) {


for (auto t: mod.buildTiles(resources_)) { for (auto t: mod.buildTiles(resources_)) {
Tile::ID id = tiles_.size(); Tile::ID id = tiles_.size();
tiles_map_[t->name_] = id;
tilesMap_[t->name_] = id;
tiles_.push_back(std::move(t)); tiles_.push_back(std::move(t));
} }


} }


for (auto fact: mod.getWorldGens()) { for (auto fact: mod.getWorldGens()) {
worldgen_factories_.emplace(
worldgenFactories_.emplace(
std::piecewise_construct, std::piecewise_construct,
std::forward_as_tuple(fact.name), std::forward_as_tuple(fact.name),
std::forward_as_tuple(fact)); std::forward_as_tuple(fact));
} }


for (auto fact: mod.getEntities()) { for (auto fact: mod.getEntities()) {
ent_coll_factories_.push_back(fact);
entCollFactories_.push_back(fact);
} }


mods_.push_back(std::move(mod)); mods_.push_back(std::move(mod));
} }


void World::setWorldGen(std::string gen) { void World::setWorldGen(std::string gen) {
default_world_gen_ = std::move(gen);
defaultWorldGen_ = std::move(gen);
} }


void World::spawnPlayer() { void World::spawnPlayer() {
player_ = &((dynamic_cast<BodyTrait *>( player_ = &((dynamic_cast<BodyTrait *>(
planes_[current_plane_]->spawnPlayer().get()))->get(BodyTrait::Tag{}));
planes_[currentPlane_]->spawnPlayer().get()))->get(BodyTrait::Tag{}));
} }


void World::setCurrentPlane(WorldPlane &plane) { void World::setCurrentPlane(WorldPlane &plane) {
current_plane_ = plane.id_;
currentPlane_ = plane.id_;
} }


WorldPlane &World::addPlane(const std::string &gen) { WorldPlane &World::addPlane(const std::string &gen) {
WorldPlane::ID id = planes_.size(); WorldPlane::ID id = planes_.size();
auto it = worldgen_factories_.find(gen);
if (it == worldgen_factories_.end()) {
auto it = worldgenFactories_.find(gen);
if (it == worldgenFactories_.end()) {
panic << "Tried to add plane with non-existant world gen " << gen << "!"; panic << "Tried to add plane with non-existant world gen " << gen << "!";
abort(); abort();
} }


std::vector<std::unique_ptr<EntityCollection>> colls; std::vector<std::unique_ptr<EntityCollection>> colls;
colls.reserve(ent_coll_factories_.size());
for (auto &fact: ent_coll_factories_) {
colls.reserve(entCollFactories_.size());
for (auto &fact: entCollFactories_) {
colls.emplace_back(fact.create(fact.name)); colls.emplace_back(fact.create(fact.name));
} }


auto iter = items_.find(name); auto iter = items_.find(name);
if (iter == items_.end()) { if (iter == items_.end()) {
warn << "Tried to get non-existant item " << name << "!"; warn << "Tried to get non-existant item " << name << "!";
return *game_->invalid_item_;
return *game_->invalidItem_;
} }


return *iter->second; return *iter->second;
} }


Tile::ID World::getTileID(const std::string &name) { Tile::ID World::getTileID(const std::string &name) {
auto iter = tiles_map_.find(name);
if (iter == tiles_map_.end()) {
auto iter = tilesMap_.find(name);
if (iter == tilesMap_.end()) {
warn << "Tried to get non-existant item " << name << "!"; warn << "Tried to get non-existant item " << name << "!";
return Tile::INVALID_ID; return Tile::INVALID_ID;
} }
} }


SDL_Color World::backgroundColor() { SDL_Color World::backgroundColor() {
return planes_[current_plane_]->backgroundColor();
return planes_[currentPlane_]->backgroundColor();
} }


void World::draw(Win &win) { void World::draw(Win &win) {
ZoneScopedN("World draw"); ZoneScopedN("World draw");
win.cam_ = player_->pos - (win.getSize() / 2) + (player_->size / 2); win.cam_ = player_->pos - (win.getSize() / 2) + (player_->size / 2);
planes_[current_plane_]->draw(win);
planes_[currentPlane_]->draw(win);
} }


void World::update(float dt) { void World::update(float dt) {
for (auto &plane: planes_) for (auto &plane: planes_)
plane->tick(dt); plane->tick(dt);


chunk_renderer_.tick(
*planes_[current_plane_],
chunkRenderer_.tick(
*planes_[currentPlane_],
ChunkPos((int)player_->pos.x / CHUNK_WIDTH, (int)player_->pos.y / CHUNK_HEIGHT)); ChunkPos((int)player_->pos.x / CHUNK_WIDTH, (int)player_->pos.y / CHUNK_HEIGHT));
} }



+ 41
- 41
libswan/src/WorldPlane.cc View File

std::vector<std::unique_ptr<EntityCollection>> &&colls): std::vector<std::unique_ptr<EntityCollection>> &&colls):
id_(id), world_(world), gen_(std::move(gen)), id_(id), world_(world), gen_(std::move(gen)),
lighting_(std::make_unique<LightServer>(*this)), lighting_(std::make_unique<LightServer>(*this)),
ent_colls_(std::move(colls)) {
entColls_(std::move(colls)) {


for (auto &coll: ent_colls_) {
ent_colls_by_type_[coll->type()] = coll.get();
ent_colls_by_name_[coll->name()] = coll.get();
for (auto &coll: entColls_) {
entCollsByType_[coll->type()] = coll.get();
entCollsByName_[coll->name()] = coll.get();
} }
} }


EntityRef WorldPlane::spawnEntity(const std::string &name, const Entity::PackObject &obj) { EntityRef WorldPlane::spawnEntity(const std::string &name, const Entity::PackObject &obj) {
return ent_colls_by_name_.at(name)->spawn(getContext(), obj);
return entCollsByName_.at(name)->spawn(getContext(), obj);
} }


bool WorldPlane::hasChunk(ChunkPos pos) { bool WorldPlane::hasChunk(ChunkPos pos) {
// This function will be a bit weird because it's a really fucking hot function. // This function will be a bit weird because it's a really fucking hot function.
Chunk &WorldPlane::getChunk(ChunkPos pos) { Chunk &WorldPlane::getChunk(ChunkPos pos) {
// First, look through all chunks which have been in use this tick // First, look through all chunks which have been in use this tick
for (auto [chpos, chunk]: tick_chunks_) {
for (auto [chpos, chunk]: tickChunks_) {
if (chpos == pos) if (chpos == pos)
return *chunk; return *chunk;
} }


Chunk &chunk = slowGetChunk(pos); Chunk &chunk = slowGetChunk(pos);
tick_chunks_.push_back({ pos, &chunk });
tickChunks_.push_back({ pos, &chunk });
return chunk; return chunk;
} }


Chunk &chunk = iter->second; Chunk &chunk = iter->second;


gen_->genChunk(*this, chunk); gen_->genChunk(*this, chunk);
active_chunks_.push_back(&chunk);
chunk_init_list_.push_back(&chunk);
activeChunks_.push_back(&chunk);
chunkInitList_.push_back(&chunk);


// Need to tell the light engine too // Need to tell the light engine too
NewLightChunk lc; NewLightChunk lc;
for (int x = 0; x < CHUNK_WIDTH; ++x) { for (int x = 0; x < CHUNK_WIDTH; ++x) {
Tile::ID id = chunk.getTileID({ x, y }); Tile::ID id = chunk.getTileID({ x, y });
Tile &tile = world_->getTileByID(id); Tile &tile = world_->getTileByID(id);
if (tile.is_solid_) {
if (tile.isSolid_) {
lc.blocks[y * CHUNK_HEIGHT + x] = true; lc.blocks[y * CHUNK_HEIGHT + x] = true;
} }
if (tile.light_level_ > 0) {
lc.light_sources[{ x, y }] = tile.light_level_;
if (tile.lightLevel_ > 0) {
lc.light_sources[{ x, y }] = tile.lightLevel_;
} }
} }
} }
// Otherwise, it might not be active, so let's activate it // Otherwise, it might not be active, so let's activate it
} else if (!iter->second.isActive()) { } else if (!iter->second.isActive()) {
iter->second.keepActive(); iter->second.keepActive();
active_chunks_.push_back(&iter->second);
chunk_init_list_.push_back(&iter->second);
activeChunks_.push_back(&iter->second);
chunkInitList_.push_back(&iter->second);
} }


return iter->second; return iter->second;
chunk.setTileID(rp, id, newTile.image_.texture_.get()); chunk.setTileID(rp, id, newTile.image_.texture_.get());
chunk.markModified(); chunk.markModified();


if (!oldTile.is_solid_ && newTile.is_solid_) {
if (!oldTile.isSolid_ && newTile.isSolid_) {
lighting_->onSolidBlockAdded(pos); lighting_->onSolidBlockAdded(pos);
} else if (oldTile.is_solid_ && !newTile.is_solid_) {
} else if (oldTile.isSolid_ && !newTile.isSolid_) {
lighting_->onSolidBlockRemoved(pos); lighting_->onSolidBlockRemoved(pos);
} }


if (newTile.light_level_ != oldTile.light_level_) {
if (oldTile.light_level_ > 0) {
removeLight(pos, oldTile.light_level_);
if (newTile.lightLevel_ != oldTile.lightLevel_) {
if (oldTile.lightLevel_ > 0) {
removeLight(pos, oldTile.lightLevel_);
} }


if (newTile.light_level_ > 0) {
addLight(pos, newTile.light_level_);
if (newTile.lightLevel_ > 0) {
addLight(pos, newTile.lightLevel_);
} }
} }
} }


// Change tile to air and emit event // Change tile to air and emit event
setTileID(pos, air); setTileID(pos, air);
world_->evt_tile_break_.emit(getContext(), pos, world_->getTileByID(id));
world_->evtTileBreak_.emit(getContext(), pos, world_->getTileByID(id));
} }


SDL_Color WorldPlane::backgroundColor() { SDL_Color WorldPlane::backgroundColor() {
(int)floor(pbody.pos.y / CHUNK_HEIGHT)); (int)floor(pbody.pos.y / CHUNK_HEIGHT));


// Just init one chunk per frame // Just init one chunk per frame
if (chunk_init_list_.size() > 0) {
Chunk *chunk = chunk_init_list_.front();
chunk_init_list_.pop_front();
if (chunkInitList_.size() > 0) {
Chunk *chunk = chunkInitList_.front();
chunkInitList_.pop_front();
chunk->render(ctx, win.renderer_); chunk->render(ctx, win.renderer_);
} }


} }
} }


for (auto &coll: ent_colls_)
for (auto &coll: entColls_)
coll->draw(ctx, win); coll->draw(ctx, win);


if (debug_boxes_.size() > 0) {
for (auto &pos: debug_boxes_) {
if (debugBoxes_.size() > 0) {
for (auto &pos: debugBoxes_) {
win.drawRect(pos, Vec2(1, 1)); win.drawRect(pos, Vec2(1, 1));
} }
} }
ZoneScopedN("WorldPlane update"); ZoneScopedN("WorldPlane update");
std::lock_guard<std::mutex> lock(mut_); std::lock_guard<std::mutex> lock(mut_);
auto ctx = getContext(); auto ctx = getContext();
debug_boxes_.clear();
debugBoxes_.clear();


for (auto &coll: ent_colls_)
for (auto &coll: entColls_)
coll->update(ctx, dt); coll->update(ctx, dt);
} }


auto ctx = getContext(); auto ctx = getContext();


// Any chunk which has been in use since last tick should be kept alive // Any chunk which has been in use since last tick should be kept alive
for (std::pair<ChunkPos, Chunk *> &ch: tick_chunks_)
for (std::pair<ChunkPos, Chunk *> &ch: tickChunks_)
ch.second->keepActive(); ch.second->keepActive();
tick_chunks_.clear();
tickChunks_.clear();


for (auto &coll: ent_colls_)
for (auto &coll: entColls_)
coll->tick(ctx, dt); coll->tick(ctx, dt);


// Tick all chunks, figure out if any of them should be deleted or compressed // Tick all chunks, figure out if any of them should be deleted or compressed
auto iter = active_chunks_.begin();
auto last = active_chunks_.end();
auto iter = activeChunks_.begin();
auto last = activeChunks_.end();
while (iter != last) { while (iter != last) {
auto &chunk = *iter; auto &chunk = *iter;
auto action = chunk->tick(dt); auto action = chunk->tick(dt);
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();
iter = active_chunks_.erase(iter);
last = active_chunks_.end();
iter = activeChunks_.erase(iter);
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_;
chunks_.erase(chunk->pos_); chunks_.erase(chunk->pos_);
iter = active_chunks_.erase(iter);
last = active_chunks_.end();
iter = activeChunks_.erase(iter);
last = activeChunks_.end();
break; break;
case Chunk::TickAction::NOTHING: case Chunk::TickAction::NOTHING:
++iter; ++iter;
} }


void WorldPlane::debugBox(TilePos pos) { void WorldPlane::debugBox(TilePos pos) {
debug_boxes_.push_back(pos);
debugBoxes_.push_back(pos);
} }


void WorldPlane::addLight(TilePos pos, float level) { void WorldPlane::addLight(TilePos pos, float level) {
void WorldPlane::onLightChunkUpdated(const LightChunk &chunk, ChunkPos pos) { void WorldPlane::onLightChunkUpdated(const LightChunk &chunk, ChunkPos pos) {
std::lock_guard<std::mutex> lock(mut_); std::lock_guard<std::mutex> lock(mut_);
Chunk &realChunk = getChunk(pos); Chunk &realChunk = getChunk(pos);
realChunk.setLightData(chunk.light_levels);
realChunk.setLightData(chunk.lightLevels);
} }


} }

+ 6
- 6
libswan/src/traits/PhysicsTrait.cc View File

for (int y = (int)floor(body.top() + epsilon); y <= (int)floor(body.bottom() - epsilon); ++y) { for (int y = (int)floor(body.top() + epsilon); y <= (int)floor(body.bottom() - epsilon); ++y) {
int lx = (int)floor(body.left() + epsilon); int lx = (int)floor(body.left() + epsilon);
Tile &left = plane.getTile({ lx, y }); Tile &left = plane.getTile({ lx, y });
if (left.is_solid_) {
if (left.isSolid_) {
body.pos.x = (float)lx + 1.0; body.pos.x = (float)lx + 1.0;
collided = true; collided = true;
break; break;


int rx = (int)floor(body.right() - epsilon); int rx = (int)floor(body.right() - epsilon);
Tile &right = plane.getTile({ rx, y }); Tile &right = plane.getTile({ rx, y });
if (right.is_solid_) {
if (right.isSolid_) {
body.pos.x = (float)rx - body.size.x; body.pos.x = (float)rx - body.size.x;
collided = true; collided = true;
break; break;
PhysicsTrait::Physics &phys, BodyTrait::Body &body, PhysicsTrait::Physics &phys, BodyTrait::Body &body,
WorldPlane &plane, const PhysicsTrait::PhysicsProps &props) { WorldPlane &plane, const PhysicsTrait::PhysicsProps &props) {
bool collided = false; bool collided = false;
phys.on_ground = false;
phys.onGround = false;


for (int x = (int)floor(body.left() + epsilon); x <= (int)floor(body.right() - epsilon); ++x) { for (int x = (int)floor(body.left() + epsilon); x <= (int)floor(body.right() - epsilon); ++x) {
int ty = (int)floor(body.top() + epsilon); int ty = (int)floor(body.top() + epsilon);
Tile &top = plane.getTile({ x, ty }); Tile &top = plane.getTile({ x, ty });
if (top.is_solid_) {
if (top.isSolid_) {
body.pos.y = (float)ty + 1.0; body.pos.y = (float)ty + 1.0;
collided = true; collided = true;
break; break;


int by = (int)floor(body.bottom() - epsilon); int by = (int)floor(body.bottom() - epsilon);
Tile &bottom = plane.getTile({ x, by }); Tile &bottom = plane.getTile({ x, by });
if (bottom.is_solid_) {
if (bottom.isSolid_) {
body.pos.y = (float)by - body.size.y; body.pos.y = (float)by - body.size.y;
collided = true; collided = true;
phys.on_ground = true;
phys.onGround = true;
break; break;
} }
} }

+ 3
- 3
libswan/test/ItemStack.t.cc View File

Swan::ItemStack s2(&item1, 40); Swan::ItemStack s2(&item1, 40);
s2 = s1.insert(s2); s2 = s1.insert(s2);


expecteq(s1.count(), item1.max_stack_);
expecteq(s2.count(), 80 - item1.max_stack_);
expecteq(s1.count(), item1.maxStack_);
expecteq(s2.count(), 80 - item1.maxStack_);
} }


test("Insert respects max_stack_") { test("Insert respects max_stack_") {
MockItem item1({ .name = "item1", .image = "no", .max_stack = 20 });
MockItem item1({ .name = "item1", .image = "no", .maxStack = 20 });


Swan::ItemStack s1(&item1, 15); Swan::ItemStack s1(&item1, 15);
Swan::ItemStack s2(&item1, 19); Swan::ItemStack s2(&item1, 19);

+ 18
- 18
libswan/test/lib/test.cc View File



namespace testlib { namespace testlib {


static const std::string color_reset = "\033[0m";
static const std::string color_highlight = "\033[1m";
static const std::string color_testing = color_highlight;
static const std::string color_desc = "\033[33m";
static const std::string color_maybe = "\033[35m";
static const std::string color_success = "\033[32m";
static const std::string color_fail = "\033[31m";
static const std::string color_errormsg = "\033[95m";
static const std::string COLOR_RESET = "\033[0m";
static const std::string COLOR_HIGHLIGHT = "\033[1m";
static const std::string COLOR_TESTING = COLOR_HIGHLIGHT;
static const std::string COLOR_DESC = "\033[33m";
static const std::string COLOR_MAYBE = "\033[35m";
static const std::string COLOR_SUCCESS = "\033[32m";
static const std::string COLOR_FAIL = "\033[31m";
static const std::string COLOR_ERRORMSG = "\033[95m";


std::string color(const std::string &color, std::string_view str) { std::string color(const std::string &color, std::string_view str) {
return std::string(color) + std::string(str) + std::string(color_reset);
return std::string(color) + std::string(str) + std::string(COLOR_RESET);
} }


struct TestCase { struct TestCase {
static std::stringstream printFailure(const std::string &msg) { static std::stringstream printFailure(const std::string &msg) {
std::stringstream str; std::stringstream str;
str str
<< "\r" << color(color_highlight + color_fail, "✕ ")
<< color(color_fail, "Failed: ") << "\n"
<< "\r" << color(COLOR_HIGHLIGHT + COLOR_FAIL, "✕ ")
<< color(COLOR_FAIL, "Failed: ") << "\n"
<< " " << msg << "\n"; << " " << msg << "\n";
return str; return str;
} }
if (currfile != testcase.filename) { if (currfile != testcase.filename) {
currfile = testcase.filename; currfile = testcase.filename;
size_t lastslash = currfile.find_last_of('/'); size_t lastslash = currfile.find_last_of('/');
std::cout << '\n' << color(color_testing, currfile.substr(lastslash + 1)) << ":\n";
std::cout << '\n' << color(COLOR_TESTING, currfile.substr(lastslash + 1)) << ":\n";
} }


std::cout std::cout
<< color(color_highlight + color_maybe, "? ")
<< color(color_maybe, "Testing: ")
<< color(color_desc, testcase.description) << " " << std::flush;
<< color(COLOR_HIGHLIGHT + COLOR_MAYBE, "? ")
<< color(COLOR_MAYBE, "Testing: ")
<< color(COLOR_DESC, testcase.description) << " " << std::flush;


bool casefailed = false; bool casefailed = false;


totaltests += 1; totaltests += 1;
testcase.func(); testcase.func();
std::cout std::cout
<< "\r" << color(color_highlight + color_success, "✓ ")
<< color(color_success, "Success: ")
<< color(color_desc, testcase.description) << "\n";
<< "\r" << color(COLOR_HIGHLIGHT + COLOR_SUCCESS, "✓ ")
<< color(COLOR_SUCCESS, "Success: ")
<< color(COLOR_DESC, testcase.description) << "\n";
totalsuccess += 1; totalsuccess += 1;
} catch (const TestFailure &failure) { } catch (const TestFailure &failure) {
casefailed = true; casefailed = true;

+ 1
- 1
src/lighting-test.cc View File

png::image<png::rgb_pixel> image(Swan::CHUNK_WIDTH, Swan::CHUNK_HEIGHT); png::image<png::rgb_pixel> image(Swan::CHUNK_WIDTH, Swan::CHUNK_HEIGHT);
for (int y = 0; y < Swan::CHUNK_HEIGHT; ++y) { for (int y = 0; y < Swan::CHUNK_HEIGHT; ++y) {
for (int x = 0; x < Swan::CHUNK_WIDTH; ++x) { for (int x = 0; x < Swan::CHUNK_WIDTH; ++x) {
uint8_t light = cb.chunk_.light_levels[y * Swan::CHUNK_WIDTH + x];
uint8_t light = cb.chunk_.lightLevels[y * Swan::CHUNK_WIDTH + x];
bool block = false; bool block = false;
if (cb.chunk_.blocks[y * Swan::CHUNK_WIDTH + x]) { if (cb.chunk_.blocks[y * Swan::CHUNK_WIDTH + x]) {
block = true; block = true;

+ 57
- 57
src/main.cc View File

} }


int main(int argc, char **argv) { int main(int argc, char **argv) {
uint32_t winflags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
uint32_t renderflags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
float gui_scale = 1;
uint32_t winFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
uint32_t renderFlags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
float guiScale = 1;


for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--lodpi") == 0) { if (strcmp(argv[i], "--lodpi") == 0) {
winflags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
winFlags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
} else if (strcmp(argv[i], "--fullscreen") == 0) { } else if (strcmp(argv[i], "--fullscreen") == 0) {
winflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} else if (strcmp(argv[i], "--no-vsync") == 0) { } else if (strcmp(argv[i], "--no-vsync") == 0) {
renderflags &= ~SDL_RENDERER_PRESENTVSYNC;
renderFlags &= ~SDL_RENDERER_PRESENTVSYNC;
} else if (strcmp(argv[i], "--vulkan") == 0) { } else if (strcmp(argv[i], "--vulkan") == 0) {
winflags |= SDL_WINDOW_VULKAN;
winFlags |= SDL_WINDOW_VULKAN;
} else if (strcmp(argv[i], "--sw-render") == 0) { } else if (strcmp(argv[i], "--sw-render") == 0) {
renderflags &= ~SDL_RENDERER_ACCELERATED;
renderflags |= SDL_RENDERER_SOFTWARE;
renderFlags &= ~SDL_RENDERER_ACCELERATED;
renderFlags |= SDL_RENDERER_SOFTWARE;
} else if (strcmp(argv[i], "--2x") == 0) { } else if (strcmp(argv[i], "--2x") == 0) {
gui_scale = 2;
guiScale = 2;
} else if (strcmp(argv[i], "--gles") == 0) { } else if (strcmp(argv[i], "--gles") == 0) {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2");
} else { } else {
sdlassert(SDL_Init(SDL_INIT_VIDEO) >= 0, "Could not initialize SDL"); sdlassert(SDL_Init(SDL_INIT_VIDEO) >= 0, "Could not initialize SDL");
Deferred<SDL_Quit> sdl; Deferred<SDL_Quit> sdl;


int imgflags = IMG_INIT_PNG;
imgassert(IMG_Init(imgflags) == imgflags, "Could not initialize SDL_Image");
Deferred<IMG_Quit> sdl_image;
int imgFlags = IMG_INIT_PNG;
imgassert(IMG_Init(imgFlags) == imgFlags, "Could not initialize SDL_Image");
Deferred<IMG_Quit> sdlImage;


// Create the window // Create the window
CPtr<SDL_Window, SDL_DestroyWindow> window( CPtr<SDL_Window, SDL_DestroyWindow> window(
SDL_CreateWindow( SDL_CreateWindow(
"Project: SWAN", "Project: SWAN",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
(int)(640 * gui_scale), (int)(480 * gui_scale), winflags));
(int)(640 * guiScale), (int)(480 * guiScale), winFlags));


// Load and display application icon // Load and display application icon
CPtr<SDL_Surface, SDL_FreeSurface> icon( CPtr<SDL_Surface, SDL_FreeSurface> icon(
SDL_SetWindowIcon(window.get(), icon.get()); SDL_SetWindowIcon(window.get(), icon.get());


CPtr<SDL_Renderer, SDL_DestroyRenderer> renderer( CPtr<SDL_Renderer, SDL_DestroyRenderer> renderer(
SDL_CreateRenderer(window.get(), -1, renderflags));
SDL_CreateRenderer(window.get(), -1, renderFlags));
sdlassert(renderer, "Could not create renderer"); sdlassert(renderer, "Could not create renderer");
SDL_SetRenderDrawBlendMode(renderer.get(), SDL_BLENDMODE_BLEND); SDL_SetRenderDrawBlendMode(renderer.get(), SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, 255); SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, 255);


Win win(window.get(), renderer.get(), gui_scale);
Win win(window.get(), renderer.get(), guiScale);


// Init ImGUI and ImGUI_SDL // Init ImGUI and ImGUI_SDL
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
CPtr<ImGuiContext, ImGui::DestroyContext> context( CPtr<ImGuiContext, ImGui::DestroyContext> context(
ImGui::CreateContext()); ImGui::CreateContext());
ImGuiSDL::Initialize(renderer.get(), (int)win.getPixSize().x, (int)win.getPixSize().y); ImGuiSDL::Initialize(renderer.get(), (int)win.getPixSize().x, (int)win.getPixSize().y);
Deferred<ImGuiSDL::Deinitialize> imgui_sdl;
Deferred<ImGuiSDL::Deinitialize> imguiSDL;
info << "Initialized with window size " << win.getPixSize(); info << "Initialized with window size " << win.getPixSize();


// ImGuiIO is to glue SDL and ImGUI together // ImGuiIO is to glue SDL and ImGUI together
ImGuiIO& imgui_io = ImGui::GetIO();
imgui_io.BackendPlatformName = "imgui_sdl + Project: SWAN";
ImGuiIO& imguiIO = ImGui::GetIO();
imguiIO.BackendPlatformName = "imgui_sdl + Project: SWAN";


// Create a world // Create a world
Game game(win); Game game(win);
std::vector<std::string> mods{ "core.mod" }; std::vector<std::string> mods{ "core.mod" };
game.createWorld("core::default", mods); game.createWorld("core::default", mods);


auto prev_time = std::chrono::steady_clock::now();
auto prevTime = std::chrono::steady_clock::now();


float fps_acc = 0;
float tick_acc = 0;
float fpsAcc = 0;
float tickAcc = 0;


int fcount = 0;
int slow_frames = 0;
int fCount = 0;
int slowFrames = 0;
while (1) { while (1) {
ZoneScopedN("game loop"); ZoneScopedN("game loop");
RTClock total_time_clock;
RTClock totalTimeClock;


SDL_Event evt; SDL_Event evt;
while (SDL_PollEvent(&evt)) { while (SDL_PollEvent(&evt)) {


case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
if (evt.window.event == SDL_WINDOWEVENT_RESIZED) { if (evt.window.event == SDL_WINDOWEVENT_RESIZED) {
imgui_io.DisplaySize.x = (float)evt.window.data1;
imgui_io.DisplaySize.y = (float)evt.window.data2;
imguiIO.DisplaySize.x = (float)evt.window.data1;
imguiIO.DisplaySize.y = (float)evt.window.data2;
win.onResize(evt.window.data1, evt.window.data2); win.onResize(evt.window.data1, evt.window.data2);
} }
break; break;
break; break;


case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
imgui_io.MousePos.x = (float)evt.motion.x;
imgui_io.MousePos.y = (float)evt.motion.y;
if (!imgui_io.WantCaptureMouse)
imguiIO.MousePos.x = (float)evt.motion.x;
imguiIO.MousePos.y = (float)evt.motion.y;
if (!imguiIO.WantCaptureMouse)
game.onMouseMove(evt.motion.x, evt.motion.y); game.onMouseMove(evt.motion.x, evt.motion.y);
break; break;


case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
imgui_io.MouseDown[sdlButtonToImGuiButton(evt.button.button)] = true;
if (!imgui_io.WantCaptureMouse)
imguiIO.MouseDown[sdlButtonToImGuiButton(evt.button.button)] = true;
if (!imguiIO.WantCaptureMouse)
game.onMouseDown(evt.button.x, evt.button.y, evt.button.button); game.onMouseDown(evt.button.x, evt.button.y, evt.button.button);
break; break;


case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
imgui_io.MouseDown[sdlButtonToImGuiButton(evt.button.button)] = false;
if (!imgui_io.WantCaptureMouse)
imguiIO.MouseDown[sdlButtonToImGuiButton(evt.button.button)] = false;
if (!imguiIO.WantCaptureMouse)
game.onMouseUp(evt.button.x, evt.button.y, evt.button.button); game.onMouseUp(evt.button.x, evt.button.y, evt.button.button);
break; break;


case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
imgui_io.MouseWheel += (float)evt.wheel.y;
if (!imgui_io.WantCaptureMouse)
imguiIO.MouseWheel += (float)evt.wheel.y;
if (!imguiIO.WantCaptureMouse)
game.onScrollWheel(evt.wheel.y); game.onScrollWheel(evt.wheel.y);
break; break;
} }
} }


auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
std::chrono::duration<float> dur(now - prev_time);
prev_time = now;
std::chrono::duration<float> dur(now - prevTime);
prevTime = now;
float dt = dur.count(); float dt = dur.count();


// Display FPS // Display FPS
fps_acc += dt;
fcount += 1;
if (fps_acc >= 4) {
info << "FPS: " << fcount / 4.0;
fps_acc -= 4;
fcount = 0;
fpsAcc += dt;
fCount += 1;
if (fpsAcc >= 4) {
info << "FPS: " << fCount / 4.0;
fpsAcc -= 4;
fCount = 0;
} }


// We want to warn if one frame takes over 0.1 seconds... // We want to warn if one frame takes over 0.1 seconds...
if (dt > 0.1) { if (dt > 0.1) {
if (slow_frames == 0)
if (slowFrames == 0)
warn << "Delta time too high! (" << dt << "s)"; warn << "Delta time too high! (" << dt << "s)";
slow_frames += 1;
slowFrames += 1;


// And we never want to do physics as if our one frame is greater than // And we never want to do physics as if our one frame is greater than
// 0.5 seconds. // 0.5 seconds.
if (dt > 0.5) if (dt > 0.5)
dt = 0.5; dt = 0.5;
} else if (slow_frames > 0) {
if (slow_frames > 1)
warn << slow_frames << " consecutive slow frames.";
slow_frames = 0;
} else if (slowFrames > 0) {
if (slowFrames > 1)
warn << slowFrames << " consecutive slow frames.";
slowFrames = 0;
} }


// Simple case: we can keep up, only need one physics update // Simple case: we can keep up, only need one physics update
RTClock update_clock;
RTClock updateClock;
if (dt <= 1 / 25.0) { if (dt <= 1 / 25.0) {
ZoneScopedN("game update"); ZoneScopedN("game update");
game.update(dt); game.update(dt);
} }


// Tick at a consistent TICK_RATE // Tick at a consistent TICK_RATE
tick_acc += dt;
while (tick_acc >= 1.0 / TICK_RATE) {
tickAcc += dt;
while (tickAcc >= 1.0 / TICK_RATE) {
ZoneScopedN("game tick"); ZoneScopedN("game tick");
tick_acc -= 1.0 / TICK_RATE;
tickAcc -= 1.0 / TICK_RATE;
RTClock tick_clock; RTClock tick_clock;
game.tick(1.0 / TICK_RATE); game.tick(1.0 / TICK_RATE);
} }
} }


// ImGUI // ImGUI
imgui_io.DeltaTime = dt;
imguiIO.DeltaTime = dt;
ImGui::NewFrame(); ImGui::NewFrame();


{ {
ZoneScopedN("game draw"); ZoneScopedN("game draw");
RTClock draw_clock;
RTClock drawClock;
game.draw(); game.draw();
} }


ImGuiSDL::Render(ImGui::GetDrawData()); ImGuiSDL::Render(ImGui::GetDrawData());
} }


RTClock present_clock;
RTClock presentClock;
{ {
ZoneScopedN("render present"); ZoneScopedN("render present");
SDL_RenderPresent(renderer.get()); SDL_RenderPresent(renderer.get());

+ 7
- 7
src/perlin-test.cc View File

#include <PerlinNoise/PerlinNoise.hpp> #include <PerlinNoise/PerlinNoise.hpp>
#include <png++/png.hpp> #include <png++/png.hpp>


static int grassLevel(const siv::PerlinNoise &perlin, int x) {
static int getGrassLevel(const siv::PerlinNoise &perlin, int x) {
return (int)(perlin.noise(x / 50.0, 0) * 13); return (int)(perlin.noise(x / 50.0, 0) * 13);
} }


static int stoneLevel(const siv::PerlinNoise &perlin, int x) {
static int getStoneLevel(const siv::PerlinNoise &perlin, int x) {
return (int)(perlin.noise(x / 50.0, 10) * 10) + 10; return (int)(perlin.noise(x / 50.0, 10) * 10) + 10;
} }




for (int x = x1; x <= x2; ++x) { for (int x = x1; x <= x2; ++x) {
int px = x - x1; int px = x - x1;
int grass_level = grassLevel(perlin, x);
int stone_level = stoneLevel(perlin, x);
int grassLevel = getGrassLevel(perlin, x);
int stoneLevel = getStoneLevel(perlin, x);


for (int y = y1; y <= y2; ++y) { for (int y = y1; y <= y2; ++y) {
int py = y - y1; int py = y - y1;


if (y > grass_level + 10) {
if (y > grassLevel + 10) {
double l = perlin.noise(x / 41.37, y / 16.37); double l = perlin.noise(x / 41.37, y / 16.37);
if (l > 0.2) if (l > 0.2)
image[py][px] = 255; image[py][px] = 255;
else else
image[py][px] = 0; image[py][px] = 0;
} else if (y >= grass_level) {
} else if (y >= grassLevel) {
image[py][px] = 0; image[py][px] = 0;
} else { } else {
image[py][px] = 255; image[py][px] = 255;
} }


if (y == grass_level) {
if (y == grassLevel) {
image[py][px] = 128; image[py][px] = 128;
} }
} }

Loading…
Cancel
Save