From 3b21f641ad88ce286c9d2a45eeaaf62050db3dba Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 25 Sep 2024 19:36:28 -0500 Subject: [PATCH] calculate contact ponits outside of substeps by storing local positions --- src/config.h | 4 ++-- src/entity.h | 6 ++++-- src/game.c | 48 +++++++++++++++++++++++++----------------------- src/gjk.c | 17 ++--------------- src/user.c | 23 ++++++++++++++--------- 5 files changed, 47 insertions(+), 51 deletions(-) diff --git a/src/config.h b/src/config.h index 9fff9ef4..85fc1ba5 100644 --- a/src/config.h +++ b/src/config.h @@ -29,7 +29,7 @@ #define PIXELS_PER_UNIT 256.0 -#define GAME_FPS 300.0 +#define GAME_FPS 50.0 #define GAME_TIMESCALE 1.0 #define GAME_PHYSICS_SUBSTEPS 4 @@ -38,7 +38,7 @@ * E.g: At 1.5, the user thread will render 75ms back in time (if game thread runs at 50FPS) */ #define USER_INTERP_OFFSET_TICK_RATIO 1.1 -#define USER_INTERP_ENABLED 0 +#define USER_INTERP_ENABLED 1 /* ========================== * * Settings diff --git a/src/entity.h b/src/entity.h index 3885c8c2..8008c29e 100644 --- a/src/entity.h +++ b/src/entity.h @@ -64,10 +64,12 @@ struct entity_store { struct contact { - struct v2 point; + /* Contact point in local space of each entity */ + struct v2 point_local_e0; + struct v2 point_local_e1; u32 id; /* ID generated during clipping */ - f32 separation; /* How far is p0 from p1 along normal */ + f32 starting_separation; /* How far are original points along normal */ f32 accumulated_impulse; /* Accumulated impulse along normal */ b32 persisted; diff --git a/src/game.c b/src/game.c index d2319a0d..27d845e0 100644 --- a/src/game.c +++ b/src/game.c @@ -126,7 +126,7 @@ INTERNAL void spawn_test_entities(void) /* Player */ struct entity *player_ent; { - struct v2 pos = V2(0.25, -3); + struct v2 pos = V2(0.25, -10); //struct v2 pos = V2(0.25, -2); //struct v2 pos = V2(1.1230469346046448864129274625156, -1); /* Touching right side of box */ //struct v2 pos = V2(1.1230469346046448864129274625156 - 0.0001, -1); /* Touching right side of box */ @@ -255,6 +255,7 @@ INTERNAL void pre_step(void) { struct entity_store *store = G.tick.entity_store; + /* TODO: Just do this during create contact manifolds call if both are outside of substep */ for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *manifold = &store->entities[entity_index]; if (!(manifold->valid && entity_has_prop(manifold, ENTITY_PROP_ACTIVE))) continue; @@ -444,8 +445,9 @@ INTERNAL void create_contact_manifolds(void) contact->id = id; } } - contact->point = point; - contact->separation = sep; + contact->point_local_e0 = xform_invert_mul_v2(e0_xf, point); + contact->point_local_e1 = xform_invert_mul_v2(e1_xf, point); + contact->starting_separation = sep; } @@ -501,7 +503,7 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) f32 inv_i1 = 1.f / i1; struct queued_impulse { - struct v2 point; + struct v2 p0, p1; f32 impulse; }; struct queued_impulse queued_impulses[ARRAY_COUNT(manifold->contacts)]; @@ -510,23 +512,25 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) for (u32 contact_index = 0; contact_index < num_contacts; ++contact_index) { struct contact *contact = &manifold->contacts[contact_index]; - struct v2 p = contact->point; + struct v2 p0 = xform_mul_v2(e0_xf, contact->point_local_e0); + struct v2 p1 = xform_mul_v2(e1_xf, contact->point_local_e1); + f32 separation = v2_dot(v2_sub(p1, p0), normal) + contact->starting_separation; f32 bias = 0; if (apply_bias) { + //f32 bias_factor = 0.2f; f32 bias_factor = 0.2f; //f32 bias_factor = 0.025f; //f32 bias_slop = 0.0005f; - f32 bias_slop = 0.01f; + f32 bias_slop = 0.001f; //f32 bias_slop = 0.00f; - bias = (bias_factor / dt) * max_f32((contact->separation - bias_slop), 0); + bias = (bias_factor / dt) * max_f32((separation - bias_slop), 0); } - - struct v2 vcp0 = v2_sub(p, e0_xf.og); - struct v2 vcp1 = v2_sub(p, e1_xf.og); + struct v2 vcp0 = v2_sub(p0, e0_xf.og); + struct v2 vcp1 = v2_sub(p1, e1_xf.og); struct v2 vel0 = v2_add(e0->linear_velocity, v2_perp_right_mul(vcp0, e0->angular_velocity)); struct v2 vel1 = v2_add(e1->linear_velocity, v2_perp_right_mul(vcp1, e1->angular_velocity)); @@ -534,7 +538,6 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) struct v2 vrel = v2_sub(vel1, vel0); f32 vn = v2_dot(vrel, normal); - vn = max_f32(vn, 0); struct v2 idk0 = v2_perp_right_mul(vcp0, v2_wedge(vcp0, normal) * inv_i0); struct v2 idk1 = v2_perp_right_mul(vcp1, v2_wedge(vcp1, normal) * inv_i1); @@ -552,7 +555,8 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) contact->accumulated_impulse = new_accumulated_impulse; queued_impulses[contact_index] = (struct queued_impulse) { - .point = p, + .p0 = p0, + .p1 = p1, .impulse = delta_impulse }; } @@ -560,8 +564,8 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) for (u32 i = 0; i < num_contacts; ++i) { struct queued_impulse qi = queued_impulses[i]; struct v2 impulse = v2_mul(normal, qi.impulse); - entity_apply_linear_impulse(e0, impulse, qi.point); - entity_apply_linear_impulse(e1, v2_neg(impulse), qi.point); + entity_apply_linear_impulse(e0, impulse, qi.p0); + entity_apply_linear_impulse(e1, v2_neg(impulse), qi.p1); } } } @@ -1260,22 +1264,20 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) * ========================== */ { + create_contact_manifolds(); pre_step(); + (UNUSED)create_contact_manifolds; + (UNUSED)solve_collisions; + (UNUSED)integrate_positions; + f32 substep_dt = dt / GAME_PHYSICS_SUBSTEPS; for (u32 i = 0; i < GAME_PHYSICS_SUBSTEPS; ++i) { - - #if 0 - create_contact_manifolds(); + #if 1 solve_collisions(substep_dt, true); integrate_positions(substep_dt); - solve_collisions(substep_dt, false); + solve_collisions(substep_dt, false); /* Relaxation */ #else - (UNUSED)create_contact_manifolds; - (UNUSED)solve_collisions; - (UNUSED)integrate_positions; - - create_contact_manifolds(); solve_collisions(substep_dt, true); integrate_positions(substep_dt); #endif diff --git a/src/gjk.c b/src/gjk.c index 8f5add8a..e825a132 100644 --- a/src/gjk.c +++ b/src/gjk.c @@ -171,9 +171,6 @@ struct gjk_contact_points_result gjk_contact_points(struct v2_array shape0, stru /* TODO: Set all epsilons used in this function to 0.005 */ - /* TODO: Verify epsilon */ - /* FIXME: Infinite loop when epsilon too low and a shape is too far from 0 (precision issue) */ - const f32 epsilon = 0.0000100f; struct gjk_simplex s = ZI; b32 colliding = false; struct v2 *proto = NULL; @@ -322,25 +319,15 @@ struct gjk_contact_points_result gjk_contact_points(struct v2_array shape0, stru /* Check unique */ /* TODO: Better */ { + const f32 min_new_pt_dist_sq = 0.001f; b32 unique = true; -#if 0 - for (u32 i = 0; i < proto_count; ++i) { - struct v2 edge_start = proto[i]; - struct v2 edge_end = i < proto_count - 1 ? proto[i + 1] : proto[0]; - if (math_fabs(v2_wedge(v2_sub(edge_end, edge_start), v2_sub(m, edge_start))) < epsilon) { - unique = false; - break; - } - } -#else for (u32 i = 0; i < proto_count; ++i) { struct v2 p = proto[i]; - if (v2_len_sq(v2_sub(p, m)) < epsilon) { + if (v2_len_sq(v2_sub(p, m)) < min_new_pt_dist_sq) { unique = false; break; } } -#endif if (!unique) { normal = v2_norm(normal); break; diff --git a/src/user.c b/src/user.c index feafe6bb..ba3cabd0 100644 --- a/src/user.c +++ b/src/user.c @@ -446,6 +446,7 @@ INTERNAL void user_update(void) __profscope(produce_interpolated_tick); #if USER_INTERP_ENABLED + /* TODO: Use actual fps of game thread (will differ from GAME_FPS if game thread is lagging) to hide lag with slow-motion? */ f64 blend_time_offset = (1.0 / GAME_FPS) * USER_INTERP_OFFSET_TICK_RATIO; f64 blend_time = G.time > blend_time_offset ? G.time - blend_time_offset : 0; @@ -1121,14 +1122,15 @@ INTERNAL void user_update(void) f32 radius = 5; for (u32 i = 0; i < ent->num_contacts; ++i) { struct contact contact = ent->contacts[i]; + struct v2 p0 = xform_mul_v2(e0_xf, contact.point_local_e0); + struct v2 p1 = xform_mul_v2(e1_xf, contact.point_local_e1); + struct v2 point = v2_add(p0, v2_mul(v2_sub(p1, p0), 0.5f)); /* Draw point */ { u32 color = contact.persisted ? RGBA_32_F(1, 1, 0, 0.50) : RGBA_32_F(1, 0, 0, 0.50); //struct v2 point = xform_mul_v2(e0_xf, contact.p0_local); //struct v2 point = contact.p0_initial_world; - struct v2 point = contact.point; - point = xform_mul_v2(G.world_view, point); - draw_solid_circle(G.viewport_canvas, point, radius, color, 10); + draw_solid_circle(G.viewport_canvas, xform_mul_v2(G.world_view, point), radius, color, 10); } /* Draw normal */ { @@ -1136,8 +1138,8 @@ INTERNAL void user_update(void) f32 len = 0.1; f32 arrow_thickness = 2; f32 arrow_height = 5; - struct v2 start = xform_mul_v2(G.world_view, contact.point); - struct v2 end = xform_mul_v2(G.world_view, v2_add(contact.point, v2_mul(v2_norm(ent->manifold_normal), len))); + struct v2 start = xform_mul_v2(G.world_view, point); + struct v2 end = xform_mul_v2(G.world_view, v2_add(point, v2_mul(v2_norm(ent->manifold_normal), len))); draw_solid_arrow_line(G.viewport_canvas, start, end, arrow_thickness, arrow_height, color); } /* Draw id */ @@ -1148,14 +1150,16 @@ INTERNAL void user_update(void) struct string fmt = STR( "id: 0x%F\n" - "impulse: %F" + "impulse: %F\n" + "separation: %F" ); struct string text = string_format(temp.arena, fmt, FMT_HEX(contact.id), - FMT_FLOAT(contact.accumulated_impulse)); + FMT_FLOAT(contact.accumulated_impulse), + FMT_FLOAT(contact.starting_separation)); - draw_text(G.viewport_canvas, disp_font, v2_add(v2_round(xform_mul_v2(G.world_view, contact.point)), V2(0, offset_px)), text); + draw_text(G.viewport_canvas, disp_font, v2_add(v2_round(xform_mul_v2(G.world_view, point)), V2(0, offset_px)), text); } } } @@ -1263,7 +1267,8 @@ INTERNAL void user_update(void) input_move_dir = v2_mul(v2_norm(input_move_dir), move_speed); } struct v2 input_aim_pos = G.world_cursor; - if (!G.debug_camera) { + //if (!G.debug_camera) { + { queue_game_cmd(scratch.arena, &cmd_list, (struct game_cmd) { .kind = GAME_CMD_KIND_PLAYER_MOVE, .move_dir = input_move_dir,