collision grid + perf improvements

master
Dominik Madarász 2023-07-27 11:53:35 +02:00
parent e43b167435
commit 3fba1bc1d4
9 changed files with 124 additions and 72 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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