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+