From 82bc34feed8c619b48b39e9fd751b79d4bd167a5 Mon Sep 17 00:00:00 2001 From: Petter Arvidsson Date: Mon, 1 Jan 2018 23:48:43 +0100 Subject: [PATCH 1/5] Add initial implementation of greedy meshing algorithm - Implement greedy meshing algorithm - Add test cases --- libchunkrender/chunkrender.c | 76 ++++++++++++++++++-- test/testchunkrender/testchunkrender.cpp | 89 +++++++++++------------- 2 files changed, 113 insertions(+), 52 deletions(-) diff --git a/libchunkrender/chunkrender.c b/libchunkrender/chunkrender.c index 37ff4a8..877c80a 100644 --- a/libchunkrender/chunkrender.c +++ b/libchunkrender/chunkrender.c @@ -161,6 +161,11 @@ static uint8_t is_visible_through_next_slice(uint8_t face, uint8_t slice, uint8_ } } +#define is_masked(A, B, MASK) ((MASK[A] >> B) & 1) +#define set_mask(A, B, MASK) (MASK[A] |= 1UL << B) +#define clear_mask(A, B, MASK) (MASK[A] &= ~(1UL << B)) + + void generate_rectangles(uint8_t face, uint8_t slice, uint8_t *chunk_data, @@ -169,19 +174,78 @@ void generate_rectangles(uint8_t face, uint32_t block_texture[][6], rectangle_list_t *rectangle_list) { uint32_t rectangles = 0; - rectangle_t *data = rectangle_list->data; + uint32_t mask[CHUNK_SIZE]; + + for(int i=0; i < CHUNK_SIZE; i++) { + mask[i] = 0; + } + int direction = face_direction(face); - for(uint8_t a = 0; a < CHUNK_SIZE; a++) { - for(uint8_t b = 0; b < CHUNK_SIZE; b++) { + + for(uint8_t b = 0; b < CHUNK_SIZE; b++) { + for(uint8_t a = 0; a < CHUNK_SIZE; a++) { point_t point = point_in_slice(face, slice, a, b); uint32_t i = chunk_index(point); uint32_t block_type = chunk_data[i*BLOCK_SIZE + BLOCKS_HEADER_SIZE]; if(state[block_type] != STATE_GAS && is_visible_through_next_slice(face, slice, a, b, direction, chunk_data, is_transparent)) { - uint8_t *block_data = &chunk_data[i*BLOCK_SIZE + BLOCKS_HEADER_SIZE]; - rectangle_t rectangle = {a, b, 0, 0, block_data}; + set_mask(a, b, mask); + } + } + } + + rectangle_t *data = rectangle_list->data; + + for(uint8_t b = 0; b < CHUNK_SIZE; b++) { + for(uint8_t a = 0; a < CHUNK_SIZE;) { + point_t point = point_in_slice(face, slice, a, b); + uint32_t i = chunk_index(point); + + if(is_masked(a, b, mask)) { + uint32_t block_type = chunk_data[i*BLOCK_SIZE + BLOCKS_HEADER_SIZE]; + + // Calculcate the max possible width + uint8_t width; + for(width = 1; width < (CHUNK_SIZE - a); width++) { + point_t point = point_in_slice(face, slice, a + width, b); + uint32_t i = chunk_index(point); + uint32_t width_block_type = chunk_data[i*BLOCK_SIZE + BLOCKS_HEADER_SIZE]; + if(!is_masked(a + width, b, mask) || block_type != width_block_type) { + break; + } + } + + // Calculate the max possible height + uint8_t height; + for(height = 1; height < (CHUNK_SIZE - b); height++) { + uint8_t done = 0; + for(uint8_t k = 0; k < width; k++) { + point_t point = point_in_slice(face, slice, a + k, b + height); + uint32_t i = chunk_index(point); + uint32_t height_block_type = chunk_data[i*BLOCK_SIZE + BLOCKS_HEADER_SIZE]; + if(!is_masked(a + k, b + height, mask) || block_type != height_block_type) { + done = 1; + break; + } + } + if(done) { + break; + } + } + + rectangle_t rectangle = {a, b, width - 1, height - 1, &chunk_data[i*BLOCK_SIZE + BLOCKS_HEADER_SIZE]}; data[rectangles] = rectangle; rectangles++; + + for(uint8_t da = 0; da < width; da++) { + for(uint8_t db = 0; db < height; db++) { + clear_mask(a + da, b + db, mask); + } + } + + a += (width + 1); + } else { + a++; } } } @@ -198,6 +262,8 @@ chunk_block_model_t render_chunk_blocks(uint8_t *data, uint8_t *is_transparent, for(uint8_t slice = 0; slice < CHUNK_SIZE; slice++) { generate_rectangles(face, slice, data, is_transparent, state, block_texture, &rectangles); for(int i = 0; i < rectangles.size; i++) { + printf("Face: %d, slice: %d, a: %d, b: %d, width: %d, height: %d\n", face, slice, rectangles.data[i].a, + rectangles.data[i].b, rectangles.data[i].width, rectangles.data[i].height); generate_triangles(rectangles.data[i], face, slice, block_texture, model.data + vertices * 2); vertices += 6; if(vertices >= model.vertices) { diff --git a/test/testchunkrender/testchunkrender.cpp b/test/testchunkrender/testchunkrender.cpp index 1d5e937..f8a5a86 100644 --- a/test/testchunkrender/testchunkrender.cpp +++ b/test/testchunkrender/testchunkrender.cpp @@ -33,13 +33,13 @@ void validate_vertices(chunk_block_model_t model, vertex_t *vertices) { uint32_t d2 = model.data[i * 2 + 1]; vertex_t vertex = vertices[i]; - EXPECT_EQ(vertex.normal, (d1 >> OFF_NORMAL) & MASK_NORMAL) << "Normal of vertex " << i << " does not match."; - EXPECT_EQ(vertex.vertex, (d1 >> OFF_VERTEX) & MASK_VERTEX) << "Vertex number of vertex " << i << " does not match."; - EXPECT_EQ(vertex.x, (d1 >> OFF_X) & MASK_POS) << "X coordinate of vertex " << i << " does not match."; - EXPECT_EQ(vertex.y, (d1 >> OFF_Y) & MASK_POS) << "Y coordinate of vertex " << i << " does not match."; - EXPECT_EQ(vertex.z, (d1 >> OFF_Z) & MASK_POS) << "Z coordinate of vertex " << i << " does not match."; - EXPECT_EQ(vertex.du, (d2 >> OFF_DU) & MASK_UV) << "U texture coordinate of vertex " << i << " does not match."; - EXPECT_EQ(vertex.dv, (d2 >> OFF_DV) & MASK_UV) << "V texture coordinate of vertex " << i << " does not match."; + ASSERT_EQ(vertex.normal, (d1 >> OFF_NORMAL) & MASK_NORMAL) << "Normal of vertex " << i << " does not match."; + ASSERT_EQ(vertex.vertex, (d1 >> OFF_VERTEX) & MASK_VERTEX) << "Vertex number of vertex " << i << " does not match."; + ASSERT_EQ(vertex.x, (d1 >> OFF_X) & MASK_POS) << "X coordinate of vertex " << i << " does not match."; + ASSERT_EQ(vertex.y, (d1 >> OFF_Y) & MASK_POS) << "Y coordinate of vertex " << i << " does not match."; + ASSERT_EQ(vertex.z, (d1 >> OFF_Z) & MASK_POS) << "Z coordinate of vertex " << i << " does not match."; + ASSERT_EQ(vertex.du, (d2 >> OFF_DU) & MASK_UV) << "U texture coordinate of vertex " << i << " does not match."; + ASSERT_EQ(vertex.dv, (d2 >> OFF_DV) & MASK_UV) << "V texture coordinate of vertex " << i << " does not match."; } } @@ -148,78 +148,73 @@ TEST(ChunkRenderTest, TwoBlocksHasNoVerticesBetweenThem) { // Create list with 36 vertices vertex_t vertices[] = { // norm, vertex, x, y, z, u, v - {0, 2, 0, 0, 0, 0, 1}, // Left side, first block + {0, 2, 0, 0, 0, 0, 1}, // Left side {0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 1, 0}, {0, 5, 0, 0, 0, 1, 1}, {0, 2, 0, 0, 0, 0, 1}, {0, 1, 0, 0, 0, 1, 0}, - {1, 7, 1, 0, 0, 0, 1}, // Right side, second block + {1, 7, 1, 0, 0, 0, 1}, // Right side {1, 6, 1, 0, 0, 0, 0}, {1, 3, 1, 0, 0, 1, 0}, {1, 4, 1, 0, 0, 1, 1}, {1, 7, 1, 0, 0, 0, 1}, {1, 3, 1, 0, 0, 1, 0}, - {2, 2, 0, 0, 0, 0, 1}, // Up side, first block + {2, 2, 0, 0, 0, 0, 1}, // Up side {2, 5, 0, 0, 0, 0, 0}, - {2, 7, 0, 0, 0, 1, 0}, - {2, 4, 0, 0, 0, 1, 1}, - {2, 2, 0, 0, 0, 0, 1}, - {2, 7, 0, 0, 0, 1, 0}, - - {2, 2, 1, 0, 0, 0, 1}, // Up side, second block - {2, 5, 1, 0, 0, 0, 0}, {2, 7, 1, 0, 0, 1, 0}, {2, 4, 1, 0, 0, 1, 1}, - {2, 2, 1, 0, 0, 0, 1}, + {2, 2, 0, 0, 0, 0, 1}, {2, 7, 1, 0, 0, 1, 0}, - {3, 1, 0, 0, 0, 0, 1}, // Down side, first block + {3, 1, 0, 0, 0, 0, 1}, // Down side {3, 0, 0, 0, 0, 0, 0}, - {3, 3, 0, 0, 0, 1, 0}, - {3, 6, 0, 0, 0, 1, 1}, - {3, 1, 0, 0, 0, 0, 1}, - {3, 3, 0, 0, 0, 1, 0}, - - {3, 1, 1, 0, 0, 0, 1}, // Down side, second block - {3, 0, 1, 0, 0, 0, 0}, {3, 3, 1, 0, 0, 1, 0}, {3, 6, 1, 0, 0, 1, 1}, - {3, 1, 1, 0, 0, 0, 1}, + {3, 1, 0, 0, 0, 0, 1}, {3, 3, 1, 0, 0, 1, 0}, - {4, 5, 0, 0, 0, 0, 1}, // Front side, first block + {4, 5, 0, 0, 0, 0, 1}, // Front side {4, 1, 0, 0, 0, 0, 0}, - {4, 6, 0, 0, 0, 1, 0}, - {4, 7, 0, 0, 0, 1, 1}, - {4, 5, 0, 0, 0, 0, 1}, - {4, 6, 0, 0, 0, 1, 0}, - - {4, 5, 1, 0, 0, 0, 1}, // Front side, second block - {4, 1, 1, 0, 0, 0, 0}, {4, 6, 1, 0, 0, 1, 0}, {4, 7, 1, 0, 0, 1, 1}, - {4, 5, 1, 0, 0, 0, 1}, + {4, 5, 0, 0, 0, 0, 1}, {4, 6, 1, 0, 0, 1, 0}, - {5, 4, 0, 0, 0, 0, 1}, // Back side, first block - {5, 3, 0, 0, 0, 0, 0}, + {5, 4, 1, 0, 0, 0, 1}, // Back side + {5, 3, 1, 0, 0, 0, 0}, {5, 0, 0, 0, 0, 1, 0}, {5, 2, 0, 0, 0, 1, 1}, - {5, 4, 0, 0, 0, 0, 1}, - {5, 0, 0, 0, 0, 1, 0}, - - {5, 4, 1, 0, 0, 0, 1}, // Back side, second block - {5, 3, 1, 0, 0, 0, 0}, - {5, 0, 1, 0, 0, 1, 0}, - {5, 2, 1, 0, 0, 1, 1}, {5, 4, 1, 0, 0, 0, 1}, - {5, 0, 1, 0, 0, 1, 0} + {5, 0, 0, 0, 0, 1, 0} }; chunk_block_model_t model = render_chunk_blocks(data, is_transparent, state, texture); - EXPECT_EQ(model.vertices, 60) << "The number of vertices does not match"; + ASSERT_EQ(model.vertices, 36) << "The number of vertices does not match"; validate_vertices(model, vertices); } + + +TEST(ChunkRenderTest, ThreeBlocksOnTopHas36Vertices) { + clear(data); + + set(0,0,0,1,data); + set(0,1,0,1,data); + set(0,2,0,1,data); + + chunk_block_model_t model = render_chunk_blocks(data, is_transparent, state, texture); + ASSERT_EQ(model.vertices, 36) << "The number of vertices does not match"; +} + +TEST(ChunkRenderTest, ThreeBlocksInDepthHas36Vertices) { + clear(data); + + set(0,0,0,1,data); + set(0,0,1,1,data); + set(0,0,2,1,data); + + chunk_block_model_t model = render_chunk_blocks(data, is_transparent, state, texture); + ASSERT_EQ(model.vertices, 36) << "The number of vertices does not match"; +} From 8f0232187204c18b26a1b22cf0c31609fcf1470c Mon Sep 17 00:00:00 2001 From: Petter Arvidsson Date: Thu, 4 Jan 2018 21:11:55 +0100 Subject: [PATCH 2/5] Flip front back verices --- libchunkrender/chunkrender.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libchunkrender/chunkrender.c b/libchunkrender/chunkrender.c index 877c80a..c4bcd71 100644 --- a/libchunkrender/chunkrender.c +++ b/libchunkrender/chunkrender.c @@ -85,8 +85,8 @@ static uint8_t face_vertices[6][6] = { {7, 6, 3, 4, 7, 3}, // Right side {2, 5, 7, 4, 2, 7}, // Top side {1, 0, 3, 6, 1, 3}, // Bottom side - {5, 1, 6, 7, 5, 6}, // Back side - {4, 3, 0, 2, 4, 0} // Front side + {4, 3, 0, 2, 4, 0}, // Back side + {5, 1, 6, 7, 5, 6} // Front side }; static uint8_t uv[6][2] = { From 71f33a75d3d3e493b3d24a2b3e06c30ff2a1baea Mon Sep 17 00:00:00 2001 From: Petter Arvidsson Date: Thu, 4 Jan 2018 21:12:21 +0100 Subject: [PATCH 3/5] Use the correct blocks when rendering --- libchunkrender/chunkrender.c | 42 +++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/libchunkrender/chunkrender.c b/libchunkrender/chunkrender.c index c4bcd71..84f896d 100644 --- a/libchunkrender/chunkrender.c +++ b/libchunkrender/chunkrender.c @@ -83,7 +83,7 @@ static void free_chunk_block_model(chunk_block_model_t model) { static uint8_t face_vertices[6][6] = { {2, 0, 1, 5, 2, 1}, // Left side {7, 6, 3, 4, 7, 3}, // Right side - {2, 5, 7, 4, 2, 7}, // Top side + {7, 4, 2, 5, 7, 2}, // Top side {1, 0, 3, 6, 1, 3}, // Bottom side {4, 3, 0, 2, 4, 0}, // Back side {5, 1, 6, 7, 5, 6} // Front side @@ -112,18 +112,34 @@ static void generate_triangles(rectangle_t rectangle, uint8_t face, uint8_t slic for(uint8_t vertex = 0; vertex < 6; vertex++) { uint8_t a, b; - if(vertex == 0 || vertex == 4) { - a = rectangle.a; - b = rectangle.b + rectangle.height; - } else if(vertex == 1) { - a = rectangle.a; - b = rectangle.b; - } else if(vertex == 2 || vertex == 5) { - a = rectangle.a + rectangle.width; - b = rectangle.b; - } else { // vertex == 3 - a = rectangle.a + rectangle.width; - b = rectangle.b + rectangle.height; + if(face == RIGHT || face == BACK || face == TOP) { + if(vertex == 0 || vertex == 4) { + a = rectangle.a + rectangle.width; + b = rectangle.b + rectangle.height; + } else if(vertex == 1) { + a = rectangle.a + rectangle.width; + b = rectangle.b; + } else if(vertex == 2 || vertex == 5) { + a = rectangle.a; + b = rectangle.b; + } else { // vertex == 3 + a = rectangle.a; + b = rectangle.b + rectangle.height; + } + } else { + if(vertex == 0 || vertex == 4) { + a = rectangle.a; + b = rectangle.b + rectangle.height; + } else if(vertex == 1) { + a = rectangle.a; + b = rectangle.b; + } else if(vertex == 2 || vertex == 5) { + a = rectangle.a + rectangle.width; + b = rectangle.b; + } else { // vertex == 3 + a = rectangle.a + rectangle.width; + b = rectangle.b + rectangle.height; + } } point_t point = point_in_slice(face, slice, a, b); From 89bef64649e1879d9b2f76c9086514555cb53ffb Mon Sep 17 00:00:00 2001 From: Petter Arvidsson Date: Mon, 8 Jan 2018 19:25:15 +0100 Subject: [PATCH 4/5] Fix macros --- libchunkrender/chunkrender.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libchunkrender/chunkrender.c b/libchunkrender/chunkrender.c index 84f896d..4e1bd49 100644 --- a/libchunkrender/chunkrender.c +++ b/libchunkrender/chunkrender.c @@ -177,9 +177,9 @@ static uint8_t is_visible_through_next_slice(uint8_t face, uint8_t slice, uint8_ } } -#define is_masked(A, B, MASK) ((MASK[A] >> B) & 1) -#define set_mask(A, B, MASK) (MASK[A] |= 1UL << B) -#define clear_mask(A, B, MASK) (MASK[A] &= ~(1UL << B)) +#define is_masked(A, B, MASK) ((MASK[A] >> (B)) & 1U) +#define set_mask(A, B, MASK) (MASK[A] |= 1U << (B)) +#define clear_mask(A, B, MASK) (MASK[A] &= ~(1U << (B))) void generate_rectangles(uint8_t face, From f21d80e11b82efddddd48859d7d8ea730b51e013 Mon Sep 17 00:00:00 2001 From: Petter Arvidsson Date: Mon, 8 Jan 2018 19:25:33 +0100 Subject: [PATCH 5/5] Fix bug that skipped a face after every rectangle --- libchunkrender/chunkrender.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libchunkrender/chunkrender.c b/libchunkrender/chunkrender.c index 4e1bd49..dec4161 100644 --- a/libchunkrender/chunkrender.c +++ b/libchunkrender/chunkrender.c @@ -259,7 +259,7 @@ void generate_rectangles(uint8_t face, } } - a += (width + 1); + a += width; } else { a++; }