From 5b29d4f36ef4ac9680b4b1f42b77c31f78dfb6f0 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 13 Jan 2025 08:46:11 -0600 Subject: [PATCH] bullet knockback. physics damping. --- src/entity.c | 15 ++--- src/entity.h | 8 ++- src/game.c | 31 +++++---- src/phys.c | 182 +++++++++++++++++++++++++-------------------------- src/phys.h | 12 ++-- 5 files changed, 127 insertions(+), 121 deletions(-) diff --git a/src/entity.c b/src/entity.c index 69590cf3..fdb34225 100644 --- a/src/entity.c +++ b/src/entity.c @@ -304,23 +304,17 @@ void entity_apply_linear_impulse(struct entity *ent, struct v2 impulse, struct v f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale); struct v2 vcp = v2_sub(point, center); - ent->linear_velocity = v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass)); - ent->angular_velocity += v2_wedge(vcp, impulse) * inv_inertia; + entity_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass))); + entity_set_angular_velocity(ent, v2_wedge(vcp, impulse) * inv_inertia); } -#if 0 -void entity_apply_force(struct entity *ent, struct v2 impulse, struct v2 world_point) -{ -} -#endif - void entity_apply_linear_impulse_to_center(struct entity *ent, struct v2 impulse) { struct xform xf = entity_get_xform(ent); f32 scale = math_fabs(xform_get_determinant(xf)); f32 inv_mass = 1.f / (ent->mass_unscaled * scale); - ent->linear_velocity = v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass)); + entity_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass))); } void entity_apply_force_to_center(struct entity *ent, struct v2 force) @@ -333,8 +327,7 @@ void entity_apply_angular_impulse(struct entity *ent, f32 impulse) struct xform xf = entity_get_xform(ent); f32 scale = math_fabs(xform_get_determinant(xf)); f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale); - - ent->angular_velocity += impulse * inv_inertia; + entity_set_angular_velocity(ent, ent->angular_velocity + impulse * inv_inertia); } void entity_apply_torque(struct entity *ent, f32 torque) diff --git a/src/entity.h b/src/entity.h index b4ba1d13..eb818a3c 100644 --- a/src/entity.h +++ b/src/entity.h @@ -175,12 +175,16 @@ struct entity { f32 linear_ground_friction; f32 angular_ground_friction; + /* Use entity_set_linear_velocity & entity_set_angular_velocity to set */ struct v2 linear_velocity; /* m/s */ f32 angular_velocity; /* rad/s */ struct v2 force; f32 torque; + f32 linear_damping; + f32 angular_damping; + /* ====================================================================== */ /* Sprite */ @@ -226,6 +230,7 @@ struct entity { struct v2 bullet_src_pos; struct v2 bullet_src_dir; f32 bullet_impulse; + f32 bullet_knockback; b32 bullet_has_hit; /* ====================================================================== */ @@ -356,8 +361,9 @@ void entity_set_xform(struct entity *ent, struct xform xf); void entity_set_local_xform(struct entity *ent, struct xform xf); /* Movement */ +INLINE void entity_set_linear_velocity(struct entity *ent, struct v2 velocity) { ent->linear_velocity = v2_clamp_len(velocity, GAME_MAX_LINEAR_VELOCITY); } +INLINE void entity_set_angular_velocity(struct entity *ent, f32 velocity) { ent->angular_velocity = clamp_f32(velocity, -GAME_MAX_ANGULAR_VELOCITY, GAME_MAX_ANGULAR_VELOCITY); } void entity_apply_linear_impulse(struct entity *ent, struct v2 impulse, struct v2 world_point); -void entity_apply_force(struct entity *ent, struct v2 impulse, struct v2 world_point); void entity_apply_linear_impulse_to_center(struct entity *ent, struct v2 impulse); void entity_apply_force_to_center(struct entity *ent, struct v2 force); void entity_apply_angular_impulse(struct entity *ent, f32 impulse); diff --git a/src/game.c b/src/game.c index 1b73c749..5fb27225 100644 --- a/src/game.c +++ b/src/game.c @@ -345,19 +345,18 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, array) if (entity_is_valid_and_active(e0) && entity_is_valid_and_active(e1)) { /* Bullet hit entity */ if (entity_has_prop(e0, ENTITY_PROP_BULLET) || entity_has_prop(e1, ENTITY_PROP_BULLET)) { - struct v2 normal = data->normal; + struct v2 normal = data->normal; /* Impact normal */ + struct v2 vrel = v2_neg(data->vrel); /* Impact velocity */ + struct entity *target = e0; struct entity *bullet = e1; if (entity_has_prop(e0, ENTITY_PROP_BULLET)) { target = e1; bullet = e0; normal = v2_neg(normal); + vrel = v2_neg(vrel); } - struct entity *src = entity_from_handle(store, bullet->bullet_src); - (UNUSED)bullet; - (UNUSED)target; - (UNUSED)src; if (bullet->bullet_has_hit || entity_handle_eq(src->top, target->top)) { /* Ignore collision if bullet already spent or if weapon and @@ -377,20 +376,29 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, array) struct xform xf = entity_get_xform(tracer); xf.og = point; entity_set_xform(tracer, xf); - tracer->linear_velocity = V2(0, 0); + entity_set_linear_velocity(tracer, V2(0, 0)); } + /* Update target */ + struct v2 knockback = v2_mul(v2_norm(vrel), bullet->bullet_knockback); + entity_apply_linear_impulse(target, knockback, point); + /* Create test blood */ /* TODO: Remove this */ { struct xform xf = XFORM_TRS(.t = point); struct entity *decal = entity_alloc(root); decal->sprite = sprite_tag_from_path(STR("res/graphics/blood.ase")); + decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.5f); entity_set_xform(decal, xf); + f32 angular_velocity_range = 5; entity_enable_prop(decal, ENTITY_PROP_PHYSICAL_KINEMATIC); - decal->linear_velocity = v2_mul(v2_norm(normal), 0.5f); - decal->angular_velocity = 1 - (((f32)sys_rand_u32() / (f32)U32_MAX) * 2); + entity_set_linear_velocity(decal, v2_mul(v2_norm(v2_neg(vrel)), 0.5f)); + entity_set_angular_velocity(decal, angular_velocity_range - (((f32)sys_rand_u32() / (f32)U32_MAX) * (angular_velocity_range * 2))); + + decal->linear_damping = 5.0f; + decal->angular_damping = 5.0f; } } } @@ -821,6 +829,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) bullet->bullet_src_pos = rel_pos; bullet->bullet_src_dir = rel_dir; bullet->bullet_impulse = 0.75f; + bullet->bullet_knockback = 10; bullet->mass_unscaled = 0.04f; bullet->inertia_unscaled = 0.00001f; @@ -883,7 +892,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } entity_set_xform(joint_ent, XFORM_IDENT); /* Reset joint ent position */ - joint_ent->linear_velocity = v2_mul(v2_clamp_len(ent->control.move, 1), ent->control_force_max_speed); + entity_set_linear_velocity(joint_ent, v2_mul(v2_clamp_len(ent->control.move, 1), ent->control_force_max_speed)); } } #endif @@ -973,7 +982,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) new_vel = diff / dt; } } - joint_ent->angular_velocity = new_vel; + entity_set_angular_velocity(joint_ent, new_vel); } } #endif @@ -1098,7 +1107,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) if (entity_is_valid_and_active(tracer)) { entity_set_xform(tracer, xf); entity_enable_prop(tracer, ENTITY_PROP_PHYSICAL_KINEMATIC); - tracer->linear_velocity = ent->linear_velocity; + entity_set_linear_velocity(tracer, ent->linear_velocity); tracer->tracer_start = pos; tracer->tracer_start_velocity = ent->linear_velocity; tracer->tracer_gradient_end = pos; diff --git a/src/phys.c b/src/phys.c index 24261d6e..4aab9dc1 100644 --- a/src/phys.c +++ b/src/phys.c @@ -457,10 +457,10 @@ void phys_warm_start_contacts(struct phys_ctx *ctx) w1 += v2_wedge(vcp1, impulse) * inv_i1; } - e0->linear_velocity = v0; - e0->angular_velocity = w0; - e1->linear_velocity = v1; - e1->angular_velocity = w1; + entity_set_linear_velocity(e0, v0); + entity_set_angular_velocity(e0, w0); + entity_set_linear_velocity(e1, v1); + entity_set_angular_velocity(e1, w1); } } } @@ -573,10 +573,10 @@ void phys_solve_contacts(struct phys_ctx *ctx, f32 dt, b32 apply_bias) w1 += v2_wedge(vcp1, impulse) * inv_i1; } - e0->linear_velocity = v0; - e0->angular_velocity = w0; - e1->linear_velocity = v1; - e1->angular_velocity = w1; + entity_set_linear_velocity(e0, v0); + entity_set_angular_velocity(e0, w0); + entity_set_linear_velocity(e1, v1); + entity_set_angular_velocity(e1, w1); } } } @@ -685,8 +685,8 @@ void phys_warm_start_motor_joints(struct phys_ctx *ctx) struct v2 vcp0 = v2_sub(xform_mul_v2(e0_xf, joint->point_local_e0), e0_xf.og); struct v2 vcp1 = v2_sub(xform_mul_v2(e1_xf, joint->point_local_e1), e1_xf.og); - e0->linear_velocity = v2_sub(e0->linear_velocity, v2_mul(joint->linear_impulse, inv_m0)); - e1->linear_velocity = v2_add(e1->linear_velocity, v2_mul(joint->linear_impulse, inv_m1)); + entity_set_linear_velocity(e0, v2_sub(e0->linear_velocity, v2_mul(joint->linear_impulse, inv_m0))); + entity_set_linear_velocity(e1, v2_add(e1->linear_velocity, v2_mul(joint->linear_impulse, inv_m1))); e0->angular_velocity -= (v2_wedge(vcp0, joint->linear_impulse) + joint->angular_impulse) * inv_i0; e1->angular_velocity += (v2_wedge(vcp1, joint->linear_impulse) + joint->angular_impulse) * inv_i1; } @@ -761,10 +761,10 @@ void phys_solve_motor_joints(struct phys_ctx *ctx, f32 dt) w1 += v2_wedge(vcp1, impulse) * inv_i1; } - e0->linear_velocity = v0; - e0->angular_velocity = w0; - e1->linear_velocity = v1; - e1->angular_velocity = w1; + entity_set_linear_velocity(e0, v0); + entity_set_angular_velocity(e0, w0); + entity_set_linear_velocity(e1, v1); + entity_set_angular_velocity(e1, w1); } } @@ -903,7 +903,7 @@ void phys_warm_start_mouse_joints(struct phys_ctx *ctx) f32 inv_i = joint->inv_i; struct xform xf = entity_get_xform(ent); struct v2 vcp = v2_sub(xform_mul_v2(xf, joint->point_local_start), xf.og); - ent->linear_velocity = v2_add(ent->linear_velocity, v2_mul(joint->linear_impulse, inv_m)); + entity_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(joint->linear_impulse, inv_m))); ent->angular_velocity += (v2_wedge(vcp, joint->linear_impulse) + joint->angular_impulse) * inv_i; } } @@ -984,12 +984,83 @@ void phys_solve_mouse_joints(struct phys_ctx *ctx, f32 dt) w += v2_wedge(vcp, impulse) * inv_i; } - ent->linear_velocity = v; - ent->angular_velocity = w; + entity_set_linear_velocity(ent, v); + entity_set_angular_velocity(ent, w); } } } +/* ========================== * + * Integration + * ========================== */ + +INTERNAL struct xform get_derived_xform(struct entity *ent, f32 dt) +{ + struct xform xf = entity_get_xform(ent); + + struct v2 step_linear_velocity = v2_mul(ent->linear_velocity, dt); + f32 step_angular_velocity = ent->angular_velocity * dt; + + xf.og = v2_add(xf.og, step_linear_velocity); + xf = xform_basis_rotated_world(xf, step_angular_velocity); + return xf; +} + +void phys_integrate_forces(struct phys_ctx *ctx, f32 dt) +{ + __prof; + struct entity_store *store = ctx->store; + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!entity_is_valid_and_active(ent)) continue; + + b32 is_dynamic = entity_has_prop(ent, ENTITY_PROP_PHYSICAL_DYNAMIC); + b32 is_kinematic = entity_has_prop(ent, ENTITY_PROP_PHYSICAL_KINEMATIC); + if (is_dynamic || is_kinematic) { + struct v2 linear_velocity = ent->linear_velocity; + f32 angular_velocity = ent->angular_velocity; + f32 linear_damping_factor = max_f32(1.0f - (ent->linear_damping * dt), 0); + f32 angular_damping_factor = max_f32(1.0f - (ent->angular_damping * dt), 0); + + /* Integrate forces */ + if (is_dynamic) { + struct xform xf = entity_get_xform(ent); + f32 det_abs = math_fabs(xform_get_determinant(xf)); + f32 mass = ent->mass_unscaled * det_abs; + f32 inertia = ent->inertia_unscaled * det_abs; + struct v2 force_accel = v2_mul(v2_div(ent->force, mass), dt); + f32 torque_accel = (ent->torque / inertia) * dt; + linear_velocity = v2_add(linear_velocity, force_accel); + angular_velocity += torque_accel; + } + + /* Apply damping */ + linear_velocity = v2_mul(linear_velocity, linear_damping_factor); + angular_velocity *= angular_damping_factor; + + /* Update entity */ + entity_set_linear_velocity(ent, linear_velocity); + entity_set_angular_velocity(ent, angular_velocity); + ent->force = V2(0, 0); + ent->torque = 0; + } + + } +} + +void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt) +{ + __prof; + struct entity_store *store = ctx->store; + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!entity_is_valid_and_active(ent)) continue; + if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL_DYNAMIC) && !entity_has_prop(ent, ENTITY_PROP_PHYSICAL_KINEMATIC)) continue; + + entity_set_xform(ent, get_derived_xform(ent, dt)); + } +} + /* ========================== * * Earliest time of impact * ========================== */ @@ -1010,16 +1081,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f struct collider_shape e0_collider = e0->local_collider; struct xform e0_xf_t0 = entity_get_xform(e0); - struct xform e0_xf_t1 = e0_xf_t0; - { - /* Calculate xform at t=1 */ - struct v2 linear_velocity = v2_clamp_len(e0->linear_velocity, GAME_MAX_LINEAR_VELOCITY); - f32 angular_velocity = clamp_f32(e0->angular_velocity, -GAME_MAX_ANGULAR_VELOCITY, GAME_MAX_ANGULAR_VELOCITY); - struct v2 tick_linear_velocity = v2_mul(linear_velocity, step_dt); - f32 tick_angular_velocity = angular_velocity * step_dt; - e0_xf_t1.og = v2_add(e0_xf_t1.og, tick_linear_velocity); - e0_xf_t1 = xform_basis_rotated_world(e0_xf_t1, tick_angular_velocity); - } + struct xform e0_xf_t1 = get_derived_xform(e0, step_dt); /* Start e1 index at e0 index + 1 to prevent redundant checks */ for (u64 e1_index = e0_index + 1; e1_index < store->reserved; ++e1_index) { @@ -1036,16 +1098,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f struct collider_shape e1_collider = e1->local_collider; struct xform e1_xf_t0 = entity_get_xform(e1); - struct xform e1_xf_t1 = e1_xf_t0; - { - /* Calculate xform at t=1 */ - struct v2 linear_velocity = v2_clamp_len(e1->linear_velocity, GAME_MAX_LINEAR_VELOCITY); - f32 angular_velocity = clamp_f32(e1->angular_velocity, -GAME_MAX_ANGULAR_VELOCITY, GAME_MAX_ANGULAR_VELOCITY); - struct v2 tick_linear_velocity = v2_mul(linear_velocity, step_dt); - f32 tick_angular_velocity = angular_velocity * step_dt; - e1_xf_t1.og = v2_add(e1_xf_t1.og, tick_linear_velocity); - e1_xf_t1 = xform_basis_rotated_world(e1_xf_t1, tick_angular_velocity); - } + struct xform e1_xf_t1 = get_derived_xform(e1, step_dt); f32 t = collider_time_of_impact(&e0_collider, &e1_collider, e0_xf_t0, e1_xf_t0, e0_xf_t1, e1_xf_t1, tolerance, max_iterations); if (t != 0 && t < smallest_t) { @@ -1057,61 +1110,6 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f return smallest_t; } -/* ========================== * - * Integration - * ========================== */ - -void phys_integrate_forces(struct phys_ctx *ctx, f32 dt) -{ - __prof; - struct entity_store *store = ctx->store; - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; - if (!entity_is_valid_and_active(ent)) continue; - if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL_DYNAMIC) && !entity_has_prop(ent, ENTITY_PROP_PHYSICAL_KINEMATIC)) continue; - - struct xform xf = entity_get_xform(ent); - f32 det_abs = math_fabs(xform_get_determinant(xf)); - f32 mass = ent->mass_unscaled * det_abs; - f32 inertia = ent->inertia_unscaled * det_abs; - - /* Determine force & torque acceleration */ - struct v2 force_accel = v2_mul(v2_div(ent->force, mass), dt); - f32 torque_accel = (ent->torque / inertia) * dt; - - /* Integrate & clamp */ - ent->linear_velocity = v2_clamp_len(v2_add(ent->linear_velocity, force_accel), GAME_MAX_LINEAR_VELOCITY); - ent->angular_velocity = clamp_f32(ent->angular_velocity + torque_accel, -GAME_MAX_ANGULAR_VELOCITY, GAME_MAX_ANGULAR_VELOCITY); - - /* Reset forces */ - ent->force = V2(0, 0); - ent->torque = 0; - } -} - -void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt) -{ - __prof; - struct entity_store *store = ctx->store; - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; - if (!entity_is_valid_and_active(ent)) continue; - if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL_DYNAMIC) && !entity_has_prop(ent, ENTITY_PROP_PHYSICAL_KINEMATIC)) continue; - - /* Clamp velocities */ - ent->linear_velocity = v2_clamp_len(ent->linear_velocity, GAME_MAX_LINEAR_VELOCITY); - ent->angular_velocity = clamp_f32(ent->angular_velocity, -GAME_MAX_ANGULAR_VELOCITY, GAME_MAX_ANGULAR_VELOCITY); - - struct v2 tick_linear_velocity = v2_mul(ent->linear_velocity, dt); - f32 tick_angular_velocity = ent->angular_velocity * dt; - - struct xform xf = entity_get_xform(ent); - xf.og = v2_add(xf.og, tick_linear_velocity); - xf = xform_basis_rotated_world(xf, tick_angular_velocity); - entity_set_xform(ent, xf); - } -} - /* ========================== * * Step * ========================== */ diff --git a/src/phys.h b/src/phys.h index 1d9c9d4c..56b2beaa 100644 --- a/src/phys.h +++ b/src/phys.h @@ -177,12 +177,6 @@ void phys_prepare_mouse_joints(struct phys_ctx *ctx); void phys_warm_start_mouse_joints(struct phys_ctx *ctx); void phys_solve_mouse_joints(struct phys_ctx *ctx, f32 dt); -/* ========================== * - * Earliest time of impact - * ========================== */ - -f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations); - /* ========================== * * Integration * ========================== */ @@ -190,6 +184,12 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f void phys_integrate_forces(struct phys_ctx *ctx, f32 dt); void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt); +/* ========================== * + * Earliest time of impact + * ========================== */ + +f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations); + /* ========================== * * Step * ========================== */