summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAaditya Dhruv <[email protected]>2026-01-30 00:17:39 -0600
committerAaditya Dhruv <[email protected]>2026-01-30 00:17:39 -0600
commit8150b91d4076d15f8df5cd66acc1b8076a2ee1a9 (patch)
treefe74fd18e409a215ed05074efe29b3b0a465bfd5 /src
parent3729fe29b862a8b1d58967c45942535e7087b73b (diff)
Update chunk rendering, improve chunk loading
- On world_init, LOAD_CHUNK amount of chunks are preloaded - Chunks are loaded around the player's current chunk, in a square shape. The size of the shape is controlled by CHUNK_DISTANCE - Allow chunks struct to be independent of a position. We load a chunk TO a position in the world chunk grid (x, y). This allows us to "wrap" chunks, so we have an endless world, but we are really just wrapping around
Diffstat (limited to 'src')
-rw-r--r--src/block.c5
-rw-r--r--src/block.h9
-rw-r--r--src/chunk.c54
-rw-r--r--src/chunk.h29
-rw-r--r--src/engine.c82
-rw-r--r--src/engine.h11
-rw-r--r--src/world.c37
-rw-r--r--src/world.h7
8 files changed, 176 insertions, 58 deletions
diff --git a/src/block.c b/src/block.c
index 80a62d2..2197b2a 100644
--- a/src/block.c
+++ b/src/block.c
@@ -1,3 +1,4 @@
+#include "cglm/affine.h"
#include "cglm/cam.h"
#include "cglm/cglm.h"
#include "cglm/io.h"
@@ -131,7 +132,7 @@ void block_update(struct block* blk) {
// RTS matrix - rotate, translate, scale
glm_mat4_identity(blk->model);
float angle = glm_rad(blk->angle);
- vec3 scale = { 0.90f, 0.90f, 0.90f };
+ // 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);
@@ -159,7 +160,7 @@ int block_draw(struct block* blk, struct shader* shader) {
};
glUniform3fv(loc, 6, (void*)colors);
glDrawElements(GL_TRIANGLES, blk->_vertex_count, GL_UNSIGNED_INT, 0);
- block_update(blk);
+ glBindVertexArray(0);
return 0;
}
diff --git a/src/block.h b/src/block.h
index 1da88ac..b19265d 100644
--- a/src/block.h
+++ b/src/block.h
@@ -12,6 +12,15 @@ struct block {
mat4 model;
float angle;
};
+/**
+ * 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);
void block_debug(struct block* blk);
+void block_update(struct block* blk);
diff --git a/src/chunk.c b/src/chunk.c
index 3741629..4d15486 100644
--- a/src/chunk.c
+++ b/src/chunk.c
@@ -1,15 +1,9 @@
#include "chunk.h"
#include "block.h"
-#include "cglm/io.h"
-#include "cglm/util.h"
-#include "cglm/vec2.h"
-#include "cglm/vec3.h"
#include "world.h"
-#include <math.h>
-#include <stdio.h>
+#include "cglm/cglm.h"
#include <stdlib.h>
#include <string.h>
-#include <time.h>
#define MIN(x, y) (x < y) ? x : y
@@ -103,8 +97,8 @@ void _chunk_plains_gen(struct chunk* chunk) {
vec3 poi1 = { rand() % CHUNK_WIDTH, rand() % CHUNK_LENGTH, poi_min + (rand() % (poi_max - poi_min))};
vec3 poi2 = { rand() % CHUNK_WIDTH, rand() % CHUNK_LENGTH, -poi_min + (rand() % (poi_max - poi_min))};
- for (int x = 0; x < CHUNK_LENGTH; x++) {
- for (int y = 0; y < CHUNK_WIDTH; y++) {
+ for (int x = 0; x < CHUNK_WIDTH; x++) {
+ for (int y = 0; y < CHUNK_LENGTH; y++) {
// Minimum z height
// Interpolation formula - simple linear
vec2 target = { x, y };
@@ -114,10 +108,50 @@ 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 + (CHUNK_WIDTH * chunk->coord[0]), h, -y - (CHUNK_LENGTH * chunk->coord[1])};
+ vec3 pos = {x, h, -y };
block_init(pos, blk);
chunk->blocks[x][y][h] = blk;
}
}
}
}
+
+
+// Kind of like the block_update of chunks
+void chunk_load(struct chunk *chunk, int coord[2]) {
+ vec3 translation = {CHUNK_WIDTH * coord[0], 0, - (CHUNK_LENGTH * coord[1])};
+ 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 (blk != NULL) {
+ // 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);
+ }
+ }
+ }
+ }
+}
+
+void chunk_draw(struct chunk* chunk, struct shader* shader) {
+ 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);
+ counter += 1;
+ }
+ }
+ }
+}
+
+void chunk_unload(struct chunk* chunk) {
+}
diff --git a/src/chunk.h b/src/chunk.h
index 4d06f5f..ac24a2d 100644
--- a/src/chunk.h
+++ b/src/chunk.h
@@ -22,6 +22,31 @@ struct chunk {
vec2 coord;
};
+/**
+ * Generate a chunk at coords for the given world. Memory allocation for chunk is
+ * handled by the function.
+ *
+ */
int chunk_gen(struct world* wld, vec2 coord, struct chunk** chunk);
-int chunk_gen_structures(void* neighbor_data, struct chunk* chunk);
-int chunk_gen_terrain(void* neighbor_data, struct chunk* chunk);
+/**
+ * Load a chunk to the given coordinates. Essentially, a chunk only knows of
+ * 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
+ * @param chunk Chunk to load
+ * @param coord coordinates in world space
+ */
+void chunk_load(struct chunk* chunk, int coord[2]);
+/**
+ * Unload a chunk. Delete GPU 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
+ * @param chunk Chunk to draw
+ * @param shader Shader to pass to block_draw
+ */
+void chunk_draw(struct chunk* chunk, struct shader* shader);
diff --git a/src/engine.c b/src/engine.c
index ed2b6c2..2d509f3 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -1,11 +1,13 @@
#include "engine.h"
#include "block.h"
#include "camera.h"
+#include "cglm/io.h"
#include "chunk.h"
#include "input.h"
#include "window.h"
#include "world.h"
#include <junk/vector.h>
+#include <string.h>
#include <time.h>
void _engine_insert_chunk_ptrs(struct engine* engine, struct chunk* chunk);
@@ -31,42 +33,49 @@ int engine_init(struct engine *engine) {
engine->shader = shader;
// Setup Objects to draw
- vector_init(&engine->objects);
- // Setup root chunk
- struct world* world;
- world_init(time(NULL), &world);
- vec2 curr_chunk = { 0, 0 };
- int chunk_distance = 2;
- for (int i = 0; i < chunk_distance; i++) {
- for (int j = 0; j < chunk_distance; j++) {
- struct chunk* chunk;
- vec2 chunk_coord = { curr_chunk[0] + i, curr_chunk[1] + j };
- world_get_chunk(world, chunk_coord, &chunk);
- _engine_insert_chunk_ptrs(engine, chunk);
- }
- }
-
+ // memset(engine->loaded_chunks, 0, (1 + CHUNK_DISTANCE * 2) * (1 + CHUNK_DISTANCE * 2));
// Setup camera
camera_init(&engine->camera);
- vec3 camera_pos = { 0.0f, 5.0f, 0.0f };
+ vec3 camera_pos = { 0.0f, 15.0f, 0.0f };
camera_set_position(engine->camera, camera_pos);
+ // Setup root chunk
+ struct world* world;
+ world_init(time(NULL), &world);
+ engine->world = world;
// Final step - Start the game
engine->game_loop = 1;
return 0;
}
-void _engine_insert_chunk_ptrs(struct engine* engine, struct chunk* chunk) {
- 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;
- }
- if (VECTOR_INSERT(engine->objects, (void*)blk) == -1) exit(1);
- counter += 1;
+
+void engine_update(struct engine* engine) {
+ int curr_chunk[2] = { (engine->camera->position[0] / CHUNK_WIDTH), (engine->camera->position[2]) / CHUNK_LENGTH };
+ // Chunk update
+ struct chunk* c = {0};
+ int coord[2] = { curr_chunk[0], curr_chunk[1] };
+ world_get_chunk(engine->world, coord, &c);
+
+ // We moved a chunk - load new chunks with chunk_load
+ if (engine->curr_chunk[0] != curr_chunk[0] || engine->curr_chunk[1] != curr_chunk[1]) {
+ fprintf(stderr, "CHUNK Update! From (%d, %d) to (%d, %d)\n",
+ engine->curr_chunk[0],
+ engine->curr_chunk[1],
+ curr_chunk[0],
+ curr_chunk[1]);
+ // 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);
+ // 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
+ chunk_load(chunk, chunk_coord);
}
}
}
@@ -79,10 +88,21 @@ void engine_draw(struct engine* engine) {
glEnable(GL_DEPTH_TEST);
//glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
glUseProgram(engine->shader->program);
- for (int i = 0; i < vector_length(engine->objects); i++) {
- struct block* block = vector_get(engine->objects, i);
- camera_update(engine->camera, engine->shader);
- block_draw(block, engine->shader);
+ // Update engine managed objects
+ engine_update(engine);//(1 + CHUNK_DISTANCE * 2) * (1 + CHUNK_DISTANCE * 2)
+ camera_update(engine->camera, engine->shader);
+ for (int i = -CHUNK_DISTANCE; i <= CHUNK_DISTANCE; i++) {
+ for (int j = -CHUNK_DISTANCE; j <= CHUNK_DISTANCE; j++) {
+ struct chunk* chunk = {0};
+ // // Load chunk
+ // Ensure the y coordinate is negative, because in OpenGL +z-axis (y in chunk system) is towards
+ // 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);
+ }
}
SDL_RenderPresent(engine->window->renderer);
}
diff --git a/src/engine.h b/src/engine.h
index 2eb7518..0633bcc 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -2,12 +2,21 @@
#include "window.h"
#include "shader.h"
#include "junk/vector.h"
+// CHUNK_DISTANCE is essentially render distance, it shows you how many chunks
+// around the user you can see
+// The number of loaded chunks can be determined as follows:
+// 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 5
+
struct engine {
struct window* window;
struct shader* shader;
- struct vector* objects;
struct camera* camera;
int game_loop;
+ int curr_chunk[2];
+ struct world* world;
};
/**
diff --git a/src/world.c b/src/world.c
index 72a0c21..41fdd17 100644
--- a/src/world.c
+++ b/src/world.c
@@ -1,29 +1,48 @@
#include "world.h"
+#include "cglm/io.h"
+#include "chunk.h"
#include <stdlib.h>
#include <string.h>
+// LOAD_DISTANCE determines how many chunks are loaded on world creation
+#define LOAD_DISTANCE 4
+
int world_init(int32_t seed, struct world** world) {
srand(seed);
struct world* wld = malloc(sizeof(struct world));
memset(wld, 0, sizeof(struct world));
wld->seed = seed;
//TODO: Improve loading here
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < 2; j++) {
+ for (int i = 0; i < LOAD_DISTANCE; i++) {
+ for (int j = 0; j < LOAD_DISTANCE; j++) {
struct chunk* chunk;
- vec2 coords = { i, j };
- chunk_gen(wld, coords, &chunk);
- wld->chunks[i][j] = chunk;
-
+ int coords[2] = { i, j };
+ world_get_chunk(wld, coords, &chunk);
}
}
*world = wld;
return 0;
}
-int world_get_chunk(struct world* world, vec2 coord, struct chunk** chunk) {
+int world_get_chunk(struct world* world, int coord[2], struct chunk** chunk) {
+ int w = ((abs(coord[0]) / WORLD_WIDTH) + 1) * WORLD_WIDTH;
+ int l = ((abs(coord[1]) / WORLD_LENGTH) + 1) * WORLD_LENGTH;
+ int x = (coord[0] + w) % WORLD_WIDTH;
+ int y = (coord[1] + l) % WORLD_LENGTH;
+ vec2 new_coord = { x, y };
+ struct chunk* c = world->chunks[x][y];
+ if (c != NULL) {
+ *chunk = c;
+ } else {
+ chunk_gen(world, new_coord, chunk);
+ world->chunks[x][y] = *chunk;
+ }
+ return 0;
+}
+
+void world_get_chunk_real_coord(struct world* world, vec2 coord, int out[2]) {
int x = (int)coord[0] % WORLD_WIDTH;
int y = (int)coord[1] % WORLD_LENGTH;
- *chunk = world->chunks[x][y];
- return 0;
+ out[0] = x;
+ out[1] = y;
}
diff --git a/src/world.h b/src/world.h
index c65a1d0..ec0328b 100644
--- a/src/world.h
+++ b/src/world.h
@@ -1,8 +1,8 @@
#pragma once
#include "chunk.h"
#include <stdint.h>
-#define WORLD_LENGTH 32
-#define WORLD_WIDTH 32
+#define WORLD_LENGTH 6
+#define WORLD_WIDTH 6
struct world {
struct chunk* chunks[WORLD_WIDTH][WORLD_LENGTH];
@@ -11,4 +11,5 @@ struct world {
int world_init(int32_t seed, struct world** world);
int world_save(int32_t seed);
-int world_get_chunk(struct world* world, vec2 coord, struct chunk** chunk);
+int world_get_chunk(struct world* world, int coord[2], struct chunk** chunk);
+void world_get_chunk_real_coord(struct world* world, vec2 coord, int out[2]);