From 3fba1bc1d4d19637cdd3eae3027811c4737897e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Thu, 27 Jul 2023 11:53:35 +0200 Subject: [PATCH] collision grid + perf improvements --- code/foundation/src/dev/debug_draw.c | 2 +- code/foundation/src/models/entity.c | 5 + code/foundation/src/systems/systems.c | 140 +++++++++++++----------- code/foundation/src/world/world.c | 13 +++ code/foundation/src/world/world.h | 3 +- code/games/survival/src/main.c | 4 +- code/games/survival/src/platform.c | 21 ++++ code/games/survival/src/system_mob.c | 7 +- code/games/survival/src/system_weapon.c | 1 + 9 files changed, 124 insertions(+), 72 deletions(-) diff --git a/code/foundation/src/dev/debug_draw.c b/code/foundation/src/dev/debug_draw.c index c00e470..7c0dec4 100644 --- a/code/foundation/src/dev/debug_draw.c +++ b/code/foundation/src/dev/debug_draw.c @@ -3,7 +3,7 @@ static debug_draw_queue draw_queue = {0}; -#if !defined(_DEBUG) || 1 +#if !defined(_DEBUG) || 0 static bool draw_is_enabled = false; #else static bool draw_is_enabled = true; diff --git a/code/foundation/src/models/entity.c b/code/foundation/src/models/entity.c index 5b6d3d9..ed6e147 100644 --- a/code/foundation/src/models/entity.c +++ b/code/foundation/src/models/entity.c @@ -19,10 +19,12 @@ uint64_t entity_spawn(uint16_t class_id) { if (class_id != EKIND_SERVER) { librg_entity_track(world_tracker(), e); + librg_entity_track(world_collision_grid(), e); ecs_set(world_ecs(), e, Velocity, { 0 }); entity_set_position(e, (float)(rand() % world_dim()), (float)(rand() % world_dim())); librg_entity_owner_set(world_tracker(), e, (int64_t)e); + librg_entity_owner_set(world_collision_grid(), e, (int64_t)e); } return (uint64_t)e; @@ -59,6 +61,7 @@ bool entity_spawn_provided(uint16_t id) { void entity_batch_despawn(uint64_t *ids, size_t num_ids) { for (size_t i = 0; i < num_ids; i++ ) { + librg_entity_untrack(world_collision_grid(), ids[i]); librg_entity_untrack(world_tracker(), ids[i]); ecs_delete(world_ecs(), ids[i]); } @@ -66,6 +69,7 @@ void entity_batch_despawn(uint64_t *ids, size_t num_ids) { void entity_despawn(uint64_t ent_id) { librg_entity_untrack(world_tracker(), ent_id); + librg_entity_untrack(world_collision_grid(), ent_id); ecs_delete(world_ecs(), ent_id); } @@ -75,6 +79,7 @@ void entity_set_position(uint64_t ent_id, float x, float y) { p->x = x; p->y = y; librg_entity_chunk_set(world_tracker(), ent_id, librg_chunk_from_realpos(world_tracker(), x, y, 0)); + librg_entity_chunk_set(world_collision_grid(), ent_id, librg_chunk_from_realpos(world_collision_grid(), x, y, 0)); entity_wake(ent_id); } diff --git a/code/foundation/src/systems/systems.c b/code/foundation/src/systems/systems.c index cefac87..261d0e7 100644 --- a/code/foundation/src/systems/systems.c +++ b/code/foundation/src/systems/systems.c @@ -135,6 +135,8 @@ void BlockCollisions(ecs_iter_t *it) { } } +#define MAX_PHYSBODY_CHECKS 512 + void BodyCollisions(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); Velocity *v = ecs_field(it, Velocity, 2); @@ -154,85 +156,90 @@ void BodyCollisions(ecs_iter_t *it) { } #endif - ecs_iter_t it2 = ecs_query_iter(it->world, ecs_rigidbodies); + size_t ents_count = MAX_PHYSBODY_CHECKS; + static int64_t ents[MAX_PHYSBODY_CHECKS] = {0}; + librg_chunk chk = librg_entity_chunk_get(world_collision_grid(), it->entities[i]); + librg_world_fetch_chunk(world_collision_grid(), chk, ents, &ents_count); - while (ecs_query_next(&it2)) { - for (int j = 0; j < it2.count; j++) { - if (it->entities[i] == it2.entities[j]) continue; + for (size_t j = 0; j < ents_count; j++) { + uint64_t ent_id = (uint64_t)ents[j]; + if (it->entities[i] == ent_id) continue; + if (!ecs_get(it->world, ent_id, PhysicsBody)) continue; + if ( ecs_get(it->world, ent_id, TriggerOnly)) continue; + if ( ecs_get(it->world, ent_id, IsInVehicle)) continue; - Position *p2 = ecs_field(&it2, Position, 1); - Velocity *v2 = ecs_field(&it2, Velocity, 2); - PhysicsBody *b2 = ecs_field(&it2, PhysicsBody, 3); + Position *p2 = ecs_get_mut(it->world, ent_id, Position); + Velocity *v2 = ecs_get_mut(it->world, ent_id, Velocity); + PhysicsBody *b2 = ecs_get_mut(it->world, ent_id, PhysicsBody); - float p_x = p[i].x; - float p_y = p[i].y; - float p2_x = p2[j].x /*+ v2[j].x*/; - float p2_y = p2[j].y /*+ v2[j].y*/; - - c2AABB box_a = { - .min = { p_x - WORLD_BLOCK_SIZE / 2, p_y - WORLD_BLOCK_SIZE / 4 }, - .max = { p_x + WORLD_BLOCK_SIZE / 2, p_y + WORLD_BLOCK_SIZE / 4 }, - }; - - c2AABB box_b = { - .min = { p2_x - WORLD_BLOCK_SIZE / 2, p2_y - WORLD_BLOCK_SIZE / 4 }, - .max = { p2_x + WORLD_BLOCK_SIZE / 2, p2_y + WORLD_BLOCK_SIZE / 4 }, - }; + float p_x = p[i].x; + float p_y = p[i].y; + float p2_x = p2->x /*+ v2->x*/; + float p2_y = p2->y /*+ v2->y*/; - // do a basic sweep first - float r1x = (box_a.max.x-box_a.min.x); - float r1y = (box_a.max.y-box_a.min.y); - float r1 = (r1x*r1x + r1y*r1y)*.5f; + c2AABB box_a = { + .min = { p_x - WORLD_BLOCK_SIZE / 2, p_y - WORLD_BLOCK_SIZE / 4 }, + .max = { p_x + WORLD_BLOCK_SIZE / 2, p_y + WORLD_BLOCK_SIZE / 4 }, + }; - float r2x = (box_b.max.x-box_b.min.x); - float r2y = (box_b.max.y-box_b.min.y); - float r2 = (r2x*r2x + r2y*r2y)*.5f; + c2AABB box_b = { + .min = { p2_x - WORLD_BLOCK_SIZE / 2, p2_y - WORLD_BLOCK_SIZE / 4 }, + .max = { p2_x + WORLD_BLOCK_SIZE / 2, p2_y + WORLD_BLOCK_SIZE / 4 }, + }; - { - float dx = (p2_x-p_x); - float dy = (p2_y-p_y); - float d = (dx*dx + dy*dy); + // do a basic sweep first + float r1x = (box_a.max.x-box_a.min.x); + float r1y = (box_a.max.y-box_a.min.y); + float r1 = (r1x*r1x + r1y*r1y)*.5f; - if (d > r1 && d > r2) - continue; - } + float r2x = (box_b.max.x-box_b.min.x); + float r2y = (box_b.max.y-box_b.min.y); + float r2 = (r2x*r2x + r2y*r2y)*.5f; - c2Circle circle_a = { - .p = { p_x, p_y }, - .r = r1/2.f, - }; + { + float dx = (p2_x-p_x); + float dy = (p2_y-p_y); + float d = (dx*dx + dy*dy); - c2Circle circle_b = { - .p = { p2_x, p2_y }, - .r = r2/2.f, - }; + if (d > r1 && d > r2) + continue; + } - const void *shapes_a[] = { &circle_a, &box_a }; - const void *shapes_b[] = { &circle_b, &box_b }; - - c2Manifold m = { 0 }; - c2Collide(shapes_a[b[i].kind], 0, b[i].kind, shapes_b[b2[j].kind], 0, b2[j].kind, &m); + c2Circle circle_a = { + .p = { p_x, p_y }, + .r = r1/2.f, + }; - c2v n = m.n; - - for (int k = 0; k < m.count; k++) { - float d = m.depths[k]; + c2Circle circle_b = { + .p = { p2_x, p2_y }, + .r = r2/2.f, + }; + + const void *shapes_a[] = { &circle_a, &box_a }; + const void *shapes_b[] = { &circle_b, &box_b }; + + c2Manifold m = { 0 }; + c2Collide(shapes_a[b[i].kind], 0, b[i].kind, shapes_b[b2->kind], 0, b2->kind, &m); + + c2v n = m.n; + + for (int k = 0; k < m.count; k++) { + float d = m.depths[k]; #if 0 - { - c2v pos = m.contact_points[k]; - debug_v2 a = { pos.x, pos.y }; - debug_v2 b = { pos.x + n.x*d, pos.y + n.y*d }; - debug_push_line(a, b, 0xF77FFFFF); - } -#endif - - float m1 = b2[j].mass == INFINITE_MASS ? b[i].mass : b2[j].mass; - float m2 = b[i].mass == INFINITE_MASS ? (ZPL_F32_MAX-1.0f) : b[i].mass; - float mass_ratio = m1 / m2; - - v[i].x -= n.x*d*mass_ratio; - v[i].y -= n.y*d*mass_ratio; + { + c2v pos = m.contact_points[k]; + debug_v2 a = { pos.x, pos.y }; + debug_v2 b = { pos.x + n.x*d, pos.y + n.y*d }; + debug_push_line(a, b, 0xF77FFFFF); } +#endif + + float m1 = b2->mass == INFINITE_MASS ? b[i].mass : b2->mass; + float m2 = b[i].mass == INFINITE_MASS ? (ZPL_F32_MAX-1.0f) : b[i].mass; + float mass_ratio = m1 / m2; + + v[i].x -= n.x*d*mass_ratio; + v[i].y -= n.y*d*mass_ratio; } } } @@ -257,6 +264,7 @@ void IntegratePositions(ecs_iter_t *it) { p[i].y += v[i].y*safe_dt_val; librg_entity_chunk_set(world_tracker(), it->entities[i], librg_chunk_from_realpos(world_tracker(), p[i].x, p[i].y, 0)); + librg_entity_chunk_set(world_collision_grid(), it->entities[i], librg_chunk_from_realpos(world_collision_grid(), p[i].x, p[i].y, 0)); s[i].tick_delay = 0.0f; s[i].last_update = 0.0f; diff --git a/code/foundation/src/world/world.c b/code/foundation/src/world/world.c index a2313e1..43224f0 100644 --- a/code/foundation/src/world/world.c +++ b/code/foundation/src/world/world.c @@ -292,6 +292,7 @@ void world_chunk_setup_grid(void) { static inline void world_configure_tracker(void) { world.tracker = librg_world_create(); + world.collision_grid = librg_world_create(); ZPL_ASSERT_MSG(world.tracker, "[ERROR] An error occurred while trying to create a server world."); @@ -303,6 +304,13 @@ void world_configure_tracker(void) { librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create); librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove); librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update); + + /* config our collision grid */ + uint16_t chks = world.chunk_size / 2; + uint16_t chkm = world.chunk_amount * 2; + librg_config_chunksize_set(world.collision_grid, WORLD_BLOCK_SIZE * chks, WORLD_BLOCK_SIZE * chks, 1); + librg_config_chunkamount_set(world.collision_grid, chkm, chkm, 0); + librg_config_chunkoffset_set(world.collision_grid, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG); } static inline @@ -379,6 +387,7 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { } int32_t world_destroy(void) { + librg_world_destroy(world.collision_grid); librg_world_destroy(world.tracker); ecs_fini(world.ecs); zpl_mfree(world.chunk_mapping); @@ -548,6 +557,10 @@ librg_world* world_tracker() { return world.tracker; } +librg_world* world_collision_grid() { + return world.collision_grid; +} + void world_pause(void) { ecs_set_time_scale(world.ecs, 0.0f); world.is_paused = true; diff --git a/code/foundation/src/world/world.h b/code/foundation/src/world/world.h index c6b3fad..5ccd8cf 100644 --- a/code/foundation/src/world/world.h +++ b/code/foundation/src/world/world.h @@ -56,7 +56,7 @@ typedef struct { ecs_query_t *ecs_clientinfo; ecs_query_t *ecs_layeroverriden; ecs_entity_t *chunk_mapping; - librg_world *tracker; + librg_world *tracker, *collision_grid; world_pkt_reader_proc *reader_proc; world_pkt_writer_proc *writer_proc; } world_data; @@ -77,6 +77,7 @@ ecs_query_t *world_ecs_alive_player(void); ecs_query_t *world_ecs_clientinfo(void); void world_set_stage(ecs_world_t *ecs); librg_world *world_tracker(void); +librg_world *world_collision_grid(void); // NOTE(zaklaus): World simulation time control void world_pause(void); diff --git a/code/games/survival/src/main.c b/code/games/survival/src/main.c index d5be192..471f2db 100644 --- a/code/games/survival/src/main.c +++ b/code/games/survival/src/main.c @@ -49,6 +49,7 @@ int main(int argc, char** argv) { zpl_opts_add(&opts, "p", "preview-map", "draw world preview", ZPL_OPTS_FLAG); zpl_opts_add(&opts, "s", "seed", "world seed", ZPL_OPTS_INT); zpl_opts_add(&opts, "r", "random-seed", "generate random world seed", ZPL_OPTS_FLAG); + zpl_opts_add(&opts, "ed", "enable-dash", "enables flecs dash", ZPL_OPTS_FLAG); //zpl_opts_add(&opts, "cs", "chunk-size", "amount of blocks within a chunk (single axis)", ZPL_OPTS_INT); zpl_opts_add(&opts, "ws", "world-size", "amount of chunks within a world (single axis)", ZPL_OPTS_INT); zpl_opts_add(&opts, "ip", "host", "host IP address", ZPL_OPTS_STRING); @@ -64,6 +65,7 @@ int main(int argc, char** argv) { int8_t is_viewer_only = zpl_opts_has_arg(&opts, "viewer-only"); int8_t is_server_only = zpl_opts_has_arg(&opts, "server-only"); + int8_t is_dash_enabled = zpl_opts_has_arg(&opts, "enable-dash"); int32_t seed = (int32_t)zpl_opts_integer(&opts, "seed", DEFAULT_WORLD_SEED); uint16_t world_size = (uint16_t)zpl_opts_integer(&opts, "world-size", DEFAULT_WORLD_SIZE); uint16_t chunk_size = DEFAULT_CHUNK_SIZE; //zpl_opts_integer(&opts, "chunk-size", DEFAULT_CHUNK_SIZE); @@ -88,7 +90,7 @@ int main(int argc, char** argv) { } sighandler_register(); - game_init(host, port, play_mode, 1, seed, chunk_size, world_size, 0); + game_init(host, port, play_mode, 1, seed, chunk_size, world_size, is_dash_enabled); game_setup_ecs(); game_run(); diff --git a/code/games/survival/src/platform.c b/code/games/survival/src/platform.c index fef43dc..65c914e 100644 --- a/code/games/survival/src/platform.c +++ b/code/games/survival/src/platform.c @@ -90,6 +90,9 @@ void platform_input() { } } +void recalc_max_mobs(); +extern uint64_t mob_kills; + void platform_render() { platform_resize_window(); @@ -117,8 +120,26 @@ void platform_render() { nk_end(game_ui); } + notification_draw(); + + if (nk_begin(game_ui, "Debug stuff", nk_rect(400, 10, 220, 140), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_TITLE)) + { + nk_layout_row_dynamic(game_ui, 0, 1); + if (nk_button_label(game_ui, "max_mobs hack")) { + mob_kills = 2000; + recalc_max_mobs(); + } + if (nk_button_label(game_ui, "big hp")) { + ecs_entity_t plr = camera_get().ent_id; + Health *hp = ecs_get_mut(world_ecs(), plr, Health); + hp->hp = hp->max_hp = 999999; + } + nk_end(game_ui); + } #endif + game_draw_ui(); } EndDrawing(); diff --git a/code/games/survival/src/system_mob.c b/code/games/survival/src/system_mob.c index 1e6090f..742375d 100644 --- a/code/games/survival/src/system_mob.c +++ b/code/games/survival/src/system_mob.c @@ -1,18 +1,19 @@ #define MOB_SPAWN_DELAY (uint16_t)(20*1.5f) #define MOB_SPAWN_PERCENTAGE_FROM_MAX 0.05f #define MOB_INITIAL_MAX 50 +#define MOB_HARD_CAP 5000 #define MOB_GROWTH_FACTOR 0.65f #define MOB_GROWTH_CONTROL 1.17f -#define MOB_SPAWN_DIST 12*WORLD_BLOCK_SIZE; +#define MOB_SPAWN_DIST 22*WORLD_BLOCK_SIZE; #define MOB_MAX_SPAWN_TRIES 5 +uint64_t mob_kills = 0; static uint64_t max_mobs = MOB_INITIAL_MAX; -static uint64_t mob_kills = 0; static uint16_t mob_spawn_timer = MOB_SPAWN_DELAY; static int16_t player_spawn_counter = -1; void recalc_max_mobs() { - max_mobs = (uint64_t)(MOB_INITIAL_MAX + MOB_GROWTH_FACTOR * zpl_pow((float)mob_kills, MOB_GROWTH_CONTROL)); + max_mobs = zpl_min(MOB_HARD_CAP, (uint64_t)(MOB_INITIAL_MAX + MOB_GROWTH_FACTOR * zpl_pow((float)mob_kills, MOB_GROWTH_CONTROL))); } void MobDetectPlayers(ecs_iter_t *it) { diff --git a/code/games/survival/src/system_weapon.c b/code/games/survival/src/system_weapon.c index 4cf9477..364a6de 100644 --- a/code/games/survival/src/system_weapon.c +++ b/code/games/survival/src/system_weapon.c @@ -135,6 +135,7 @@ void WeaponProjectileHit(ecs_iter_t* it) { pawn_velocity[j].x += (dx/dd)*WEAPON_HIT_FORCE_PUSH; pawn_velocity[j].y += (dy/dd)*WEAPON_HIT_FORCE_PUSH; + entity_despawn(it->entities[i]); } } }