commit b788836
uint
·
2026-08-05 02:52:23 +0000 UTC
parent 6f81f92
add per neighbor face hiding
7 files changed,
+162,
-31
M
TODO
+6,
-5
1@@ -1,11 +1,11 @@
2 uint:
3+[-] Chunk meshing
4+ [ ] Greedily
5+
6+[ ] Block texture atlas
7 [ ] Review `block`
8 [ ] Small3DLib inline?? check ANSI
9-
10 [ ] Consolidate variable initaliasation methods
11-[-] Face Hiding
12-[-] Chunk meshing
13- [ ] Greedily
14 [-] Render multiplayer
15 [ ] Players
16 [-] Multiplayer
17@@ -19,7 +19,8 @@ uint:
18
19 [ ] Check it works on Windows
20
21-[-] Remove `renderer_backend`
22+[X] Face Hiding
23+[X] Remove `renderer_backend`
24 [X] mc_*, mg_random type cleanup (int->i8 on bool)
25 [X] Clean up test_scene.h
26 [X] Maus wayland
+3,
-0
1@@ -19,6 +19,9 @@ void world_destroy(ClientWorld* world);
2 /* remove chunk from world (clears data, marks unsued) */
3 void world_remove_chunk(ClientWorld* world, i32 x, i32 z);
4
5+/* rebuild all loaded chunk meshes with world-aware face culling */
6+i8 world_mesh_create(ClientWorld* world);
7+
8 /* update chunk with data from server
9 returns 1 on success, 0 on failure */
10 i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8* data);
+1,
-1
1@@ -51,7 +51,7 @@ void chunk_section_set(ChunkSection* section, i32 x, i32 y, i32 z, BlockType blo
2
3 /* create a mesh for a chunk section
4 returns 1 on success, 0 on failure */
5-i8 chunk_section_mesh_create(ChunkSection* section);
6+i8 chunk_section_mesh_create(ChunkSection* section, const ChunkSection* neighbours[6]);
7
8 /* destroy a chunk sections mesh */
9 void chunk_section_mesh_destroy(ChunkSection* section);
+113,
-6
1@@ -6,8 +6,14 @@
2
3 #define MC_CHUNK_SECTION_BLOCK_BYTES 8192
4
5-static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z);
6-static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z);
7+static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z);
8+static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z);
9+static ChunkSection* world_chunk_section_find(ClientWorld* world, i32 x, i32 z, i32 section);
10+static i8 world_chunk_section_mesh_create(ClientWorld* world, Chunk* chunk, i32 section);
11+static i8 world_chunk_remesh(ClientWorld* world, Chunk* chunk, u16 section_mask);
12+
13+static const i32 neighbour_offsets[6][3] = { { 0, 0, -1 }, { 0, 0, 1 }, { -1, 0, 0 },
14+ { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 } };
15
16 /* find chunk in world by coordinates
17 returns NULL if not found */
18@@ -27,7 +33,7 @@ static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z)
19 }
20
21 /* get chunk from world by coordinates
22- returns NULL if not found and no free chunk available */
23+ returns NULL if not found or no free chunk available */
24 static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z)
25 {
26 Chunk* chunk;
27@@ -54,6 +60,71 @@ static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z)
28 return NULL;
29 }
30
31+/* find chunk section in world by coordinates
32+ returns NULL if not found */
33+static ChunkSection* world_chunk_section_find(ClientWorld* world, i32 x, i32 z, i32 section)
34+{
35+ Chunk* chunk;
36+
37+ if (section < 0 || section >= CHUNK_SECTION_COUNT)
38+ return NULL;
39+
40+ chunk = world_chunk_find(world, x, z);
41+ if (!chunk || !(chunk->section_mask & (1U << section)))
42+ return NULL;
43+
44+ return &chunk->sections[section];
45+}
46+
47+/* rebuild chunk section mesh with face culling
48+ returns 1 on success, 0 on failure */
49+static i8 world_chunk_section_mesh_create(ClientWorld* world, Chunk* chunk, i32 section)
50+{
51+ const ChunkSection* neighbours[6];
52+ i32 face;
53+
54+ for (face = 0; face < 6; face++) {
55+ neighbours[face] = world_chunk_section_find(world, chunk->x + neighbour_offsets[face][0],
56+ chunk->z + neighbour_offsets[face][2],
57+ section + neighbour_offsets[face][1]);
58+ }
59+
60+ return chunk_section_mesh_create(&chunk->sections[section], neighbours);
61+}
62+
63+/* rebuild chunk mesh and neighbouring chunk meshes for sections in section_mask
64+ returns 1 on success, 0 on failure */
65+static i8 world_chunk_remesh(ClientWorld* world, Chunk* chunk, u16 section_mask)
66+{
67+ Chunk* nbr;
68+ i32 face;
69+ i32 section;
70+ i32 adj_section;
71+
72+ for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
73+ if (!(section_mask & (1U << section)))
74+ continue;
75+
76+ if ((chunk->section_mask & (1U << section)) && !world_chunk_section_mesh_create(world, chunk, section))
77+ return 0;
78+
79+ for (face = 0; face < 6; face++) {
80+ adj_section = section + neighbour_offsets[face][1];
81+ nbr = world_chunk_find(world, chunk->x + neighbour_offsets[face][0],
82+ chunk->z + neighbour_offsets[face][2]);
83+
84+ if (!nbr || adj_section < 0 || adj_section >= CHUNK_SECTION_COUNT ||
85+ !(nbr->section_mask & (1U << adj_section)))
86+ continue;
87+
88+ if (!world_chunk_section_mesh_create(world, nbr, adj_section))
89+ return 0;
90+ }
91+ }
92+
93+ return 1;
94+}
95+
96 void world_init(ClientWorld* world)
97 {
98 if (!world)
99@@ -76,13 +147,45 @@ void world_destroy(ClientWorld* world)
100 void world_remove_chunk(ClientWorld* world, i32 x, i32 z)
101 {
102 Chunk* chunk;
103+ u16 section_mask;
104
105 if (!world)
106 return;
107
108 chunk = world_chunk_find(world, x, z);
109- if (chunk)
110+ if (chunk) {
111+ section_mask = chunk->section_mask;
112 chunk_remove(chunk);
113+ chunk->x = x;
114+ chunk->z = z;
115+ world_chunk_remesh(world, chunk, section_mask);
116+ }
117+}
118+
119+i8 world_mesh_create(ClientWorld* world)
120+{
121+ Chunk* chunk;
122+ i32 chunk_index;
123+ i32 section;
124+
125+ if (!world)
126+ return 0;
127+
128+ for (chunk_index = 0; chunk_index < CLIENT_WORLD_MAX_CHUNKS; chunk_index++) {
129+ chunk = &world->chunks[chunk_index];
130+ if (!chunk->used)
131+ continue;
132+
133+ for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
134+ if (!(chunk->section_mask & (1U << section)))
135+ continue;
136+
137+ if (!world_chunk_section_mesh_create(world, chunk, section))
138+ return 0;
139+ }
140+ }
141+
142+ return 1;
143 }
144
145 i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8* data)
146@@ -90,6 +193,7 @@ i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8*
147 Chunk* chunk;
148 i32 section;
149 i32 data_section;
150+ u16 remesh_mask;
151
152 if (!world || !update || !data)
153 return 0;
154@@ -98,8 +202,11 @@ i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8*
155 if (!chunk)
156 return 1;
157
158- if (update->replace)
159+ remesh_mask = update->mask;
160+ if (update->replace) {
161+ remesh_mask |= chunk->section_mask;
162 chunk_clear(chunk);
163+ }
164
165 /* decode each section in chunk */
166 data_section = 0;
167@@ -121,5 +228,5 @@ i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8*
168 data_section++;
169 }
170
171- return 1;
172+ return world_chunk_remesh(world, chunk, remesh_mask);
173 }
+3,
-3
1@@ -57,10 +57,10 @@ static void create_local_world(void)
2 chunk->z = z;
3 chunk_section_create(&chunk->sections[0]);
4 chunk->section_mask = 1;
5-
6- if (!chunk_section_mesh_create(&chunk->sections[0]))
7- mg_die(MG_EC_MESH_CREATE, "Failed to create chunk mesh");
8 }
9+
10+ if (!world_mesh_create(&game.world))
11+ mg_die(MG_EC_MESH_CREATE, "Failed to create world mesh");
12 }
13
14 /* destroy game resources */
+0,
-1
1@@ -170,7 +170,6 @@ static i8 face_covers_cube(const ModelFace* face)
2 return face->cullface & 1 ? high[plane] == 1 : low[plane] == 0;
3 }
4
5-
6 /* load JSON file
7 returns parsed JSON, or NULL on failure */
8 static cJSON* json_load(const char* path)
+36,
-15
1@@ -6,8 +6,10 @@
2
3 #define MAX_MESH_VERTICES 65535U
4
5-static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count, const BlockFace* face,
6- i32 x, i32 y, i32 z);
7+static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count, const BlockFace* face,
8+ i32 x, i32 y, i32 z);
9+static BlockType chunk_section_get_neighbour(const ChunkSection* section, const ChunkSection* neighbours[6], i32 x,
10+ i32 y, i32 z);
11
12 /* 6 = number of faces on a cube */
13 static const i32 face_offsets[6][3] = {
14@@ -42,6 +44,29 @@ static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* i
15 indices[(*index_count)++] = (u16)(base + 3);
16 }
17
18+/* get block type from section or neighbouring sections
19+ returns the BlockType or BLOCK_AIR if not found */
20+static BlockType chunk_section_get_neighbour(const ChunkSection* section, const ChunkSection* neighbours[6], i32 x,
21+ i32 y, i32 z)
22+{
23+ if (neighbours) {
24+ if (z < 0 && neighbours[0])
25+ return chunk_section_get(neighbours[0], x, y, CHUNK_SECTION_DEPTH - 1);
26+ if (z >= CHUNK_SECTION_DEPTH && neighbours[1])
27+ return chunk_section_get(neighbours[1], x, y, 0);
28+ if (x < 0 && neighbours[2])
29+ return chunk_section_get(neighbours[2], CHUNK_SECTION_WIDTH - 1, y, z);
30+ if (x >= CHUNK_SECTION_WIDTH && neighbours[3])
31+ return chunk_section_get(neighbours[3], 0, y, z);
32+ if (y < 0 && neighbours[4])
33+ return chunk_section_get(neighbours[4], x, CHUNK_SECTION_HEIGHT - 1, z);
34+ if (y >= CHUNK_SECTION_HEIGHT && neighbours[5])
35+ return chunk_section_get(neighbours[5], x, 0, z);
36+ }
37+
38+ return chunk_section_get(section, x, y, z);
39+}
40+
41 void chunk_clear(Chunk* chunk)
42 {
43 i32 section;
44@@ -123,12 +148,6 @@ i8 chunk_section_decode(ChunkSection* section, const u8* data, size_t size)
45 chunk_section_set(section, x, y, z, state);
46 }
47
48- /* create mesh for section */
49- if (!chunk_section_mesh_create(section)) {
50- mg_log(LOG_ERR, "Failed to create server section mesh");
51- return 0;
52- }
53-
54 return 1;
55 }
56
57@@ -166,7 +185,7 @@ void chunk_section_set(ChunkSection* section, i32 x, i32 y, i32 z, BlockType blo
58 section->blocks[x][y][z] = block;
59 }
60
61-i8 chunk_section_mesh_create(ChunkSection* section)
62+i8 chunk_section_mesh_create(ChunkSection* section, const ChunkSection* neighbours[6])
63 {
64 MGVertex* vertices[BLOCK_TEXTURE_COUNT];
65 u16* indices[BLOCK_TEXTURE_COUNT];
66@@ -187,6 +206,8 @@ i8 chunk_section_mesh_create(ChunkSection* section)
67 if (!section)
68 return 0;
69
70+ chunk_section_mesh_destroy(section);
71+
72 memset(vertices, 0, sizeof(vertices));
73 memset(indices, 0, sizeof(indices));
74 memset(face_counts, 0, sizeof(face_counts));
75@@ -211,9 +232,9 @@ i8 chunk_section_mesh_create(ChunkSection* section)
76 continue;
77 if (faces[face].cullface >= 0) {
78 texture = faces[face].cullface;
79- neighbour = chunk_section_get(section, x + face_offsets[texture][0],
80- y + face_offsets[texture][1],
81- z + face_offsets[texture][2]);
82+ neighbour = chunk_section_get_neighbour(
83+ section, neighbours, x + face_offsets[texture][0],
84+ y + face_offsets[texture][1], z + face_offsets[texture][2]);
85 if (neighbour != BLOCK_AIR && block_model_occludes(neighbour))
86 continue;
87 }
88@@ -257,9 +278,9 @@ i8 chunk_section_mesh_create(ChunkSection* section)
89 continue;
90 if (faces[face].cullface >= 0) {
91 texture = faces[face].cullface;
92- neighbour = chunk_section_get(section, x + face_offsets[texture][0],
93- y + face_offsets[texture][1],
94- z + face_offsets[texture][2]);
95+ neighbour = chunk_section_get_neighbour(
96+ section, neighbours, x + face_offsets[texture][0],
97+ y + face_offsets[texture][1], z + face_offsets[texture][2]);
98 if (neighbour != BLOCK_AIR && block_model_occludes(neighbour))
99 continue;
100 }