angular velocity testing
This commit is contained in:
parent
a0600fc419
commit
e3055062e6
@ -17,6 +17,7 @@ READONLY struct entity _g_entity_nil = {
|
||||
.cached_global_xform = XFORM_IDENT_NOCAST,
|
||||
.cached_global_xform_dirty = false,
|
||||
.mass_unscaled = 1,
|
||||
.inertia_unscaled = 1,
|
||||
.sprite_local_xform = XFORM_IDENT_NOCAST,
|
||||
.sprite_tint = COLOR_WHITE
|
||||
};
|
||||
@ -27,6 +28,7 @@ GLOBAL READONLY struct entity g_entity_default = {
|
||||
.cached_global_xform = XFORM_IDENT_NOCAST,
|
||||
.cached_global_xform_dirty = true,
|
||||
.mass_unscaled = 1,
|
||||
.inertia_unscaled = 1,
|
||||
.sprite_local_xform = XFORM_IDENT_NOCAST,
|
||||
.sprite_tint = COLOR_WHITE
|
||||
};
|
||||
|
||||
30
src/entity.h
30
src/entity.h
@ -12,8 +12,10 @@ enum entity_prop {
|
||||
ENTITY_PROP_RELEASE,
|
||||
|
||||
ENTITY_PROP_PHYSICAL,
|
||||
ENTITY_PROP_IMPULSE,
|
||||
ENTITY_PROP_LINEAR_IMPULSE,
|
||||
ENTITY_PROP_ANGULAR_IMPULSE,
|
||||
ENTITY_PROP_FORCE,
|
||||
ENTITY_PROP_TORQUE,
|
||||
|
||||
ENTITY_PROP_PLAYER_CONTROLLED,
|
||||
ENTITY_PROP_CAMERA,
|
||||
@ -99,16 +101,17 @@ struct entity {
|
||||
struct gjk_simplex simplex;
|
||||
struct gjk_prototype prototype;
|
||||
struct v2 pendir;
|
||||
b32 velocity_intersects;
|
||||
struct xform xf0;
|
||||
struct xform xf1;
|
||||
b32 solved;
|
||||
|
||||
|
||||
struct xform predicted_xform;
|
||||
|
||||
|
||||
|
||||
b32 test_torque_applied;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -134,7 +137,8 @@ struct entity {
|
||||
/* ====================================================================== */
|
||||
/* Control */
|
||||
|
||||
f32 control_move_force; /* How much force is applied to achieve desired control movement */
|
||||
f32 control_force; /* How much force is applied to achieve desired control movement */
|
||||
f32 control_torque; /* How much torque is applied to achieve desired control focus */
|
||||
struct {
|
||||
struct v2 move;
|
||||
struct v2 focus;
|
||||
@ -145,21 +149,31 @@ struct entity {
|
||||
|
||||
/* ENTITY_PROP_PHYSICAL */
|
||||
|
||||
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 kgm2 before any transformations */
|
||||
f32 ground_friction;
|
||||
|
||||
struct v2 linear_velocity; /* m/s */
|
||||
//f32 angular_velocity; /* rad/s */
|
||||
f32 angular_velocity; /* rad/s */
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Impulse */
|
||||
/* Force */
|
||||
|
||||
/* ENTITY_PROP_LINEAR_IMPULSE */
|
||||
/* ENTITY_PROP_FORCE */
|
||||
/* ENTITY_PROP_IMPULSE */
|
||||
|
||||
/* Applies force to parent entity */
|
||||
struct v2 force;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Force */
|
||||
|
||||
/* ENTITY_PROP_ANGULAR_IMPULSE */
|
||||
/* ENTITY_PROP_TORQUE */
|
||||
|
||||
/* Applies torque to parent entity */
|
||||
f32 torque;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Sprite */
|
||||
|
||||
|
||||
560
src/game.c
560
src/game.c
@ -121,8 +121,8 @@ INTERNAL void spawn_test_entities(void)
|
||||
/* Player */
|
||||
struct entity *player_ent;
|
||||
{
|
||||
//struct v2 pos = V2(-1, -1);
|
||||
struct v2 pos = V2(1.1230469346046448864129274625156, -1); /* Touching right side of box */
|
||||
struct v2 pos = V2(0.25, -3);
|
||||
//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(0.374142020941, -0.246118023992); /* Touching glitch spot */
|
||||
struct v2 size = V2(1, 1);
|
||||
@ -142,8 +142,8 @@ INTERNAL void spawn_test_entities(void)
|
||||
e->sprite_span_name = STR("idle.two_handed");
|
||||
|
||||
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
|
||||
e->control_move_force = 4500;
|
||||
//e->control_move_force = 500000;
|
||||
e->control_force = 4500;
|
||||
e->control_torque = 10;
|
||||
e->control.focus = V2(0, -1);
|
||||
|
||||
entity_enable_prop(e, ENTITY_PROP_PHYSICAL);
|
||||
@ -559,8 +559,8 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
bullet->bullet_src = ent->handle;
|
||||
bullet->bullet_src_pos = rel_pos;
|
||||
bullet->bullet_src_dir = rel_dir;
|
||||
//bullet->bullet_impulse = 0.25;
|
||||
bullet->bullet_impulse = 100;
|
||||
bullet->bullet_impulse = 0.25;
|
||||
//bullet->bullet_impulse = 1;
|
||||
bullet->mass_unscaled = 0.04;
|
||||
|
||||
entity_enable_prop(bullet, ENTITY_PROP_BULLET);
|
||||
@ -569,7 +569,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Create forces from control
|
||||
* Create forces from control move
|
||||
* ========================== */
|
||||
|
||||
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
|
||||
@ -580,221 +580,16 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
|
||||
struct entity *f = entity_alloc(ent);
|
||||
entity_enable_prop(f, ENTITY_PROP_FORCE);
|
||||
f->force = v2_mul(move, ent->control_move_force);
|
||||
f->force = v2_mul(move, ent->control_force);
|
||||
activate_now(f);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Create ground friction force
|
||||
* Create forces from control focus (aim)
|
||||
* ========================== */
|
||||
|
||||
/* TODO: Do this globally rather than creating entities for constant forces */
|
||||
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;
|
||||
if (ent->ground_friction != 0) {
|
||||
struct v2 linear_velocity = ent->linear_velocity;
|
||||
|
||||
if (!v2_eq(linear_velocity, V2(0, 0))) {
|
||||
/* FIXME: Incorrect behavior at low FPS & low entity density */
|
||||
const f32 clamp_epsilon = 0.01;
|
||||
f32 linear_velocity_len = v2_len(linear_velocity);
|
||||
if (linear_velocity_len >= clamp_epsilon) {
|
||||
f32 force_len = -linear_velocity_len * ent->ground_friction;
|
||||
struct entity *force = entity_alloc(ent);
|
||||
entity_enable_prop(force, ENTITY_PROP_FORCE);
|
||||
force->force = v2_mul(v2_norm(linear_velocity), force_len);
|
||||
activate_now(force);
|
||||
} else {
|
||||
/* If linear_velocity is below clamp_epsilon, stop entity movement. */
|
||||
struct xform xf = entity_get_xform(ent);
|
||||
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
struct entity *impulse = entity_alloc(ent);
|
||||
entity_enable_prop(impulse, ENTITY_PROP_IMPULSE);
|
||||
impulse->force = v2_mul(v2_neg(linear_velocity), mass);
|
||||
activate_now(impulse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Integrate forces
|
||||
* ========================== */
|
||||
|
||||
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 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
|
||||
/* Determine acceleration from forces and impulses */
|
||||
struct v2 tick_acceleration = V2(0, 0);
|
||||
for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) {
|
||||
if (entity_has_prop(child, ENTITY_PROP_IMPULSE)) {
|
||||
tick_acceleration = v2_add(tick_acceleration, child->force);
|
||||
entity_enable_prop(child, ENTITY_PROP_RELEASE);
|
||||
} else if (entity_has_prop(child, ENTITY_PROP_FORCE)) {
|
||||
tick_acceleration = v2_add(tick_acceleration, v2_mul(child->force, dt));
|
||||
entity_enable_prop(child, ENTITY_PROP_RELEASE);
|
||||
}
|
||||
}
|
||||
tick_acceleration = v2_div(tick_acceleration, mass);
|
||||
|
||||
/* Integrate */
|
||||
struct v2 tick_velocity = v2_mul(ent->linear_velocity, dt);
|
||||
tick_velocity = v2_add(tick_velocity, v2_mul(tick_acceleration, dt));
|
||||
xf.og = v2_add(xf.og, tick_velocity);
|
||||
ent->predicted_xform = xf;
|
||||
ent->linear_velocity = v2_div(tick_velocity, dt);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Release forces
|
||||
* ========================== */
|
||||
|
||||
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_FORCE) || entity_has_prop(ent, ENTITY_PROP_IMPULSE)) {
|
||||
entity_enable_prop(ent, ENTITY_PROP_RELEASE);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Collision
|
||||
* ========================== */
|
||||
|
||||
for (u64 e0_index = 0; e0_index < store->reserved; ++e0_index) {
|
||||
struct entity *e0 = &store->entities[e0_index];
|
||||
if (!(e0->valid && entity_has_prop(e0, ENTITY_PROP_ACTIVE))) continue;
|
||||
if (!entity_has_prop(e0, ENTITY_PROP_PHYSICAL)) continue;
|
||||
if (!entity_has_prop(e0, ENTITY_PROP_PLAYER_CONTROLLED) && !entity_has_prop(e0, ENTITY_PROP_BULLET)) continue;
|
||||
|
||||
struct xform xf0 = entity_get_xform(e0);
|
||||
struct xform xf1 = e0->predicted_xform;
|
||||
struct quad e0_quad;
|
||||
struct v2_array e0_poly;
|
||||
{
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, e0->sprite);
|
||||
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), e0->animation_frame);
|
||||
e0_quad = xform_mul_quad(e0->sprite_local_xform, quad_from_rect(slice.rect));
|
||||
e0_quad = xform_mul_quad(xf1, e0_quad);
|
||||
e0_poly = (struct v2_array) {
|
||||
.count = ARRAY_COUNT(e0_quad.e),
|
||||
.points = e0_quad.e
|
||||
};
|
||||
}
|
||||
|
||||
b32 any_collision = false;
|
||||
b32 colliding = false;
|
||||
struct v2 point0 = V2(0, 0);
|
||||
struct v2 point1 = V2(0, 0);
|
||||
struct entity *colliding_with = entity_nil();
|
||||
struct gjk_simplex simplex = { 0 };
|
||||
struct gjk_prototype prototype = { 0 };
|
||||
struct v2 velocity = V2(0, 0);
|
||||
struct v2 pen = V2(0, 0);
|
||||
b32 solved = false;
|
||||
b32 velocity_intersects = false;
|
||||
(UNUSED)pen;
|
||||
for (u64 e1_index = 0; e1_index < store->reserved; ++e1_index) {
|
||||
struct entity *e1 = &store->entities[e1_index];
|
||||
if (e1 == e0) continue;
|
||||
if (!(e1->valid && entity_has_prop(e1, ENTITY_PROP_ACTIVE))) continue;
|
||||
if (!entity_has_prop(e1, ENTITY_PROP_PHYSICAL)) continue;
|
||||
if (entity_has_prop(e1, ENTITY_PROP_PLAYER_CONTROLLED)) continue;
|
||||
|
||||
struct xform e1_xf = entity_get_xform(e1);
|
||||
struct quad e1_quad;
|
||||
struct v2_array e1_poly;
|
||||
{
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, e1->sprite);
|
||||
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), e1->animation_frame);
|
||||
e1_quad = xform_mul_quad(e1->sprite_local_xform, quad_from_rect(slice.rect));
|
||||
e1_quad = xform_mul_quad(e1_xf, e1_quad);
|
||||
e1_poly = (struct v2_array) {
|
||||
.count = ARRAY_COUNT(e1_quad.e),
|
||||
.points = e1_quad.e
|
||||
};
|
||||
}
|
||||
|
||||
/* Testing */
|
||||
{
|
||||
/* TODO: Continuity gen check */
|
||||
|
||||
velocity = v2_sub(xf1.og, xf0.og);
|
||||
//velocity = V2(0.014992147684, -0.000010356307);
|
||||
//velocity = V2(-0.0001, -0.01);
|
||||
//velocity = V2(0.005, 0);
|
||||
//velocity = V2(-500, -500);
|
||||
|
||||
//velocity = V2(-1, -1);
|
||||
//velocity = v2_neg(velocity);
|
||||
|
||||
|
||||
//velocity = v2_neg(velocity);
|
||||
//xf0.og = v2_sub(xf1.og, velocity);
|
||||
xf1.og = v2_add(xf0.og, velocity);
|
||||
}
|
||||
|
||||
struct gjk_extended_result res = gjk_extended(e0_poly, e1_poly);
|
||||
|
||||
colliding = res.colliding;
|
||||
point0 = res.p0;
|
||||
point1 = res.p1;
|
||||
colliding_with = e1;
|
||||
simplex = res.simplex;
|
||||
prototype = res.prototype;
|
||||
velocity_intersects = res.velocity_intersects;
|
||||
solved = res.solved;
|
||||
|
||||
if (colliding) {
|
||||
any_collision = true;
|
||||
pen = v2_sub(point1, point0);
|
||||
}
|
||||
}
|
||||
|
||||
e0->colliding = colliding;
|
||||
e0->colliding_with = colliding_with->handle;
|
||||
e0->point = point0;
|
||||
e0->simplex = simplex;
|
||||
e0->prototype = prototype;
|
||||
e0->xf0 = xf0;
|
||||
e0->xf1 = xf1;
|
||||
e0->pendir = velocity;
|
||||
e0->velocity_intersects = velocity_intersects;
|
||||
e0->solved = solved;
|
||||
|
||||
if (colliding_with->valid) {
|
||||
colliding_with->colliding = colliding;
|
||||
colliding_with->colliding_with = e0->handle;
|
||||
colliding_with->point = point1;
|
||||
}
|
||||
|
||||
(UNUSED)any_collision;
|
||||
{
|
||||
#if 0
|
||||
if (any_collision) {
|
||||
xf1.og = v2_add(xf1.og, pen);
|
||||
//e0->verlet_xform.og = xf0.og;
|
||||
e0->verlet_xform.og = v2_sub(xf1.og, velocity);
|
||||
}
|
||||
#endif
|
||||
entity_set_xform(e0, xf1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Player aim
|
||||
* ========================== */
|
||||
|
||||
#if 1
|
||||
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;
|
||||
@ -857,7 +652,342 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
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_PLAYER_CONTROLLED)) {
|
||||
struct xform xf = entity_get_xform(ent);
|
||||
|
||||
/* Solve for final angle using law of sines */
|
||||
f32 final_xf_angle;
|
||||
{
|
||||
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("attach.wep"), ent->animation_frame);
|
||||
|
||||
f32 forward_hold_angle_offset;
|
||||
{
|
||||
struct xform xf_unrotated = xform_rotated_to(xf, 0);
|
||||
struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_local_xform, slice.center));
|
||||
forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og));
|
||||
}
|
||||
|
||||
struct v2 ent_pos = xf.og;
|
||||
struct v2 focus_pos = v2_add(ent_pos, ent->control.focus);
|
||||
|
||||
struct v2 hold_pos;
|
||||
{
|
||||
struct xform hold_pos_xf = xform_translated(ent->sprite_local_xform, slice.center);
|
||||
hold_pos_xf = xform_mul(xf, hold_pos_xf);
|
||||
|
||||
struct v2 hold_pos_xf_dir = xform_basis_mul_v2(hold_pos_xf, slice.dir);
|
||||
hold_pos_xf = xform_rotated_to(hold_pos_xf, v2_angle(hold_pos_xf_dir) + PI / 2);
|
||||
|
||||
if (v2_eq(hold_pos_xf.og, ent_pos)) {
|
||||
/* If hold pos is same as origin (E.G if pivot is being used as hold pos), then move hold pos forward a tad to avoid issue */
|
||||
hold_pos_xf = xform_translated(hold_pos_xf, V2(0, -1));
|
||||
}
|
||||
hold_pos = hold_pos_xf.og;
|
||||
}
|
||||
|
||||
struct v2 hold_dir = xform_basis_mul_v2(xf, xform_basis_mul_v2(ent->sprite_local_xform, slice.dir));
|
||||
struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos);
|
||||
struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos);
|
||||
|
||||
f32 hold_ent_len = v2_len(hold_ent_dir);
|
||||
f32 focus_ent_len = v2_len(focus_ent_dir);
|
||||
|
||||
f32 final_hold_angle_btw_ent_and_focus = v2_angle_from_dirs(hold_ent_dir, hold_dir);
|
||||
f32 final_focus_angle_btw_ent_and_hold = math_asin((math_sin(final_hold_angle_btw_ent_and_focus) * hold_ent_len) / focus_ent_len);
|
||||
f32 final_ent_angle_btw_focus_and_hold = PI - (final_focus_angle_btw_ent_and_hold + final_hold_angle_btw_ent_and_focus);
|
||||
|
||||
final_xf_angle = v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - forward_hold_angle_offset;
|
||||
}
|
||||
|
||||
if (!F32_IS_NAN(final_xf_angle)) {
|
||||
const f32 angle_error_allowed = 0.001;
|
||||
f32 cur_angle = v2_angle(xf.bx);
|
||||
f32 diff = final_xf_angle - cur_angle;
|
||||
if (math_fabs(diff) > angle_error_allowed) {
|
||||
|
||||
#if 0
|
||||
/* Create force */
|
||||
struct entity *f = entity_alloc(ent);
|
||||
entity_enable_prop(f, ENTITY_PROP_TORQUE);
|
||||
|
||||
i32 dir = math_fsign(diff);
|
||||
f32 damp = math_fabs(math_fmod(diff, 2 * PI)) / (2 * PI);
|
||||
f32 torque = ent->control_torque * dir * damp;
|
||||
|
||||
f->torque = torque;
|
||||
activate_now(f);
|
||||
#else
|
||||
/* TODO: Remove this (testing) */
|
||||
/* Create force */
|
||||
if (!ent->test_torque_applied) {
|
||||
ent->test_torque_applied = true;
|
||||
struct entity *f = entity_alloc(ent);
|
||||
entity_enable_prop(f, ENTITY_PROP_ANGULAR_IMPULSE);
|
||||
f->torque = 10;
|
||||
activate_now(f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========================== *
|
||||
* Create ground friction force
|
||||
* ========================== */
|
||||
|
||||
/* TODO: Do this globally rather than creating entities for constant forces */
|
||||
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;
|
||||
if (ent->ground_friction != 0) {
|
||||
#if 1
|
||||
/* Linear velocity */
|
||||
{
|
||||
struct v2 linear_velocity = ent->linear_velocity;
|
||||
if (!v2_eq(linear_velocity, V2(0, 0))) {
|
||||
/* FIXME: Incorrect behavior at low FPS & low entity density */
|
||||
const f32 clamp_epsilon = 0.01;
|
||||
f32 linear_velocity_len = v2_len(linear_velocity);
|
||||
if (linear_velocity_len >= clamp_epsilon) {
|
||||
f32 force_len = -linear_velocity_len * ent->ground_friction;
|
||||
struct entity *force = entity_alloc(ent);
|
||||
entity_enable_prop(force, ENTITY_PROP_FORCE);
|
||||
force->force = v2_mul(v2_norm(linear_velocity), force_len);
|
||||
activate_now(force);
|
||||
} else {
|
||||
/* If linear_velocity is below clamp_epsilon, stop entity movement. */
|
||||
struct xform xf = entity_get_xform(ent);
|
||||
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
struct entity *impulse = entity_alloc(ent);
|
||||
entity_enable_prop(impulse, ENTITY_PROP_LINEAR_IMPULSE);
|
||||
impulse->force = v2_mul(v2_neg(linear_velocity), mass);
|
||||
activate_now(impulse);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Angular velocity */
|
||||
{
|
||||
f32 angular_velocity = ent->angular_velocity;
|
||||
if (angular_velocity != 0) {
|
||||
/* FIXME: Incorrect behavior at low FPS & low entity density */
|
||||
const f32 clamp_epsilon = 0.001;
|
||||
if (math_fabs(angular_velocity) >= clamp_epsilon) {
|
||||
struct entity *torque = entity_alloc(ent);
|
||||
entity_enable_prop(torque, ENTITY_PROP_TORQUE);
|
||||
/* TODO: Different ground friction for rotation */
|
||||
torque->torque = -angular_velocity * 3;
|
||||
activate_now(torque);
|
||||
} else {
|
||||
/* If angular_velocity is below clamp_epsilon, stop entity movement. */
|
||||
struct xform xf = entity_get_xform(ent);
|
||||
f32 inertia = ent->inertia_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
struct entity *impulse = entity_alloc(ent);
|
||||
entity_enable_prop(impulse, ENTITY_PROP_ANGULAR_IMPULSE);
|
||||
impulse->torque = -angular_velocity * inertia;
|
||||
activate_now(impulse);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* TODO: Remove this (gravity test) */
|
||||
struct xform xf = entity_get_xform(ent);
|
||||
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
struct entity *impulse = entity_alloc(ent);
|
||||
entity_enable_prop(impulse, ENTITY_PROP_FORCE);
|
||||
impulse->force = v2_mul(V2(0, 9.81), mass);
|
||||
activate_now(impulse);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Integrate forces
|
||||
* ========================== */
|
||||
|
||||
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 acceleration from forces and impulses */
|
||||
struct v2 tick_linear_acceleration = V2(0, 0);
|
||||
f32 tick_angular_acceleration = 0;
|
||||
for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) {
|
||||
if (entity_has_prop(child, ENTITY_PROP_LINEAR_IMPULSE)) {
|
||||
tick_linear_acceleration = v2_add(tick_linear_acceleration, child->force);
|
||||
entity_enable_prop(child, ENTITY_PROP_RELEASE);
|
||||
} else if (entity_has_prop(child, ENTITY_PROP_FORCE)) {
|
||||
tick_linear_acceleration = v2_add(tick_linear_acceleration, v2_mul(child->force, dt));
|
||||
entity_enable_prop(child, ENTITY_PROP_RELEASE);
|
||||
} else if (entity_has_prop(child, ENTITY_PROP_ANGULAR_IMPULSE)) {
|
||||
tick_angular_acceleration += child->torque;
|
||||
entity_enable_prop(child, ENTITY_PROP_RELEASE);
|
||||
} else if (entity_has_prop(child, ENTITY_PROP_TORQUE)) {
|
||||
tick_angular_acceleration += child->torque * dt;
|
||||
entity_enable_prop(child, ENTITY_PROP_RELEASE);
|
||||
}
|
||||
}
|
||||
tick_linear_acceleration = v2_div(tick_linear_acceleration, mass);
|
||||
tick_angular_acceleration = tick_angular_acceleration / inertia;
|
||||
|
||||
/* Integrate */
|
||||
struct v2 tick_linear_velocity = v2_add(v2_mul(ent->linear_velocity, dt), v2_mul(tick_linear_acceleration, dt));
|
||||
f32 tick_angular_velocity = (ent->angular_velocity * dt) + (tick_angular_acceleration * dt);
|
||||
xf.og = v2_add(xf.og, tick_linear_velocity);
|
||||
xf = xform_rotated(xf, tick_angular_velocity);
|
||||
ent->predicted_xform = xf;
|
||||
ent->linear_velocity = v2_div(tick_linear_velocity, dt);
|
||||
ent->angular_velocity = tick_angular_velocity / dt;
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Release impulses
|
||||
* ========================== */
|
||||
|
||||
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;
|
||||
|
||||
/* TODO: Easier checking for single entity with multiple properties */
|
||||
if (entity_has_prop(ent, ENTITY_PROP_LINEAR_IMPULSE) || entity_has_prop(ent, ENTITY_PROP_ANGULAR_IMPULSE) || entity_has_prop(ent, ENTITY_PROP_FORCE) || entity_has_prop(ent, ENTITY_PROP_TORQUE)) {
|
||||
entity_enable_prop(ent, ENTITY_PROP_RELEASE);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Collision
|
||||
* ========================== */
|
||||
|
||||
for (u64 e0_index = 0; e0_index < store->reserved; ++e0_index) {
|
||||
struct entity *e0 = &store->entities[e0_index];
|
||||
if (!(e0->valid && entity_has_prop(e0, ENTITY_PROP_ACTIVE))) continue;
|
||||
if (!entity_has_prop(e0, ENTITY_PROP_PHYSICAL)) continue;
|
||||
if (!entity_has_prop(e0, ENTITY_PROP_PLAYER_CONTROLLED) && !entity_has_prop(e0, ENTITY_PROP_BULLET)) continue;
|
||||
|
||||
struct xform xf0 = entity_get_xform(e0);
|
||||
struct xform xf1 = e0->predicted_xform;
|
||||
struct quad e0_quad;
|
||||
struct v2_array e0_poly;
|
||||
{
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, e0->sprite);
|
||||
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), e0->animation_frame);
|
||||
e0_quad = xform_mul_quad(e0->sprite_local_xform, quad_from_rect(slice.rect));
|
||||
e0_quad = xform_mul_quad(xf1, e0_quad);
|
||||
e0_poly = (struct v2_array) {
|
||||
.count = ARRAY_COUNT(e0_quad.e),
|
||||
.points = e0_quad.e
|
||||
};
|
||||
}
|
||||
|
||||
b32 any_collision = false;
|
||||
b32 colliding = false;
|
||||
struct v2 point0 = V2(0, 0);
|
||||
struct v2 point1 = V2(0, 0);
|
||||
struct entity *colliding_with = entity_nil();
|
||||
struct gjk_simplex simplex = { 0 };
|
||||
struct gjk_prototype prototype = { 0 };
|
||||
struct v2 velocity = V2(0, 0);
|
||||
struct v2 pen = V2(0, 0);
|
||||
b32 solved = false;
|
||||
(UNUSED)pen;
|
||||
for (u64 e1_index = 0; e1_index < store->reserved; ++e1_index) {
|
||||
struct entity *e1 = &store->entities[e1_index];
|
||||
if (e1 == e0) continue;
|
||||
if (!(e1->valid && entity_has_prop(e1, ENTITY_PROP_ACTIVE))) continue;
|
||||
if (!entity_has_prop(e1, ENTITY_PROP_PHYSICAL)) continue;
|
||||
if (entity_has_prop(e1, ENTITY_PROP_PLAYER_CONTROLLED)) continue;
|
||||
|
||||
struct xform e1_xf = entity_get_xform(e1);
|
||||
struct quad e1_quad;
|
||||
struct v2_array e1_poly;
|
||||
{
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, e1->sprite);
|
||||
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), e1->animation_frame);
|
||||
e1_quad = xform_mul_quad(e1->sprite_local_xform, quad_from_rect(slice.rect));
|
||||
e1_quad = xform_mul_quad(e1_xf, e1_quad);
|
||||
e1_poly = (struct v2_array) {
|
||||
.count = ARRAY_COUNT(e1_quad.e),
|
||||
.points = e1_quad.e
|
||||
};
|
||||
}
|
||||
|
||||
/* Testing */
|
||||
{
|
||||
/* TODO: Continuity gen check */
|
||||
|
||||
velocity = v2_sub(xf1.og, xf0.og);
|
||||
//velocity = V2(0.014992147684, -0.000010356307);
|
||||
//velocity = V2(-0.0001, -0.01);
|
||||
//velocity = V2(0.005, 0);
|
||||
//velocity = V2(-500, -500);
|
||||
|
||||
//velocity = V2(-1, -1);
|
||||
//velocity = v2_neg(velocity);
|
||||
|
||||
|
||||
//velocity = v2_neg(velocity);
|
||||
//xf0.og = v2_sub(xf1.og, velocity);
|
||||
xf1.og = v2_add(xf0.og, velocity);
|
||||
}
|
||||
|
||||
struct gjk_extended_result res = gjk_extended(e0_poly, e1_poly);
|
||||
|
||||
colliding = res.colliding;
|
||||
point0 = res.p0;
|
||||
point1 = res.p1;
|
||||
colliding_with = e1;
|
||||
simplex = res.simplex;
|
||||
prototype = res.prototype;
|
||||
solved = res.solved;
|
||||
|
||||
if (colliding) {
|
||||
any_collision = true;
|
||||
pen = v2_sub(point1, point0);
|
||||
}
|
||||
}
|
||||
|
||||
e0->colliding = colliding;
|
||||
e0->colliding_with = colliding_with->handle;
|
||||
e0->point = point0;
|
||||
e0->simplex = simplex;
|
||||
e0->prototype = prototype;
|
||||
e0->xf0 = xf0;
|
||||
e0->xf1 = xf1;
|
||||
e0->pendir = velocity;
|
||||
e0->solved = solved;
|
||||
|
||||
if (colliding_with->valid) {
|
||||
colliding_with->colliding = colliding;
|
||||
colliding_with->colliding_with = e0->handle;
|
||||
colliding_with->point = point1;
|
||||
}
|
||||
|
||||
(UNUSED)any_collision;
|
||||
{
|
||||
#if 0
|
||||
if (any_collision) {
|
||||
xf1.og = v2_add(xf1.og, pen);
|
||||
//e0->verlet_xform.og = xf0.og;
|
||||
e0->verlet_xform.og = v2_sub(xf1.og, velocity);
|
||||
}
|
||||
#endif
|
||||
entity_set_xform(e0, xf1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Initialize bullet kinematics from sources
|
||||
@ -884,7 +1014,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
/* Create impulse */
|
||||
struct entity *impulse = entity_alloc(ent);
|
||||
impulse->force = velocity;
|
||||
entity_enable_prop(impulse, ENTITY_PROP_IMPULSE);
|
||||
entity_enable_prop(impulse, ENTITY_PROP_LINEAR_IMPULSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,6 @@ struct gjk_extended_result {
|
||||
/* For debugging */
|
||||
b32 solved;
|
||||
struct gjk_simplex simplex;
|
||||
b32 velocity_intersects;
|
||||
struct gjk_prototype prototype;
|
||||
};
|
||||
|
||||
|
||||
14
src/user.c
14
src/user.c
@ -993,7 +993,7 @@ INTERNAL void user_update(void)
|
||||
(UNUSED)e1;
|
||||
(UNUSED)colliding;
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
/* Create shapes */
|
||||
struct quad ent_quad_xf0;
|
||||
struct v2_array ent_poly_xf0;
|
||||
@ -1082,7 +1082,7 @@ INTERNAL void user_update(void)
|
||||
/* Draw pendir */
|
||||
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
|
||||
f32 thickness = 2;
|
||||
u32 color = ent->velocity_intersects ? COLOR_RED : COLOR_YELLOW;
|
||||
u32 color = COLOR_YELLOW;
|
||||
|
||||
struct v2 start = G.world_view.og;
|
||||
struct v2 ray = xform_basis_mul_v2(G.world_view, ent->pendir);
|
||||
@ -1136,7 +1136,7 @@ INTERNAL void user_update(void)
|
||||
|
||||
/* Draw point */
|
||||
{
|
||||
u32 color = entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED) ? RGBA_32_F(1, 0, 0, 0.5) : RGBA_32_F(0, 1, 1, 0.5);
|
||||
u32 color = entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED) ? RGBA_32_F(1, 0, 0, 0.75) : RGBA_32_F(0, 1, 1, 0.75);
|
||||
f32 radius = 5;
|
||||
struct v2 point = xform_mul_v2(G.world_view, ent->point);
|
||||
draw_solid_circle(G.viewport_canvas, point, radius, color, 10);
|
||||
@ -1323,8 +1323,12 @@ INTERNAL void user_update(void)
|
||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("debug_camera: %F"), FMT_STR(G.debug_camera ? STR("true") : STR("false"))));
|
||||
pos.y += spacing;
|
||||
|
||||
struct v2 player_collision_vel = entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)->pendir;
|
||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("player collision velocity: (%F, %F)"), FMT_FLOAT_P((f64)player_collision_vel.x, 12), FMT_FLOAT_P((f64)player_collision_vel.y, 12)));
|
||||
struct v2 player_linear_vel = entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)->linear_velocity;
|
||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("player linear velocity: (%F, %F)"), FMT_FLOAT_P((f64)player_linear_vel.x, 12), FMT_FLOAT_P((f64)player_linear_vel.y, 12)));
|
||||
pos.y += spacing;
|
||||
|
||||
f32 player_angular_vel = entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)->angular_velocity;
|
||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("player angular velocity: %F"), FMT_FLOAT_P((f64)player_angular_vel, 12)));
|
||||
pos.y += spacing;
|
||||
|
||||
struct v2 xf0_pos = entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)->xf0.og;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user