Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add initial implementation of greedy meshing algorithm #4

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
122 changes: 102 additions & 20 deletions libchunkrender/chunkrender.c
Expand Up @@ -83,10 +83,10 @@ 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
{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] = {
Expand All @@ -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);

Expand Down Expand Up @@ -161,6 +177,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)) & 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,
uint8_t slice,
uint8_t *chunk_data,
Expand All @@ -169,19 +190,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;
} else {
a++;
}
}
}
Expand All @@ -198,6 +278,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) {
Expand Down
89 changes: 42 additions & 47 deletions test/testchunkrender/testchunkrender.cpp
Expand Up @@ -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.";
}
}

Expand Down Expand Up @@ -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";
}