diff --git a/core/include/vf/Chunk.h b/core/include/vf/Chunk.h new file mode 100644 index 0000000..7de93df --- /dev/null +++ b/core/include/vf/Chunk.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include "Block.h" + +namespace vf { + +constexpr int chunkSize = 16; +constexpr int chunkVolume = chunkSize * chunkSize * chunkSize; + +inline bool inChunkBounds(int x, int y, int z) { + return x >= 0 && x < chunkSize && y >= 0 && y < chunkSize && z >= 0 && z < chunkSize; +} + +inline int localIndex(int x, int y, int z) { + return (y * chunkSize + z) * chunkSize + x; +} + +class Chunk { +public: + Chunk() { blocks.fill(Block::Air); } + + Block at(int x, int y, int z) const { return blocks[localIndex(x, y, z)]; } + void set(int x, int y, int z, Block b) { blocks[localIndex(x, y, z)] = b; } + + bool empty() const { + for (Block b : blocks) + if (b != Block::Air) return false; + return true; + } + + const std::array& raw() const { return blocks; } + std::array& raw() { return blocks; } + +private: + std::array blocks; +}; + +} // namespace vf diff --git a/core/include/vf/World.h b/core/include/vf/World.h new file mode 100644 index 0000000..525e7d2 --- /dev/null +++ b/core/include/vf/World.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include "Chunk.h" +#include "Math.h" + +namespace vf { + +inline int floorDiv(int a, int b) { + int q = a / b; + int r = a % b; + if (r != 0 && ((r < 0) != (b < 0))) --q; + return q; +} + +inline int floorMod(int a, int b) { + int r = a % b; + if (r != 0 && ((r < 0) != (b < 0))) r += b; + return r; +} + +class World { +public: + Chunk* chunkAt(const Vec3i& coord) { + auto it = chunks.find(coord); + return it == chunks.end() ? nullptr : it->second.get(); + } + + const Chunk* chunkAt(const Vec3i& coord) const { + auto it = chunks.find(coord); + return it == chunks.end() ? nullptr : it->second.get(); + } + + Chunk& ensureChunk(const Vec3i& coord) { + auto it = chunks.find(coord); + if (it != chunks.end()) return *it->second; + auto inserted = chunks.emplace(coord, std::make_unique()); + return *inserted.first->second; + } + + bool removeChunk(const Vec3i& coord) { return chunks.erase(coord) != 0; } + + Block blockAt(int gx, int gy, int gz) const { + Vec3i coord{floorDiv(gx, chunkSize), floorDiv(gy, chunkSize), floorDiv(gz, chunkSize)}; + const Chunk* chunk = chunkAt(coord); + if (!chunk) return Block::Air; + return chunk->at(floorMod(gx, chunkSize), floorMod(gy, chunkSize), floorMod(gz, chunkSize)); + } + + void setBlock(int gx, int gy, int gz, Block b) { + Vec3i coord{floorDiv(gx, chunkSize), floorDiv(gy, chunkSize), floorDiv(gz, chunkSize)}; + Chunk& chunk = ensureChunk(coord); + chunk.set(floorMod(gx, chunkSize), floorMod(gy, chunkSize), floorMod(gz, chunkSize), b); + } + + bool solidAt(int gx, int gy, int gz) const { return isSolid(blockAt(gx, gy, gz)); } + + const std::unordered_map, Vec3iHash>& allChunks() const { + return chunks; + } + + std::size_t chunkCount() const { return chunks.size(); } + +private: + std::unordered_map, Vec3iHash> chunks; +}; + +} // namespace vf