mirror of
https://github.com/xtreme8000/CavEX.git
synced 2025-01-22 09:11:55 -05:00
Move chunk meshing and local server to own threads
This commit is contained in:
parent
eb2b52c0ca
commit
42e5246696
19 changed files with 1215 additions and 659 deletions
|
@ -20,16 +20,12 @@ static enum block_render_type getRenderType(struct block_info* this) {
|
|||
}
|
||||
|
||||
static uint8_t getTextureIndex(struct block_info* this, enum side side) {
|
||||
struct block_data right
|
||||
= world_get_block(this->world, this->x + 1, this->y, this->z);
|
||||
struct block_data left
|
||||
= world_get_block(this->world, this->x - 1, this->y, this->z);
|
||||
struct block_data back
|
||||
= world_get_block(this->world, this->x, this->y, this->z + 1);
|
||||
struct block_data front
|
||||
= world_get_block(this->world, this->x, this->y, this->z - 1);
|
||||
struct block_data* right = this->neighbours + SIDE_RIGHT;
|
||||
struct block_data* left = this->neighbours + SIDE_LEFT;
|
||||
struct block_data* back = this->neighbours + SIDE_BACK;
|
||||
struct block_data* front = this->neighbours + SIDE_FRONT;
|
||||
|
||||
if(right.type == this->block->type) {
|
||||
if(right->type == this->block->type) {
|
||||
switch(side) {
|
||||
case SIDE_TOP:
|
||||
case SIDE_BOTTOM: return TEXTURE_INDEX(9, 1);
|
||||
|
@ -39,7 +35,7 @@ static uint8_t getTextureIndex(struct block_info* this, enum side side) {
|
|||
}
|
||||
}
|
||||
|
||||
if(left.type == this->block->type) {
|
||||
if(left->type == this->block->type) {
|
||||
switch(side) {
|
||||
case SIDE_TOP:
|
||||
case SIDE_BOTTOM: return TEXTURE_INDEX(9, 1);
|
||||
|
@ -49,7 +45,7 @@ static uint8_t getTextureIndex(struct block_info* this, enum side side) {
|
|||
}
|
||||
}
|
||||
|
||||
if(back.type == this->block->type) {
|
||||
if(back->type == this->block->type) {
|
||||
switch(side) {
|
||||
case SIDE_TOP:
|
||||
case SIDE_BOTTOM: return TEXTURE_INDEX(9, 1);
|
||||
|
@ -59,7 +55,7 @@ static uint8_t getTextureIndex(struct block_info* this, enum side side) {
|
|||
}
|
||||
}
|
||||
|
||||
if(front.type == this->block->type) {
|
||||
if(front->type == this->block->type) {
|
||||
switch(side) {
|
||||
case SIDE_TOP:
|
||||
case SIDE_BOTTOM: return TEXTURE_INDEX(9, 1);
|
||||
|
@ -75,13 +71,13 @@ static uint8_t getTextureIndex(struct block_info* this, enum side side) {
|
|||
[SIDE_LEFT] = TEXTURE_INDEX(10, 1), [SIDE_RIGHT] = TEXTURE_INDEX(10, 1),
|
||||
};
|
||||
|
||||
if(left.type && !right.type)
|
||||
if(left->type && !right->type)
|
||||
tex[SIDE_RIGHT] = TEXTURE_INDEX(11, 1);
|
||||
else if(right.type && !left.type)
|
||||
else if(right->type && !left->type)
|
||||
tex[SIDE_LEFT] = TEXTURE_INDEX(11, 1);
|
||||
else if(front.type && !back.type)
|
||||
else if(front->type && !back->type)
|
||||
tex[SIDE_BACK] = TEXTURE_INDEX(11, 1);
|
||||
else if(back.type && !front.type)
|
||||
else if(back->type && !front->type)
|
||||
tex[SIDE_FRONT] = TEXTURE_INDEX(11, 1);
|
||||
else
|
||||
tex[SIDE_BACK] = TEXTURE_INDEX(11, 1);
|
||||
|
|
|
@ -21,15 +21,15 @@ static enum block_render_type getRenderType(struct block_info* this) {
|
|||
|
||||
static uint8_t getTextureIndex(struct block_info* this, enum side side) {
|
||||
switch(side) {
|
||||
case SIDE_TOP: return TEXTURE_INDEX(5, 9);
|
||||
case SIDE_TOP:
|
||||
return (this->neighbours[SIDE_TOP].type == BLOCK_SNOW) ?
|
||||
TEXTURE_INDEX(2, 4) :
|
||||
TEXTURE_INDEX(5, 9);
|
||||
case SIDE_BOTTOM: return TEXTURE_INDEX(2, 0);
|
||||
default:
|
||||
if(world_get_block(this->world, this->x, this->y + 1, this->z).type
|
||||
== BLOCK_SNOW) {
|
||||
return TEXTURE_INDEX(4, 4);
|
||||
} else {
|
||||
return TEXTURE_INDEX(5, 10);
|
||||
}
|
||||
return (this->neighbours[SIDE_TOP].type == BLOCK_SNOW) ?
|
||||
TEXTURE_INDEX(4, 4) :
|
||||
TEXTURE_INDEX(5, 10);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,7 @@ getSideMask(struct block_info* this, enum side side, struct block_info* it) {
|
|||
face_occlusion_full() :
|
||||
face_occlusion_empty();
|
||||
case SIDE_BOTTOM: return face_occlusion_full();
|
||||
default:
|
||||
return (world_get_block(this->world, this->x, this->y + 1, this->z)
|
||||
.type
|
||||
== this->block->type) ?
|
||||
face_occlusion_full() :
|
||||
face_occlusion_rect(block_height);
|
||||
default: return face_occlusion_rect(block_height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
500
source/chunk.c
500
source/chunk.c
|
@ -31,9 +31,10 @@ void chunk_init(struct chunk* c, struct world* world, w_coord_t x, w_coord_t y,
|
|||
c->has_displist[k] = false;
|
||||
c->rebuild_displist = false;
|
||||
c->world = world;
|
||||
c->reference_count = 0;
|
||||
}
|
||||
|
||||
void chunk_destroy(struct chunk* c) {
|
||||
static void chunk_destroy(struct chunk* c) {
|
||||
assert(c);
|
||||
|
||||
free(c->blocks);
|
||||
|
@ -42,6 +43,21 @@ void chunk_destroy(struct chunk* c) {
|
|||
if(c->has_displist[k])
|
||||
displaylist_destroy(c->mesh + k);
|
||||
}
|
||||
|
||||
free(c);
|
||||
}
|
||||
|
||||
void chunk_ref(struct chunk* c) {
|
||||
assert(c);
|
||||
c->reference_count++;
|
||||
}
|
||||
|
||||
void chunk_unref(struct chunk* c) {
|
||||
assert(c);
|
||||
c->reference_count--;
|
||||
|
||||
if(!c->reference_count)
|
||||
chunk_destroy(c);
|
||||
}
|
||||
|
||||
struct block_data chunk_get_block(struct chunk* c, c_coord_t x, c_coord_t y,
|
||||
|
@ -56,8 +72,9 @@ struct block_data chunk_get_block(struct chunk* c, c_coord_t x, c_coord_t y,
|
|||
};
|
||||
}
|
||||
|
||||
static struct block_data chunk_lookup_block(struct chunk* c, w_coord_t x,
|
||||
w_coord_t y, w_coord_t z) {
|
||||
// for global world lookup
|
||||
struct block_data chunk_lookup_block(struct chunk* c, w_coord_t x, w_coord_t y,
|
||||
w_coord_t z) {
|
||||
assert(c);
|
||||
struct chunk* other = c;
|
||||
|
||||
|
@ -65,14 +82,14 @@ static struct block_data chunk_lookup_block(struct chunk* c, w_coord_t x,
|
|||
|| z >= CHUNK_SIZE)
|
||||
other = world_find_chunk(c->world, c->x + x, c->y + y, c->z + z);
|
||||
|
||||
return other ? chunk_get_block(other, x & CHUNK_SIZE_BITS,
|
||||
y & CHUNK_SIZE_BITS, z & CHUNK_SIZE_BITS) :
|
||||
(struct block_data) {
|
||||
.type = (y < WORLD_HEIGHT) ? 1 : 0,
|
||||
.metadata = 0,
|
||||
.sky_light = 0,
|
||||
.torch_light = 0,
|
||||
};
|
||||
return other ?
|
||||
chunk_get_block(other, W2C_COORD(x), W2C_COORD(y), W2C_COORD(z)) :
|
||||
(struct block_data) {
|
||||
.type = (y < WORLD_HEIGHT) ? 1 : 0,
|
||||
.metadata = 0,
|
||||
.sky_light = (y < WORLD_HEIGHT) ? 0 : 15,
|
||||
.torch_light = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
|
||||
|
@ -87,453 +104,36 @@ void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
|
|||
|
||||
// TODO: diagonal chunks, just sharing edge or single point
|
||||
|
||||
if(x == 0) {
|
||||
struct chunk* other = world_find_chunk(c->world, c->x - 1, c->y, c->z);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
bool cond[6] = {
|
||||
x == 0, x == CHUNK_SIZE - 1, y == 0, y == CHUNK_SIZE - 1,
|
||||
z == 0, z == CHUNK_SIZE - 1,
|
||||
};
|
||||
|
||||
if(x == CHUNK_SIZE - 1) {
|
||||
struct chunk* other
|
||||
= world_find_chunk(c->world, c->x + CHUNK_SIZE, c->y, c->z);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
int offset[6][3] = {
|
||||
{-1, 0, 0}, {CHUNK_SIZE, 0, 0}, {0, -1, 0},
|
||||
{0, CHUNK_SIZE, 0}, {0, 0, -1}, {0, 0, CHUNK_SIZE},
|
||||
};
|
||||
|
||||
if(y == 0) {
|
||||
struct chunk* other = world_find_chunk(c->world, c->x, c->y - 1, c->z);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
|
||||
if(y == CHUNK_SIZE - 1) {
|
||||
struct chunk* other
|
||||
= world_find_chunk(c->world, c->x, c->y + CHUNK_SIZE, c->z);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
|
||||
if(z == 0) {
|
||||
struct chunk* other = world_find_chunk(c->world, c->x, c->y, c->z - 1);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
|
||||
if(z == CHUNK_SIZE - 1) {
|
||||
struct chunk* other
|
||||
= world_find_chunk(c->world, c->x, c->y, c->z + CHUNK_SIZE);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int chunk_test_side(enum side* on_sides, c_coord_t x, c_coord_t y,
|
||||
c_coord_t z) {
|
||||
assert(on_sides);
|
||||
|
||||
int count = 0;
|
||||
|
||||
if(x == 0)
|
||||
on_sides[count++] = SIDE_LEFT;
|
||||
|
||||
if(x == CHUNK_SIZE - 1)
|
||||
on_sides[count++] = SIDE_RIGHT;
|
||||
|
||||
if(y == 0)
|
||||
on_sides[count++] = SIDE_BOTTOM;
|
||||
|
||||
if(y == CHUNK_SIZE - 1)
|
||||
on_sides[count++] = SIDE_TOP;
|
||||
|
||||
if(z == 0)
|
||||
on_sides[count++] = SIDE_FRONT;
|
||||
|
||||
if(z == CHUNK_SIZE - 1)
|
||||
on_sides[count++] = SIDE_BACK;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void chunk_test2(struct chunk* c, struct stack* queue, bool* visited,
|
||||
uint8_t* reachable, c_coord_t x, c_coord_t y,
|
||||
c_coord_t z) {
|
||||
assert(queue && visited && reachable);
|
||||
|
||||
if(visited[CHUNK_INDEX(x, y, z)]
|
||||
|| (blocks[c->blocks[CHUNK_INDEX(x, y, z) * 3 + 0]]
|
||||
&& !blocks[c->blocks[CHUNK_INDEX(x, y, z) * 3 + 0]]
|
||||
->can_see_through))
|
||||
return;
|
||||
|
||||
stack_clear(queue);
|
||||
stack_push(queue, (uint8_t[]) {x, y, z});
|
||||
visited[CHUNK_INDEX(x, y, z)] = true;
|
||||
|
||||
uint8_t reached_sides = 0;
|
||||
|
||||
while(!stack_empty(queue)) {
|
||||
uint8_t block[3];
|
||||
stack_pop(queue, block);
|
||||
|
||||
enum side on_sides[3];
|
||||
size_t on_sides_len
|
||||
= chunk_test_side(on_sides, block[0], block[1], block[2]);
|
||||
assert(on_sides_len <= 3);
|
||||
|
||||
for(size_t k = 0; k < on_sides_len; k++)
|
||||
reached_sides |= (1 << on_sides[k]);
|
||||
|
||||
for(int s = 0; s < 6; s++) {
|
||||
int nx, ny, nz;
|
||||
blocks_side_offset(s, &nx, &ny, &nz);
|
||||
nx += block[0];
|
||||
ny += block[1];
|
||||
nz += block[2];
|
||||
|
||||
if(nx >= 0 && ny >= 0 && nz >= 0 && nx < CHUNK_SIZE
|
||||
&& ny < CHUNK_SIZE && nz < CHUNK_SIZE
|
||||
&& !visited[CHUNK_INDEX(nx, ny, nz)]
|
||||
&& (!blocks[c->blocks[CHUNK_INDEX(nx, ny, nz) * 3 + 0]]
|
||||
|| blocks[c->blocks[CHUNK_INDEX(nx, ny, nz) * 3 + 0]]
|
||||
->can_see_through)) {
|
||||
stack_push(queue, (uint8_t[]) {nx, ny, nz});
|
||||
visited[CHUNK_INDEX(nx, ny, nz)] = true;
|
||||
}
|
||||
for(int k = 0; k < 6; k++) {
|
||||
if(cond[k]) {
|
||||
struct chunk* other
|
||||
= world_find_chunk(c->world, c->x + offset[k][0],
|
||||
c->y + offset[k][1], c->z + offset[k][2]);
|
||||
if(other)
|
||||
other->rebuild_displist = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(int s = 0; s < 6; s++) {
|
||||
if(reached_sides & (1 << s))
|
||||
reachable[s] |= reached_sides;
|
||||
}
|
||||
}
|
||||
|
||||
void chunk_test(struct chunk* c) {
|
||||
bool chunk_check_built(struct chunk* c) {
|
||||
assert(c);
|
||||
|
||||
memset(c->reachable, 0, sizeof(c->reachable));
|
||||
|
||||
bool* visited = malloc(CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE);
|
||||
assert(visited);
|
||||
|
||||
memset(visited, false, CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE);
|
||||
|
||||
struct stack queue;
|
||||
stack_create(&queue, CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE / 4,
|
||||
sizeof(uint8_t[3]));
|
||||
|
||||
for(int y = 0; y < CHUNK_SIZE; y++) {
|
||||
for(int x = 0; x < CHUNK_SIZE; x++) {
|
||||
chunk_test2(c, &queue, visited, c->reachable, x, y, 0);
|
||||
chunk_test2(c, &queue, visited, c->reachable, x, 0, y);
|
||||
chunk_test2(c, &queue, visited, c->reachable, 0, x, y);
|
||||
chunk_test2(c, &queue, visited, c->reachable, x, y, CHUNK_SIZE - 1);
|
||||
chunk_test2(c, &queue, visited, c->reachable, x, CHUNK_SIZE - 1, y);
|
||||
chunk_test2(c, &queue, visited, c->reachable, CHUNK_SIZE - 1, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
stack_destroy(&queue);
|
||||
free(visited);
|
||||
}
|
||||
|
||||
static void chunk_vertex_light(struct chunk* c, uint8_t* light_data) {
|
||||
assert(c && light_data);
|
||||
|
||||
for(c_coord_t y = 0; y < CHUNK_SIZE + 2; y++) {
|
||||
for(c_coord_t z = 0; z < CHUNK_SIZE + 2; z++) {
|
||||
for(c_coord_t x = 0; x < CHUNK_SIZE + 2; x++) {
|
||||
struct block_data b1[4] = {
|
||||
chunk_lookup_block(c, x + 0, y - 1, z + 0),
|
||||
chunk_lookup_block(c, x - 1, y - 1, z + 0),
|
||||
chunk_lookup_block(c, x + 0, y - 1, z - 1),
|
||||
chunk_lookup_block(c, x - 1, y - 1, z - 1),
|
||||
};
|
||||
|
||||
struct block_data b2[4] = {
|
||||
chunk_lookup_block(c, x - 1, y + 0, z + 0),
|
||||
b1[1],
|
||||
chunk_lookup_block(c, x - 1, y + 0, z - 1),
|
||||
b1[3],
|
||||
};
|
||||
|
||||
struct block_data b3[4] = {
|
||||
chunk_lookup_block(c, x + 0, y + 0, z - 1),
|
||||
b2[2],
|
||||
b1[2],
|
||||
b1[3],
|
||||
};
|
||||
|
||||
// TODO: clean up horrible code
|
||||
|
||||
int shade_table[5] = {0, 1, 3, 5, 0};
|
||||
|
||||
int sum_sky, sum_torch, count;
|
||||
|
||||
sum_sky = sum_torch = count = 0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
if(!blocks[b1[k].type]
|
||||
|| (blocks[b1[k].type]->can_see_through
|
||||
&& !blocks[b1[k].type]->ignore_lighting)) {
|
||||
sum_sky += b1[k].sky_light;
|
||||
sum_torch += b1[k].torch_light;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sum_torch = count > 0 ? (sum_torch + count - 1) / count : 0;
|
||||
sum_sky = count > 0 ? (sum_sky + count - 1) / count : 0;
|
||||
sum_torch = MAX(sum_torch - shade_table[4 - count], 0);
|
||||
sum_sky = MAX(sum_sky - shade_table[4 - count], 0);
|
||||
|
||||
light_data[CHUNK_LIGHT_INDEX(x, y, z) * 3 + 0]
|
||||
= (sum_torch << 4) | sum_sky;
|
||||
|
||||
sum_sky = sum_torch = count = 0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
if(!blocks[b2[k].type]
|
||||
|| (blocks[b2[k].type]->can_see_through
|
||||
&& !blocks[b2[k].type]->ignore_lighting)) {
|
||||
sum_sky += b2[k].sky_light;
|
||||
sum_torch += b2[k].torch_light;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sum_torch = count > 0 ? (sum_torch + count - 1) / count : 0;
|
||||
sum_sky = count > 0 ? (sum_sky + count - 1) / count : 0;
|
||||
sum_torch = MAX(sum_torch - shade_table[4 - count], 0);
|
||||
sum_sky = MAX(sum_sky - shade_table[4 - count], 0);
|
||||
|
||||
light_data[CHUNK_LIGHT_INDEX(x, y, z) * 3 + 1]
|
||||
= (sum_torch << 4) | sum_sky;
|
||||
|
||||
sum_sky = sum_torch = count = 0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
if(!blocks[b3[k].type]
|
||||
|| (blocks[b3[k].type]->can_see_through
|
||||
&& !blocks[b3[k].type]->ignore_lighting)) {
|
||||
sum_sky += b3[k].sky_light;
|
||||
sum_torch += b3[k].torch_light;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sum_torch = count > 0 ? (sum_torch + count - 1) / count : 0;
|
||||
sum_sky = count > 0 ? (sum_sky + count - 1) / count : 0;
|
||||
sum_torch = MAX(sum_torch - shade_table[4 - count], 0);
|
||||
sum_sky = MAX(sum_sky - shade_table[4 - count], 0);
|
||||
|
||||
light_data[CHUNK_LIGHT_INDEX(x, y, z) * 3 + 2]
|
||||
= (sum_torch << 4) | sum_sky;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void chunk_rebuild(struct chunk* c, struct displaylist* d,
|
||||
uint8_t* light_data, bool count_only,
|
||||
size_t* vertices) {
|
||||
assert(c && d && light_data);
|
||||
|
||||
for(int k = 0; k < 13; k++)
|
||||
vertices[k] = 0;
|
||||
|
||||
for(c_coord_t y = 0; y < CHUNK_SIZE; y++) {
|
||||
for(c_coord_t z = 0; z < CHUNK_SIZE; z++) {
|
||||
for(c_coord_t x = 0; x < CHUNK_SIZE; x++) {
|
||||
struct block_data local = chunk_get_block(c, x, y, z);
|
||||
|
||||
if(blocks[local.type]) {
|
||||
struct block_info local_info = (struct block_info) {
|
||||
.block = &local,
|
||||
.world = c->world,
|
||||
.x = c->x + x,
|
||||
.y = c->y + y,
|
||||
.z = c->z + z,
|
||||
};
|
||||
|
||||
uint8_t vertex_light[24];
|
||||
bool light_loaded = count_only;
|
||||
|
||||
for(int k = 0; k < SIDE_MAX; k++) {
|
||||
enum side s = (enum side)k;
|
||||
|
||||
int ox, oy, oz;
|
||||
blocks_side_offset(s, &ox, &oy, &oz);
|
||||
|
||||
struct block_data neighbours
|
||||
= chunk_lookup_block(c, x + ox, y + oy, z + oz);
|
||||
|
||||
struct block_info neighbours_info
|
||||
= (struct block_info) {
|
||||
.block = &neighbours,
|
||||
.world = c->world,
|
||||
.x = c->x + x + ox,
|
||||
.y = c->y + y + oy,
|
||||
.z = c->z + z + oz,
|
||||
};
|
||||
|
||||
bool face_visible = true;
|
||||
|
||||
if(blocks[neighbours.type]
|
||||
&& ((!blocks[local.type]->transparent
|
||||
&& !blocks[neighbours.type]->transparent)
|
||||
|| blocks[local.type]->transparent)) {
|
||||
struct face_occlusion* a
|
||||
= blocks[local.type]->getSideMask(
|
||||
&local_info, s, &neighbours_info);
|
||||
struct face_occlusion* b
|
||||
= blocks[neighbours.type]->getSideMask(
|
||||
&neighbours_info, blocks_side_opposite(s),
|
||||
&local_info);
|
||||
|
||||
face_visible = face_occlusion_test(a, b);
|
||||
}
|
||||
|
||||
int dp_index = k;
|
||||
|
||||
if(blocks[local.type]->transparent)
|
||||
dp_index += 6;
|
||||
|
||||
if(blocks[local.type]->double_sided)
|
||||
dp_index = 12;
|
||||
|
||||
if(face_visible) {
|
||||
if(!light_loaded) {
|
||||
light_loaded = true;
|
||||
vertex_light[0] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 0, z + 0) * 3
|
||||
+ 0];
|
||||
vertex_light[1] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 0, z + 0) * 3
|
||||
+ 0];
|
||||
vertex_light[2] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 0, z + 1) * 3
|
||||
+ 0];
|
||||
vertex_light[3] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 0, z + 1) * 3
|
||||
+ 0];
|
||||
vertex_light[4] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 2, z + 0) * 3
|
||||
+ 0];
|
||||
vertex_light[5] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 2, z + 0) * 3
|
||||
+ 0];
|
||||
vertex_light[6] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 2, z + 1) * 3
|
||||
+ 0];
|
||||
vertex_light[7] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 2, z + 1) * 3
|
||||
+ 0];
|
||||
|
||||
vertex_light[8] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 0, z + 0) * 3
|
||||
+ 1];
|
||||
vertex_light[9] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 1, z + 0) * 3
|
||||
+ 1];
|
||||
vertex_light[10] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 1, z + 1) * 3
|
||||
+ 1];
|
||||
vertex_light[11] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 0, z + 1) * 3
|
||||
+ 1];
|
||||
vertex_light[12] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 2, y + 0, z + 0) * 3
|
||||
+ 1];
|
||||
vertex_light[13] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 2, y + 1, z + 0) * 3
|
||||
+ 1];
|
||||
vertex_light[14] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 2, y + 1, z + 1) * 3
|
||||
+ 1];
|
||||
vertex_light[15] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 2, y + 0, z + 1) * 3
|
||||
+ 1];
|
||||
|
||||
vertex_light[16] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 0, z + 0) * 3
|
||||
+ 2];
|
||||
vertex_light[17] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 0, z + 0) * 3
|
||||
+ 2];
|
||||
vertex_light[18] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 1, z + 0) * 3
|
||||
+ 2];
|
||||
vertex_light[19] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 1, z + 0) * 3
|
||||
+ 2];
|
||||
vertex_light[20] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 0, z + 2) * 3
|
||||
+ 2];
|
||||
vertex_light[21] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 0, z + 2) * 3
|
||||
+ 2];
|
||||
vertex_light[22] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 1, y + 1, z + 2) * 3
|
||||
+ 2];
|
||||
vertex_light[23] = light_data
|
||||
[CHUNK_LIGHT_INDEX(x + 0, y + 1, z + 2) * 3
|
||||
+ 2];
|
||||
}
|
||||
|
||||
vertices[dp_index]
|
||||
+= blocks[local.type]->renderBlock(
|
||||
d + dp_index, &local_info, s,
|
||||
&neighbours_info, vertex_light,
|
||||
count_only)
|
||||
* 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void chunk_check_built(struct chunk* c) {
|
||||
assert(c);
|
||||
|
||||
if(c->rebuild_displist) {
|
||||
uint8_t* light_data = malloc((CHUNK_SIZE + 2) * (CHUNK_SIZE + 2)
|
||||
* (CHUNK_SIZE + 2) * 3);
|
||||
assert(light_data);
|
||||
|
||||
chunk_vertex_light(c, light_data);
|
||||
|
||||
for(int k = 0; k < 13; k++) {
|
||||
if(c->has_displist[k]) {
|
||||
displaylist_destroy(c->mesh + k);
|
||||
c->has_displist[k] = false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t vertices[13];
|
||||
chunk_rebuild(c, c->mesh, light_data, true, vertices);
|
||||
|
||||
for(int k = 0; k < 13; k++) {
|
||||
if(vertices[k] > 0 && vertices[k] <= 0xFFFF * 4) {
|
||||
displaylist_init(c->mesh + k, vertices[k], 3 * 2 + 2 * 1 + 1);
|
||||
displaylist_begin(c->mesh + k, GX_QUADS, GX_VTXFMT0,
|
||||
vertices[k]);
|
||||
c->has_displist[k] = true;
|
||||
}
|
||||
}
|
||||
|
||||
chunk_rebuild(c, c->mesh, light_data, false, vertices);
|
||||
|
||||
for(int k = 0; k < 13; k++) {
|
||||
if(c->has_displist[k])
|
||||
displaylist_end(c->mesh + k);
|
||||
}
|
||||
|
||||
free(light_data);
|
||||
|
||||
chunk_test(c);
|
||||
|
||||
if(c->rebuild_displist && chunk_mesher_send(c)) {
|
||||
c->rebuild_displist = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void chunk_pre_render(struct chunk* c, mat4 view) {
|
||||
|
@ -543,6 +143,8 @@ void chunk_pre_render(struct chunk* c, mat4 view) {
|
|||
}
|
||||
|
||||
static void check_matrix_set(struct chunk* c, bool* needs_matrix) {
|
||||
assert(c && needs_matrix);
|
||||
|
||||
if(*needs_matrix) {
|
||||
gfx_matrix_modelview(c->model_view);
|
||||
*needs_matrix = false;
|
||||
|
@ -552,8 +154,6 @@ static void check_matrix_set(struct chunk* c, bool* needs_matrix) {
|
|||
void chunk_render(struct chunk* c, bool pass, float x, float y, float z) {
|
||||
assert(c);
|
||||
|
||||
chunk_check_built(c);
|
||||
|
||||
bool needs_matrix = true;
|
||||
int offset = pass ? 6 : 0;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <m-lib/m-i-list.h>
|
||||
|
||||
#include "block/blocks.h"
|
||||
#include "chunk_mesher.h"
|
||||
#include "platform/graphics/displaylist.h"
|
||||
#include "world.h"
|
||||
|
||||
|
@ -28,12 +29,12 @@ struct chunk {
|
|||
mat4 model_view;
|
||||
w_coord_t x, y, z;
|
||||
uint8_t* blocks;
|
||||
bool has_faces;
|
||||
struct displaylist mesh[13];
|
||||
bool has_displist[13];
|
||||
bool rebuild_displist;
|
||||
struct world* world;
|
||||
uint8_t reachable[6];
|
||||
size_t reference_count;
|
||||
struct chunk_step {
|
||||
bool visited;
|
||||
enum side from;
|
||||
|
@ -41,20 +42,24 @@ struct chunk {
|
|||
int steps;
|
||||
} tmp_data;
|
||||
ILIST_INTERFACE(ilist_chunks, struct chunk);
|
||||
ILIST_INTERFACE(ilist_chunks2, struct chunk);
|
||||
};
|
||||
|
||||
ILIST_DEF(ilist_chunks, struct chunk, M_POD_OPLIST)
|
||||
ILIST_DEF(ilist_chunks2, struct chunk, M_POD_OPLIST)
|
||||
|
||||
void chunk_init(struct chunk* c, struct world* world, w_coord_t x, w_coord_t y,
|
||||
w_coord_t z);
|
||||
void chunk_destroy(struct chunk* c);
|
||||
void chunk_ref(struct chunk* c);
|
||||
void chunk_unref(struct chunk* c);
|
||||
struct block_data chunk_get_block(struct chunk* c, c_coord_t x, c_coord_t y,
|
||||
c_coord_t z);
|
||||
struct block_data chunk_lookup_block(struct chunk* c, w_coord_t x, w_coord_t y,
|
||||
w_coord_t z);
|
||||
void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
|
||||
struct block_data blk);
|
||||
void chunk_check_built(struct chunk* c);
|
||||
bool chunk_check_built(struct chunk* c);
|
||||
void chunk_render(struct chunk* c, bool pass, float x, float y, float z);
|
||||
void chunk_pre_render(struct chunk* c, mat4 view);
|
||||
void chunk_test(struct chunk* c);
|
||||
|
||||
#endif
|
||||
|
|
544
source/chunk_mesher.c
Normal file
544
source/chunk_mesher.c
Normal file
|
@ -0,0 +1,544 @@
|
|||
#include <assert.h>
|
||||
#include <gccore.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "chunk_mesher.h"
|
||||
#include "platform/graphics/displaylist.h"
|
||||
#include "stack.h"
|
||||
#include "world.h"
|
||||
|
||||
#define BLK_INDEX(x, y, z) \
|
||||
((x) + ((z) + (y) * (CHUNK_SIZE + 2)) * (CHUNK_SIZE + 2))
|
||||
#define BLK_INDEX2(x, y, z) ((x) + ((z) + (y)*CHUNK_SIZE) * CHUNK_SIZE)
|
||||
#define BLK_DATA(b, x, y, z) ((b)[BLK_INDEX((x) + 1, (y) + 1, (z) + 1)])
|
||||
|
||||
struct chunk_mesher_rpc {
|
||||
struct chunk* chunk;
|
||||
// ingoing
|
||||
struct {
|
||||
struct block_data* blocks;
|
||||
} request;
|
||||
// outgoing
|
||||
struct {
|
||||
struct displaylist mesh[13];
|
||||
bool has_displist[13];
|
||||
uint8_t reachable[6];
|
||||
} result;
|
||||
};
|
||||
|
||||
static struct chunk_mesher_rpc rpc_msg[CHUNK_MESHER_QLENGTH];
|
||||
static mqbox_t mesher_requests;
|
||||
static mqbox_t mesher_results;
|
||||
static mqbox_t mesher_empty_msg;
|
||||
|
||||
static int chunk_test_side(enum side* on_sides, c_coord_t x, c_coord_t y,
|
||||
c_coord_t z) {
|
||||
assert(on_sides);
|
||||
|
||||
int count = 0;
|
||||
|
||||
if(x == 0)
|
||||
on_sides[count++] = SIDE_LEFT;
|
||||
|
||||
if(x == CHUNK_SIZE - 1)
|
||||
on_sides[count++] = SIDE_RIGHT;
|
||||
|
||||
if(y == 0)
|
||||
on_sides[count++] = SIDE_BOTTOM;
|
||||
|
||||
if(y == CHUNK_SIZE - 1)
|
||||
on_sides[count++] = SIDE_TOP;
|
||||
|
||||
if(z == 0)
|
||||
on_sides[count++] = SIDE_FRONT;
|
||||
|
||||
if(z == CHUNK_SIZE - 1)
|
||||
on_sides[count++] = SIDE_BACK;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void chunk_test(struct block_data* bd, struct stack* queue,
|
||||
bool* visited, uint8_t* reachable, c_coord_t x,
|
||||
c_coord_t y, c_coord_t z) {
|
||||
assert(bd && queue && visited && reachable);
|
||||
|
||||
if(visited[BLK_INDEX2(x, y, z)]
|
||||
|| (blocks[BLK_DATA(bd, x, y, z).type]
|
||||
&& !blocks[BLK_DATA(bd, x, y, z).type]->can_see_through))
|
||||
return;
|
||||
|
||||
stack_clear(queue);
|
||||
stack_push(queue, (uint8_t[]) {x, y, z});
|
||||
visited[BLK_INDEX2(x, y, z)] = true;
|
||||
|
||||
uint8_t reached_sides = 0;
|
||||
|
||||
while(!stack_empty(queue)) {
|
||||
uint8_t block[3];
|
||||
stack_pop(queue, block);
|
||||
|
||||
enum side on_sides[3];
|
||||
size_t on_sides_len
|
||||
= chunk_test_side(on_sides, block[0], block[1], block[2]);
|
||||
assert(on_sides_len <= 3);
|
||||
|
||||
for(size_t k = 0; k < on_sides_len; k++)
|
||||
reached_sides |= (1 << on_sides[k]);
|
||||
|
||||
for(int s = 0; s < 6; s++) {
|
||||
int nx, ny, nz;
|
||||
blocks_side_offset(s, &nx, &ny, &nz);
|
||||
nx += block[0];
|
||||
ny += block[1];
|
||||
nz += block[2];
|
||||
|
||||
if(nx >= 0 && ny >= 0 && nz >= 0 && nx < CHUNK_SIZE
|
||||
&& ny < CHUNK_SIZE && nz < CHUNK_SIZE
|
||||
&& !visited[BLK_INDEX2(nx, ny, nz)]
|
||||
&& (!blocks[BLK_DATA(bd, nx, ny, nz).type]
|
||||
|| blocks[BLK_DATA(bd, nx, ny, nz).type]->can_see_through)) {
|
||||
stack_push(queue, (uint8_t[]) {nx, ny, nz});
|
||||
visited[BLK_INDEX2(nx, ny, nz)] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int s = 0; s < 6; s++) {
|
||||
if(reached_sides & (1 << s))
|
||||
reachable[s] |= reached_sides;
|
||||
}
|
||||
}
|
||||
|
||||
static void chunk_test_init(struct block_data* bd, uint8_t* reachable) {
|
||||
assert(bd && reachable);
|
||||
|
||||
memset(reachable, 0, 6 * sizeof(uint8_t));
|
||||
|
||||
bool* visited = malloc(CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE);
|
||||
assert(visited);
|
||||
|
||||
memset(visited, false, CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE);
|
||||
|
||||
struct stack queue;
|
||||
stack_create(&queue, CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE / 4,
|
||||
sizeof(uint8_t[3]));
|
||||
|
||||
for(int y = 0; y < CHUNK_SIZE; y++) {
|
||||
for(int x = 0; x < CHUNK_SIZE; x++) {
|
||||
chunk_test(bd, &queue, visited, reachable, x, y, 0);
|
||||
chunk_test(bd, &queue, visited, reachable, x, 0, y);
|
||||
chunk_test(bd, &queue, visited, reachable, 0, x, y);
|
||||
chunk_test(bd, &queue, visited, reachable, x, y, CHUNK_SIZE - 1);
|
||||
chunk_test(bd, &queue, visited, reachable, x, CHUNK_SIZE - 1, y);
|
||||
chunk_test(bd, &queue, visited, reachable, CHUNK_SIZE - 1, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
stack_destroy(&queue);
|
||||
free(visited);
|
||||
}
|
||||
|
||||
static void chunk_mesher_vertex_light(struct block_data* bd,
|
||||
uint8_t* light_data) {
|
||||
assert(bd && light_data);
|
||||
|
||||
for(c_coord_t y = 0; y < CHUNK_SIZE + 2; y++) {
|
||||
for(c_coord_t z = 0; z < CHUNK_SIZE + 2; z++) {
|
||||
for(c_coord_t x = 0; x < CHUNK_SIZE + 2; x++) {
|
||||
struct block_data b1[4] = {
|
||||
BLK_DATA(bd, x + 0, y - 1, z + 0),
|
||||
BLK_DATA(bd, x - 1, y - 1, z + 0),
|
||||
BLK_DATA(bd, x + 0, y - 1, z - 1),
|
||||
BLK_DATA(bd, x - 1, y - 1, z - 1),
|
||||
};
|
||||
|
||||
struct block_data b2[4] = {
|
||||
BLK_DATA(bd, x - 1, y + 0, z + 0),
|
||||
b1[1],
|
||||
BLK_DATA(bd, x - 1, y + 0, z - 1),
|
||||
b1[3],
|
||||
};
|
||||
|
||||
struct block_data b3[4] = {
|
||||
BLK_DATA(bd, x + 0, y + 0, z - 1),
|
||||
b2[2],
|
||||
b1[2],
|
||||
b1[3],
|
||||
};
|
||||
|
||||
// TODO: clean up horrible code
|
||||
|
||||
int shade_table[5] = {0, 1, 3, 5, 0};
|
||||
|
||||
int sum_sky, sum_torch, count;
|
||||
|
||||
sum_sky = sum_torch = count = 0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
if(!blocks[b1[k].type]
|
||||
|| (blocks[b1[k].type]->can_see_through
|
||||
&& !blocks[b1[k].type]->ignore_lighting)) {
|
||||
sum_sky += b1[k].sky_light;
|
||||
sum_torch += b1[k].torch_light;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sum_torch = count > 0 ? (sum_torch + count - 1) / count : 0;
|
||||
sum_sky = count > 0 ? (sum_sky + count - 1) / count : 0;
|
||||
sum_torch = MAX(sum_torch - shade_table[4 - count], 0);
|
||||
sum_sky = MAX(sum_sky - shade_table[4 - count], 0);
|
||||
|
||||
light_data[BLK_INDEX(x, y, z) * 3 + 0]
|
||||
= (sum_torch << 4) | sum_sky;
|
||||
|
||||
sum_sky = sum_torch = count = 0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
if(!blocks[b2[k].type]
|
||||
|| (blocks[b2[k].type]->can_see_through
|
||||
&& !blocks[b2[k].type]->ignore_lighting)) {
|
||||
sum_sky += b2[k].sky_light;
|
||||
sum_torch += b2[k].torch_light;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sum_torch = count > 0 ? (sum_torch + count - 1) / count : 0;
|
||||
sum_sky = count > 0 ? (sum_sky + count - 1) / count : 0;
|
||||
sum_torch = MAX(sum_torch - shade_table[4 - count], 0);
|
||||
sum_sky = MAX(sum_sky - shade_table[4 - count], 0);
|
||||
|
||||
light_data[BLK_INDEX(x, y, z) * 3 + 1]
|
||||
= (sum_torch << 4) | sum_sky;
|
||||
|
||||
sum_sky = sum_torch = count = 0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
if(!blocks[b3[k].type]
|
||||
|| (blocks[b3[k].type]->can_see_through
|
||||
&& !blocks[b3[k].type]->ignore_lighting)) {
|
||||
sum_sky += b3[k].sky_light;
|
||||
sum_torch += b3[k].torch_light;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sum_torch = count > 0 ? (sum_torch + count - 1) / count : 0;
|
||||
sum_sky = count > 0 ? (sum_sky + count - 1) / count : 0;
|
||||
sum_torch = MAX(sum_torch - shade_table[4 - count], 0);
|
||||
sum_sky = MAX(sum_sky - shade_table[4 - count], 0);
|
||||
|
||||
light_data[BLK_INDEX(x, y, z) * 3 + 2]
|
||||
= (sum_torch << 4) | sum_sky;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void chunk_mesher_rebuild(struct block_data* bd, w_coord_t cx,
|
||||
w_coord_t cy, w_coord_t cz,
|
||||
struct displaylist* d, uint8_t* light_data,
|
||||
bool count_only, size_t* vertices) {
|
||||
assert(bd && d && light_data && vertices);
|
||||
|
||||
for(int k = 0; k < 13; k++)
|
||||
vertices[k] = 0;
|
||||
|
||||
for(c_coord_t y = 0; y < CHUNK_SIZE; y++) {
|
||||
for(c_coord_t z = 0; z < CHUNK_SIZE; z++) {
|
||||
for(c_coord_t x = 0; x < CHUNK_SIZE; x++) {
|
||||
struct block_data local = BLK_DATA(bd, x, y, z);
|
||||
|
||||
if(blocks[local.type]) {
|
||||
struct block_data neighbours[6];
|
||||
struct block_info neighbours_info[6];
|
||||
|
||||
for(int k = 0; k < SIDE_MAX; k++) {
|
||||
enum side s = (enum side)k;
|
||||
|
||||
int ox, oy, oz;
|
||||
blocks_side_offset(s, &ox, &oy, &oz);
|
||||
|
||||
neighbours[k] = BLK_DATA(bd, x + ox, y + oy, z + oz);
|
||||
|
||||
neighbours_info[k] = (struct block_info) {
|
||||
.block = neighbours + k,
|
||||
.neighbours = NULL,
|
||||
.x = cx + x + ox,
|
||||
.y = cy + y + oy,
|
||||
.z = cz + z + oz,
|
||||
};
|
||||
}
|
||||
|
||||
struct block_info local_info = (struct block_info) {
|
||||
.block = &local,
|
||||
.neighbours = neighbours,
|
||||
.x = cx + x,
|
||||
.y = cy + y,
|
||||
.z = cz + z,
|
||||
};
|
||||
|
||||
uint8_t vertex_light[24];
|
||||
bool light_loaded = count_only;
|
||||
|
||||
for(int k = 0; k < SIDE_MAX; k++) {
|
||||
enum side s = (enum side)k;
|
||||
|
||||
bool face_visible = true;
|
||||
|
||||
if(blocks[neighbours[k].type]
|
||||
&& ((!blocks[local.type]->transparent
|
||||
&& !blocks[neighbours[k].type]->transparent)
|
||||
|| blocks[local.type]->transparent)) {
|
||||
struct face_occlusion* a
|
||||
= blocks[local.type]->getSideMask(
|
||||
&local_info, s, neighbours_info + k);
|
||||
struct face_occlusion* b
|
||||
= blocks[neighbours[k].type]->getSideMask(
|
||||
neighbours_info + k,
|
||||
blocks_side_opposite(s), &local_info);
|
||||
|
||||
face_visible = face_occlusion_test(a, b);
|
||||
}
|
||||
|
||||
int dp_index = k;
|
||||
|
||||
if(blocks[local.type]->transparent)
|
||||
dp_index += 6;
|
||||
|
||||
if(blocks[local.type]->double_sided)
|
||||
dp_index = 12;
|
||||
|
||||
if(face_visible) {
|
||||
if(!light_loaded) {
|
||||
light_loaded = true;
|
||||
vertex_light[0]
|
||||
= light_data[BLK_INDEX(x + 0, y + 0, z + 0)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[1]
|
||||
= light_data[BLK_INDEX(x + 1, y + 0, z + 0)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[2]
|
||||
= light_data[BLK_INDEX(x + 1, y + 0, z + 1)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[3]
|
||||
= light_data[BLK_INDEX(x + 0, y + 0, z + 1)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[4]
|
||||
= light_data[BLK_INDEX(x + 0, y + 2, z + 0)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[5]
|
||||
= light_data[BLK_INDEX(x + 1, y + 2, z + 0)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[6]
|
||||
= light_data[BLK_INDEX(x + 1, y + 2, z + 1)
|
||||
* 3
|
||||
+ 0];
|
||||
vertex_light[7]
|
||||
= light_data[BLK_INDEX(x + 0, y + 2, z + 1)
|
||||
* 3
|
||||
+ 0];
|
||||
|
||||
vertex_light[8]
|
||||
= light_data[BLK_INDEX(x + 0, y + 0, z + 0)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[9]
|
||||
= light_data[BLK_INDEX(x + 0, y + 1, z + 0)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[10]
|
||||
= light_data[BLK_INDEX(x + 0, y + 1, z + 1)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[11]
|
||||
= light_data[BLK_INDEX(x + 0, y + 0, z + 1)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[12]
|
||||
= light_data[BLK_INDEX(x + 2, y + 0, z + 0)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[13]
|
||||
= light_data[BLK_INDEX(x + 2, y + 1, z + 0)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[14]
|
||||
= light_data[BLK_INDEX(x + 2, y + 1, z + 1)
|
||||
* 3
|
||||
+ 1];
|
||||
vertex_light[15]
|
||||
= light_data[BLK_INDEX(x + 2, y + 0, z + 1)
|
||||
* 3
|
||||
+ 1];
|
||||
|
||||
vertex_light[16]
|
||||
= light_data[BLK_INDEX(x + 0, y + 0, z + 0)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[17]
|
||||
= light_data[BLK_INDEX(x + 1, y + 0, z + 0)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[18]
|
||||
= light_data[BLK_INDEX(x + 1, y + 1, z + 0)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[19]
|
||||
= light_data[BLK_INDEX(x + 0, y + 1, z + 0)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[20]
|
||||
= light_data[BLK_INDEX(x + 0, y + 0, z + 2)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[21]
|
||||
= light_data[BLK_INDEX(x + 1, y + 0, z + 2)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[22]
|
||||
= light_data[BLK_INDEX(x + 1, y + 1, z + 2)
|
||||
* 3
|
||||
+ 2];
|
||||
vertex_light[23]
|
||||
= light_data[BLK_INDEX(x + 0, y + 1, z + 2)
|
||||
* 3
|
||||
+ 2];
|
||||
}
|
||||
|
||||
vertices[dp_index]
|
||||
+= blocks[local.type]->renderBlock(
|
||||
d + dp_index, &local_info, s,
|
||||
neighbours_info + k, vertex_light,
|
||||
count_only)
|
||||
* 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void chunk_mesher_build(struct chunk_mesher_rpc* req) {
|
||||
uint8_t* light_data
|
||||
= malloc((CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * 3);
|
||||
assert(light_data);
|
||||
|
||||
chunk_mesher_vertex_light(req->request.blocks, light_data);
|
||||
|
||||
for(int k = 0; k < 13; k++)
|
||||
req->result.has_displist[k] = false;
|
||||
|
||||
size_t vertices[13];
|
||||
chunk_mesher_rebuild(req->request.blocks, req->chunk->x, req->chunk->y,
|
||||
req->chunk->z, req->result.mesh, light_data, true,
|
||||
vertices);
|
||||
|
||||
for(int k = 0; k < 13; k++) {
|
||||
if(vertices[k] > 0 && vertices[k] <= 0xFFFF * 4) {
|
||||
displaylist_init(req->result.mesh + k, vertices[k],
|
||||
3 * 2 + 2 * 1 + 1);
|
||||
displaylist_begin(req->result.mesh + k, GX_QUADS, GX_VTXFMT0,
|
||||
vertices[k]);
|
||||
req->result.has_displist[k] = true;
|
||||
}
|
||||
}
|
||||
|
||||
chunk_mesher_rebuild(req->request.blocks, req->chunk->x, req->chunk->y,
|
||||
req->chunk->z, req->result.mesh, light_data, false,
|
||||
vertices);
|
||||
|
||||
free(light_data);
|
||||
|
||||
for(int k = 0; k < 13; k++) {
|
||||
if(req->result.has_displist[k])
|
||||
displaylist_end(req->result.mesh + k);
|
||||
}
|
||||
|
||||
chunk_test_init(req->request.blocks, req->result.reachable);
|
||||
|
||||
free(req->request.blocks);
|
||||
}
|
||||
|
||||
static void* chunk_mesher_local_thread(void* user) {
|
||||
while(1) {
|
||||
struct chunk_mesher_rpc* request;
|
||||
MQ_Receive(mesher_requests, (mqmsg_t*)&request, MQ_MSG_BLOCK);
|
||||
chunk_mesher_build(request);
|
||||
MQ_Send(mesher_results, request, MQ_MSG_BLOCK);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void chunk_mesher_init() {
|
||||
MQ_Init(&mesher_requests, CHUNK_MESHER_QLENGTH);
|
||||
MQ_Init(&mesher_results, CHUNK_MESHER_QLENGTH);
|
||||
MQ_Init(&mesher_empty_msg, CHUNK_MESHER_QLENGTH);
|
||||
|
||||
for(int k = 0; k < CHUNK_MESHER_QLENGTH; k++)
|
||||
MQ_Send(mesher_empty_msg, rpc_msg + k, MQ_MSG_BLOCK);
|
||||
|
||||
lwp_t thread;
|
||||
LWP_CreateThread(&thread, chunk_mesher_local_thread, NULL, NULL, 0, 4);
|
||||
}
|
||||
|
||||
void chunk_mesher_receive() {
|
||||
struct chunk_mesher_rpc* result;
|
||||
while(MQ_Receive(mesher_results, (mqmsg_t*)&result, MQ_MSG_NOBLOCK)) {
|
||||
for(int k = 0; k < 13; k++) {
|
||||
if(result->chunk->has_displist[k])
|
||||
displaylist_destroy(result->chunk->mesh + k);
|
||||
|
||||
result->chunk->mesh[k] = result->result.mesh[k];
|
||||
result->chunk->has_displist[k] = result->result.has_displist[k];
|
||||
}
|
||||
|
||||
for(int k = 0; k < 6; k++)
|
||||
result->chunk->reachable[k] = result->result.reachable[k];
|
||||
|
||||
chunk_unref(result->chunk);
|
||||
|
||||
MQ_Send(mesher_empty_msg, result, MQ_MSG_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
bool chunk_mesher_send(struct chunk* c) {
|
||||
assert(c);
|
||||
|
||||
struct chunk_mesher_rpc* request;
|
||||
if(!MQ_Receive(mesher_empty_msg, (mqmsg_t*)&request, MQ_MSG_NOBLOCK))
|
||||
return false;
|
||||
|
||||
struct block_data* bd
|
||||
= malloc((CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * (CHUNK_SIZE + 2)
|
||||
* sizeof(struct block_data));
|
||||
|
||||
if(!bd) {
|
||||
MQ_Send(mesher_empty_msg, request, MQ_MSG_BLOCK);
|
||||
return false;
|
||||
}
|
||||
|
||||
chunk_ref(c);
|
||||
|
||||
request->chunk = c;
|
||||
request->request.blocks = bd;
|
||||
|
||||
for(w_coord_t y = -1; y < CHUNK_SIZE + 1; y++) {
|
||||
for(w_coord_t z = -1; z < CHUNK_SIZE + 1; z++) {
|
||||
for(w_coord_t x = -1; x < CHUNK_SIZE + 1; x++) {
|
||||
bd[BLK_INDEX(x + 1, y + 1, z + 1)]
|
||||
= chunk_lookup_block(c, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MQ_Send(mesher_requests, request, MQ_MSG_BLOCK);
|
||||
return true;
|
||||
}
|
14
source/chunk_mesher.h
Normal file
14
source/chunk_mesher.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef CHUNK_MESHER_H
|
||||
#define CHUNK_MESHER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define CHUNK_MESHER_QLENGTH 8
|
||||
|
||||
struct chunk;
|
||||
|
||||
void chunk_mesher_init(void);
|
||||
void chunk_mesher_receive(void);
|
||||
bool chunk_mesher_send(struct chunk* c);
|
||||
|
||||
#endif
|
|
@ -6,6 +6,8 @@
|
|||
#include "../../platform/input.h"
|
||||
#include "../game_state.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
static void screen_ingame_render3D(struct screen* s, mat4 view) {
|
||||
if(gstate.world_loaded && gstate.camera_hit.hit) {
|
||||
struct block_data blk
|
||||
|
@ -14,7 +16,6 @@ static void screen_ingame_render3D(struct screen* s, mat4 view) {
|
|||
render_block_selection(view,
|
||||
&(struct block_info) {
|
||||
.block = &blk,
|
||||
.world = &gstate.world,
|
||||
.x = gstate.camera_hit.x,
|
||||
.y = gstate.camera_hit.y,
|
||||
.z = gstate.camera_hit.z,
|
||||
|
@ -52,7 +53,7 @@ static void screen_ingame_render2D(struct screen* s, int width, int height) {
|
|||
char str[64];
|
||||
sprintf(str, GAME_NAME " Alpha %i.%i.%i (impl. MC B1.7.3)", VERSION_MAJOR,
|
||||
VERSION_MINOR, VERSION_PATCH);
|
||||
gutil_text(4, 4 + 16 * 0, str, 16);
|
||||
gutil_text(4, 4 + 17 * 0, str, 16);
|
||||
|
||||
sprintf(str, "%0.1f fps, wait: gpu %0.1fms, vsync %0.1fms",
|
||||
gstate.stats.fps, gstate.stats.dt_gpu * 1000.0F,
|
||||
|
@ -69,20 +70,25 @@ static void screen_ingame_render2D(struct screen* s, int width, int height) {
|
|||
// sprintf(str, "daytime: %0.2f", daytime);
|
||||
// gutil_text(4, 4 + 17 * 4, str, 16);
|
||||
|
||||
sprintf(str, "side: %s, (%i, %i, %i)",
|
||||
block_side_name(gstate.camera_hit.side), gstate.camera_hit.x,
|
||||
gstate.camera_hit.y, gstate.camera_hit.z);
|
||||
gutil_text(4, 4 + 17 * 5, str, 16);
|
||||
if(gstate.camera_hit.hit) {
|
||||
sprintf(str, "side: %s, (%i, %i, %i)",
|
||||
block_side_name(gstate.camera_hit.side), gstate.camera_hit.x,
|
||||
gstate.camera_hit.y, gstate.camera_hit.z);
|
||||
gutil_text(4, 4 + 17 * 5, str, 16);
|
||||
}
|
||||
|
||||
gutil_text(32 + 32 + 6, 429 + 16 - 5, "Movement", 10);
|
||||
gutil_text(32 + 32 + 6 + gutil_font_width("Movement", 10) + 6 + 32 + 6,
|
||||
429 + 16 - 5, "Inventory", 10);
|
||||
sprintf(str, "mem used %iKiB", mallinfo().uordblks / 1024);
|
||||
gutil_text(4, 4 + 17 * 6, str, 16);
|
||||
|
||||
int icon_offset = 32;
|
||||
icon_offset += gutil_control_icon(icon_offset, CONTROL_A, "Inventory");
|
||||
icon_offset += gutil_control_icon(icon_offset, CONTROL_B, "Jump");
|
||||
if(gstate.camera_hit.hit) {
|
||||
icon_offset += gutil_control_icon(icon_offset, CONTROL_C, "Place");
|
||||
icon_offset += gutil_control_icon(icon_offset, CONTROL_Z, "Mine");
|
||||
}
|
||||
|
||||
gfx_bind_texture(TEXTURE_GUI);
|
||||
gutil_texquad(32, 429, 448 / 2, 64, 32 / 2, 32, 32, 32);
|
||||
gutil_texquad(32 + 32 + 6 + gutil_font_width("Movement", 10) + 6, 429,
|
||||
448 / 2, 0, 32 / 2, 32, 32, 32);
|
||||
// gutil_texquad(32 + 96, 429, 480 / 2, 0, 32 / 2, 32, 32, 32);
|
||||
|
||||
// draw inventory
|
||||
/*gutil_texquad((782 - 176 * 2) / 2, (480 - 167 * 2) / 2, 176 / 2, 135,
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
#include <fat.h>
|
||||
#endif
|
||||
|
||||
#include "chunk_mesher.h"
|
||||
#include "game/game_state.h"
|
||||
#include "game/gui/screen.h"
|
||||
#include "network/client_interface.h"
|
||||
#include "network/server_local.h"
|
||||
#include "platform/graphics/gfx.h"
|
||||
#include "platform/graphics/gui_util.h"
|
||||
|
@ -66,12 +68,20 @@ int main(void) {
|
|||
gstate.config.render_distance = 192.0F;
|
||||
|
||||
world_create(&gstate.world);
|
||||
world_preload(&gstate.world, loading_progress);
|
||||
|
||||
// world_preload(&gstate.world, loading_progress);
|
||||
gstate.world_loaded = true;
|
||||
|
||||
ptime_t last_frame = time_get();
|
||||
float daytime = 1.0F;
|
||||
|
||||
clin_init();
|
||||
svin_init();
|
||||
chunk_mesher_init();
|
||||
|
||||
struct server_local server;
|
||||
server_local_create(&server);
|
||||
|
||||
while(1) {
|
||||
ptime_t this_frame = time_get();
|
||||
gstate.stats.dt = time_diff_s(last_frame, this_frame);
|
||||
|
@ -80,6 +90,8 @@ int main(void) {
|
|||
|
||||
input_poll();
|
||||
|
||||
clin_update();
|
||||
|
||||
bool render_world
|
||||
= gstate.current_screen->render_world && gstate.world_loaded;
|
||||
|
||||
|
@ -87,6 +99,7 @@ int main(void) {
|
|||
camera_update(&gstate.camera, gstate.stats.dt);
|
||||
|
||||
world_pre_render(&gstate.world, &gstate.camera, gstate.camera.view);
|
||||
world_build_chunks(&gstate.world, CHUNK_MESHER_QLENGTH);
|
||||
|
||||
struct camera* c = &gstate.camera;
|
||||
camera_ray_pick(&gstate.world, c->x, c->y, c->z,
|
||||
|
@ -104,6 +117,10 @@ int main(void) {
|
|||
|
||||
gfx_flip_buffers(&gstate.stats.dt_gpu, &gstate.stats.dt_vsync);
|
||||
|
||||
// must not modify displaylists while still rendering!
|
||||
chunk_mesher_receive();
|
||||
world_render_completed(&gstate.world, render_world);
|
||||
|
||||
gfx_clear_buffers(0x79 * daytime, 0xA6 * daytime, 0xFF * daytime);
|
||||
|
||||
if(render_world) {
|
||||
|
|
|
@ -1,6 +1,102 @@
|
|||
#include <gccore.h>
|
||||
|
||||
#include "../game/game_state.h"
|
||||
#include "client_interface.h"
|
||||
#include "server_interface.h"
|
||||
|
||||
void clin_chunk(w_coord_t x, w_coord_t y, w_coord_t z, c_coord_t sx,
|
||||
c_coord_t sy, c_coord_t sz, void* data) { }
|
||||
#define RPC_INBOX_SIZE 8
|
||||
static struct client_rpc rpc_msg[RPC_INBOX_SIZE];
|
||||
mqbox_t clin_inbox;
|
||||
mqbox_t clin_empty_msg;
|
||||
|
||||
void clin_unload_chunk(w_coord_t x, w_coord_t z) { }
|
||||
void clin_chunk(w_coord_t x, w_coord_t y, w_coord_t z, w_coord_t sx,
|
||||
w_coord_t sy, w_coord_t sz, uint8_t* ids, uint8_t* metadata,
|
||||
uint8_t* lighting) {
|
||||
assert(sx > 0 && sz > 0 && y >= 0 && y + sy <= WORLD_HEIGHT);
|
||||
assert(ids && metadata && lighting);
|
||||
|
||||
// TODO: metadata
|
||||
|
||||
uint8_t* ids_t = ids;
|
||||
uint8_t* metadata_t = metadata;
|
||||
uint8_t* lighting_t = lighting;
|
||||
|
||||
for(w_coord_t oy = y; oy < y + sy; oy++) {
|
||||
for(w_coord_t oz = z; oz < z + sz; oz++) {
|
||||
for(w_coord_t ox = x; ox < x + sx; ox++) {
|
||||
world_set_block(&gstate.world, ox, oy, oz,
|
||||
(struct block_data) {
|
||||
.type = *ids_t,
|
||||
.metadata = *metadata_t,
|
||||
.sky_light = (*lighting_t) & 0xF,
|
||||
.torch_light = (*lighting_t) >> 4,
|
||||
});
|
||||
ids_t++;
|
||||
metadata_t++;
|
||||
lighting_t++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(ids);
|
||||
free(metadata);
|
||||
free(lighting);
|
||||
}
|
||||
|
||||
void clin_unload_chunk(w_coord_t x, w_coord_t z) {
|
||||
for(w_coord_t k = 0; k < WORLD_HEIGHT; k += CHUNK_SIZE) {
|
||||
struct chunk* c = world_find_chunk(&gstate.world, x * CHUNK_SIZE, k,
|
||||
z * CHUNK_SIZE);
|
||||
|
||||
if(c)
|
||||
world_unload_chunk(&gstate.world, c);
|
||||
}
|
||||
}
|
||||
|
||||
void clin_process(struct client_rpc* call) {
|
||||
assert(call);
|
||||
|
||||
switch(call->type) {
|
||||
case CRPC_CHUNK:
|
||||
clin_chunk(call->payload.chunk.x, call->payload.chunk.y,
|
||||
call->payload.chunk.z, call->payload.chunk.sx,
|
||||
call->payload.chunk.sy, call->payload.chunk.sz,
|
||||
call->payload.chunk.ids, call->payload.chunk.metadata,
|
||||
call->payload.chunk.lighting);
|
||||
break;
|
||||
case CRPC_UNLOAD_CHUNK:
|
||||
clin_unload_chunk(call->payload.unload_chunk.x,
|
||||
call->payload.unload_chunk.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void clin_init() {
|
||||
MQ_Init(&clin_inbox, RPC_INBOX_SIZE);
|
||||
MQ_Init(&clin_empty_msg, RPC_INBOX_SIZE);
|
||||
|
||||
for(int k = 0; k < RPC_INBOX_SIZE; k++)
|
||||
MQ_Send(clin_empty_msg, rpc_msg + k, MQ_MSG_BLOCK);
|
||||
}
|
||||
|
||||
void clin_update() {
|
||||
mqmsg_t call;
|
||||
while(MQ_Receive(clin_inbox, &call, MQ_MSG_NOBLOCK)) {
|
||||
clin_process(call);
|
||||
MQ_Send(clin_empty_msg, call, MQ_MSG_BLOCK);
|
||||
}
|
||||
|
||||
svin_rpc_send(&(struct server_rpc) {
|
||||
.type = SRPC_PLAYER_POS,
|
||||
.payload.player_pos.x = gstate.camera.x,
|
||||
.payload.player_pos.y = gstate.camera.y,
|
||||
.payload.player_pos.z = gstate.camera.z,
|
||||
});
|
||||
}
|
||||
|
||||
void clin_rpc_send(struct client_rpc* call) {
|
||||
struct client_rpc* empty;
|
||||
MQ_Receive(clin_empty_msg, (mqmsg_t*)&empty, MQ_MSG_BLOCK);
|
||||
*empty = *call;
|
||||
MQ_Send(clin_inbox, empty, MQ_MSG_BLOCK);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,29 @@
|
|||
|
||||
#include "../world.h"
|
||||
|
||||
void clin_chunk(w_coord_t x, w_coord_t y, w_coord_t z, c_coord_t sx,
|
||||
c_coord_t sy, c_coord_t sz, void* data);
|
||||
enum client_rpc_type {
|
||||
CRPC_CHUNK,
|
||||
CRPC_UNLOAD_CHUNK,
|
||||
};
|
||||
|
||||
void clin_unload_chunk(w_coord_t x, w_coord_t z);
|
||||
struct client_rpc {
|
||||
enum client_rpc_type type;
|
||||
union {
|
||||
struct {
|
||||
w_coord_t x, y, z;
|
||||
w_coord_t sx, sy, sz;
|
||||
uint8_t* ids;
|
||||
uint8_t* metadata;
|
||||
uint8_t* lighting;
|
||||
} chunk;
|
||||
struct {
|
||||
w_coord_t x, z;
|
||||
} unload_chunk;
|
||||
} payload;
|
||||
};
|
||||
|
||||
void clin_init(void);
|
||||
void clin_update(void);
|
||||
void clin_rpc_send(struct client_rpc* call);
|
||||
|
||||
#endif
|
||||
|
|
38
source/network/server_interface.c
Normal file
38
source/network/server_interface.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <assert.h>
|
||||
#include <gccore.h>
|
||||
|
||||
#include "server_interface.h"
|
||||
|
||||
#define RPC_INBOX_SIZE 8
|
||||
static struct server_rpc rpc_msg[RPC_INBOX_SIZE];
|
||||
mqbox_t svin_inbox;
|
||||
mqbox_t svin_empty_msg;
|
||||
|
||||
void svin_init() {
|
||||
MQ_Init(&svin_inbox, RPC_INBOX_SIZE);
|
||||
MQ_Init(&svin_empty_msg, RPC_INBOX_SIZE);
|
||||
|
||||
for(int k = 0; k < RPC_INBOX_SIZE; k++)
|
||||
MQ_Send(svin_empty_msg, rpc_msg + k, MQ_MSG_BLOCK);
|
||||
}
|
||||
|
||||
void svin_process_messages(void (*process)(struct server_rpc*, void*),
|
||||
void* user, bool block) {
|
||||
assert(process);
|
||||
|
||||
mqmsg_t call;
|
||||
while(
|
||||
MQ_Receive(svin_inbox, &call, block ? MQ_MSG_BLOCK : MQ_MSG_NOBLOCK)) {
|
||||
process(call, user);
|
||||
MQ_Send(svin_empty_msg, call, MQ_MSG_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
void svin_rpc_send(struct server_rpc* call) {
|
||||
assert(call);
|
||||
|
||||
struct server_rpc* empty;
|
||||
MQ_Receive(svin_empty_msg, (mqmsg_t*)&empty, MQ_MSG_BLOCK);
|
||||
*empty = *call;
|
||||
MQ_Send(svin_inbox, empty, MQ_MSG_BLOCK);
|
||||
}
|
|
@ -1,9 +1,24 @@
|
|||
#ifndef SERVER_INTERFACE_H
|
||||
#define SERVER_INTERFACE_H
|
||||
|
||||
struct server_interface {
|
||||
void (*player_pos)(void* user, double x, double y, double z);
|
||||
void* user;
|
||||
#include <stdbool.h>
|
||||
|
||||
enum server_rpc_type {
|
||||
SRPC_PLAYER_POS,
|
||||
};
|
||||
|
||||
struct server_rpc {
|
||||
enum server_rpc_type type;
|
||||
union {
|
||||
struct {
|
||||
double x, y, z;
|
||||
} player_pos;
|
||||
} payload;
|
||||
};
|
||||
|
||||
void svin_init(void);
|
||||
void svin_process_messages(void (*process)(struct server_rpc*, void*),
|
||||
void* user, bool block);
|
||||
void svin_rpc_send(struct server_rpc* call);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,65 +3,233 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "client_interface.h"
|
||||
#include "server_interface.h"
|
||||
#include "server_local.h"
|
||||
|
||||
static void server_local_player_pos(void* user, double x, double y, double z) {
|
||||
assert(user);
|
||||
struct server_local* local = user;
|
||||
local->player.x = x;
|
||||
local->player.y = y;
|
||||
local->player.z = z;
|
||||
#define CHUNK_DIST2(x1, x2, z1, z2) \
|
||||
(((x1) - (x2)) * ((x1) - (x2)) + ((z1) - (z2)) * ((z1) - (z2)))
|
||||
|
||||
static const char* chunk_files[] = {
|
||||
"x0 z-10.vc", "x-1 z-2.vc", "x-3 z-5.vc", "x-5 z-8.vc", "x-8 z-11.vc",
|
||||
"x0 z-11.vc", "x-1 z-3.vc", "x-3 z-6.vc", "x-5 z-9.vc", "x-8 z-1.vc",
|
||||
"x0 z-1.vc", "x-1 z-4.vc", "x-3 z-7.vc", "x-6 z-10.vc", "x-8 z-2.vc",
|
||||
"x0 z-2.vc", "x-1 z-5.vc", "x-3 z-8.vc", "x-6 z-11.vc", "x-8 z-3.vc",
|
||||
"x0 z-3.vc", "x-1 z-6.vc", "x-3 z-9.vc", "x-6 z-1.vc", "x-8 z-4.vc",
|
||||
"x0 z-4.vc", "x-1 z-7.vc", "x-4 z-10.vc", "x-6 z-2.vc", "x-8 z-5.vc",
|
||||
"x0 z-5.vc", "x-1 z-8.vc", "x-4 z-11.vc", "x-6 z-3.vc", "x-8 z-6.vc",
|
||||
"x0 z-6.vc", "x-1 z-9.vc", "x-4 z-1.vc", "x-6 z-4.vc", "x-8 z-7.vc",
|
||||
"x0 z-7.vc", "x-2 z-10.vc", "x-4 z-2.vc", "x-6 z-5.vc", "x-8 z-8.vc",
|
||||
"x0 z-8.vc", "x-2 z-11.vc", "x-4 z-3.vc", "x-6 z-6.vc", "x-8 z-9.vc",
|
||||
"x0 z-9.vc", "x-2 z-1.vc", "x-4 z-4.vc", "x-6 z-7.vc", "x-9 z-10.vc",
|
||||
"x-10 z-10.vc", "x-2 z-2.vc", "x-4 z-5.vc", "x-6 z-8.vc", "x-9 z-11.vc",
|
||||
"x-10 z-11.vc", "x-2 z-3.vc", "x-4 z-6.vc", "x-6 z-9.vc", "x-9 z-1.vc",
|
||||
"x-10 z-1.vc", "x-2 z-4.vc", "x-4 z-7.vc", "x-7 z-10.vc", "x-9 z-2.vc",
|
||||
"x-10 z-2.vc", "x-2 z-5.vc", "x-4 z-8.vc", "x-7 z-11.vc", "x-9 z-3.vc",
|
||||
"x-10 z-3.vc", "x-2 z-6.vc", "x-4 z-9.vc", "x-7 z-1.vc", "x-9 z-4.vc",
|
||||
"x-10 z-4.vc", "x-2 z-7.vc", "x-5 z-10.vc", "x-7 z-2.vc", "x-9 z-5.vc",
|
||||
"x-10 z-5.vc", "x-2 z-8.vc", "x-5 z-11.vc", "x-7 z-3.vc", "x-9 z-6.vc",
|
||||
"x-10 z-6.vc", "x-2 z-9.vc", "x-5 z-1.vc", "x-7 z-4.vc", "x-9 z-7.vc",
|
||||
"x-10 z-7.vc", "x-3 z-10.vc", "x-5 z-2.vc", "x-7 z-5.vc", "x-9 z-8.vc",
|
||||
"x-10 z-8.vc", "x-3 z-11.vc", "x-5 z-3.vc", "x-7 z-6.vc", "x-9 z-9.vc",
|
||||
"x-10 z-9.vc", "x-3 z-1.vc", "x-5 z-4.vc", "x-7 z-7.vc", "x-1 z-10.vc",
|
||||
"x-3 z-2.vc", "x-5 z-5.vc", "x-7 z-8.vc", "x-1 z-11.vc", "x-3 z-3.vc",
|
||||
"x-5 z-6.vc", "x-7 z-9.vc", "x-1 z-1.vc", "x-3 z-4.vc", "x-5 z-7.vc",
|
||||
"x-8 z-10.vc",
|
||||
};
|
||||
|
||||
static w_coord_t chunk_files_lookup[sizeof(chunk_files) / sizeof(*chunk_files)]
|
||||
[2];
|
||||
|
||||
static const char* find_chunk(int x, int z) {
|
||||
for(size_t k = 0; k < sizeof(chunk_files) / sizeof(*chunk_files); k++) {
|
||||
if(chunk_files_lookup[k][0] == x && chunk_files_lookup[k][1] == z)
|
||||
return chunk_files[k];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void find_chunk_cache() {
|
||||
for(size_t k = 0; k < sizeof(chunk_files) / sizeof(*chunk_files); k++) {
|
||||
int chunk_x, chunk_z;
|
||||
FILE* f = fopen(chunk_files[k], "rb");
|
||||
assert(f);
|
||||
fread((uint8_t*)&chunk_x + 3, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_x + 2, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_x + 1, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_x + 0, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 3, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 2, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 1, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 0, sizeof(uint8_t), 1, f);
|
||||
fclose(f);
|
||||
|
||||
chunk_files_lookup[k][0] = chunk_x;
|
||||
chunk_files_lookup[k][1] = chunk_z;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_chunk(const char* file, int* chunk_x, int* chunk_z,
|
||||
uint8_t** ids, uint8_t** metadata, uint8_t** lighting) {
|
||||
uint8_t* chunk_data = malloc(16 * 16 * 128 * 3);
|
||||
|
||||
FILE* f = fopen(file, "rb");
|
||||
assert(f);
|
||||
fread((uint8_t*)chunk_x + 3, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_x + 2, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_x + 1, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_x + 0, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_z + 3, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_z + 2, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_z + 1, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)chunk_z + 0, sizeof(uint8_t), 1, f);
|
||||
fread(chunk_data, 1, 16 * 16 * 128 * 3, f);
|
||||
fclose(f);
|
||||
|
||||
*ids = malloc(16 * 16 * 128);
|
||||
*metadata = malloc(16 * 16 * 128);
|
||||
*lighting = malloc(16 * 16 * 128);
|
||||
|
||||
#define CHUNK_INDEX(x, y, z) ((x) + ((z) + (y)*16) * 16)
|
||||
|
||||
for(int y = 0; y < 128; y++) {
|
||||
for(int z = 0; z < 16; z++) {
|
||||
for(int x = 0; x < 16; x++) {
|
||||
(*ids)[CHUNK_INDEX(x, y, z)]
|
||||
= chunk_data[CHUNK_INDEX(x, y, z) * 3 + 0];
|
||||
(*metadata)[CHUNK_INDEX(x, y, z)]
|
||||
= chunk_data[CHUNK_INDEX(x, y, z) * 3 + 1];
|
||||
(*lighting)[CHUNK_INDEX(x, y, z)]
|
||||
= chunk_data[CHUNK_INDEX(x, y, z) * 3 + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(chunk_data);
|
||||
}
|
||||
|
||||
static void server_local_process(struct server_rpc* call, void* user) {
|
||||
assert(call && user);
|
||||
|
||||
struct server_local* s = user;
|
||||
|
||||
switch(call->type) {
|
||||
case SRPC_PLAYER_POS:
|
||||
s->player.x = call->payload.player_pos.x;
|
||||
s->player.y = call->payload.player_pos.y;
|
||||
s->player.z = call->payload.player_pos.z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void server_local_update(struct server_local* s) {
|
||||
assert(s);
|
||||
|
||||
svin_process_messages(server_local_process, s, false);
|
||||
|
||||
s->last_chunk_load = time_get();
|
||||
|
||||
w_coord_t px = WCOORD_CHUNK_OFFSET(floor(s->player.x));
|
||||
w_coord_t pz = WCOORD_CHUNK_OFFSET(floor(s->player.z));
|
||||
|
||||
struct loaded_chunk* c_furthest = NULL;
|
||||
w_coord_t c_furthest_dist2 = 0;
|
||||
for(size_t k = 0; k < s->loaded_chunks_length; k++) {
|
||||
// can unload chunk?
|
||||
w_coord_t d
|
||||
= CHUNK_DIST2(px, s->loaded_chunks[k].x, pz, s->loaded_chunks[k].z);
|
||||
if((abs(px - s->loaded_chunks[k].x) > MAX_VIEW_DISTANCE
|
||||
|| abs(pz - s->loaded_chunks[k].z) > MAX_VIEW_DISTANCE)
|
||||
&& d > c_furthest_dist2) {
|
||||
c_furthest_dist2 = d;
|
||||
c_furthest = s->loaded_chunks + k;
|
||||
}
|
||||
}
|
||||
|
||||
if(c_furthest) {
|
||||
// unload just one chunk
|
||||
clin_rpc_send(&(struct client_rpc) {
|
||||
.type = CRPC_UNLOAD_CHUNK,
|
||||
.payload.unload_chunk.x = c_furthest->x,
|
||||
.payload.unload_chunk.z = c_furthest->z,
|
||||
});
|
||||
*c_furthest = s->loaded_chunks[--s->loaded_chunks_length];
|
||||
}
|
||||
|
||||
// iterate over all chunks that should be loaded
|
||||
bool c_nearest = false;
|
||||
w_coord_t c_nearest_x, c_nearest_z;
|
||||
w_coord_t c_nearest_dist2;
|
||||
if(s->loaded_chunks_length < MAX_CHUNKS) {
|
||||
for(w_coord_t z = pz - MAX_VIEW_DISTANCE; z <= pz + MAX_VIEW_DISTANCE;
|
||||
z++) {
|
||||
for(w_coord_t x = px - MAX_VIEW_DISTANCE;
|
||||
x <= px + MAX_VIEW_DISTANCE; x++) {
|
||||
bool loaded = false;
|
||||
for(size_t k = 0; k < s->loaded_chunks_length; k++) {
|
||||
if(x == s->loaded_chunks[k].x
|
||||
&& z == s->loaded_chunks[k].z) {
|
||||
loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
w_coord_t d = CHUNK_DIST2(px, x, pz, z);
|
||||
if(!loaded && (d < c_nearest_dist2 || !c_nearest)
|
||||
&& find_chunk(x, z)) {
|
||||
c_nearest_dist2 = d;
|
||||
c_nearest_x = x;
|
||||
c_nearest_z = z;
|
||||
c_nearest = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load just one chunk
|
||||
if(c_nearest) {
|
||||
const char* str = find_chunk(c_nearest_x, c_nearest_z);
|
||||
if(str) {
|
||||
s->loaded_chunks[s->loaded_chunks_length++]
|
||||
= (struct loaded_chunk) {.x = c_nearest_x, .z = c_nearest_z};
|
||||
|
||||
int x2, z2;
|
||||
uint8_t *ids, *metadata, *lighting;
|
||||
load_chunk(str, &x2, &z2, &ids, &metadata, &lighting);
|
||||
assert(x2 == c_nearest_x && z2 == c_nearest_z);
|
||||
|
||||
clin_rpc_send(&(struct client_rpc) {
|
||||
.type = CRPC_CHUNK,
|
||||
.payload.chunk.x = c_nearest_x * CHUNK_SIZE,
|
||||
.payload.chunk.y = 0,
|
||||
.payload.chunk.z = c_nearest_z * CHUNK_SIZE,
|
||||
.payload.chunk.sx = CHUNK_SIZE,
|
||||
.payload.chunk.sy = WORLD_HEIGHT,
|
||||
.payload.chunk.sz = CHUNK_SIZE,
|
||||
.payload.chunk.ids = ids,
|
||||
.payload.chunk.metadata = metadata,
|
||||
.payload.chunk.lighting = lighting,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void* server_local_thread(void* user) {
|
||||
find_chunk_cache();
|
||||
|
||||
while(1) {
|
||||
server_local_update(user);
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void server_local_create(struct server_local* s) {
|
||||
assert(s);
|
||||
s->loaded_chunks_length = 0;
|
||||
s->iface.user = s;
|
||||
s->iface.player_pos = server_local_player_pos;
|
||||
}
|
||||
|
||||
struct server_interface* server_local_interface(struct server_local* s) {
|
||||
assert(s);
|
||||
return &s->iface;
|
||||
}
|
||||
|
||||
void server_local_update(struct server_local* s, float dt) {
|
||||
assert(s && dt > 0);
|
||||
|
||||
w_coord_t px = WCOORD_CHUNK_OFFSET(floor(s->player.x));
|
||||
w_coord_t pz = WCOORD_CHUNK_OFFSET(floor(s->player.z));
|
||||
|
||||
for(size_t k = 0; k < s->loaded_chunks_length; k++) {
|
||||
// can unload chunk?
|
||||
if(abs(px - s->loaded_chunks[k].x) > MAX_VIEW_DISTANCE + 1
|
||||
|| abs(pz - s->loaded_chunks[k].z) > MAX_VIEW_DISTANCE + 1) {
|
||||
// unload just one chunk
|
||||
clin_unload_chunk(s->loaded_chunks[k].x, s->loaded_chunks[k].z);
|
||||
s->loaded_chunks[k]
|
||||
= s->loaded_chunks[(s->loaded_chunks_length--) - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over all chunks that should be loaded
|
||||
for(w_coord_t z = pz - MAX_VIEW_DISTANCE; z < pz + MAX_VIEW_DISTANCE; z++) {
|
||||
for(w_coord_t x = px - MAX_VIEW_DISTANCE; x < px + MAX_VIEW_DISTANCE;
|
||||
x++) {
|
||||
bool loaded = false;
|
||||
for(size_t k = 0; k < s->loaded_chunks_length; k++) {
|
||||
if(x == s->loaded_chunks[k].x && z == s->loaded_chunks[k].z) {
|
||||
loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!loaded && s->loaded_chunks_length < MAX_CHUNKS) {
|
||||
// load just one chunk
|
||||
s->loaded_chunks[s->loaded_chunks_length++]
|
||||
= (struct loaded_chunk) {.x = x, .z = z};
|
||||
clin_chunk(x, 0, z, CHUNK_SIZE, WORLD_HEIGHT, CHUNK_SIZE, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
s->last_chunk_load = time_get();
|
||||
|
||||
lwp_t thread;
|
||||
LWP_CreateThread(&thread, server_local_thread, s, NULL, 0, 8);
|
||||
}
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include "../world.h"
|
||||
#include "server_interface.h"
|
||||
|
||||
#define MAX_VIEW_DISTANCE 5 // in chunks
|
||||
#define MAX_VIEW_DISTANCE 3 // in chunks
|
||||
#define MAX_CHUNKS ((MAX_VIEW_DISTANCE * 2 + 2) * (MAX_VIEW_DISTANCE * 2 + 2))
|
||||
|
||||
struct server_local {
|
||||
|
@ -19,11 +18,9 @@ struct server_local {
|
|||
bool modified;
|
||||
} loaded_chunks[MAX_CHUNKS];
|
||||
size_t loaded_chunks_length;
|
||||
struct server_interface iface;
|
||||
ptime_t last_chunk_load;
|
||||
};
|
||||
|
||||
void server_local_create(struct server_local* s);
|
||||
struct server_interface* server_local_interface(struct server_local* s);
|
||||
void server_local_update(struct server_local* s, float dt);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,19 @@
|
|||
#include "gfx.h"
|
||||
#include "gui_util.h"
|
||||
|
||||
int gutil_control_icon(int x, enum gutil_control_icon icon, char* str) {
|
||||
gfx_bind_texture(TEXTURE_GUI);
|
||||
int scale = 32;
|
||||
int text_scale = 10;
|
||||
|
||||
gutil_texquad(x, gfx_height() - scale * 8 / 5, (448 + (icon % 2) * 32) / 2,
|
||||
(icon / 2) * 32, 32 / 2, 32, scale, scale);
|
||||
gutil_text(x + scale + text_scale / 2,
|
||||
gfx_height() - scale * 8 / 5 + (scale - text_scale) / 2, str,
|
||||
text_scale);
|
||||
return scale + text_scale + gutil_font_width(str, text_scale);
|
||||
}
|
||||
|
||||
void gutil_texquad_col(int x, int y, int tx, int ty, int sx, int sy, int width,
|
||||
int height, uint8_t r, uint8_t g, uint8_t b) {
|
||||
GX_Begin(GX_QUADS, GX_VTXFMT2, 4);
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum gutil_control_icon {
|
||||
CONTROL_A = 0,
|
||||
CONTROL_B = 1,
|
||||
CONTROL_1 = 2,
|
||||
CONTROL_2 = 3,
|
||||
CONTROL_DPAD = 4,
|
||||
CONTROL_MOTION = 5,
|
||||
CONTROL_Z = 6,
|
||||
CONTROL_C = 7,
|
||||
CONTROL_JOYSTICK = 8,
|
||||
CONTROL_PLUS = 10,
|
||||
CONTROL_MINUS = 11,
|
||||
};
|
||||
|
||||
int gutil_control_icon(int x, enum gutil_control_icon icon, char* str);
|
||||
void gutil_texquad_col(int x, int y, int tx, int ty, int sx, int sy, int width,
|
||||
int height, uint8_t r, uint8_t g, uint8_t b);
|
||||
void gutil_texquad(int x, int y, int tx, int ty, int sx, int sy, int width,
|
||||
|
|
198
source/world.c
198
source/world.c
|
@ -3,96 +3,47 @@
|
|||
#include "platform/graphics/gfx.h"
|
||||
#include "world.h"
|
||||
|
||||
char* chunk_files[] = {
|
||||
"x0 z-10.vc", "x-1 z-2.vc", "x-3 z-5.vc", "x-5 z-8.vc", "x-8 z-11.vc",
|
||||
"x0 z-11.vc", "x-1 z-3.vc", "x-3 z-6.vc", "x-5 z-9.vc", "x-8 z-1.vc",
|
||||
"x0 z-1.vc", "x-1 z-4.vc", "x-3 z-7.vc", "x-6 z-10.vc", "x-8 z-2.vc",
|
||||
"x0 z-2.vc", "x-1 z-5.vc", "x-3 z-8.vc", "x-6 z-11.vc", "x-8 z-3.vc",
|
||||
"x0 z-3.vc", "x-1 z-6.vc", "x-3 z-9.vc", "x-6 z-1.vc", "x-8 z-4.vc",
|
||||
"x0 z-4.vc", "x-1 z-7.vc", "x-4 z-10.vc", "x-6 z-2.vc", "x-8 z-5.vc",
|
||||
"x0 z-5.vc", "x-1 z-8.vc", "x-4 z-11.vc", "x-6 z-3.vc", "x-8 z-6.vc",
|
||||
"x0 z-6.vc", "x-1 z-9.vc", "x-4 z-1.vc", "x-6 z-4.vc", "x-8 z-7.vc",
|
||||
"x0 z-7.vc", "x-2 z-10.vc", "x-4 z-2.vc", "x-6 z-5.vc", "x-8 z-8.vc",
|
||||
"x0 z-8.vc", "x-2 z-11.vc", "x-4 z-3.vc", "x-6 z-6.vc", "x-8 z-9.vc",
|
||||
"x0 z-9.vc", "x-2 z-1.vc", "x-4 z-4.vc", "x-6 z-7.vc", "x-9 z-10.vc",
|
||||
"x-10 z-10.vc", "x-2 z-2.vc", "x-4 z-5.vc", "x-6 z-8.vc", "x-9 z-11.vc",
|
||||
"x-10 z-11.vc", "x-2 z-3.vc", "x-4 z-6.vc", "x-6 z-9.vc", "x-9 z-1.vc",
|
||||
"x-10 z-1.vc", "x-2 z-4.vc", "x-4 z-7.vc", "x-7 z-10.vc", "x-9 z-2.vc",
|
||||
"x-10 z-2.vc", "x-2 z-5.vc", "x-4 z-8.vc", "x-7 z-11.vc", "x-9 z-3.vc",
|
||||
"x-10 z-3.vc", "x-2 z-6.vc", "x-4 z-9.vc", "x-7 z-1.vc", "x-9 z-4.vc",
|
||||
"x-10 z-4.vc", "x-2 z-7.vc", "x-5 z-10.vc", "x-7 z-2.vc", "x-9 z-5.vc",
|
||||
"x-10 z-5.vc", "x-2 z-8.vc", "x-5 z-11.vc", "x-7 z-3.vc", "x-9 z-6.vc",
|
||||
"x-10 z-6.vc", "x-2 z-9.vc", "x-5 z-1.vc", "x-7 z-4.vc", "x-9 z-7.vc",
|
||||
"x-10 z-7.vc", "x-3 z-10.vc", "x-5 z-2.vc", "x-7 z-5.vc", "x-9 z-8.vc",
|
||||
"x-10 z-8.vc", "x-3 z-11.vc", "x-5 z-3.vc", "x-7 z-6.vc", "x-9 z-9.vc",
|
||||
"x-10 z-9.vc", "x-3 z-1.vc", "x-5 z-4.vc", "x-7 z-7.vc", "x-1 z-10.vc",
|
||||
"x-3 z-2.vc", "x-5 z-5.vc", "x-7 z-8.vc", "x-1 z-11.vc", "x-3 z-3.vc",
|
||||
"x-5 z-6.vc", "x-7 z-9.vc", "x-1 z-1.vc", "x-3 z-4.vc", "x-5 z-7.vc",
|
||||
"x-8 z-10.vc",
|
||||
};
|
||||
void world_load_chunk(struct world* w, struct chunk* c) {
|
||||
assert(w && c);
|
||||
assert(!dict_chunks_get(
|
||||
w->chunks,
|
||||
CHUNK_TO_ID(c->x / CHUNK_SIZE, c->y / CHUNK_SIZE, c->z / CHUNK_SIZE)));
|
||||
|
||||
static struct chunk chunks_base[sizeof(chunk_files) / sizeof(*chunk_files) * 8];
|
||||
static struct chunk* chunks_end = chunks_base;
|
||||
chunk_ref(c);
|
||||
dict_chunks_set_at(
|
||||
w->chunks,
|
||||
CHUNK_TO_ID(c->x / CHUNK_SIZE, c->y / CHUNK_SIZE, c->z / CHUNK_SIZE),
|
||||
c);
|
||||
}
|
||||
|
||||
static void load_chunk(struct world* w, struct chunk** c, char* file) {
|
||||
uint8_t* chunk_data = malloc(16 * 16 * 128 * 3);
|
||||
void world_unload_chunk(struct world* w, struct chunk* c) {
|
||||
assert(w && c);
|
||||
assert(dict_chunks_get(
|
||||
w->chunks,
|
||||
CHUNK_TO_ID(c->x / CHUNK_SIZE, c->y / CHUNK_SIZE, c->z / CHUNK_SIZE)));
|
||||
|
||||
int32_t chunk_x, chunk_z;
|
||||
dict_chunks_erase(
|
||||
w->chunks,
|
||||
CHUNK_TO_ID(c->x / CHUNK_SIZE, c->y / CHUNK_SIZE, c->z / CHUNK_SIZE));
|
||||
|
||||
FILE* f = fopen(file, "rb");
|
||||
assert(f);
|
||||
fread((uint8_t*)&chunk_x + 3, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_x + 2, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_x + 1, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_x + 0, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 3, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 2, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 1, sizeof(uint8_t), 1, f);
|
||||
fread((uint8_t*)&chunk_z + 0, sizeof(uint8_t), 1, f);
|
||||
fread(chunk_data, 1, 16 * 16 * 128 * 3, f);
|
||||
fclose(f);
|
||||
if(w->world_chunk_cache == c)
|
||||
w->world_chunk_cache = NULL;
|
||||
|
||||
for(int k = 0; k < 8; k++) {
|
||||
chunk_init(*c, w, chunk_x * 16, k * 16, chunk_z * 16);
|
||||
dict_chunks_set_at(w->chunks, CHUNK_TO_ID(chunk_x, k, chunk_z), *c);
|
||||
|
||||
for(int y = 0; y < CHUNK_SIZE; y++) {
|
||||
for(int z = 0; z < CHUNK_SIZE; z++) {
|
||||
for(int x = 0; x < CHUNK_SIZE; x++) {
|
||||
uint8_t blockid
|
||||
= chunk_data[(x + ((y + k * 16) * 16 + z) * 16) * 3
|
||||
+ 0];
|
||||
uint8_t metadata
|
||||
= chunk_data[(x + ((y + k * 16) * 16 + z) * 16) * 3
|
||||
+ 1];
|
||||
uint8_t light
|
||||
= chunk_data[(x + ((y + k * 16) * 16 + z) * 16) * 3
|
||||
+ 2];
|
||||
|
||||
chunk_set_block(*c, x, y, z,
|
||||
(struct block_data) {
|
||||
.type = blockid,
|
||||
.metadata = metadata,
|
||||
.sky_light = light & 0xF,
|
||||
.torch_light = light >> 4,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(*c)++;
|
||||
}
|
||||
|
||||
free(chunk_data);
|
||||
chunk_unref(c);
|
||||
}
|
||||
|
||||
static void world_bfs(struct world* w, ilist_chunks_t render, float x, float y,
|
||||
float z, vec4* planes) {
|
||||
assert(w && render && planes);
|
||||
|
||||
for(struct chunk* c = chunks_base; c != chunks_end; c++) {
|
||||
dict_chunks_it_t it;
|
||||
dict_chunks_it(it, w->chunks);
|
||||
|
||||
while(!dict_chunks_end_p(it)) {
|
||||
struct chunk* c = dict_chunks_ref(it)->value;
|
||||
c->tmp_data.visited = false;
|
||||
c->tmp_data.steps = 0;
|
||||
dict_chunks_next(it);
|
||||
}
|
||||
|
||||
enum side sides[6]
|
||||
|
@ -115,6 +66,7 @@ static void world_bfs(struct world* w, ilist_chunks_t render, float x, float y,
|
|||
while(!ilist_chunks_empty_p(queue)) {
|
||||
struct chunk* current = ilist_chunks_pop_front(queue);
|
||||
ilist_chunks_push_back(render, current);
|
||||
chunk_ref(current);
|
||||
|
||||
for(int s = 0; s < 6; s++) {
|
||||
struct chunk* neigh
|
||||
|
@ -147,11 +99,8 @@ void world_create(struct world* w) {
|
|||
assert(w);
|
||||
|
||||
dict_chunks_init(w->chunks);
|
||||
ilist_chunks2_init(w->gpu_busy_chunks);
|
||||
w->world_chunk_cache = NULL;
|
||||
|
||||
for(size_t k = 0; k < sizeof(chunk_files) / sizeof(*chunk_files); k++)
|
||||
load_chunk(w, &chunks_end, chunk_files[k]);
|
||||
|
||||
w->anim_timer = time_get();
|
||||
}
|
||||
|
||||
|
@ -164,19 +113,40 @@ struct block_data world_get_block(struct world* w, w_coord_t x, w_coord_t y,
|
|||
assert(w);
|
||||
struct chunk* c = world_find_chunk(w, x, y, z);
|
||||
|
||||
return c ? chunk_get_block(c, x & CHUNK_SIZE_BITS, y & CHUNK_SIZE_BITS,
|
||||
z & CHUNK_SIZE_BITS) :
|
||||
(struct block_data) {.type = (y < WORLD_HEIGHT) ? 1 : 0};
|
||||
return c ? chunk_get_block(c, W2C_COORD(x), W2C_COORD(y), W2C_COORD(z)) :
|
||||
(struct block_data) {
|
||||
.type = (y < WORLD_HEIGHT) ? 1 : 0,
|
||||
.metadata = 0,
|
||||
.sky_light = (y < WORLD_HEIGHT) ? 0 : 15,
|
||||
.torch_light = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void world_set_block(struct world* w, w_coord_t x, w_coord_t y, w_coord_t z,
|
||||
struct block_data blk) {
|
||||
assert(w);
|
||||
|
||||
if(y < 0 || y >= WORLD_HEIGHT)
|
||||
return;
|
||||
|
||||
struct chunk* c = world_find_chunk(w, x, y, z);
|
||||
|
||||
if(!c) {
|
||||
c = malloc(sizeof(struct chunk));
|
||||
assert(c);
|
||||
|
||||
int cx = WCOORD_CHUNK_OFFSET(x);
|
||||
int cy = y / CHUNK_SIZE;
|
||||
int cz = WCOORD_CHUNK_OFFSET(z);
|
||||
chunk_init(c, w, cx * CHUNK_SIZE, cy * CHUNK_SIZE, cz * CHUNK_SIZE);
|
||||
chunk_ref(c);
|
||||
|
||||
dict_chunks_set_at(w->chunks, CHUNK_TO_ID(cx, cy, cz), c);
|
||||
w->world_chunk_cache = c;
|
||||
}
|
||||
|
||||
if(c)
|
||||
chunk_set_block(c, x & CHUNK_SIZE_BITS, y & CHUNK_SIZE_BITS,
|
||||
z & CHUNK_SIZE_BITS, blk);
|
||||
chunk_set_block(c, W2C_COORD(x), W2C_COORD(y), W2C_COORD(z), blk);
|
||||
}
|
||||
|
||||
struct chunk* world_find_chunk_neighbour(struct world* w, struct chunk* c,
|
||||
|
@ -209,11 +179,9 @@ struct chunk* world_find_chunk(struct world* w, w_coord_t x, w_coord_t y,
|
|||
int cy = y / CHUNK_SIZE;
|
||||
int cz = WCOORD_CHUNK_OFFSET(z);
|
||||
|
||||
if(w->world_chunk_cache
|
||||
&& CHUNK_TO_ID(cx, cy, cz)
|
||||
== CHUNK_TO_ID(w->world_chunk_cache->x / CHUNK_SIZE,
|
||||
w->world_chunk_cache->y / CHUNK_SIZE,
|
||||
w->world_chunk_cache->z / CHUNK_SIZE))
|
||||
if(w->world_chunk_cache && cx == w->world_chunk_cache->x / CHUNK_SIZE
|
||||
&& cy == w->world_chunk_cache->y / CHUNK_SIZE
|
||||
&& cz == w->world_chunk_cache->z / CHUNK_SIZE)
|
||||
return w->world_chunk_cache;
|
||||
|
||||
struct chunk** res = dict_chunks_get(w->chunks, CHUNK_TO_ID(cx, cy, cz));
|
||||
|
@ -254,8 +222,7 @@ bool world_block_intersection(struct world* w, struct ray* r, w_coord_t x,
|
|||
struct AABB bbox;
|
||||
|
||||
if(blocks[blk.type]->getBoundingBox(
|
||||
&(struct block_info) {
|
||||
.block = &blk, .world = w, .x = x, .y = y, .z = z},
|
||||
&(struct block_info) {.block = &blk, .x = x, .y = y, .z = z},
|
||||
false, &bbox)) {
|
||||
aabb_translate(&bbox, x, y, z);
|
||||
return aabb_intersection_ray(&bbox, r, s);
|
||||
|
@ -282,6 +249,51 @@ void world_pre_render(struct world* w, struct camera* c, mat4 view) {
|
|||
}
|
||||
}
|
||||
|
||||
void world_build_chunks(struct world* w, size_t tokens) {
|
||||
ilist_chunks_it_t it;
|
||||
ilist_chunks_it(it, w->render);
|
||||
|
||||
while(tokens > 0 && !ilist_chunks_end_p(it)) {
|
||||
if(chunk_check_built(ilist_chunks_ref(it)))
|
||||
tokens--;
|
||||
ilist_chunks_next(it);
|
||||
}
|
||||
|
||||
dict_chunks_it_t it2;
|
||||
dict_chunks_it(it2, w->chunks);
|
||||
|
||||
while(tokens > 0 && !dict_chunks_end_p(it2)) {
|
||||
if(chunk_check_built(dict_chunks_ref(it2)->value))
|
||||
tokens--;
|
||||
dict_chunks_next(it2);
|
||||
}
|
||||
}
|
||||
|
||||
void world_render_completed(struct world* w, bool new_render) {
|
||||
assert(w);
|
||||
|
||||
ilist_chunks2_it_t it;
|
||||
ilist_chunks2_it(it, w->gpu_busy_chunks);
|
||||
|
||||
while(!ilist_chunks2_end_p(it)) {
|
||||
chunk_unref(ilist_chunks2_ref(it));
|
||||
ilist_chunks2_next(it);
|
||||
}
|
||||
|
||||
ilist_chunks2_reset(w->gpu_busy_chunks);
|
||||
|
||||
if(new_render) {
|
||||
ilist_chunks_it_t it2;
|
||||
ilist_chunks_it(it2, w->render);
|
||||
|
||||
while(!ilist_chunks_end_p(it2)) {
|
||||
// move imaginary reference token from "render" to "gpu_busy_chunks"
|
||||
ilist_chunks2_push_back(w->gpu_busy_chunks, ilist_chunks_ref(it2));
|
||||
ilist_chunks_next(it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t world_render(struct world* w, struct camera* c, bool pass) {
|
||||
assert(w && c);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ struct block_data {
|
|||
|
||||
struct block_info {
|
||||
struct block_data* block;
|
||||
struct world* world;
|
||||
struct block_data* neighbours;
|
||||
w_coord_t x, y, z;
|
||||
};
|
||||
|
||||
|
@ -35,11 +35,16 @@ struct world {
|
|||
dict_chunks_t chunks;
|
||||
struct chunk* world_chunk_cache;
|
||||
ilist_chunks_t render;
|
||||
ilist_chunks2_t gpu_busy_chunks;
|
||||
ptime_t anim_timer;
|
||||
};
|
||||
|
||||
void world_create(struct world* w);
|
||||
void world_destroy(struct world* w);
|
||||
void world_load_chunk(struct world* w, struct chunk* c);
|
||||
void world_unload_chunk(struct world* w, struct chunk* c);
|
||||
void world_build_chunks(struct world* w, size_t tokens);
|
||||
void world_render_completed(struct world* w, bool new_render);
|
||||
struct chunk* world_find_chunk_neighbour(struct world* w, struct chunk* c,
|
||||
enum side s);
|
||||
struct chunk* world_find_chunk(struct world* w, w_coord_t x, w_coord_t y,
|
||||
|
|
Loading…
Reference in a new issue