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
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 					}