summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAaditya Dhruv <[email protected]>2026-01-31 03:22:27 -0600
committerAaditya Dhruv <[email protected]>2026-01-31 03:22:27 -0600
commit7f13b0abaa76a5e90674d5733f8162f02ceab693 (patch)
treee280ad2a4a881e79d2519f2558dcab6a36d5b389 /src
parent0c3e1f450f591d871c2779504b4113daf891fd1b (diff)
Rework chunk rendering to get a 100x performance boost!
- Switching to semi-chunk mesh rendering just 100x the framerate, it was running at around 100fps for 3 CHUNK_DISTANCE, and pushing it above would make it drop to 30. Now it runs at 9000 frames per second with CHUNK_DISTANCE of 8, probably can push it even more - What is bizarre is this is just from the reduction in draw calls, I still need to implement face culling for invisible blocks (simple) and frustrum culling (using AABB) or maybe octrees - Block is way way more simplifed, it's just metadata about a coordinate in the chunk block array - All rendering and mesh generation code is handled by chunks. There is a VAO, single VBO and EBO for each chunk. The data buffer is loaded into the GPU with a chunk_load, and it stays like that until it is loaded again. chunk_load is called if we move chunks, in engine_update. Here we unload existing chunks, then load the new ones
Diffstat (limited to 'src')
-rw-r--r--src/block.c200
-rw-r--r--src/block.h37
-rw-r--r--src/camera.c2
-rw-r--r--src/chunk.c294
-rw-r--r--src/chunk.h27
-rw-r--r--src/engine.c32
-rw-r--r--src/engine.h4
-rw-r--r--src/world.c6
-rw-r--r--src/world.h4
9 files changed, 314 insertions, 292 deletions
diff --git a/src/block.c b/src/block.c
index d03a137..8911ba3 100644
--- a/src/block.c
+++ b/src/block.c
@@ -15,203 +15,7 @@
#include <stdio.h>
#include <string.h>
-
-#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
-
-void block_update(struct block* blk);
-int block_init(vec3 pos, struct block* blk) {
- // Store buffer data into struct
- // Initialize vbo and ebo for block
-
- memcpy(blk->coords, pos, sizeof(vec3));
- block_load_gpu(blk);
- block_update(blk);
+int block_init(struct block* blk, enum BLOCK_ID block_id) {
+ blk->block_id = block_id;
return 0;
}
-
-void block_load_gpu(struct block* blk) {
- // ========== Constants of a block ================
- // Local world coordinates
- float vertices[] = {
- 1.0f, 1.0f, 0.0f, // top-right
- 0.0f, 0.0f, 1.0f, // Front normal
- 1.0f, 1.0f,
- 0.0f, 1.0f, 0.0f, // top-left
- 0.0f, 0.0f, 1.0f, // Front normal
- 0.0f, 1.0f,
- 0.0f, 0.0f, 0.0f, // bottom-left
- 0.0f, 0.0f, 1.0f, // Front normal
- 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, // bottom-right
- 0.0f, 0.0f, 1.0f, // Front normal
- 1.0f, 0.0f,
-
- 0.0f, 1.0f, -1.0f, // top-left (back plane)
- 0.0f, 0.0f, -1.0f, // Back normal
- 0.0f, 1.0f,
- 1.0f, 1.0f, -1.0f, // top-right (back plane)
- 0.0f, 0.0f, -1.0f, // Back normal
- 1.0f, 1.0f,
- 1.0f, 0.0f, -1.0f, // bottom-right (back plane)
- 0.0f, 0.0f, -1.0f, // Back normal
- 1.0f, 0.0f,
- 0.0f, 0.0f, -1.0f, // bottom-left (back plane)
- 0.0f, 0.0f, -1.0f, // Back normal
- 0.0f, 0.0f,
-
- 1.0f, 1.0f, -1.0f, // top-right (back plane)
- 1.0f, 0.0f, 0.0f, // Right normal
- 1.0f, 1.0f,
- 1.0f, 1.0f, 0.0f, // top-right
- 1.0f, 0.0f, 0.0f, // Right normal
- 0.0f, 1.0f,
- 1.0f, 0.0f, 0.0f, // bottom-right
- 1.0f, 0.0f, 0.0f, // Right normal
- 0.0f, 0.0f,
- 1.0f, 0.0f, -1.0f, // bottom-right (back plane)
- 1.0f, 0.0f, 0.0f, // Right normal
- 1.0f, 0.0f,
-
- 0.0f, 1.0f, 0.0f, // top-left
- -1.0f, 0.0f, 0.0f, // Left normal
- 1.0f, 1.0f,
- 0.0f, 1.0f, -1.0f, // top-left (back plane)
- -1.0f, 0.0f, 0.0f, // Left normal
- 0.0f, 1.0f,
- 0.0f, 0.0f, -1.0f, // bottom-left (back plane)
- -1.0f, 0.0f, 0.0f, // Left normal
- 0.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, // bottom-left
- -1.0f, 0.0f, 0.0f, // Left normal
- 1.0f, 0.0f,
-
- 1.0f, 1.0f, -1.0f, // top-right (back plane)
- 0.0f, 1.0f, 0.0f, // Top normal
- 1.0f, 1.0f,
- 0.0f, 1.0f, -1.0f, // top-left (back plane)
- 0.0f, 1.0f, 0.0f, // Top normal
- 0.0f, 1.0f,
- 0.0f, 1.0f, 0.0f, // top-left
- 0.0f, 1.0f, 0.0f, // Top normal
- 0.0f, 0.0f,
- 1.0f, 1.0f, 0.0f, // top-right
- 0.0f, 1.0f, 0.0f, // Top normal
- 1.0f, 0.0f,
-
- 1.0f, 0.0f, -1.0f, // bottom-right (back plane)
- 0.0f, -1.0f, 0.0f, // Bottom normal
- 1.0f, 1.0f,
- 0.0f, 0.0f, -1.0f, // bottom-left (back plane)
- 0.0f, -1.0f, 0.0f, // Bottom normal
- 0.0f, 1.0f,
- 0.0f, 0.0f, 0.0f, // bottom-left
- 0.0f, -1.0f, 0.0f, // Bottom normal
- 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, // bottom-right
- 0.0f, -1.0f, 0.0f, // Bottom normal
- 1.0f, 0.0f,
- };
- // int vertex_order[] = {
- // 1, 2, 3, 3, 0, 1, // Front
- // 5, 6, 7, 7, 4, 5, // Back
- // 9, 10, 11, 11, 8, 9, // Right
- // 13, 14, 15, 15, 12, 13, // Left
- // 17, 18, 19, 19, 16, 17, // Top
- // 21, 22, 23, 23, 20, 21, // Bottom
- //
- // };
- int vertex_order[] = {
- 1, 2, 3, 3, 0, 1, // Front
- 5, 6, 7, 7, 4, 5, // Back
- 9, 10, 11, 11, 8, 9, // Right
- 13, 14, 15, 15, 12, 13, // Left
- 17, 18, 19, 19, 16, 17, // Top
- 21, 22, 23, 23, 20, 21, // Bottom
- };
-
- // ================ OpenGL work ================
- create_vbo(&blk->_vbo, (void*)vertices, sizeof(float) * ARRAY_SIZE(vertices));
- create_ebo(&blk->_ebo, (void*)vertex_order, sizeof(int) * ARRAY_SIZE(vertex_order));
-
-
- blk->_vertex_count = ARRAY_SIZE(vertex_order);
- glGenVertexArrays(1, &blk->_vao);
- glBindVertexArray(blk->_vao);
- // Enable 3 attribs - position normals texture
- glEnableVertexAttribArray(0);
- glEnableVertexAttribArray(1);
- glEnableVertexAttribArray(2);
- // set vao_buffer to pos buffer obj
- glBindBuffer(GL_ARRAY_BUFFER, blk->_vbo);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
- // set vao_buffer to normals buffer obj
- glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(3*sizeof(float)));
- // set vao_buffer to texture buffer obj
- glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(6*sizeof(float)));
- // Set EBO to the vertex_order
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, blk->_ebo);
- //NOTE: This is important, otherwise with multiple block_init calls, it
- //creates a segfault since the bindings get all messed up. Why it gets
- //messed up? Let's say we make 2 blocks. Block 1 creates VBOs, then VAO,
- //then binds everything. Now VAO is still bound. Block 2 init starts. First
- //call is create_vbo. Since VAO is already bound, it gets bound to the OLD
- //VAO!! Always clear before use.
- glBindVertexArray(0);
-}
-
-void block_update(struct block* blk) {
- //=============== Matrix Work ==============
-
- // RTS matrix - rotate, translate, scale
- glm_mat4_identity(blk->model);
- float angle = glm_rad(blk->angle);
- // vec3 scale = { 0.90f, 0.90f, 0.90f };
- glm_translate(blk->model, blk->coords);
- // glm_scale(blk->model, scale);
- // glm_rotate_at(blk->model, pivot, angle, rot_axis);
- // View matrix (camera)
- //blk->angle = fmodf(blk->angle + 0.005f, 360.0f);
-}
-
-// Register block vbos and ebos to context
-int block_draw(struct block* blk, struct shader* shader, struct texture* texture) {
- glBindVertexArray(blk->_vao);
- set_uniform_mat4("model", shader, blk->model);
- // GLuint loc = glGetUniformLocation(shader->program, "face_colors");
- // if (loc == -1) {
- // fprintf(stderr, "Invalid var %s for get_uniform_mat4: Does not exist\n", "face_colors");
- // exit(1);
- // return -1;
- // }
- // float colors[] = {
- // 0.761f, 0.424f, 0.0f,
- // 0.761f, 0.424f, 0.0f,
- // 0.761f, 0.424f, 0.0f,
- // 0.761f, 0.424f, 0.0f,
- // 0.404f, 0.776f, 0.027f,
- // 0.761f, 0.424f, 0.0f,
- // };
- // glUniform3fv(loc, 6, (void*)colors);
- // texture_draw(texture);
- glDrawElements(GL_TRIANGLES, blk->_vertex_count, GL_UNSIGNED_INT, 0);
- glBindVertexArray(0);
- return 0;
-}
-
-void block_debug(struct block *blk) {
- fprintf(stderr, "==== Block Debug ====\n");
- fprintf(stderr, "==== Block Coords ====\n");
- glm_vec3_print(blk->coords, stderr);
- fprintf(stderr, "==== Block Model ====\n");
- glm_mat4_print(blk->model, stderr);
-
-}
-
-void block_unload(struct block *blk) {
- // Clear VBO data
- glDeleteBuffers(1, &blk->_vbo);
- // Clear EBO data
- glDeleteBuffers(1, &blk->_ebo);
- // Clear VAO
- glDeleteVertexArrays(1, &blk->_vao);
-}
diff --git a/src/block.h b/src/block.h
index ad81c47..d8e75d7 100644
--- a/src/block.h
+++ b/src/block.h
@@ -1,38 +1,13 @@
#pragma once
-#include "cglm/types.h"
-#include "glad/glad.h"
-#include "shader.h"
-#include "texture.h"
+enum BLOCK_ID {
+ BLOCK_GRASS,
+};
struct block {
- vec3 coords;
- GLuint _vao;
- GLuint _vbo;
- GLuint _ebo;
- GLuint _tbo;
- int _vertex_count;
- mat4 model;
- float angle;
+ enum BLOCK_ID block_id;
};
/**
- * Create a "block" object, which is the building blocks of this world.
- * Blocks belong in chunks, and chunks belong in worlds. vec3 pos here is the coordinates of the block in WORLD space.
- * However, a common method to render these blocks will be that the chunk will set the coordinates in "chunk space", and
- * on a chunk_load, we will translate the blocks to wherever the chunk is loaded
- *
- *
- */
-int block_init(vec3 pos, struct block* blk);
-int block_draw(struct block* blk, struct shader* shader, struct texture* texture);
-void block_debug(struct block* blk);
-void block_update(struct block* blk);
-
-/**
- * Remove GPU related data of a block. This is usually called by chunk_unload
+ * A block struct defines what kind of block we will be rendering. It's the metadata of the block array in a chunk
*
*/
-void block_unload(struct block* blk);
-/**
- * Load GPU data of a block
- */
-void block_load_gpu(struct block* blk);
+int block_init(struct block* blk, enum BLOCK_ID block_id);
diff --git a/src/camera.c b/src/camera.c
index 7a23a17..cbea1ff 100644
--- a/src/camera.c
+++ b/src/camera.c
@@ -56,7 +56,7 @@ void camera_move(struct camera *camera, enum DIRECTION move) {
// Right hand rule - this will be on the righ (positive)
glm_vec3_crossn(unit_direction, camera->up, unit_direction);
}
- float scale = 0.2f;
+ float scale = 0.8f;
glm_vec3_scale(unit_direction, scale, unit_direction);
glm_vec3_add(camera->position, unit_direction, camera->position);
}
diff --git a/src/chunk.c b/src/chunk.c
index 3a7b334..80395d1 100644
--- a/src/chunk.c
+++ b/src/chunk.c
@@ -1,13 +1,20 @@
#include "chunk.h"
#include "block.h"
+#include "cglm/types.h"
+#include "cglm/vec3.h"
+#include "shader.h"
+#include "util.h"
#include "world.h"
#include "cglm/cglm.h"
+#include <junk/vector.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MIN(x, y) (x < y) ? x : y
#define MAX(x, y) (x > y) ? x : y
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
void _chunk_plains_gen(struct chunk* chunk);
@@ -108,8 +115,7 @@ void _chunk_plains_gen(struct chunk* chunk) {
for (int h = 0; h < z_final; h++) {
struct block* blk = malloc(sizeof(struct block));
// Adjust block coordinates with global chunk coordinates
- vec3 pos = {x, h, -y };
- block_init(pos, blk);
+ block_init(blk, BLOCK_GRASS);
chunk->blocks[x][y][h] = blk;
}
}
@@ -118,63 +124,267 @@ void _chunk_plains_gen(struct chunk* chunk) {
}
-// Kind of like the block_update of chunks
+int* _chunk_face_order_add(int* face_order, int size, int idx) {
+ int* buf = malloc(size);
+ memcpy(buf, face_order, size);
+ for (int i = 0; i < size / sizeof(int); i++) {
+ buf[i] += idx;
+ }
+ return buf;
+}
+float* _chunk_face_add(float* face, int size, vec3 pos) {
+ // "Hack" to update the face coords,
+ // glm_vec3_add just does a[0] = a[0] + b[0], so using
+ // offsets will work
+ int unit = 8;
+ float* buf = malloc(size);
+ memcpy(buf, face, size);
+ glm_vec3_add(buf, pos, buf);
+ glm_vec3_add(buf + unit, pos, buf + unit);
+ glm_vec3_add(buf + 2 * unit, pos, buf + 2 * unit);
+ glm_vec3_add(buf + 3 * unit, pos, buf + 3 * unit);
+ return buf;
+}
+
+/**
+ * Two step function:
+ * 1. Generate mesh based on neighboring block data
+ * 2. Send data to GPU
+ *
+ * NOTE: GPU
+ */
void chunk_load(struct chunk *chunk, int coord[2]) {
- vec3 translation = {CHUNK_WIDTH * coord[0], 0, - (CHUNK_LENGTH * coord[1])};
+ fprintf(stderr, "Loaded chunk (%d, %d)\n", coord[0], coord[1]);
+ // ================ OpenGL work ================
+ // Initalize vertices and vertex order vectors. These will be dynamically
+ // sized buffer data we send to the GPU
+ struct vector* vertices;
+ struct vector* vertex_order;
+ vector_init(&vertices);
+ vector_init(&vertex_order);
+
+ // =============== Face Data ===================
+ float front_face[] = {
+ 1.0f, 1.0f, 0.0f, // top-right
+ 0.0f, 0.0f, 1.0f, // Front normal
+ 1.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, // top-left
+ 0.0f, 0.0f, 1.0f, // Front normal
+ 0.0f, 1.0f,
+ 0.0f, 0.0f, 0.0f, // bottom-left
+ 0.0f, 0.0f, 1.0f, // Front normal
+ 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, // bottom-right
+ 0.0f, 0.0f, 1.0f, // Front normal
+ 1.0f, 0.0f,
+ };
+ float back_face[] = {
+ 0.0f, 1.0f, -1.0f, // top-left (back plane)
+ 0.0f, 0.0f, -1.0f, // Back normal
+ 0.0f, 1.0f,
+ 1.0f, 1.0f, -1.0f, // top-right (back plane)
+ 0.0f, 0.0f, -1.0f, // Back normal
+ 1.0f, 1.0f,
+ 1.0f, 0.0f, -1.0f, // bottom-right (back plane)
+ 0.0f, 0.0f, -1.0f, // Back normal
+ 1.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f, // bottom-left (back plane)
+ 0.0f, 0.0f, -1.0f, // Back normal
+ 0.0f, 0.0f,
+ };
+ float right_face[] = {
+ 1.0f, 1.0f, -1.0f, // top-right (back plane)
+ 1.0f, 0.0f, 0.0f, // Right normal
+ 1.0f, 1.0f,
+ 1.0f, 1.0f, 0.0f, // top-right
+ 1.0f, 0.0f, 0.0f, // Right normal
+ 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, // bottom-right
+ 1.0f, 0.0f, 0.0f, // Right normal
+ 0.0f, 0.0f,
+ 1.0f, 0.0f, -1.0f, // bottom-right (back plane)
+ 1.0f, 0.0f, 0.0f, // Right normal
+ 1.0f, 0.0f,
+ };
+ float left_face[] = {
+ 0.0f, 1.0f, 0.0f, // top-left
+ -1.0f, 0.0f, 0.0f, // Left normal
+ 1.0f, 1.0f,
+ 0.0f, 1.0f, -1.0f, // top-left (back plane)
+ -1.0f, 0.0f, 0.0f, // Left normal
+ 0.0f, 1.0f,
+ 0.0f, 0.0f, -1.0f, // bottom-left (back plane)
+ -1.0f, 0.0f, 0.0f, // Left normal
+ 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, // bottom-left
+ -1.0f, 0.0f, 0.0f, // Left normal
+ 1.0f, 0.0f,
+ };
+ float top_face[] = {
+ 1.0f, 1.0f, -1.0f, // top-right (back plane)
+ 0.0f, 1.0f, 0.0f, // Top normal
+ 1.0f, 1.0f,
+ 0.0f, 1.0f, -1.0f, // top-left (back plane)
+ 0.0f, 1.0f, 0.0f, // Top normal
+ 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, // top-left
+ 0.0f, 1.0f, 0.0f, // Top normal
+ 0.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f, // top-right
+ 0.0f, 1.0f, 0.0f, // Top normal
+ 1.0f, 0.0f,
+ };
+ float bottom_face[] = {
+ 1.0f, 0.0f, -1.0f, // bottom-right (back plane)
+ 0.0f, -1.0f, 0.0f, // Bottom normal
+ 1.0f, 1.0f,
+ 0.0f, 0.0f, -1.0f, // bottom-left (back plane)
+ 0.0f, -1.0f, 0.0f, // Bottom normal
+ 0.0f, 1.0f,
+ 0.0f, 0.0f, 0.0f, // bottom-left
+ 0.0f, -1.0f, 0.0f, // Bottom normal
+ 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, // bottom-right
+ 0.0f, -1.0f, 0.0f, // Bottom normal
+ 1.0f, 0.0f,
+ };
+
+ int vertex_draw_order[] = {
+ 1, 2, 3, 3, 0, 1, // CCW 2-triangles (quad)
+ };
+ // ============= Face detection algorithm =============
+ int vertex_index = 0;
+ int blk_c = 0;
for (int x = 0; x < CHUNK_WIDTH; x++) {
for (int y = 0; y < CHUNK_HEIGHT; y++) {
for (int z = 0; z < CHUNK_LENGTH; z++) {
struct block* blk = chunk->blocks[x][z][y];
+ // If not air block
if (blk != NULL) {
- // If chunk is unloaded, send block data to GPU
- if (chunk->loaded == 0) {
- // Add GPU data
- block_load_gpu(blk);
- }
- // Translate to world coordinates
- // First do block updates, set the position of the block in local
- // chunk coordinates
- block_update(blk);
- // Then translate them to world coordinates
- glm_translate(blk->model, translation);
+ blk_c += 1;
+ vec3 pos = { x, y, -z };
+ // Position of block in world coords
+ // glm_vec3_add(pos, translation, pos);
+
+ VECTOR_INSERT(vertices, _chunk_face_add(front_face,
+ sizeof(front_face), pos));
+ VECTOR_INSERT(vertex_order,
+ _chunk_face_order_add(vertex_draw_order,
+ sizeof(vertex_draw_order), vertex_index));
+ vertex_index += 4;
+
+ VECTOR_INSERT(vertices, _chunk_face_add(back_face,
+ sizeof(back_face), pos));
+ VECTOR_INSERT(vertex_order,
+ _chunk_face_order_add(vertex_draw_order,
+ sizeof(vertex_draw_order), vertex_index));
+ vertex_index += 4;
+
+ VECTOR_INSERT(vertices, _chunk_face_add(right_face,
+ sizeof(right_face), pos));
+ VECTOR_INSERT(vertex_order,
+ _chunk_face_order_add(vertex_draw_order,
+ sizeof(vertex_draw_order), vertex_index));
+ vertex_index += 4;
+
+ VECTOR_INSERT(vertices, _chunk_face_add(left_face,
+ sizeof(left_face), pos));
+ VECTOR_INSERT(vertex_order,
+ _chunk_face_order_add(vertex_draw_order,
+ sizeof(vertex_draw_order), vertex_index));
+ vertex_index += 4;
+
+ VECTOR_INSERT(vertices, _chunk_face_add(top_face,
+ sizeof(top_face), pos));
+ VECTOR_INSERT(vertex_order,
+ _chunk_face_order_add(vertex_draw_order,
+ sizeof(vertex_draw_order), vertex_index));
+ vertex_index += 4;
+
+ VECTOR_INSERT(vertices, _chunk_face_add(bottom_face,
+ sizeof(bottom_face), pos));
+ VECTOR_INSERT(vertex_order,
+ _chunk_face_order_add(vertex_draw_order,
+ sizeof(vertex_draw_order), vertex_index));
+ vertex_index += 4;
}
}
}
}
- if (chunk->loaded == 0) {
- //We've reloaded all data - flip bit again
- chunk->loaded = 1;
+ fprintf(stderr, "Chunk blk_c: %d\n", blk_c);
+
+ float tmp_vertex[vector_length(vertices) * sizeof(front_face)];
+ int tmp_order[vector_length(vertex_order) * sizeof(vertex_draw_order)];
+ fprintf(stderr, "Chunk blk_c: %d v_s: %d, v_o: %d\n", blk_c, vector_length(vertices) / 6, vector_length(vertex_order) / 6);
+ for (int i = 0; i < vector_length(vertices); i++) {
+ float* face = vector_get(vertices, i);
+ // Copy from heap mem to tmp buffer, and then free
+ memcpy(tmp_vertex + (i*ARRAY_SIZE(front_face)), face, sizeof(front_face));
+ free(face);
}
+ for (int i = 0; i < vector_length(vertex_order); i++) {
+ int* order = vector_get(vertex_order, i);
+ // Copy from heap mem to tmp buffer, and then free
+ memcpy(tmp_order + (i*ARRAY_SIZE(vertex_draw_order)), order, sizeof(vertex_draw_order));
+ free(order);
+ }
+
+ // Create VBO and EBO buffer data
+ // VBO EBO size is sizeof() because we want TOTAL BYTES (float * count)
+ create_vbo(&chunk->_vbo, (void*)tmp_vertex, sizeof(tmp_vertex));
+ create_ebo(&chunk->_ebo, (void*)tmp_order, sizeof(tmp_order));
+ // Here we only want ARRAY_SIZE, not float * count
+ chunk->vertex_count = vector_length(vertex_order) * ARRAY_SIZE(vertex_draw_order);
+
+
+ glGenVertexArrays(1, &chunk->_vao);
+ glBindVertexArray(chunk->_vao);
+ // Enable 3 attribs - position normals texture
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glEnableVertexAttribArray(2);
+ // set vao_buffer to pos buffer obj
+ glBindBuffer(GL_ARRAY_BUFFER, chunk->_vbo);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
+ // set vao_buffer to normals buffer obj
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(3*sizeof(float)));
+ // set vao_buffer to texture buffer obj
+ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(6*sizeof(float)));
+ // Set EBO to the vertex_order
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->_ebo);
+ //NOTE: This is important, otherwise with multiple chunk_load calls, it
+ //creates a segfault since the bindings get all messed up. Why it gets
+ //messed up? Let's say we make 2 chunks. Chunk 1 creates VBOs, then VAO,
+ //then binds everything. Now VAO is still bound. Chunk 2 init starts. First
+ //call is create_vbo. Since VAO is already bound, it gets bound to the OLD
+ //VAO!! Always clear before use.
+ glBindVertexArray(0);
+ // Translation to WORLD units
+ vec3 translation = {CHUNK_WIDTH * coord[0], 0, - (CHUNK_LENGTH * coord[1])};
+ // Set the matrix for world coordinate translation
+ glm_mat4_identity(chunk->model);
+ glm_translate(chunk->model, translation);
+ chunk->loaded = 1;
}
void chunk_draw(struct chunk* chunk, struct shader* shader, struct texture* texture) {
- int counter = 0;
- for (int i = 0; i < CHUNK_WIDTH; i++) {
- for (int j = 0; j < CHUNK_LENGTH; j++) {
- for (int k = 0; k < CHUNK_HEIGHT; k++) {
- struct block* blk = chunk->blocks[i][j][k];
- if (blk == NULL) {
- continue;
- }
- block_draw(blk, shader, texture);
- counter += 1;
- }
- }
- }
+ glBindVertexArray(chunk->_vao);
+ set_uniform_mat4("model", shader, chunk->model);
+ glDrawElements(GL_TRIANGLES, chunk->vertex_count, GL_UNSIGNED_INT, 0);
+ glBindVertexArray(0);
}
void chunk_unload(struct chunk* chunk) {
- for (int i = 0; i < CHUNK_WIDTH; i++) {
- for (int j = 0; j < CHUNK_LENGTH; j++) {
- for (int k = 0; k < CHUNK_HEIGHT; k++) {
- struct block* blk = chunk->blocks[i][j][k];
- if (blk == NULL) {
- continue;
- }
- block_unload(blk);
- }
- }
- }
chunk->loaded = 0;
+ // Clear VBO data
+ glDeleteBuffers(1, &chunk->_vbo);
+ // Clear EBO data
+ glDeleteBuffers(1, &chunk->_ebo);
+ // Clear VAO
+ glDeleteVertexArrays(1, &chunk->_vao);
+ chunk->loaded = 0;
+}
+// Regenerate chunk data
+void chunk_update(struct chunk *chunk) {
}
diff --git a/src/chunk.h b/src/chunk.h
index d49ba80..12fa111 100644
--- a/src/chunk.h
+++ b/src/chunk.h
@@ -1,8 +1,11 @@
#pragma once
#include "block.h"
+#include "cglm/types.h"
+#include "shader.h"
#include "texture.h"
#include "world.h"
#include <stdint.h>
+#include <junk/vector.h>
#define CHUNK_WIDTH 16
#define CHUNK_LENGTH 16
#define CHUNK_HEIGHT 128
@@ -17,11 +20,21 @@ enum biome {
// World.c compiles -> includes world.h -> first line is include chunk.h -> struct world
// is used here, which messes it up. Forward declare to avoid errors
struct world;
+/**
+ * A chunk is the "basic" rendering unit used - it will allocate buffers for all blocks in a chunk, hide covered faces of blocks, generate a chunk mesh and dispatch that buffer data to the GPU
+ *
+ */
struct chunk {
struct block* blocks[CHUNK_WIDTH][CHUNK_LENGTH][CHUNK_HEIGHT];
+ GLuint _vao;
+ GLuint _vbo;
+ GLuint _ebo;
+ int vertex_count;
+ mat4 model;
enum biome biome;
vec2 coord;
int loaded;
+ int dirty;
};
/**
@@ -35,19 +48,27 @@ int chunk_gen(struct world* wld, vec2 coord, struct chunk** chunk);
* it's local coordinate system. We want to load this particular chunk to a
* location in WORLD coordinates, which is what coord is. This vec2 will be
* used to translate the blocks that constitute the chunk
+ *
+ * The chunk will allocate a VAO/VBO/EBO buffer to render the chunk mesh. This GPU data is usually not updated in the loop, unless a chunk_update is called
* @param chunk Chunk to load
* @param coord coordinates in world space
*/
void chunk_load(struct chunk* chunk, int coord[2]);
/**
- * Unload a chunk. Delete block GPU and memory data, not the chunk data itself
+ * Chunk updates are performed on already loaded chunks. It will redraw the
+ * chunk mesh as needed based on block updates and whatnot.
+ * @param chunk Chunk to load
+ * @param coord coordinates in world space
+ */
+void chunk_update(struct chunk* chunk);
+/**
+ * Unload a chunk. Delete GPU and memory data, not the chunk data itself
*
* @param chunk Chunk to load
*/
void chunk_unload(struct chunk* chunk);
/*
- * Similar to block_draw, this dispatches calls to OpenGL to draw the chunk.
- * Technically this wraps block_draw, so block_draw is the one doing all the work
+ * This dispatches calls to OpenGL to draw the chunk.
* @param chunk Chunk to draw
* @param shader Shader to pass to block_draw
* @param texture Textures that block_draw will use
diff --git a/src/engine.c b/src/engine.c
index c5a9220..ab6cf92 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -14,8 +14,6 @@
#include <string.h>
#include <time.h>
-void _engine_insert_chunk_ptrs(struct engine* engine, struct chunk* chunk);
-
int engine_init(struct engine *engine) {
// Setup the Window
struct window* window = malloc(sizeof(struct window));
@@ -54,6 +52,16 @@ int engine_init(struct engine *engine) {
struct world* world;
world_init(time(NULL), &world);
engine->world = world;
+ //TODO: Move this loop to a function and flip chunk_coord sign correctly ONCE
+ for (int i = -CHUNK_DISTANCE; i <= CHUNK_DISTANCE; i++) {
+ for (int j = -CHUNK_DISTANCE; j <= CHUNK_DISTANCE; j++) {
+ struct chunk* chunk;
+ int chunk_coord[2] = { engine->curr_chunk[0] + i, -engine->curr_chunk[1] + j };
+ world_get_chunk(engine->world, chunk_coord, &chunk);
+ // Load chunk
+ chunk_load(chunk, chunk_coord);
+ }
+ }
// Final step - Start the game
engine->game_loop = 1;
return 0;
@@ -77,19 +85,25 @@ void engine_update(struct engine* engine) {
for (int i = -CHUNK_DISTANCE; i <= CHUNK_DISTANCE; i++) {
for (int j = -CHUNK_DISTANCE; j <= CHUNK_DISTANCE; j++) {
struct chunk* chunk;
- int chunk_coord[2] = { engine->curr_chunk[0] + i, engine->curr_chunk[1] + j };
+ int chunk_coord[2] = { engine->curr_chunk[0] + i, -engine->curr_chunk[1] + j };
world_get_chunk(engine->world, chunk_coord, &chunk);
- // Get "real" coords as in non-negative numbers, that can go in a array
- int real_coord[2] = { i + CHUNK_DISTANCE, j + CHUNK_DISTANCE };
- // engine->loaded_chunks[real_coord[0]][real_coord[1]] = chunk;
- // Load chunk
+ // unload chunk
// TODO: Fix some VAO/VBO bug when negative y
- // chunk_unload(chunk);
+ chunk_unload(chunk);
}
}
// Update the curr_chunk
memcpy(engine->curr_chunk, curr_chunk, sizeof(vec2));
// Load chunks of CHUNK_DISTANCE around curr_chunk
+ for (int i = -CHUNK_DISTANCE; i <= CHUNK_DISTANCE; i++) {
+ for (int j = -CHUNK_DISTANCE; j <= CHUNK_DISTANCE; j++) {
+ struct chunk* chunk;
+ int chunk_coord[2] = { engine->curr_chunk[0] + i, -engine->curr_chunk[1] + j };
+ world_get_chunk(engine->world, chunk_coord, &chunk);
+ // Load chunk
+ chunk_load(chunk, chunk_coord);
+ }
+ }
}
}
@@ -139,8 +153,6 @@ void engine_start(struct engine* engine) {
// user, so we want inwards to be positive, so flip sign
int chunk_coord[2] = { engine->curr_chunk[0] + i, -engine->curr_chunk[1] + j };
world_get_chunk(engine->world, chunk_coord, &chunk);
- // Load chunk
- chunk_load(chunk, chunk_coord);
chunk_draw(chunk, engine->shader, engine->texture);
}
}
diff --git a/src/engine.h b/src/engine.h
index e7ffdaa..567d9a5 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -9,7 +9,7 @@
// We want a square around curr_chunk, and a side of the square will be 1
// (center chunk) + 2 * CHUNK_DISTANCE (either side of center)
// loaded chunks = (1 + CHUNK_DISTANCE * 2)^2
-#define CHUNK_DISTANCE 3
+#define CHUNK_DISTANCE 5
struct engine {
struct window* window;
@@ -31,7 +31,7 @@ int engine_init(struct engine* engine);
/**
* Take all objects in the engine, apply the shader pipeline and draw on the window
- * Apply block, chunk and camera updates as well. This is the main game loop
+ * Apply chunk and camera updates as well. This is the main game loop
*
* @param engine The target engine
*/
diff --git a/src/world.c b/src/world.c
index 41fdd17..4ce248b 100644
--- a/src/world.c
+++ b/src/world.c
@@ -5,7 +5,7 @@
#include <string.h>
// LOAD_DISTANCE determines how many chunks are loaded on world creation
-#define LOAD_DISTANCE 4
+#define LOAD_DISTANCE 10
int world_init(int32_t seed, struct world** world) {
srand(seed);
@@ -13,8 +13,8 @@ int world_init(int32_t seed, struct world** world) {
memset(wld, 0, sizeof(struct world));
wld->seed = seed;
//TODO: Improve loading here
- for (int i = 0; i < LOAD_DISTANCE; i++) {
- for (int j = 0; j < LOAD_DISTANCE; j++) {
+ for (int i = -LOAD_DISTANCE; i <= LOAD_DISTANCE; i++) {
+ for (int j = -LOAD_DISTANCE; j <= LOAD_DISTANCE; j++) {
struct chunk* chunk;
int coords[2] = { i, j };
world_get_chunk(wld, coords, &chunk);
diff --git a/src/world.h b/src/world.h
index ec0328b..a447d8e 100644
--- a/src/world.h
+++ b/src/world.h
@@ -1,8 +1,8 @@
#pragma once
#include "chunk.h"
#include <stdint.h>
-#define WORLD_LENGTH 6
-#define WORLD_WIDTH 6
+#define WORLD_LENGTH 32
+#define WORLD_WIDTH 32
struct world {
struct chunk* chunks[WORLD_WIDTH][WORLD_LENGTH];