more physics testing

This commit is contained in:
jacob 2024-09-12 15:36:20 -05:00
parent ea56dab9bf
commit a946583bc8
6 changed files with 102 additions and 91 deletions

View File

@ -344,11 +344,10 @@ void entity_apply_angular_impulse(struct entity *ent, f32 impulse)
ent->angular_velocity += impulse * inv_inertia; ent->angular_velocity += impulse * inv_inertia;
} }
#if 0
void entity_apply_torque(struct entity *ent, f32 torque) void entity_apply_torque(struct entity *ent, f32 torque)
{ {
ent->torque += torque;
} }
#endif
/* ========================== * /* ========================== *
* Tree * Tree

View File

@ -142,7 +142,8 @@ struct entity {
f32 mass_unscaled; /* Mass of entity in kg before any transformations */ f32 mass_unscaled; /* Mass of entity in kg before any transformations */
f32 inertia_unscaled; /* Inertia of entity in kg*m^2 before any transformations */ f32 inertia_unscaled; /* Inertia of entity in kg*m^2 before any transformations */
f32 ground_friction; f32 linear_ground_friction;
f32 angular_ground_friction;
struct v2 linear_velocity; /* m/s */ struct v2 linear_velocity; /* m/s */
f32 angular_velocity; /* rad/s */ f32 angular_velocity; /* rad/s */

View File

