add infinite chunk generation

This commit is contained in:
JISAUAY 2025-11-12 11:27:50 -06:00
parent 3ba2d75cff
commit 1ee72dae9a
8 changed files with 68 additions and 40 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -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) ![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)

View File

@ -73,9 +73,9 @@ class Camera
void move(float time) // time passed in seconds since last move 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? this->vertical_velocity.y -= (GRAVITY * time); // no way! physics reference?
glm::vec3 velocity = this->forward_velocity + this->horizontal_velocity + this->vertical_velocity; glm::vec3 velocity = this->forward_velocity + this->horizontal_velocity + this->vertical_velocity;
@ -84,7 +84,7 @@ class Camera
this->position += movement; this->position += movement;
// Don't want to clip into the ground right // 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->position.y = 0.0f;
this->vertical_velocity.y = 0.0f; this->vertical_velocity.y = 0.0f;

View File

@ -1,5 +1,6 @@
#include "Chunk.h" #include "Chunk.h"
#include <vector> #include <vector>
#include <iostream>
const int VERTEX_SIZE = 6; const int VERTEX_SIZE = 6;
@ -64,6 +65,10 @@ Chunk::Chunk() {
// mesh is initially null (nullptr) // mesh is initially null (nullptr)
} }
Chunk::Chunk(std::unordered_set<glm::ivec3> voxels) {
this->voxels = voxels;
}
Mesh* Chunk::getMesh() { Mesh* Chunk::getMesh() {
// Generate mesh on first access if not already generated // Generate mesh on first access if not already generated
if (!mesh && !voxels.empty()) { if (!mesh && !voxels.empty()) {
@ -76,6 +81,8 @@ void Chunk::generateMesh() {
std::vector<float> vboData; std::vector<float> vboData;
std::vector<unsigned int> eboData; std::vector<unsigned int> eboData;
// std::cout << "Generating Chunk mesh..." << std::endl;
for (const glm::ivec3 pos : voxels) { for (const glm::ivec3 pos : voxels) {
if (voxels.count(pos + glm::ivec3(0, 1, 0)) == 0) { if (voxels.count(pos + glm::ivec3(0, 1, 0)) == 0) {
addFace(pos, topFace, vboData, eboData); addFace(pos, topFace, vboData, eboData);

View File

@ -31,6 +31,7 @@ class Chunk {
std::unordered_set<glm::ivec3> voxels; std::unordered_set<glm::ivec3> voxels;
Chunk(); Chunk();
Chunk(std::unordered_set<glm::ivec3> voxels);
Mesh* getMesh(); // Returns pointer, can be null if not generated yet Mesh* getMesh(); // Returns pointer, can be null if not generated yet

View File

@ -40,7 +40,7 @@ void Mesh::init() {
void Mesh::uploadData(const std::vector<float> &vboData, const std::vector<unsigned int> &eboData) { void Mesh::uploadData(const std::vector<float> &vboData, const std::vector<unsigned int> &eboData) {
m_indexCount = eboData.size(); 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 // Bind VAO first, then upload buffer data
glBindVertexArray(m_VAO); glBindVertexArray(m_VAO);

View File

@ -16,7 +16,18 @@ void World::flip_voxel(glm::ivec3 position) {
Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) { Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) {
if (this->chunks.count(chunk_position) == 0) { 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<glm::ivec3> 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]; return &this->chunks[chunk_position];
} }

View File

@ -28,10 +28,12 @@ const float Z_NEAR = 0.1f;
bool WIREFRAME_MODE = !true; bool WIREFRAME_MODE = !true;
const float FOV = 110.0f; const float FOV = 110.0f;
const int VIEW_DISTANCE = 4; // In Chunks
bool W_pressed, S_pressed, A_pressed, D_pressed = false; bool W_pressed, S_pressed, A_pressed, D_pressed = false;
glm::mat4 projection = glm::mat4(1.0f); 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 // Callback function for when the window is resized
// glfw: whenever the window size changed (by OS or user resize) this callback function executes // 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); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 2. --- Create a Window --- // 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) { if (window == NULL) {
std::cerr << "Failed to create GLFW window" << std::endl; std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate(); glfwTerminate();
@ -205,17 +207,9 @@ int main() {
// Load Cube OBJ // Load Cube OBJ
World world; World world;
Object3D cube = Object3D("objs/cube.obj"); // Object3D cube = Object3D("objs/cube.obj");
// Place cubes in the world // std::cout << "World created with " << world.chunks.size() << " chunks" << std::endl;
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;
// Mesh generation now happens automatically when first rendering // Mesh generation now happens automatically when first rendering
if (WIREFRAME_MODE) if (WIREFRAME_MODE)
@ -251,31 +245,35 @@ int main() {
std::cout << "\n=== RENDER DEBUG ===" << std::endl; 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 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 << "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 ---- // ---- PASS 1: Draw solid grey cubes ----
glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0); // Push solid faces "back" glPolygonOffset(1.0, 1.0); // Push solid faces "back"
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
shader.setVec3("u_Color", glm::vec3(0.5, 0.5, 0.5)); 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);
glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(chunk_pos * CHUNK_SIZE));
model = glm::translate(model, glm::vec3(chunk_offset)); glm::mat4 mvp = projection * view * model;
glm::mat4 mvp = projection * view * model; shader.setMat4("u_mvp", mvp);
shader.setMat4("u_mvp", mvp);
if (!debug_printed) { Chunk* chunk = world.get_or_create_chunk(chunk_pos);
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;
}
Mesh* chunkMesh = chunk.getMesh(); Mesh* chunk_mesh = chunk->getMesh();
if (chunkMesh) { if (chunk_mesh)
chunkMesh->draw(); chunk_mesh->draw();
}
} }
} }
@ -289,17 +287,23 @@ int main() {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Just the lines glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Just the lines
shader.setVec3("u_Color", glm::vec3(1.0, 1.0, 1.0)); 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);
glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(chunk_pos * CHUNK_SIZE));
model = glm::translate(model, glm::vec3(chunk_offset)); glm::mat4 mvp = projection * view * model;
glm::mat4 mvp = projection * view * model; shader.setMat4("u_mvp", mvp);
shader.setMat4("u_mvp", mvp);
Mesh* chunkMesh = chunk.getMesh(); Chunk* chunk = world.get_or_create_chunk(chunk_pos);
if (chunkMesh) {
chunkMesh->draw(); Mesh* chunk_mesh = chunk->getMesh();
if (chunk_mesh)
chunk_mesh->draw();
}
} }
} }