Move chunk meshing and local server to own threads

This commit is contained in:
xtreme8000 2022-11-30 12:07:12 +01:00
parent eb2b52c0ca
commit 42e5246696
19 changed files with 1215 additions and 659 deletions

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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
View 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
View 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

View file

@ -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,

View file

@ -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) {

View file

@ -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);
}

View file

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

View 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);
}

View file

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

View file

@ -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);
}

View file

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

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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,