@ -127,8 +127,9 @@ INTERNAL void spawn_test_entities(void)
//struct v2 pos = V2(0.374142020941, -0.246118023992); /* Touching glitch spot */ //struct v2 pos = V2(0.374142020941, -0.246118023992); /* Touching glitch spot */
struct v2 size = V2(1, 1); struct v2 size = V2(1, 1);
//f32 r = PI / 4; //f32 r = PI / 4;
f32 r = PI / 3; //f32 r = PI / 3;
//f32 r = 0; //f32 r = PI / 2;
f32 r = 0;
f32 skew = 0; f32 skew = 0;
struct entity *e = entity_alloc(root); struct entity *e = entity_alloc(root);
@ -143,15 +144,16 @@ INTERNAL void spawn_test_entities(void)
e->sprite_span_name = STR("idle.two_handed"); e->sprite_span_name = STR("idle.two_handed");
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
//e->control_force = 4500; e->control_force = 4500;
e->control_force = 1200; //e->control_force = 1200;
e->control_torque = 10; e->control_torque = 10;
e->control.focus = V2(0, -1); e->control.focus = V2(0, -1);
entity_enable_prop(e, ENTITY_PROP_PHYSICAL); entity_enable_prop(e, ENTITY_PROP_PHYSICAL);
e->mass_unscaled = 100; e->mass_unscaled = 100;
e->inertia_unscaled = 5; e->inertia_unscaled = 25;
e->ground_friction = 1000; e->linear_ground_friction = 1000;
e->angular_ground_friction = 100;
//entity_enable_prop(e, ENTITY_PROP_TEST); //entity_enable_prop(e, ENTITY_PROP_TEST);
@ -192,13 +194,14 @@ INTERNAL void spawn_test_entities(void)
entity_enable_prop(e, ENTITY_PROP_PHYSICAL); entity_enable_prop(e, ENTITY_PROP_PHYSICAL);
#if 0 #if 0
e->mass_unscaled = 300; e->mass_unscaled = 1000;
e->inertia_unscaled = 500; e->inertia_unscaled = 1000;
#else #else
e->mass_unscaled = F32_INFINITY; e->mass_unscaled = F32_INFINITY;
e->inertia_unscaled = F32_INFINITY; e->inertia_unscaled = F32_INFINITY;
#endif #endif
//e->ground_friction = 1000; e->linear_ground_friction = 10000;
e->angular_ground_friction = 10000;
entity_set_xform(e, XFORM_TRS(.t = pos, .s = size, .r = rot)); entity_set_xform(e, XFORM_TRS(.t = pos, .s = size, .r = rot));
} }
@ -761,17 +764,17 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
#endif #endif
/* ========================== * /* ========================== *
* Create ground friction force * Create ground friction force (gravity)
* ========================== */ * ========================== */
#if 0 #if 1
/* TODO: Do this globally rather than creating entities for constant forces */ /* TODO: Do this globally rather than creating entities for constant forces */
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
struct entity *ent = &store->entities[entity_index]; struct entity *ent = &store->entities[entity_index];
if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue;
if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue; if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue;
if (ent->ground_friction != 0) { #if 1
#if 0 if (ent->linear_ground_friction != 0) {
/* Linear velocity */ /* Linear velocity */
{ {
struct v2 linear_velocity = ent->linear_velocity; struct v2 linear_velocity = ent->linear_velocity;
@ -780,52 +783,45 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
const f32 clamp_epsilon = 0.01; const f32 clamp_epsilon = 0.01;
f32 linear_velocity_len = v2_len(linear_velocity); f32 linear_velocity_len = v2_len(linear_velocity);
if (linear_velocity_len >= clamp_epsilon) { if (linear_velocity_len >= clamp_epsilon) {
f32 force_len = -linear_velocity_len * ent->ground_friction; f32 force_len = -linear_velocity_len * ent->linear_ground_friction;
struct entity *force = entity_alloc(ent); struct v2 force = v2_mul(v2_norm(linear_velocity), force_len);
entity_enable_prop(force, ENTITY_PROP_FORCE); entity_apply_force_to_center(ent, force);
force->force = v2_mul(v2_norm(linear_velocity), force_len);
activate_now(force);
} else { } else {
/* If linear_velocity is below clamp_epsilon, stop entity movement. */ /* If linear_velocity is below clamp_epsilon, stop entity movement. */
struct xform xf = entity_get_xform(ent); struct xform xf = entity_get_xform(ent);
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf)); f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
struct entity *impulse = entity_alloc(ent); struct v2 impulse = v2_mul(v2_neg(linear_velocity), mass);
entity_enable_prop(impulse, ENTITY_PROP_LINEAR_IMPULSE); entity_apply_linear_impulse_to_center(ent, impulse);
impulse->force = v2_mul(v2_neg(linear_velocity), mass);
activate_now(impulse);
} }
} }
} }
}
if (ent->angular_ground_friction != 0) {
/* Angular velocity */ /* Angular velocity */
{ {
f32 angular_velocity = ent->angular_velocity; f32 angular_velocity = ent->angular_velocity;
if (angular_velocity != 0) { if (angular_velocity != 0) {
/* FIXME: Incorrect behavior at low FPS & low entity density */ /* FIXME: Incorrect (just testing) */
const f32 clamp_epsilon = 0.001; const f32 clamp_epsilon = 0.001;
if (math_fabs(angular_velocity) >= clamp_epsilon) { if (math_fabs(angular_velocity) >= clamp_epsilon) {
struct entity *torque = entity_alloc(ent); f32 torque = -angular_velocity * ent->angular_ground_friction;
entity_enable_prop(torque, ENTITY_PROP_TORQUE); entity_apply_torque(ent, torque);
/* TODO: Different ground friction for rotation */
torque->torque = -angular_velocity * 3;
activate_now(torque);
} else { } else {
/* If angular_velocity is below clamp_epsilon, stop entity movement. */ /* If angular_velocity is below clamp_epsilon, stop entity movement. */
struct xform xf = entity_get_xform(ent); struct xform xf = entity_get_xform(ent);
f32 inertia = ent->inertia_unscaled * math_fabs(xform_get_determinant(xf)); f32 inertia = ent->inertia_unscaled * math_fabs(xform_get_determinant(xf));
struct entity *impulse = entity_alloc(ent); f32 impulse = -angular_velocity * inertia;
entity_enable_prop(impulse, ENTITY_PROP_ANGULAR_IMPULSE); entity_apply_angular_impulse(ent, impulse);
impulse->torque = -angular_velocity * inertia;
activate_now(impulse);
} }
} }
} }
}
#else #else
/* TODO: Remove this (gravity test) */ /* TODO: Remove this (gravity test) */
struct xform xf = entity_get_xform(ent); struct xform xf = entity_get_xform(ent);
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf)); f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
entity_apply_force_to_center(ent, V2(0, 9.81 * mass)); entity_apply_force_to_center(ent, V2(0, 9.81 * mass));
#endif #endif
}
} }
#endif #endif
@ -833,6 +829,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
* Integrate forces * Integrate forces
* ========================== */ * ========================== */
#if 0
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
struct entity *ent = &store->entities[entity_index]; struct entity *ent = &store->entities[entity_index];
if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue;
@ -845,9 +842,10 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
/* Determine force & torque acceleration */ /* Determine force & torque acceleration */
struct v2 tick_force_accel = v2_mul(v2_div(ent->force, mass), dt); struct v2 tick_force_accel = v2_mul(v2_div(ent->force, mass), dt);
f32 tick_torque_accel = ent->torque / inertia * dt; f32 tick_torque_accel = (ent->torque / inertia) * dt;
/* Integrate */ /* Integrate */
/* TODO: Don't need to multiply each term by dt separately */
struct v2 tick_linear_velocity = v2_add(v2_mul(ent->linear_velocity, dt), v2_mul(tick_force_accel, dt)); struct v2 tick_linear_velocity = v2_add(v2_mul(ent->linear_velocity, dt), v2_mul(tick_force_accel, dt));
f32 tick_angular_velocity = (ent->angular_velocity * dt) + (tick_torque_accel * dt); f32 tick_angular_velocity = (ent->angular_velocity * dt) + (tick_torque_accel * dt);
@ -861,6 +859,30 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
entity_set_xform(ent, xf); entity_set_xform(ent, xf);
} }
#else
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue;
if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) 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 */
ent->linear_velocity = v2_add(ent->linear_velocity, force_accel);
ent->angular_velocity += torque_accel;
/* Reset forces */
ent->force = V2(0, 0);
ent->torque = 0;
}
#endif
#if 1 #if 1
/* ========================== * /* ========================== *
@ -942,13 +964,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
} }
{ {
#if 0 #if 1
if (colliding) {
xf1.og = v2_add(xf1.og, pen);
//e0->verlet_xform.og = xf0.og;
//e0->verlet_xform.og = v2_sub(xf1.og, velocity);
}
#else
if (colliding) { if (colliding) {
struct entity *e1 = colliding_with; struct entity *e1 = colliding_with;
struct xform e1_xf = entity_get_xform(e1); struct xform e1_xf = entity_get_xform(e1);
@ -972,11 +988,10 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
struct v2 pen_norm = v2_norm(pen); struct v2 pen_norm = v2_norm(pen);
#if 1 #if 1
f32 bias_factor = 10; f32 bias_factor = 0.2;
f32 bias_slop = 0.01; f32 bias_slop = 0.005;
f32 bias = (v2_len(pen) * bias_factor) - bias_slop; f32 bias = (bias_factor / dt) * max((v2_len(pen) - bias_slop), 0);
bias = max(bias, 0);
#else #else
f32 bias = 0; f32 bias = 0;
#endif #endif
@ -991,6 +1006,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
struct v2 vrel = v2_sub(p1_vel, p0_vel); struct v2 vrel = v2_sub(p1_vel, p0_vel);
f32 vn = v2_dot(vrel, pen_norm); f32 vn = v2_dot(vrel, pen_norm);
vn = max_f32(vn, 0);
struct v2 idk0 = v2_perp_mul(vcp0, v2_wedge(vcp0, pen_norm) * inv_i0); struct v2 idk0 = v2_perp_mul(vcp0, v2_wedge(vcp0, pen_norm) * inv_i0);
@ -1000,7 +1016,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
/* (to be applied along n) */ /* (to be applied along n) */
f32 j = (vn + bias) / k; f32 j = (vn + bias) / k;
j = max_f32(j, 0); //j = max_f32(j, 0);
@ -1031,6 +1047,23 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
} }
#endif #endif
/* ========================== *
* Update positions
* ========================== */
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue;
if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue;
struct xform xf = entity_get_xform(ent);
struct v2 tick_linear_velocity = v2_mul(ent->linear_velocity, dt);
f32 tick_angular_velocity = ent->angular_velocity * dt;
xf.og = v2_add(xf.og, tick_linear_velocity);
xf = xform_rotated(xf, tick_angular_velocity);
entity_set_xform(ent, xf);
}
/* ========================== * /* ========================== *
* Initialize bullet kinematics from sources * Initialize bullet kinematics from sources
* ========================== */ * ========================== */
@ -1053,7 +1086,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
entity_set_xform(ent, xf); entity_set_xform(ent, xf);
entity_enable_prop(ent, ENTITY_PROP_PHYSICAL); entity_enable_prop(ent, ENTITY_PROP_PHYSICAL);
entity_apply_linear_impulse_to_center(src, impulse); entity_apply_linear_impulse_to_center(ent, impulse);
} }
} }

