From 0ed2a403dc790edc00f5a474bf7eb50d546eb13e Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 19 Aug 2024 15:02:41 -0500 Subject: [PATCH] impulses & forces --- src/entity.c | 2 + src/entity.h | 22 ++++- src/game.c | 258 +++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 209 insertions(+), 73 deletions(-) diff --git a/src/entity.c b/src/entity.c index 3a35c9c5..86999428 100644 --- a/src/entity.c +++ b/src/entity.c @@ -17,6 +17,7 @@ READONLY struct entity _g_entity_nil = { .cached_global_xform = XFORM_IDENT_NOCAST, .cached_global_xform_dirty = false, .verlet_xform = XFORM_IDENT_NOCAST, + .density = 1, .sprite_local_xform = XFORM_IDENT_NOCAST, .sprite_tint = COLOR_WHITE }; @@ -27,6 +28,7 @@ GLOBAL READONLY struct entity g_entity_default = { .cached_global_xform = XFORM_IDENT_NOCAST, .cached_global_xform_dirty = true, .verlet_xform = XFORM_IDENT_NOCAST, + .density = 1, .sprite_local_xform = XFORM_IDENT_NOCAST, .sprite_tint = COLOR_WHITE }; diff --git a/src/entity.h b/src/entity.h index d19fd50b..347067e5 100644 --- a/src/entity.h +++ b/src/entity.h @@ -9,9 +9,11 @@ enum entity_prop { ENTITY_PROP_ACTIVE, - ENTITY_PROP_RELEASE_NEXT_TICK, + ENTITY_PROP_RELEASE, ENTITY_PROP_PHYSICAL, + ENTITY_PROP_IMPULSE, + ENTITY_PROP_FORCE, ENTITY_PROP_PLAYER_CONTROLLED, ENTITY_PROP_CAMERA, @@ -105,6 +107,8 @@ struct entity { /* ENTITY_PROP_PHYSICAL */ + f32 density; + /* Xform of the entity from the previous frame (used to calculate velocity) */ struct xform verlet_xform; @@ -112,6 +116,22 @@ struct entity { struct v2 final_velocity; struct v2 final_acceleration; + /* ====================================================================== */ + /* Impulse */ + + /* ENTITY_PROP_IMPULSE */ + + /* Applies impulse velocity to parent entity */ + struct v2 impulse; + + /* ====================================================================== */ + /* Impulse */ + + /* ENTITY_PROP_FORCE */ + + /* Applies force to parent entity */ + struct v2 force; + /* ====================================================================== */ /* Sprite */ diff --git a/src/game.c b/src/game.c index e77fa248..115d443c 100644 --- a/src/game.c +++ b/src/game.c @@ -170,6 +170,18 @@ INTERNAL void spawn_test_entities(void) } } +/* ========================== * + * Activate + * ========================== */ + +INTERNAL void activate_now(struct entity *ent) +{ + entity_enable_prop(ent, ENTITY_PROP_ACTIVE); + ent->activation_tick = G.tick.tick_id; + ent->verlet_xform = entity_get_xform(ent); + ++ent->continuity_gen; +} + /* ========================== * * Update * ========================== */ @@ -244,35 +256,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) }; } - /* ========================== * - * Release entities - * ========================== */ - - /* TODO: Breadth first iteration to only release parent entities (since - * child entities will be released along with parent anyway) */ - - { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - struct entity **ents_to_release = arena_dry_push(temp.arena, struct entity *); - u64 ents_to_release_count = 0; - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; - if (!ent->valid) continue; - - if (entity_has_prop(ent, ENTITY_PROP_RELEASE_NEXT_TICK)) { - *arena_push(temp.arena, struct entity *) = ent; - ++ents_to_release_count; - } - } - - for (u64 i = 0; i < ents_to_release_count; ++i) { - entity_release(store, ents_to_release[i]); - } - - arena_temp_end(temp); - } - /* ========================== * * Activate entities * ========================== */ @@ -284,10 +267,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) if (!entity_has_prop(ent, ENTITY_PROP_ACTIVE)) { u64 atick = ent->activation_tick; if (atick != 0 || G.tick.tick_id >= atick) { - entity_enable_prop(ent, ENTITY_PROP_ACTIVE); - ent->activation_tick = G.tick.tick_id; - ent->verlet_xform = entity_get_xform(ent); - ++ent->continuity_gen; + activate_now(ent); } } } @@ -539,6 +519,107 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } + /* ========================== * + * Create forces from control + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + + struct v2 move = ent->control.move; + if (!v2_eq(move, V2(0, 0))) { + struct xform xf = entity_get_xform(ent); + + struct v2 tick_velocity = v2_sub(xf.og, ent->verlet_xform.og); + + /* Player movement */ + if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + f32 max_speed = ent->player_max_speed; + f32 acceleration_rate = ent->player_acceleration; + acceleration_rate = clamp_f32(acceleration_rate, 0, GAME_FPS); /* Can't integrate acceleration rate higher than FPS */ + struct v2 target_velocity = v2_mul(ent->control.move, max_speed); + struct v2 target_acceleration = v2_sub(target_velocity, v2_div(tick_velocity, dt)); + struct v2 acceleration = v2_mul(target_acceleration, acceleration_rate); + + /* Create force */ + struct entity *f = entity_alloc(ent); + entity_enable_prop(f, ENTITY_PROP_FORCE); + f->force = acceleration; + activate_now(f); + } + } + } + + /* ========================== * + * Create constant forces + * ========================== */ + + /* TODO: Just do this in simulation processing */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue; + + struct xform xf = entity_get_xform(ent); + struct v2 velocity = v2_div(v2_sub(xf.og, ent->verlet_xform.og), dt); + + /* Ground friction */ + { + struct entity *f = entity_alloc(ent); + + f32 force_len = -v2_len(velocity) * 8; + + entity_enable_prop(f, ENTITY_PROP_FORCE); + f->force = v2_mul(v2_norm(velocity), force_len); + + activate_now(f); + } + } + + /* ========================== * + * Apply impulses to verlet xform + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + + /* FIXME: Why does marking all ents with ENTITY_PROP_RELEASE here raise exception */ + + if (entity_has_prop(ent, ENTITY_PROP_IMPULSE)) { + struct entity *parent = entity_from_handle(store, ent->parent); + if (entity_has_prop(parent, ENTITY_PROP_ACTIVE)) { + struct v2 tick_velocity = v2_mul(ent->impulse, dt); + parent->verlet_xform.og = v2_sub(parent->verlet_xform.og, tick_velocity); + entity_enable_prop(ent, ENTITY_PROP_RELEASE); + } + } + } + + /* ========================== * + * Apply forces to verlet xform + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + + f32 density = ent->density; + f32 mass = density; /* TODO: Real mass calculation */ + + if (entity_has_prop(ent, ENTITY_PROP_FORCE)) { + struct entity *parent = entity_from_handle(store, ent->parent); + if (entity_has_prop(parent, ENTITY_PROP_ACTIVE)) { + struct v2 acceleration = v2_div(ent->force, mass); + struct v2 tick_acceleration = v2_mul(acceleration, dt * dt); + parent->verlet_xform.og = v2_sub(parent->verlet_xform.og, tick_acceleration); + entity_enable_prop(ent, ENTITY_PROP_RELEASE); + } + } + } + /* ========================== * * Simulate entity physics * ========================== */ @@ -547,21 +628,19 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *ent = &store->entities[entity_index]; if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; - if (!entity_has_prop(ent, entity_has_prop(ent, ENTITY_PROP_PHYSICAL))) continue; + if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue; + struct xform xf = entity_get_xform(ent); /* Calculate velocity from old position */ - - struct v2 velocity = V2(0, 0); + struct v2 tick_velocity = V2(0, 0); { - velocity = v2_sub(xf.og, ent->verlet_xform.og); - velocity = v2_div(velocity, dt); + tick_velocity = v2_sub(xf.og, ent->verlet_xform.og); ent->verlet_xform = xf; } /* Player angle */ - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { /* Solve for final angle using law of sines */ f32 final_xf_angle; @@ -616,27 +695,11 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } - /* TODO: Accumulate forces to calculate acceleration */ - struct v2 acceleration = V2(0, 0); - { - /* Player movement */ - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - f32 max_speed = ent->player_max_speed; - f32 acceleration_rate = ent->player_acceleration; - acceleration_rate = clamp_f32(acceleration_rate, 0, GAME_FPS); /* Can't integrate acceleration rate higher than FPS */ - struct v2 target_velocity = v2_mul(ent->control.move, max_speed); - struct v2 target_acceleration = v2_sub(target_velocity, velocity); - acceleration = v2_mul(target_acceleration, acceleration_rate); - } - } + /* Apply velocity */ + xf.og = v2_add(xf.og, tick_velocity); - /* Integrate acceleration & velocity */ - struct v2 acceleration_tick = v2_mul(acceleration, dt * dt); - struct v2 velocity_tick = v2_add(v2_mul(velocity, dt), acceleration_tick); - xf.og = v2_add(xf.og, velocity_tick); - - ent->final_velocity = velocity; - ent->final_acceleration = acceleration; + ent->final_velocity = v2_div(tick_velocity, dt); + ent->final_acceleration = v2_div(v2_sub(v2_sub(xf.og, ent->verlet_xform.og), tick_velocity), dt); entity_set_xform(ent, xf); } @@ -644,6 +707,31 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) * Initialize bullet kinematics from sources * ========================== */ +#if 0 + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + + /* FIXME: Apply src entity velocity to bullet velocity */ + /* TODO: Use impulse */ + + if (entity_has_prop(ent, ENTITY_PROP_BULLET) && !entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) { + struct entity *src = entity_from_handle(store, ent->bullet_src); + struct xform src_xf = entity_get_xform(src); + + struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); + struct v2 vec = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); + vec = v2_mul(v2_norm(vec), ent->bullet_impulse); + + struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(vec) + PI / 2); + entity_set_xform(ent, xf); + + ent->verlet_xform = XFORM_POS(v2_sub(pos, v2_mul(vec, dt))); + + entity_enable_prop(ent, ENTITY_PROP_PHYSICAL); + } + } +#else for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *ent = &store->entities[entity_index]; if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; @@ -655,25 +743,22 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) struct xform src_xf = entity_get_xform(src); struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); - struct v2 vec = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); - vec = v2_mul(v2_norm(vec), ent->bullet_impulse); + struct v2 velocity = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); + velocity = v2_mul(v2_norm(velocity), ent->bullet_impulse); -#if 0 - { - struct v2 src_velocity = v2_sub(src_xf.og, src->verlet_xform.og); - src_velocity = v2_div(src_velocity, dt); - vec = v2_add(vec, src_velocity); - } -#endif - - struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(vec) + PI / 2); + struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(velocity) + PI / 2); entity_set_xform(ent, xf); - - ent->verlet_xform = XFORM_POS(v2_sub(pos, v2_mul(vec, dt))); + ent->verlet_xform = xf; entity_enable_prop(ent, ENTITY_PROP_PHYSICAL); + + /* Create impulse */ + struct entity *impulse = entity_alloc(ent); + impulse->impulse = velocity; + entity_enable_prop(impulse, ENTITY_PROP_IMPULSE); } } +#endif /* ========================== * * Update camera position @@ -749,6 +834,35 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } + /* ========================== * + * Release entities + * ========================== */ + + /* TODO: Breadth first iteration to only release parent entities (since + * child entities will be released along with parent anyway) */ + + { + struct temp_arena temp = arena_temp_begin(scratch.arena); + + struct entity **ents_to_release = arena_dry_push(temp.arena, struct entity *); + u64 ents_to_release_count = 0; + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!ent->valid) continue; + + if (entity_has_prop(ent, ENTITY_PROP_RELEASE)) { + *arena_push(temp.arena, struct entity *) = ent; + ++ents_to_release_count; + } + } + + for (u64 i = 0; i < ents_to_release_count; ++i) { + entity_release(store, ents_to_release[i]); + } + + arena_temp_end(temp); + } + /* ========================== * * Publish tick * ========================== */