calculate contact ponits outside of substeps by storing local positions

This commit is contained in:
jacob 2024-09-25 19:36:28 -05:00
parent c00d43b1de
commit 3b21f641ad
5 changed files with 47 additions and 51 deletions

View File

@ -29,7 +29,7 @@
#define PIXELS_PER_UNIT 256.0 #define PIXELS_PER_UNIT 256.0
#define GAME_FPS 300.0 #define GAME_FPS 50.0
#define GAME_TIMESCALE 1.0 #define GAME_TIMESCALE 1.0
#define GAME_PHYSICS_SUBSTEPS 4 #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) * 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_OFFSET_TICK_RATIO 1.1
#define USER_INTERP_ENABLED 0 #define USER_INTERP_ENABLED 1
/* ========================== * /* ========================== *
* Settings * Settings

View File

@ -64,10 +64,12 @@ struct entity_store {
struct contact { 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 */ 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 */ f32 accumulated_impulse; /* Accumulated impulse along normal */
b32 persisted; b32 persisted;

View File

@ -126,7 +126,7 @@ INTERNAL void spawn_test_entities(void)
/* Player */ /* Player */
struct entity *player_ent; 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(0.25, -2);
//struct v2 pos = V2(1.1230469346046448864129274625156, -1); /* Touching right side of box */ //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 */ //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; 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) { for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
struct entity *manifold = &store->entities[entity_index]; struct entity *manifold = &store->entities[entity_index];
if (!(manifold->valid && entity_has_prop(manifold, ENTITY_PROP_ACTIVE))) continue; 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->id = id;
} }
} }
contact->point = point; contact->point_local_e0 = xform_invert_mul_v2(e0_xf, point);
contact->separation = sep; 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; f32 inv_i1 = 1.f / i1;
struct queued_impulse { struct queued_impulse {
struct v2 point; struct v2 p0, p1;
f32 impulse; f32 impulse;
}; };
struct queued_impulse queued_impulses[ARRAY_COUNT(manifold->contacts)]; 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) { for (u32 contact_index = 0; contact_index < num_contacts; ++contact_index) {
struct contact *contact = &manifold->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; f32 bias = 0;
if (apply_bias) { if (apply_bias) {
//f32 bias_factor = 0.2f;
f32 bias_factor = 0.2f; f32 bias_factor = 0.2f;
//f32 bias_factor = 0.025f; //f32 bias_factor = 0.025f;
//f32 bias_slop = 0.0005f; //f32 bias_slop = 0.0005f;
f32 bias_slop = 0.01f; f32 bias_slop = 0.001f;
//f32 bias_slop = 0.00f; //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(p0, e0_xf.og);
struct v2 vcp0 = v2_sub(p, e0_xf.og); struct v2 vcp1 = v2_sub(p1, e1_xf.og);
struct v2 vcp1 = v2_sub(p, e1_xf.og);
struct v2 vel0 = v2_add(e0->linear_velocity, v2_perp_right_mul(vcp0, e0->angular_velocity)); 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)); 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); struct v2 vrel = v2_sub(vel1, vel0);
f32 vn = v2_dot(vrel, normal); 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 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); 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; contact->accumulated_impulse = new_accumulated_impulse;
queued_impulses[contact_index] = (struct queued_impulse) { queued_impulses[contact_index] = (struct queued_impulse) {
.point = p, .p0 = p0,
.p1 = p1,
.impulse = delta_impulse .impulse = delta_impulse
}; };
} }
@ -560,8 +564,8 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias)
for (u32 i = 0; i < num_contacts; ++i) { for (u32 i = 0; i < num_contacts; ++i) {
struct queued_impulse qi = queued_impulses[i]; struct queued_impulse qi = queued_impulses[i];
struct v2 impulse = v2_mul(normal, qi.impulse); struct v2 impulse = v2_mul(normal, qi.impulse);
entity_apply_linear_impulse(e0, impulse, qi.point); entity_apply_linear_impulse(e0, impulse, qi.p0);
entity_apply_linear_impulse(e1, v2_neg(impulse), qi.point); 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(); pre_step();
(UNUSED)create_contact_manifolds;
(UNUSED)solve_collisions;
(UNUSED)integrate_positions;
f32 substep_dt = dt / GAME_PHYSICS_SUBSTEPS; f32 substep_dt = dt / GAME_PHYSICS_SUBSTEPS;
for (u32 i = 0; i < GAME_PHYSICS_SUBSTEPS; ++i) { for (u32 i = 0; i < GAME_PHYSICS_SUBSTEPS; ++i) {
#if 1
#if 0
create_contact_manifolds();
solve_collisions(substep_dt, true); solve_collisions(substep_dt, true);
integrate_positions(substep_dt); integrate_positions(substep_dt);
solve_collisions(substep_dt, false); solve_collisions(substep_dt, false); /* Relaxation */
#else #else
(UNUSED)create_contact_manifolds;
(UNUSED)solve_collisions;
(UNUSED)integrate_positions;
create_contact_manifolds();
solve_collisions(substep_dt, true); solve_collisions(substep_dt, true);
integrate_positions(substep_dt); integrate_positions(substep_dt);
#endif #endif

View File

@ -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: 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; struct gjk_simplex s = ZI;
b32 colliding = false; b32 colliding = false;
struct v2 *proto = NULL; struct v2 *proto = NULL;
@ -322,25 +319,15 @@ struct gjk_contact_points_result gjk_contact_points(struct v2_array shape0, stru
/* Check unique */ /* Check unique */
/* TODO: Better */ /* TODO: Better */
{ {
const f32 min_new_pt_dist_sq = 0.001f;
b32 unique = true; 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) { for (u32 i = 0; i < proto_count; ++i) {
struct v2 p = proto[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; unique = false;
break; break;
} }
} }
#endif
if (!unique) { if (!unique) {
normal = v2_norm(normal); normal = v2_norm(normal);
break; break;

View File

@ -446,6 +446,7 @@ INTERNAL void user_update(void)
__profscope(produce_interpolated_tick); __profscope(produce_interpolated_tick);
#if USER_INTERP_ENABLED #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_offset = (1.0 / GAME_FPS) * USER_INTERP_OFFSET_TICK_RATIO;
f64 blend_time = G.time > blend_time_offset ? G.time - blend_time_offset : 0; 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; f32 radius = 5;
for (u32 i = 0; i < ent->num_contacts; ++i) { for (u32 i = 0; i < ent->num_contacts; ++i) {
struct contact contact = ent->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 */ /* Draw point */
{ {
u32 color = contact.persisted ? RGBA_32_F(1, 1, 0, 0.50) : RGBA_32_F(1, 0, 0, 0.50); 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 = xform_mul_v2(e0_xf, contact.p0_local);
//struct v2 point = contact.p0_initial_world; //struct v2 point = contact.p0_initial_world;
struct v2 point = contact.point; draw_solid_circle(G.viewport_canvas, xform_mul_v2(G.world_view, point), radius, color, 10);
point = xform_mul_v2(G.world_view, point);
draw_solid_circle(G.viewport_canvas, point, radius, color, 10);
} }
/* Draw normal */ /* Draw normal */
{ {
@ -1136,8 +1138,8 @@ INTERNAL void user_update(void)
f32 len = 0.1; f32 len = 0.1;
f32 arrow_thickness = 2; f32 arrow_thickness = 2;
f32 arrow_height = 5; f32 arrow_height = 5;
struct v2 start = xform_mul_v2(G.world_view, contact.point); struct v2 start = xform_mul_v2(G.world_view, point);
struct v2 end = xform_mul_v2(G.world_view, v2_add(contact.point, v2_mul(v2_norm(ent->manifold_normal), len))); 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_solid_arrow_line(G.viewport_canvas, start, end, arrow_thickness, arrow_height, color);
} }
/* Draw id */ /* Draw id */
@ -1148,14 +1150,16 @@ INTERNAL void user_update(void)
struct string fmt = STR( struct string fmt = STR(
"id: 0x%F\n" "id: 0x%F\n"
"impulse: %F" "impulse: %F\n"
"separation: %F"
); );
struct string text = string_format(temp.arena, fmt, struct string text = string_format(temp.arena, fmt,
FMT_HEX(contact.id), 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); input_move_dir = v2_mul(v2_norm(input_move_dir), move_speed);
} }
struct v2 input_aim_pos = G.world_cursor; 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) { queue_game_cmd(scratch.arena, &cmd_list, (struct game_cmd) {
.kind = GAME_CMD_KIND_PLAYER_MOVE, .kind = GAME_CMD_KIND_PLAYER_MOVE,
.move_dir = input_move_dir, .move_dir = input_move_dir,