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

View File

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

View File

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

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

View File

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