commit 0970030

uint  ·  2026-07-31 00:01:05 +0000 UTC
parent 061c859
add chunk meshing
4 files changed,  +274, -22
+31, -22
  1@@ -5,20 +5,17 @@
  2 #include "util.h"
  3 #include "client/camera.h"
  4 #include "renderer/renderer.h"
  5-#include "renderer/test_block.h"
  6 #include "world/block.h"
  7 #include "world/chunk.h"
  8 
  9 static MGTexture block_textures[BLOCK_LAST];
 10-static MGMesh standard_block_mesh;
 11-static Chunk chunk;
 12+static Chunk chunks[WORLD_WIDTH][WORLD_DEPTH];
 13 static MGCamera cam;
 14 
 15 void test_scene_create(void)
 16 {
 17-	standard_block_mesh = block_mesh_create();
 18-	if (standard_block_mesh == MGMESH_INVALID)
 19-		mg_die(MG_ERR_MESH_CREATE, "Failed to create test block mesh");
 20+	i32 x;
 21+	i32 z;
 22 
 23 	block_textures[BLOCK_BRICK] = renderer_texture_load("assets/resourcepacks/default/blocks/brick.png");
 24 	if (block_textures[BLOCK_BRICK] == MGTEXTURE_INVALID)
 25@@ -32,7 +29,16 @@ void test_scene_create(void)
 26 	if (block_textures[BLOCK_STONE] == MGTEXTURE_INVALID)
 27 		mg_die(MG_ERR_TEXTURE_LOAD, "Failed to load stone texture");
 28 
 29-	chunk_create(&chunk);
 30+	for (z = 0; z < WORLD_DEPTH; z++) {
 31+		for (x = 0; x < WORLD_WIDTH; x++) {
 32+			chunk_create(&chunks[x][z]);
 33+
 34+			if (!chunk_mesh_create(&chunks[x][z]))
 35+				mg_die(
 36+						MG_ERR_MESH_CREATE,
 37+						"Failed to create chunk mesh");
 38+		}
 39+	}
 40 
 41 	camera_init(&cam, MG_V3(8.0f, 38.0f, 24.0f));
 42 	cam.yaw = MG_AngleDeg(-180.0f);
 43@@ -43,13 +49,12 @@ void test_scene_draw(MGContext* ctx)
 44 {
 45 	MGMat4 proj;
 46 	MGMat4 view;
 47-	MGMat4 model;
 48 	MGMat4 mvp;
 49-	i32 width;
 50-	i32 height;
 51+	MGMat4 model;
 52 	i32 x;
 53-	i32 y;
 54 	i32 z;
 55+	i32 width;
 56+	i32 height;
 57 	float aspect;
 58 	BlockType block;
 59 
 60@@ -63,17 +68,15 @@ void test_scene_draw(MGContext* ctx)
 61 	proj = camera_get_proj(&cam, aspect);
 62 	view = camera_get_view(&cam);
 63 
 64-	for (z = 0; z < CHUNK_DEPTH; z++) {
 65-		for (y = 0; y < CHUNK_HEIGHT; y++) {
 66-			for (x = 0; x < CHUNK_WIDTH; x++) {
 67-				block = chunk_get(&chunk, x, y, z);
 68+	for (z = 0; z < WORLD_DEPTH; z++) {
 69+		for (x = 0; x < WORLD_WIDTH; x++) {
 70+			model = MG_Translate(MG_V3(x * CHUNK_WIDTH, 0.0f, z * CHUNK_DEPTH));
 71+			mvp = MG_MulM4(proj, MG_MulM4(view, model));
 72 
 73-				if (block == BLOCK_AIR)
 74+			for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
 75+				if (chunks[x][z].meshes[block] == MGMESH_INVALID)
 76 					continue;
 77-
 78-				model = MG_Translate(MG_V3(x, y, z));
 79-				mvp = MG_MulM4(proj, MG_MulM4(view, model));
 80-				renderer_mesh_draw(standard_block_mesh, block_textures[block], &mvp);
 81+				renderer_mesh_draw(chunks[x][z].meshes[block], block_textures[block], &mvp);
 82 			}
 83 		}
 84 	}
 85@@ -81,15 +84,21 @@ void test_scene_draw(MGContext* ctx)
 86 
 87 void test_scene_destroy(void)
 88 {
 89+	i32 x;
 90+	i32 z;
 91+
 92+	for (z = 0; z < WORLD_DEPTH; z++) {
 93+		for (x = 0; x < WORLD_WIDTH; x++)
 94+			chunk_mesh_destroy(&chunks[x][z]);
 95+	}
 96+
 97 	renderer_texture_destroy(block_textures[BLOCK_BRICK]);
 98 	renderer_texture_destroy(block_textures[BLOCK_DIRT]);
 99 	renderer_texture_destroy(block_textures[BLOCK_STONE]);
100-	renderer_mesh_destroy(standard_block_mesh);
101 
102 	block_textures[BLOCK_BRICK] = MGTEXTURE_INVALID;
103 	block_textures[BLOCK_DIRT] = MGTEXTURE_INVALID;
104 	block_textures[BLOCK_STONE] = MGTEXTURE_INVALID;
105-	standard_block_mesh = MGMESH_INVALID;
106 }
107 
108 #endif /* TEST_SCENE_H */
+11, -0
 1@@ -3,13 +3,18 @@
 2 
 3 #include "types.h"
 4 #include "world/block.h"
 5+#include "renderer/renderer.h"
 6 
 7 #define CHUNK_WIDTH 16
 8 #define CHUNK_HEIGHT 32
 9 #define CHUNK_DEPTH 16
10 
11+#define WORLD_WIDTH 10
12+#define WORLD_DEPTH 10
13+
14 typedef struct {
15 	BlockType blocks[CHUNK_WIDTH][CHUNK_HEIGHT][CHUNK_DEPTH];
16+	MGMesh meshes[BLOCK_LAST];
17 } Chunk;
18 
19 /* create a new chunk 
20@@ -28,5 +33,11 @@ BlockType chunk_get(const Chunk* chunk, i32 x, i32 y, i32 z);
21          the future? who knows */
22 void chunk_set(Chunk* chunk, i32 x, i32 y, i32 z, BlockType block);
23 
24+/**/
25+i8 chunk_mesh_create(Chunk* chunk);
26+
27+/**/
28+void chunk_mesh_destroy(Chunk* chunk);
29+
30 #endif /* CHUNK_H */
31 
+1, -0
1@@ -169,6 +169,7 @@ void renderer_backend_create(MGContext* ctx)
2 	desc.environment.defaults.depth_format = SG_PIXELFORMAT_DEPTH;
3 	desc.environment.defaults.sample_count = 1;
4 	desc.logger.func = sokol_log;
5+	desc.buffer_pool_size = MAX_MESHES * 2;
6 
7 	sg_setup(&desc);
8 
+231, -0
  1@@ -1,11 +1,99 @@
  2+#include <stdlib.h>
  3+#include <string.h>
  4+
  5+#include "util.h"
  6 #include "world/chunk.h"
  7 
  8+#define MAX_MESH_VERTICES 65535U
  9+
 10+static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count,
 11+                     u32* index_count, i32 face, i32 x, i32 y, i32 z);
 12+
 13+/* 6 = number of faces on a cube */
 14+static const i32 face_offsets[6][3] = {
 15+	{  0,  0, -1 },
 16+	{  0,  0,  1 },
 17+	{ -1,  0,  0 },
 18+	{  1,  0,  0 },
 19+	{  0, -1,  0 },
 20+	{  0,  1,  0 }
 21+};
 22+
 23+static const MGVertex face_vertices[24] = {
 24+	/* north: -z */
 25+ 	{ 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, RGBA(255, 0, 0, 255) },
 26+	{ 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 0, 255) },
 27+	{ 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, RGBA(255, 0, 0, 255) },
 28+	{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, RGBA(255, 0, 0, 255) },
 29+
 30+	/* south: +z */
 31+	{ 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, RGBA(0, 255, 0, 255) },
 32+	{ 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 0, 255) },
 33+	{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, RGBA(0, 255, 0, 255) },
 34+	{ 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, RGBA(0, 255, 0, 255) },
 35+
 36+	/* west: -x */
 37+	{ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, RGBA(0, 0, 255, 255) },
 38+	{ 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 0, 255, 255) },
 39+	{ 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, RGBA(0, 0, 255, 255) },
 40+	{ 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, RGBA(0, 0, 255, 255) },
 41+
 42+	/* east: +x */
 43+	{ 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, RGBA(255, 255, 0, 255) },
 44+	{ 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 255, 0, 255) },
 45+	{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, RGBA(255, 255, 0, 255) },
 46+	{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, RGBA(255, 255, 0, 255) },
 47+
 48+	/* bottom: -y */
 49+	{ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, RGBA(255, 0, 255, 255) },
 50+	{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 255, 255) },
 51+	{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, RGBA(255, 0, 255, 255) },
 52+	{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, RGBA(255, 0, 255, 255) },
 53+
 54+	/* top: +y */
 55+	{ 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, RGBA(0, 255, 255, 255) },
 56+	{ 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 255, 255) },
 57+	{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, RGBA(0, 255, 255, 255) },
 58+	{ 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, RGBA(0, 255, 255, 255) }
 59+};
 60+
 61+static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count,
 62+                     u32* index_count, i32 face, i32 x, i32 y, i32 z)
 63+{
 64+	MGVertex vtx;
 65+	u32 base;
 66+	i32 i;
 67+
 68+	base = *vertex_count;
 69+
 70+	for (i = 0; i < 4; i++) {
 71+		vtx = face_vertices[face * 4 + i];
 72+
 73+		vtx.x += x;
 74+		vtx.y += y;
 75+		vtx.z += z;
 76+
 77+		vertices[(*vertex_count)++] = vtx;
 78+	}
 79+
 80+	indices[(*index_count)++] = (u16)(base + 0);
 81+	indices[(*index_count)++] = (u16)(base + 1);
 82+	indices[(*index_count)++] = (u16)(base + 2);
 83+	indices[(*index_count)++] = (u16)(base + 0);
 84+	indices[(*index_count)++] = (u16)(base + 2);
 85+	indices[(*index_count)++] = (u16)(base + 3);
 86+}
 87+
 88 void chunk_create(Chunk* chunk)
 89 {
 90 	i32 x;
 91 	i32 y;
 92 	i32 z;
 93 
 94+	if (!chunk)
 95+		return;
 96+
 97+	memset(chunk, 0, sizeof(*chunk));
 98 	for (z = 0; z < CHUNK_DEPTH; z++) {
 99 		for (x = 0; x < CHUNK_WIDTH; x++) {
100 			for (y = 0; y < CHUNK_HEIGHT - 5; y++)
101@@ -56,3 +144,146 @@ void chunk_set(Chunk* chunk, i32 x, i32 y, i32 z, BlockType block)
102 	chunk->blocks[x][y][z] = block;
103 }
104 
105+/* create a mesh for chunk by generating vertices
106+   and indices for each (visible) face */
107+i8 chunk_mesh_create(Chunk* chunk)
108+{
109+	MGVertex* vertices[BLOCK_LAST];
110+	u16* indices[BLOCK_LAST];
111+	u32 face_counts[BLOCK_LAST];
112+	u32 vertex_counts[BLOCK_LAST];
113+	u32 index_counts[BLOCK_LAST];
114+	MGMeshDesc desc;
115+	BlockType block;
116+	i32 face;
117+	i32 x;
118+	i32 y;
119+	i32 z;
120+
121+	if (!chunk)
122+		return 0;
123+
124+	memset(vertices, 0, sizeof(vertices));
125+	memset(indices, 0, sizeof(indices));
126+	memset(face_counts, 0, sizeof(face_counts));
127+	memset(vertex_counts, 0, sizeof(vertex_counts));
128+	memset(index_counts, 0, sizeof(index_counts));
129+
130+	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++)
131+		chunk->meshes[block] = MGMESH_INVALID;
132+
133+	/* count visible faces belonging to each block type  */
134+	for (z = 0; z < CHUNK_DEPTH; z++) {
135+		for (y = 0; y < CHUNK_HEIGHT; y++) {
136+			for (x = 0; x < CHUNK_WIDTH; x++) {
137+				block = chunk_get(chunk, x, y, z);
138+
139+				if (block == BLOCK_AIR)
140+					continue;
141+
142+				for (face = 0; face < 6; face++) {
143+					if (chunk_get(
144+					    chunk, x + face_offsets[face][0],
145+					    y + face_offsets[face][1],
146+					    z + face_offsets[face][2]
147+					) == BLOCK_AIR)
148+						face_counts[block]++;
149+				}
150+			}
151+		}
152+	}
153+
154+	/* allocate  for each block type */
155+	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
156+		if (face_counts[block] == 0)
157+			continue;
158+
159+		if (face_counts[block] * 4U > MAX_MESH_VERTICES) {
160+			mg_log(LOG_ERR, "Chunk mesh exceeds u16 vertex limit");
161+			goto fail;
162+		}
163+
164+		vertices[block] = malloc(face_counts[block] * 4U * sizeof(*vertices[block]));
165+		indices[block] = malloc(face_counts[block] * 6U * sizeof(*indices[block]));
166+		if (!vertices[block] || !indices[block]) {
167+			mg_log(LOG_ERR, "Failed to allocate chunk mesh");
168+			goto fail;
169+		}
170+	}
171+
172+	/* omit only faces whose neighbour is air */
173+	for (z = 0; z < CHUNK_DEPTH; z++) {
174+		for (y = 0; y < CHUNK_HEIGHT; y++) {
175+			for (x = 0; x < CHUNK_WIDTH; x++) {
176+				block = chunk_get(chunk, x, y, z);
177+
178+				if (block == BLOCK_AIR)
179+					continue;
180+
181+				for (face = 0; face < 6; face++) {
182+					if (chunk_get(
183+					    chunk,
184+					    x + face_offsets[face][0],
185+					    y + face_offsets[face][1],
186+					    z + face_offsets[face][2]
187+					) != BLOCK_AIR)
188+						continue;
189+
190+					add_face(
191+					    vertices[block], indices[block],
192+					    &vertex_counts[block], &index_counts[block],
193+					    face, x, y, z
194+					);
195+				}
196+			}
197+		}
198+	}
199+
200+	/* upload one mesh for each texture */
201+	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
202+		if (index_counts[block] == 0)
203+			continue;
204+
205+		desc.vtc = vertices[block];
206+		desc.vtcc = vertex_counts[block];
207+		desc.idc = indices[block];
208+		desc.idcc = index_counts[block];
209+
210+		chunk->meshes[block] = renderer_mesh_create(&desc);
211+
212+		if (chunk->meshes[block] == MGMESH_INVALID) {
213+			mg_log(LOG_ERR, "Failed to upload chunk mesh");
214+			goto fail;
215+		}
216+	}
217+
218+	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
219+		free(indices[block]);
220+		free(vertices[block]);
221+	}
222+
223+	return 1;
224+
225+fail:
226+	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
227+		free(indices[block]);
228+		free(vertices[block]);
229+	}
230+
231+	chunk_mesh_destroy(chunk);
232+	return 0;
233+}
234+
235+void chunk_mesh_destroy(Chunk* chunk)
236+{
237+	BlockType block;
238+
239+	if (!chunk)
240+		return;
241+
242+	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
243+		renderer_mesh_destroy(chunk->meshes[block]);
244+		chunk->meshes[block] = MGMESH_INVALID;
245+	}
246+}
247+