From 49258497471f23e5abcc5093cada10e758918f54 Mon Sep 17 00:00:00 2001 From: JISAUAY Date: Wed, 19 Nov 2025 14:30:31 -0600 Subject: [PATCH] trying to restructure how chunks hold voxels --- src/Camera.h | 2 +- src/Chunk.cpp | 48 ++++++++++++++++++++++++++++++------------------ src/Chunk.h | 45 +++++++++++++++++++++++++++++++++++++++++---- src/World.cpp | 46 ++++++++++++++++++++++------------------------ src/World.h | 4 ++-- src/main.cpp | 14 +------------- 6 files changed, 97 insertions(+), 62 deletions(-) diff --git a/src/Camera.h b/src/Camera.h index 063a7ab..99ba6f6 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -115,7 +115,7 @@ class Camera for (int y = min.y; y < max.y && !collided; y++) { for (int z = min.z; z < max.z && !collided; z++) { glm::ivec3 calculated_position = glm::ivec3(x, y, z); - if (world->get_voxel(calculated_position).has_value()) { + if (world->get_voxel(calculated_position) != VoxelKind::Air) { collided = true; // std::cout << "Collided!!" << std::endl; } diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 0bc1864..e22f0e9 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -49,13 +49,13 @@ void addFace(glm::ivec3 pos, const float *faceVertices, float voxelKind, std::ve float world_x = v[0] + pos.x; float world_y = v[1] + pos.y; float world_z = v[2] + pos.z; - + // Debug: Print first vertex of first face if (vbo.empty() && i == 0) { std::cout << "[MESH] First vertex - Local pos: (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl; std::cout << "[MESH] First vertex - World: (" << world_x << ", " << world_y << ", " << world_z << ")" << std::endl; } - + vbo.push_back(world_x); vbo.push_back(world_y); vbo.push_back(world_z); @@ -73,16 +73,12 @@ void addFace(glm::ivec3 pos, const float *faceVertices, float voxelKind, std::ve } Chunk::Chunk() { - // mesh is initially null (nullptr) -} - -Chunk::Chunk(std::unordered_map voxels) { - this->voxels = voxels; + this->data.fill(VoxelKind::Air); } Mesh* Chunk::getMesh() { // Generate mesh on first access if not already generated - if (!mesh && !voxels.empty()) { + if (!mesh && !this->isEmpty()) { generateMesh(); } return mesh.get(); @@ -103,14 +99,35 @@ void Chunk::generateMesh() { std::pair(glm::ivec3(0, 0, -1), backFace) // back }; - for (const std::pair& pair : voxels) { - const glm::ivec3& pos = pair.first; - const VoxelKind& kind = pair.second; + for (int i = 0; i < TOTAL; ++i) { + VoxelKind kind = this->data[i]; + if (kind == VoxelKind::Air) continue; - for (const std::pair& neighborPair : neighbors) { + int x = i % SIZE; + int y = (i / SIZE) % SIZE; + int z = i / (SIZE * SIZE); + + glm::ivec3 pos(x, y, z); + + for (const auto& neighborPair : neighbors) { glm::ivec3 dir = neighborPair.first; const float* face = neighborPair.second; - if (voxels.count(pos + dir) == 0) { + int nx = x + dir.x; + int ny = y + dir.y; + int nz = z + dir.z; + + bool neighbour_is_solid = false; + // in-bounds neighbor -> check data + if (nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE && nz >= 0 && nz < SIZE) { + std::cout << "Accessing " << nx << ", " << ny << ", " << nz << std::endl; + neighbour_is_solid = (data[index(nx, ny, nz)] != VoxelKind::Air); + } else { + // neighbour out of this chunk: treat as air (exposed face) + // or call into World to query adjacent chunk here + neighbour_is_solid = false; + } + + if (!neighbour_is_solid) { addFace(pos, face, static_cast(kind), vboData, eboData); } } @@ -127,10 +144,5 @@ void Chunk::generateMesh() { } void Chunk::regenerateMesh() { - std::cout << "[REGENERATE] Regenerating mesh with " << voxels.size() << " voxels" << std::endl; - if (!voxels.empty()) { - auto first = voxels.begin(); - std::cout << "[REGENERATE] First voxel at local: (" << first->first.x << ", " << first->first.y << ", " << first->first.z << ")" << std::endl; - } this->generateMesh(); } diff --git a/src/Chunk.h b/src/Chunk.h index 925b453..886cca9 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -1,14 +1,19 @@ #ifndef CHUNK_H #define CHUNK_H +#include #include #include +#include +#include +#include #include #include "Mesh.h" -enum VoxelKind { +enum class VoxelKind : uint8_t { + Air, Dirt, Stone }; @@ -16,6 +21,7 @@ enum VoxelKind { const int CHUNK_SIZE = 32; // Hash function for glm::ivec3 to use with unordered_set +// Still used in World.h namespace std { template <> struct hash { @@ -33,18 +39,49 @@ namespace std { class Chunk { public: - std::unordered_map voxels; Chunk(); - Chunk(std::unordered_map voxels); Mesh* getMesh(); // Returns pointer, can be null if not generated yet void generateMesh(); void regenerateMesh(); + // Accessors + inline VoxelKind get(int x, int y, int z) const { + return data[index(x, y, z)]; + } + inline VoxelKind get(glm::ivec3 pos) const { + std::cout << "Getting Pos (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl; + return data[index(pos.x, pos.y, pos.z)]; + } + + // Mutators + inline void set(int x, int y, int z, VoxelKind v) { + data[index(x, y, z)] = v; + } + inline void set(glm::ivec3 pos, VoxelKind v) { + // std::cout << "Setting Pos (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl; + data[index(pos.x, pos.y, pos.z)] = v; + } + + // Other + inline bool isEmpty() const { + return std::all_of(data.begin(), data.end(), [](VoxelKind k){ + return k == VoxelKind::Air; + }); + } + private: + static constexpr int SIZE = CHUNK_SIZE; + static constexpr int TOTAL = SIZE * SIZE * SIZE; + std::array data; + std::unique_ptr mesh; // Nullable mesh + + static inline constexpr int index(int x, int y, int z) noexcept { + return x + (y * SIZE) + (z * SIZE * SIZE); + } }; -#endif CHUNK_H +#endif // CHUNK_H diff --git a/src/World.cpp b/src/World.cpp index 3552ec5..47402b0 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -22,29 +22,26 @@ glm::ivec3 floor_divide(glm::ivec3 value, int divisor) { World::World() {} -void World::set_voxel(glm::ivec3 position, std::optional kind) { +void World::set_voxel(glm::ivec3 position, VoxelKind kind) { glm::ivec3 chunk_position = floor_divide(position, CHUNK_SIZE); glm::ivec3 local_position = positive_modulo(position, CHUNK_SIZE); - std::cout << "[SET_VOXEL] World pos: (" << position.x << ", " << position.y << ", " << position.z << ")" << std::endl; - std::cout << "[SET_VOXEL] Chunk pos: (" << chunk_position.x << ", " << chunk_position.y << ", " << chunk_position.z << ")" << std::endl; - std::cout << "[SET_VOXEL] Local pos: (" << local_position.x << ", " << local_position.y << ", " << local_position.z << ")" << std::endl; + // std::cout << "[SET_VOXEL] World pos: (" << position.x << ", " << position.y << ", " << position.z << ")" << std::endl; + // std::cout << "[SET_VOXEL] Chunk pos: (" << chunk_position.x << ", " << chunk_position.y << ", " << chunk_position.z << ")" << std::endl; + // std::cout << "[SET_VOXEL] Local pos: (" << local_position.x << ", " << local_position.y << ", " << local_position.z << ")" << std::endl; Chunk * chunk = get_or_create_chunk(chunk_position); - if (!kind.has_value()) { - chunk->voxels.erase(chunk->voxels.find(local_position)); - } else { - chunk->voxels[local_position] = kind.value(); - } + chunk->set(local_position, kind); chunk->regenerateMesh(); } Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) { - if (this->chunks.count(chunk_position) == 0) { - // Fill in new chunk with a flat plane of voxels at y=0 - std::unordered_map voxels; + // Fill in new chunk with a flat plane of voxels at y=0 + auto [it, inserted] = this->chunks.try_emplace(chunk_position); + Chunk& chunk = it->second; + if (inserted) { if (chunk_position.y == 0) { for (int z = 0; z < CHUNK_SIZE; z++) { for (int y = 0; y < CHUNK_SIZE; y++) { @@ -52,19 +49,23 @@ Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) { glm::ivec3 offset = glm::ivec3(x, y, z); glm::ivec3 world_voxel_position = chunk_position * CHUNK_SIZE + offset; + VoxelKind kind; if (world_voxel_position.y == 0) { - voxels[offset] = VoxelKind::Dirt; + kind = VoxelKind::Dirt; } else if (world_voxel_position.y < 0) { - voxels[offset] = VoxelKind::Stone; + kind = VoxelKind::Stone; + } else { + kind = VoxelKind::Air; } + + chunk.set(offset, kind); } } } } - - this->chunks[chunk_position] = Chunk(voxels); } - return &this->chunks[chunk_position]; + + return &chunk; } std::optional> World::raycast_voxel(glm::vec3 start, glm::vec3 direction, float max_dist) { @@ -133,7 +134,7 @@ std::optional> World::raycast_voxel(glm::vec3 // std::cout << "[RAYCAST] Iteration " << iterations << " at voxel (" << pos.x << ", " << pos.y << ", " << pos.z << ")" << std::endl; - if (this->get_voxel(pos).has_value()) { + if (this->get_voxel(pos) != VoxelKind::Air) { // std::cout << "[RAYCAST] Hit voxel at (" << pos.x << ", " << pos.y << ", " << pos.z << ") after " << iterations << " iterations" << std::endl; // std::cout << "[RAYCAST] Returning normal: (" << -last_move.x << ", " << -last_move.y << ", " << -last_move.z << ")" << std::endl; return std::make_pair(pos, -last_move); @@ -162,14 +163,11 @@ std::optional> World::raycast_voxel(glm::vec3 return std::nullopt; } -std::optional World::get_voxel(glm::ivec3 position) { +VoxelKind World::get_voxel(glm::ivec3 position) { glm::ivec3 chunk_position = floor_divide(position, CHUNK_SIZE); glm::ivec3 local_position = positive_modulo(position, CHUNK_SIZE); Chunk* chunk = this->get_or_create_chunk(chunk_position); - auto it = chunk->voxels.find(local_position); - if (it != chunk->voxels.end()) { - return it->second; - } - return std::nullopt; + + return chunk->get(position); } diff --git a/src/World.h b/src/World.h index fa51d65..9993f68 100644 --- a/src/World.h +++ b/src/World.h @@ -15,11 +15,11 @@ class World World(); - void set_voxel(glm::ivec3 position, std::optional kind); + void set_voxel(glm::ivec3 position, VoxelKind kind); std::optional> raycast_voxel(glm::vec3 start, glm::vec3 direction, float max_dist); - std::optional get_voxel(glm::ivec3 position); + VoxelKind get_voxel(glm::ivec3 position); Chunk* get_or_create_chunk(glm::ivec3 chunk_position); }; diff --git a/src/main.cpp b/src/main.cpp index af55869..ad335d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -248,7 +248,7 @@ void processInput(GLFWwindow *window) auto [target_block, normal] = raycast_result.value(); std::cout << "Hit block at: (" << target_block.x << ", " << target_block.y << ", " << target_block.z << ")" << std::endl; std::cout << "Removing block at: (" << target_block.x << ", " << target_block.y << ", " << target_block.z << ")" << std::endl; - world.set_voxel(target_block, std::nullopt); + world.set_voxel(target_block, VoxelKind::Air); } else { std::cout << "No block hit within range" << std::endl; } @@ -437,18 +437,6 @@ int main() { Chunk* chunk = world.get_or_create_chunk(chunk_pos); Mesh* chunk_mesh = chunk->getMesh(); - if (chunk_mesh) { - // Debug: Log when rendering chunks with your placed block - static bool logged_placed_block = false; - if (!logged_placed_block && chunk->voxels.count(glm::ivec3(0, 1, 31)) > 0) { - std::cout << "\n[RENDER] ===== FOUND PLACED BLOCK =====" << std::endl; - std::cout << "[RENDER] Drawing chunk at pos (" << chunk_pos.x << ", " << chunk_pos.y << ", " << chunk_pos.z << ")" << std::endl; - std::cout << "[RENDER] Translation: (" << (chunk_pos.x * CHUNK_SIZE) << ", " << (chunk_pos.y * CHUNK_SIZE) << ", " << (chunk_pos.z * CHUNK_SIZE) << ")" << std::endl; - std::cout << "[RENDER] Final world position should be: (" << (chunk_pos.x * CHUNK_SIZE + 0) << ", " << (chunk_pos.y * CHUNK_SIZE + 1) << ", " << (chunk_pos.z * CHUNK_SIZE + 31) << ")" << std::endl; - logged_placed_block = true; - } - chunk_mesh->draw(); - } } } }