Add light propagation on block change

This commit is contained in:
xtreme8000 2023-01-15 19:24:42 +01:00
parent 9c23f4aaf1
commit 103a70a642
37 changed files with 266 additions and 52 deletions

View file

@ -96,6 +96,7 @@ struct block block_bed = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 1,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -60,6 +60,7 @@ struct block block_brown_mushroom = {
.luminance = 1,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -73,6 +73,7 @@ struct block block_cactus = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -77,6 +77,7 @@ struct block block_cake = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -60,6 +60,7 @@ struct block block_cobweb = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 1,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -60,6 +60,7 @@ struct block block_crops = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -69,6 +69,7 @@ struct block block_farmland = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 15,
.ignore_lighting = true,
.flammable = false,
.block_item = {

View file

@ -69,6 +69,7 @@ struct block block_fence = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = true,
.block_item = {

View file

@ -59,6 +59,7 @@ struct block block_fire = {
.luminance = 15,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -60,6 +60,7 @@ struct block block_flower = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -61,6 +61,7 @@ struct block block_glass = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -61,6 +61,7 @@ struct block block_ice = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 1,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -77,6 +77,7 @@ struct block block_ladder = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -73,6 +73,7 @@ struct block block_lava = {
.luminance = 15,
.double_sided = false,
.can_see_through = true,
.opacity = 15,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -78,6 +78,7 @@ struct block block_leaves = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 1,
.ignore_lighting = false,
.flammable = true,
.block_item = {

View file

@ -59,6 +59,7 @@ struct block block_portal = {
.luminance = 11,
.double_sided = false,
.can_see_through = true,
.opacity = 1,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -73,6 +73,7 @@ struct block block_stone_pressure_plate = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {
@ -97,6 +98,7 @@ struct block block_wooden_pressure_plate = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -75,6 +75,7 @@ struct block block_rail = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.rail_curved_possible = true,
.ignore_lighting = false,
.flammable = false,
@ -99,6 +100,7 @@ struct block block_powered_rail = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.rail_curved_possible = false,
.ignore_lighting = false,
.flammable = false,
@ -123,6 +125,7 @@ struct block block_detector_rail = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.rail_curved_possible = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -60,6 +60,7 @@ struct block block_red_mushroom = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -60,6 +60,7 @@ struct block block_reed = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -60,6 +60,7 @@ struct block block_rose = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -64,6 +64,7 @@ struct block block_sapling = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = false,

View file

@ -77,6 +77,7 @@ struct block block_slab = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 15,
.ignore_lighting = true,
.flammable = false,
.block_item = {

View file

@ -75,6 +75,7 @@ struct block block_snow = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -60,6 +60,7 @@ struct block block_spawner = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 1,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -127,6 +127,7 @@ struct block block_wooden_stairs = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 15,
.ignore_lighting = true,
.flammable = true,
.block_item = {
@ -153,6 +154,7 @@ struct block block_stone_stairs = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 15,
.ignore_lighting = true,
.flammable = false,
.block_item = {

View file

@ -68,6 +68,7 @@ struct block block_tallgrass = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = true,
.ignore_lighting = false,
.flammable = true,
@ -92,6 +93,7 @@ struct block block_deadbush = {
.luminance = 0,
.double_sided = true,
.can_see_through = true,
.opacity = 0,
.render_block_data.cross_random_displacement = false,
.ignore_lighting = false,
.flammable = true,

View file

@ -77,6 +77,7 @@ struct block block_torch = {
.luminance = 14,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {
@ -100,6 +101,7 @@ struct block block_redstone_torch = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {
@ -123,6 +125,7 @@ struct block block_redstone_torch_lit = {
.luminance = 7,
.double_sided = false,
.can_see_through = true,
.opacity = 0,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -77,6 +77,7 @@ struct block block_water_still = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 3,
.ignore_lighting = false,
.flammable = false,
.block_item = {
@ -100,6 +101,7 @@ struct block block_water_flowing = {
.luminance = 0,
.double_sided = false,
.can_see_through = true,
.opacity = 3,
.ignore_lighting = false,
.flammable = false,
.block_item = {

View file

@ -104,7 +104,8 @@ struct block {
size_t (*renderBlockAlways)(struct displaylist*, struct block_info*,
enum side, struct block_info*, uint8_t*, bool);
bool transparent;
int luminance;
uint8_t luminance : 4;
uint8_t opacity : 4;
bool double_sided;
bool can_see_through;
bool ignore_lighting;

View file

@ -126,20 +126,8 @@ struct block_data chunk_lookup_block(struct chunk* c, w_coord_t x, w_coord_t y,
};
}
void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
struct block_data blk) {
assert(c && x < CHUNK_SIZE && y < CHUNK_SIZE && z < CHUNK_SIZE);
size_t idx = CHUNK_INDEX(x, y, z) / 2 * 5;
size_t off = CHUNK_INDEX(x, y, z) % 2;
c->blocks[idx + off + 0] = blk.type;
c->blocks[idx + off + 2] = (blk.torch_light << 4) | blk.sky_light;
c->blocks[idx + 4] = (c->blocks[idx + 4] & ~(0x0F << (off * 4)))
| (blk.metadata << (off * 4));
c->rebuild_displist = true;
// trigger neighbour chunk updates
static void chunk_trigger_neighbour_update(struct chunk* c, c_coord_t x,
c_coord_t y, c_coord_t z) {
// TODO: diagonal chunks, just sharing edge or single point
bool cond[6] = {
@ -163,6 +151,35 @@ void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
}
}
void chunk_set_light(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
uint8_t light) {
assert(c && x < CHUNK_SIZE && y < CHUNK_SIZE && z < CHUNK_SIZE);
size_t idx = CHUNK_INDEX(x, y, z) / 2 * 5;
size_t off = CHUNK_INDEX(x, y, z) % 2;
c->blocks[idx + off + 2] = light;
c->rebuild_displist = true;
chunk_trigger_neighbour_update(c, x, y, z);
}
void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
struct block_data blk) {
assert(c && x < CHUNK_SIZE && y < CHUNK_SIZE && z < CHUNK_SIZE);
size_t idx = CHUNK_INDEX(x, y, z) / 2 * 5;
size_t off = CHUNK_INDEX(x, y, z) % 2;
c->blocks[idx + off + 0] = blk.type;
c->blocks[idx + off + 2] = (blk.torch_light << 4) | blk.sky_light;
c->blocks[idx + 4] = (c->blocks[idx + 4] & ~(0x0F << (off * 4)))
| (blk.metadata << (off * 4));
c->rebuild_displist = true;
chunk_trigger_neighbour_update(c, x, y, z);
}
bool chunk_check_built(struct chunk* c) {
assert(c);

View file

@ -79,6 +79,8 @@ struct block_data chunk_lookup_block(struct chunk* c, w_coord_t x, w_coord_t y,
void chunk_set_block(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
struct block_data blk);
bool chunk_check_built(struct chunk* c);
void chunk_set_light(struct chunk* c, c_coord_t x, c_coord_t y, c_coord_t z,
uint8_t light);
void chunk_render(struct chunk* c, bool pass, float x, float y, float z);
void chunk_pre_render(struct chunk* c, mat4 view, bool has_fog);

View file

@ -94,9 +94,10 @@ static void screen_ingame_update(struct screen* s, float dt) {
(struct block_data) {
.type = BLOCK_AIR,
.metadata = 0,
.sky_light = 15,
.sky_light = 0,
.torch_light = 0,
});
},
true);
gstate.held_item_animation = (struct held_anim) {
.start = time_get(),
.type = false,
@ -110,15 +111,16 @@ static void screen_ingame_update(struct screen* s, float dt) {
&& item_is_block(&item)) {
int x, y, z;
blocks_side_offset(gstate.camera_hit.side, &x, &y, &z);
world_set_block(
&gstate.world, gstate.camera_hit.x + x,
gstate.camera_hit.y + y, gstate.camera_hit.z + z,
(struct block_data) {
.type = item.id,
.metadata = 0,
.sky_light = blocks[item.id]->can_see_through ? 15 : 0,
.torch_light = blocks[item.id]->luminance,
});
world_set_block(&gstate.world, gstate.camera_hit.x + x,
gstate.camera_hit.y + y,
gstate.camera_hit.z + z,
(struct block_data) {
.type = item.id,
.metadata = 0,
.sky_light = 0,
.torch_light = 0,
},
true);
}
gstate.held_item_animation = (struct held_anim) {
.start = time_get(),

View file

@ -143,6 +143,8 @@ int main(void) {
gstate.current_screen->update(gstate.current_screen,
gstate.stats.dt);
world_update_lighting(&gstate.world);
gfx_flip_buffers(&gstate.stats.dt_gpu, &gstate.stats.dt_vsync);
// must not modify displaylists while still rendering!

View file

@ -47,7 +47,8 @@ void clin_chunk(w_coord_t x, w_coord_t y, w_coord_t z, w_coord_t sx,
.metadata = md,
.sky_light = (*lighting_t) & 0xF,
.torch_light = (*lighting_t) >> 4,
});
},
false);
ids_t++;
lighting_t++;

View file

@ -131,6 +131,8 @@ void world_create(struct world* w) {
dict_wsection_init(w->sections);
ilist_chunks_init(w->render);
ilist_chunks2_init(w->gpu_busy_chunks);
stack_create(&w->lighting_updates, 16,
sizeof(struct world_modification_entry));
w->world_chunk_cache = NULL;
w->anim_timer = time_get();
}
@ -138,6 +140,20 @@ void world_create(struct world* w) {
void world_destroy(struct world* w) {
assert(w);
stack_destroy(&w->lighting_updates);
dict_wsection_it_t it;
dict_wsection_it(it, w->sections);
while(!dict_wsection_end_p(it)) {
struct world_section* s = &dict_wsection_ref(it)->value;
for(size_t k = 0; k < COLUMN_HEIGHT; k++) {
if(s->column[k])
chunk_unref(s->column[k]);
}
dict_wsection_next(it);
}
dict_wsection_clear(w->sections);
}
@ -181,7 +197,8 @@ static void wsection_heightmap_update(struct world_section* s, c_coord_t x,
int8_t* height = s->heightmap + x + z * CHUNK_SIZE;
if(blocks[type]) {
if(blocks[type]
&& (!blocks[type]->can_see_through || blocks[type]->opacity > 0)) {
if(y > *height)
*height = y;
} else if(y == *height) {
@ -189,7 +206,9 @@ static void wsection_heightmap_update(struct world_section* s, c_coord_t x,
struct chunk* c = s->column[y / CHUNK_SIZE];
struct block_data blk = chunk_get_block(c, x, W2C_COORD(y), z);
if(c) {
if(blocks[blk.type])
if(blocks[blk.type]
&& (!blocks[blk.type]->can_see_through
|| blocks[blk.type]->opacity > 0))
break;
y--;
} else {
@ -202,42 +221,158 @@ static void wsection_heightmap_update(struct world_section* s, c_coord_t x,
}
void world_set_block(struct world* w, w_coord_t x, w_coord_t y, w_coord_t z,
struct block_data blk) {
struct block_data blk, bool light_update) {
assert(w);
if(y < 0 || y >= WORLD_HEIGHT)
return;
w_coord_t cx = WCOORD_CHUNK_OFFSET(x);
w_coord_t cz = WCOORD_CHUNK_OFFSET(z);
struct world_section* s
= dict_wsection_get(w->sections, SECTION_TO_ID(cx, cz));
if(light_update) {
stack_push(&w->lighting_updates,
&(struct world_modification_entry) {
.x = x,
.y = y,
.z = z,
.blk = blk,
});
} else {
w_coord_t cx = WCOORD_CHUNK_OFFSET(x);
w_coord_t cz = WCOORD_CHUNK_OFFSET(z);
struct world_section* s
= dict_wsection_get(w->sections, SECTION_TO_ID(cx, cz));
struct chunk* c = world_find_chunk(w, x, y, z);
struct chunk* c = world_find_chunk(w, x, y, z);
if(!c) {
c = malloc(sizeof(struct chunk));
assert(c);
if(!c) {
c = malloc(sizeof(struct chunk));
assert(c);
w_coord_t cy = y / CHUNK_SIZE;
chunk_init(c, w, cx * CHUNK_SIZE, cy * CHUNK_SIZE, cz * CHUNK_SIZE);
chunk_ref(c);
w_coord_t cy = y / CHUNK_SIZE;
chunk_init(c, w, cx * CHUNK_SIZE, cy * CHUNK_SIZE, cz * CHUNK_SIZE);
chunk_ref(c);
w->world_chunk_cache = c;
w->world_chunk_cache = c;
if(!s) {
s = dict_wsection_safe_get(w->sections, SECTION_TO_ID(cx, cz));
assert(s);
memset(s->heightmap, -1, sizeof(s->heightmap));
memset(s->column, 0, sizeof(s->column));
if(!s) {
s = dict_wsection_safe_get(w->sections, SECTION_TO_ID(cx, cz));
assert(s);
memset(s->heightmap, -1, sizeof(s->heightmap));
memset(s->column, 0, sizeof(s->column));
}
assert(s->column[cy] == NULL);
s->column[cy] = c;
}
assert(s->column[cy] == NULL);
s->column[cy] = c;
chunk_set_block(c, W2C_COORD(x), W2C_COORD(y), W2C_COORD(z), blk);
wsection_heightmap_update(s, W2C_COORD(x), y, W2C_COORD(z), blk.type);
}
}
static inline int8_t MAX_I8(int8_t a, int8_t b) {
return a > b ? a : b;
}
struct lighting_update_entry {
w_coord_t x, y, z;
};
void world_update_lighting(struct world* w) {
assert(w);
if(stack_empty(&w->lighting_updates))
return;
struct world_modification_entry source;
stack_pop(&w->lighting_updates, &source);
struct stack queue;
stack_create(&queue, 128, sizeof(struct lighting_update_entry));
stack_push(&queue,
&(struct lighting_update_entry) {
.x = source.x,
.y = source.y,
.z = source.z,
});
world_set_block(w, source.x, source.y, source.z, source.blk, false);
while(!stack_empty(&queue)) {
struct lighting_update_entry current;
stack_pop(&queue, &current);
struct world_section* s
= dict_wsection_get(w->sections,
SECTION_TO_ID(WCOORD_CHUNK_OFFSET(current.x),
WCOORD_CHUNK_OFFSET(current.z)));
struct chunk* c = world_chunk_from_section(w, s, current.y);
struct block_data old
= chunk_get_block(c, W2C_COORD(current.x), W2C_COORD(current.y),
W2C_COORD(current.z));
uint8_t old_light = (old.torch_light << 4) | old.sky_light;
uint8_t new_light_sky = 0, new_light_torch = 0;
if(current.y > s->heightmap[W2C_COORD(current.x)
+ W2C_COORD(current.z) * CHUNK_SIZE])
new_light_sky = 0xF;
if(blocks[old.type])
new_light_torch = blocks[old.type]->luminance;
if(!blocks[old.type] || blocks[old.type]->can_see_through) {
for(enum side s = 0; s < SIDE_MAX; s++) {
int x, y, z;
blocks_side_offset(s, &x, &y, &z);
struct chunk* c_other = world_find_chunk(
w, current.x + x, current.y + y, current.z + z);
if(c_other) {
struct block_data other = chunk_get_block(
c_other, W2C_COORD(current.x + x),
W2C_COORD(current.y + y), W2C_COORD(current.z + z));
int8_t opacity = blocks[old.type] ?
MAX_I8(blocks[old.type]->opacity, 1) :
1;
new_light_sky
= MAX_I8(MAX_I8((int8_t)other.sky_light - opacity, 0),
new_light_sky);
new_light_torch
= MAX_I8(MAX_I8((int8_t)other.torch_light - opacity, 0),
new_light_torch);
}
}
}
uint8_t new_light = (new_light_torch << 4) | new_light_sky;
if(old_light != new_light
|| (source.x == current.x && source.y == current.y
&& source.z == current.z)) {
chunk_set_light(c, W2C_COORD(current.x), W2C_COORD(current.y),
W2C_COORD(current.z), new_light);
for(enum side s = 0; s < SIDE_MAX; s++) {
int x, y, z;
blocks_side_offset(s, &x, &y, &z);
struct chunk* c_other = world_find_chunk(
w, current.x + x, current.y + y, current.z + z);
if(c_other)
stack_push(&queue,
&(struct lighting_update_entry) {
.x = current.x + x,
.y = current.y + y,
.z = current.z + z,
});
}
}
}
chunk_set_block(c, W2C_COORD(x), W2C_COORD(y), W2C_COORD(z), blk);
wsection_heightmap_update(s, W2C_COORD(x), y, W2C_COORD(z), blk.type);
stack_destroy(&queue);
}
struct chunk* world_find_chunk_neighbour(struct world* w, struct chunk* c,
@ -258,6 +393,12 @@ struct chunk* world_find_chunk_neighbour(struct world* w, struct chunk* c,
return res ? res->column[y + c->y / CHUNK_SIZE] : NULL;
}
struct chunk* world_chunk_from_section(struct world* w, struct world_section* s,
w_coord_t y) {
assert(w);
return (y >= 0 && y < WORLD_HEIGHT && s) ? s->column[y / CHUNK_SIZE] : NULL;
}
struct chunk* world_find_chunk(struct world* w, w_coord_t x, w_coord_t y,
w_coord_t z) {
assert(w);

View file

@ -26,6 +26,8 @@
#include "cglm/cglm.h"
#include "stack.h"
#define WORLD_HEIGHT 128
typedef int32_t w_coord_t;
@ -55,6 +57,11 @@ struct world_section {
struct chunk* column[COLUMN_HEIGHT];
};
struct world_modification_entry {
w_coord_t x, y, z;
struct block_data blk;
};
#define SECTION_TO_ID(x, z) (((int64_t)(z) << 32) | (((int64_t)(x)&0xFFFFFFFF)))
DICT_DEF2(dict_wsection, int64_t, M_BASIC_OPLIST, struct world_section,
@ -66,6 +73,7 @@ struct world {
ilist_chunks_t render;
ilist_chunks2_t gpu_busy_chunks;
ptime_t anim_timer;
struct stack lighting_updates;
};
void world_create(struct world* w);
@ -77,12 +85,15 @@ size_t 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_chunk_from_section(struct world* w, struct world_section* s,
w_coord_t y);
struct chunk* world_find_chunk(struct world* w, w_coord_t x, w_coord_t y,
w_coord_t z);
struct block_data world_get_block(struct world* w, w_coord_t x, w_coord_t y,
w_coord_t z);
void world_set_block(struct world* w, w_coord_t x, w_coord_t y, w_coord_t z,
struct block_data blk);
struct block_data blk, bool light_update);
void world_update_lighting(struct world* w);
void world_preload(struct world* w,
void (*progress)(struct world* w, float percent));
bool world_block_intersection(struct world* w, struct ray* r, w_coord_t x,