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)
## 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
{
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;

View File

@ -1,5 +1,6 @@
#include "Chunk.h"
#include <vector>
#include <iostream>
const int VERTEX_SIZE = 6;
@ -64,6 +65,10 @@ Chunk::Chunk() {
// mesh is initially null (nullptr)
}
Chunk::Chunk(std::unordered_set<glm::ivec3> 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<float> vboData;
std::vector<unsigned int> 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);

View File

@ -31,6 +31,7 @@ class Chunk {
std::unordered_set<glm::ivec3> voxels;
Chunk();
Chunk(std::unordered_set<glm::ivec3> voxels);
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) {
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);

View File

@ -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<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];
}

View File

@ -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));
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));
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();
}
}
}