View File

@ -225,8 +225,8 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array
} }
/* ========================== * /* ========================== *
* Expand simplex towards closest edge to origin inside menkowski * Expand simplex towards closest edge to origin inside menkowski
* ========================== */ * ========================== */
while (true) { while (true) {
f32 pen_len_sq = F32_INFINITY; f32 pen_len_sq = F32_INFINITY;

View File

@ -651,7 +651,7 @@ INLINE struct v2 v2_perp(struct v2 a)
return V2(-a.y, a.x); return V2(-a.y, a.x);
} }
/* Perpendicular vector scaled by s */ /* Right (clockwise) perpendicular vector scaled by s */
INLINE struct v2 v2_perp_mul(struct v2 a, f32 s) INLINE struct v2 v2_perp_mul(struct v2 a, f32 s)
{ {
return V2(s * -a.y, s * a.x); return V2(s * -a.y, s * a.x);
@ -659,9 +659,8 @@ INLINE struct v2 v2_perp_mul(struct v2 a, f32 s)
INLINE struct v2 v2_perp_towards_dir(struct v2 v, struct v2 dir) INLINE struct v2 v2_perp_towards_dir(struct v2 v, struct v2 dir)
{ {
struct v2 perp = v2_perp(v); f32 wedge = v2_wedge(v, dir);
f32 dot = v2_dot(perp, dir); return v2_perp_mul(v, (wedge >= 0) - (wedge < 0));
return v2_mul(perp, (dot >= 0) - (dot < 0));
} }
INLINE struct v2 v2_norm(struct v2 a) INLINE struct v2 v2_norm(struct v2 a)

View File

@ -993,33 +993,21 @@ INTERNAL void user_update(void)
(UNUSED)e1; (UNUSED)e1;
(UNUSED)colliding; (UNUSED)colliding;
#if 0 #if 1
/* Create shapes */ /* Create shapes */
struct quad ent_quad_xf0; struct quad ent_quad;
struct v2_array ent_poly_xf0; struct v2_array ent_poly;
struct quad ent_quad_xf1;
struct v2_array ent_poly_xf1;
struct quad e1_quad; struct quad e1_quad;
struct v2_array e1_poly; struct v2_array e1_poly;
{ {
{ {
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), ent->animation_frame); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), ent->animation_frame);
ent_quad_xf0 = xform_mul_quad(ent->sprite_local_xform, quad_from_rect(slice.rect)); ent_quad = xform_mul_quad(ent->sprite_local_xform, quad_from_rect(slice.rect));
ent_quad_xf0 = xform_mul_quad(ent->xf0, ent_quad_xf0); ent_quad = xform_mul_quad(xf, ent_quad);
ent_poly_xf0 = (struct v2_array) { ent_poly = (struct v2_array) {
.count = ARRAY_COUNT(ent_quad_xf0.e), .count = ARRAY_COUNT(ent_quad.e),
.points = ent_quad_xf0.e .points = ent_quad.e
};
}
{
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), ent->animation_frame);
ent_quad_xf1 = xform_mul_quad(ent->sprite_local_xform, quad_from_rect(slice.rect));
ent_quad_xf1 = xform_mul_quad(ent->xf1, ent_quad_xf1);
ent_poly_xf1 = (struct v2_array) {
.count = ARRAY_COUNT(ent_quad_xf1.e),
.points = ent_quad_xf1.e
}; };
} }
{ {
@ -1036,19 +1024,10 @@ INTERNAL void user_update(void)
/* Draw collision shapes */ /* Draw collision shapes */
{ {
(UNUSED)ent_poly_xf0;
(UNUSED)ent_poly_xf1;
f32 thickness = 2; f32 thickness = 2;
{ u32 color = RGBA_32_F(1, 0, 0, 0.25);
u32 color = RGBA_32_F(1, 0, 0, 0.25); struct quad quad = xform_mul_quad(G.world_view, ent_quad);
struct quad quad = xform_mul_quad(G.world_view, ent_quad_xf0); draw_solid_quad_line(G.viewport_canvas, quad, thickness, color);
draw_solid_quad_line(G.viewport_canvas, quad, thickness, color);
}
{
u32 color = RGBA_32_F(0, 1, 0, 0.25);
struct quad quad = xform_mul_quad(G.world_view, ent_quad_xf1);
draw_solid_quad_line(G.viewport_canvas, quad, thickness, color);
}
} }
/* Draw menkowski */ /* Draw menkowski */
@ -1058,8 +1037,8 @@ INTERNAL void user_update(void)
f32 thickness = 2; f32 thickness = 2;
(UNUSED)thickness; (UNUSED)thickness;
struct v2_array m = menkowski(temp.arena, ent_poly_xf0, e1_poly); struct v2_array m = menkowski(temp.arena, ent_poly, e1_poly);
//struct v2_array m = menkowski(temp.arena, ent_poly_xf0, e1_poly, v2_sub(ent->xf1.og, ent->xf0.og)); //struct v2_array m = menkowski(temp.arena, ent_poly, e1_poly, v2_sub(ent->xf1.og, ent->xf0.og));
for (u64 i = 0; i < m.count; ++i) m.points[i] = xform_mul_v2(G.world_view, m.points[i]); for (u64 i = 0; i < m.count; ++i) m.points[i] = xform_mul_v2(G.world_view, m.points[i]);
draw_solid_poly_line(G.viewport_canvas, m, true, thickness, color); draw_solid_poly_line(G.viewport_canvas, m, true, thickness, color);
@ -1071,7 +1050,7 @@ INTERNAL void user_update(void)
u32 color = RGBA_32_F(1, 1, 1, 1); u32 color = RGBA_32_F(1, 1, 1, 1);
f32 radius = 2; f32 radius = 2;
struct v2_array m = cloud(temp.arena, ent_poly_xf0, e1_poly); struct v2_array m = cloud(temp.arena, ent_poly, e1_poly);
for (u64 i = 0; i < m.count; ++i) { for (u64 i = 0; i < m.count; ++i) {
struct v2 p = xform_mul_v2(G.world_view, m.points[i]);; struct v2 p = xform_mul_v2(G.world_view, m.points[i]);;