calculate contact ponits outside of substeps by storing local positions
This commit is contained in:
parent
c00d43b1de
commit
3b21f641ad
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
48
src/game.c
48
src/game.c
@ -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
|
||||||
|
|||||||
17
src/gjk.c
17
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: 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;
|
||||||
|
|||||||
23
src/user.c
23
src/user.c
@ -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,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user