diff --git a/.attachments/chunk-generation.png b/.attachments/chunk-generation.png new file mode 100644 index 0000000..7a436e2 Binary files /dev/null and b/.attachments/chunk-generation.png differ diff --git a/README.md b/README.md index 475ce8a..b863199 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,8 @@ Instead of making the _entire_ world a single mesh, now the world is made up of ![cube world (but in chunks!)](.attachments/chunk-render.png) +## 0.4.0 : Chunk Generation - 11/12/25 + +Chunks now generate around the player in a square based on a render distance. This allows the flat plane to be infinite! + +![infinite flat cube world](.attachments/chunk-generation.png) diff --git a/src/Camera.h b/src/Camera.h index f22111a..a545a52 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -73,9 +73,9 @@ class Camera void move(float time) // time passed in seconds since last move { - bool in_air = this->position.y > 0; + bool in_air = this->position.y > 1; - if (this->position.y > 0) + if (this->position.y > 1) this->vertical_velocity.y -= (GRAVITY * time); // no way! physics reference? glm::vec3 velocity = this->forward_velocity + this->horizontal_velocity + this->vertical_velocity; @@ -84,7 +84,7 @@ class Camera this->position += movement; // Don't want to clip into the ground right - if (this->position.y <= 0 && in_air) + if (this->position.y <= 1 && in_air) { this->position.y = 0.0f; this->vertical_velocity.y = 0.0f; diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 208087c..e98269c 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1,5 +1,6 @@ #include "Chunk.h" #include +#include const int VERTEX_SIZE = 6; @@ -64,6 +65,10 @@ Chunk::Chunk() { // mesh is initially null (nullptr) } +Chunk::Chunk(std::unordered_set voxels) { + this->voxels = voxels; +} + Mesh* Chunk::getMesh() { // Generate mesh on first access if not already generated if (!mesh && !voxels.empty()) { @@ -76,6 +81,8 @@ void Chunk::generateMesh() { std::vector vboData; std::vector eboData; + // std::cout << "Generating Chunk mesh..." << std::endl; + for (const glm::ivec3 pos : voxels) { if (voxels.count(pos + glm::ivec3(0, 1, 0)) == 0) { addFace(pos, topFace, vboData, eboData); diff --git a/src/Chunk.h b/src/Chunk.h index 32e0de0..cdce79b 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -31,6 +31,7 @@ class Chunk { std::unordered_set voxels; Chunk(); + Chunk(std::unordered_set voxels); Mesh* getMesh(); // Returns pointer, can be null if not generated yet diff --git a/src/Mesh.cpp b/src/Mesh.cpp index e429e4f..bc16f36 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -40,7 +40,7 @@ void Mesh::init() { void Mesh::uploadData(const std::vector &vboData, const std::vector &eboData) { m_indexCount = eboData.size(); - std::cout << "Uploading mesh: " << vboData.size() << " floats, " << eboData.size() << " indices" << std::endl; + // std::cout << "Uploading mesh: " << vboData.size() << " floats, " << eboData.size() << " indices" << std::endl; // Bind VAO first, then upload buffer data glBindVertexArray(m_VAO); diff --git a/src/World.cpp b/src/World.cpp index 3ad6728..1465a2e 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -16,7 +16,18 @@ void World::flip_voxel(glm::ivec3 position) { Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) { if (this->chunks.count(chunk_position) == 0) { - this->chunks[chunk_position] = Chunk(); + + // Fill in new chunk with a flat plane of voxels at y=0 + std::unordered_set voxels; + if (chunk_position.y == 0) { + for (int z = 0; z <= 32; z++) { + for (int x = 0; x <= 32; x++) { + voxels.insert(glm::ivec3(x, 0, z)); + } + } + } + + this->chunks[chunk_position] = Chunk(voxels); } return &this->chunks[chunk_position]; } diff --git a/src/main.cpp b/src/main.cpp index c080291..6d0f431 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,10 +28,12 @@ const float Z_NEAR = 0.1f; bool WIREFRAME_MODE = !true; const float FOV = 110.0f; +const int VIEW_DISTANCE = 4; // In Chunks + bool W_pressed, S_pressed, A_pressed, D_pressed = false; glm::mat4 projection = glm::mat4(1.0f); -Camera camera = Camera(CAMERA_SPEED, glm::vec3(0.0f, 0.0f, 3.0f)); +Camera camera = Camera(CAMERA_SPEED, glm::vec3(0.0f, 1.0f, 3.0f)); // Callback function for when the window is resized // glfw: whenever the window size changed (by OS or user resize) this callback function executes @@ -181,7 +183,7 @@ int main() { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 2. --- Create a Window --- - GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "HelloTriangle", NULL, NULL); + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Voxel Engine 0.4.0", NULL, NULL); if (window == NULL) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); @@ -205,17 +207,9 @@ int main() { // Load Cube OBJ World world; - Object3D cube = Object3D("objs/cube.obj"); + // Object3D cube = Object3D("objs/cube.obj"); - // Place cubes in the world - for (int z = 0; z <= 256; z++) { - for (int x = 0; x <= 256; x++) { - world.flip_voxel(glm::ivec3(x, -1, z)); - } - } - - world.flip_voxel(glm::ivec3(7, 0, 13)); - std::cout << "World created with " << world.chunks.size() << " chunks" << std::endl; + // std::cout << "World created with " << world.chunks.size() << " chunks" << std::endl; // Mesh generation now happens automatically when first rendering if (WIREFRAME_MODE) @@ -251,31 +245,35 @@ int main() { std::cout << "\n=== RENDER DEBUG ===" << std::endl; std::cout << "Camera pos: (" << camera.position.x << ", " << camera.position.y << ", " << camera.position.z << ")" << std::endl; std::cout << "Camera target: (" << camera.target.x << ", " << camera.target.y << ", " << camera.target.z << ")" << std::endl; - std::cout << "Number of chunks to draw: " << world.chunks.size() << std::endl; + // std::cout << "Number of chunks to draw: " << world.chunks.size() << std::endl; } + // Generate chunks around the camera + glm::ivec3 camera_chunk = glm::ivec3(camera.position) / CHUNK_SIZE; + // ---- PASS 1: Draw solid grey cubes ---- glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0, 1.0); // Push solid faces "back" glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); shader.setVec3("u_Color", glm::vec3(0.5, 0.5, 0.5)); - for (auto& [chunk_position, chunk] : world.chunks) { + for (int y = -VIEW_DISTANCE; y < VIEW_DISTANCE; y++) { + for (int z = -VIEW_DISTANCE; z < VIEW_DISTANCE; z++) { + for (int x = -VIEW_DISTANCE; x < VIEW_DISTANCE; x++) { + glm::ivec3 chunk_offset = glm::ivec3(x, y, z); + glm::ivec3 chunk_pos = camera_chunk + chunk_offset; - glm::ivec3 chunk_offset = chunk_position * CHUNK_SIZE; - glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, glm::vec3(chunk_offset)); - glm::mat4 mvp = projection * view * model; - shader.setMat4("u_mvp", mvp); + glm::mat4 model = glm::mat4(1.0f); + model = glm::translate(model, glm::vec3(chunk_pos * CHUNK_SIZE)); + glm::mat4 mvp = projection * view * model; + shader.setMat4("u_mvp", mvp); - if (!debug_printed) { - std::cout << "Drawing chunk at (" << chunk_position.x << ", " << chunk_position.y << ", " << chunk_position.z << ")" << std::endl; - std::cout << "Chunk offset: (" << chunk_offset.x << ", " << chunk_offset.y << ", " << chunk_offset.z << ")" << std::endl; - } + Chunk* chunk = world.get_or_create_chunk(chunk_pos); - Mesh* chunkMesh = chunk.getMesh(); - if (chunkMesh) { - chunkMesh->draw(); + Mesh* chunk_mesh = chunk->getMesh(); + if (chunk_mesh) + chunk_mesh->draw(); + } } } @@ -289,17 +287,23 @@ int main() { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Just the lines shader.setVec3("u_Color", glm::vec3(1.0, 1.0, 1.0)); - for (auto& [chunk_position, chunk] : world.chunks) { + for (int y = -VIEW_DISTANCE; y < VIEW_DISTANCE; y++) { + for (int z = -VIEW_DISTANCE; z < VIEW_DISTANCE; z++) { + for (int x = -VIEW_DISTANCE; x < VIEW_DISTANCE; x++) { + glm::ivec3 chunk_offset = glm::ivec3(x, y, z); + glm::ivec3 chunk_pos = camera_chunk + chunk_offset; - glm::ivec3 chunk_offset = chunk_position * CHUNK_SIZE; - glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, glm::vec3(chunk_offset)); - glm::mat4 mvp = projection * view * model; - shader.setMat4("u_mvp", mvp); + glm::mat4 model = glm::mat4(1.0f); + model = glm::translate(model, glm::vec3(chunk_pos * CHUNK_SIZE)); + glm::mat4 mvp = projection * view * model; + shader.setMat4("u_mvp", mvp); - Mesh* chunkMesh = chunk.getMesh(); - if (chunkMesh) { - chunkMesh->draw(); + Chunk* chunk = world.get_or_create_chunk(chunk_pos); + + Mesh* chunk_mesh = chunk->getMesh(); + if (chunk_mesh) + chunk_mesh->draw(); + } } }