From e3055062e633f23103d629848489131d576b8c58 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 10 Sep 2024 16:11:39 -0500 Subject: [PATCH] angular velocity testing --- src/entity.c | 2 + src/entity.h | 30 ++- src/game.c | 560 +++++++++++++++++++++++++++++++-------------------- src/gjk.h | 1 - src/user.c | 14 +- 5 files changed, 378 insertions(+), 229 deletions(-) diff --git a/src/entity.c b/src/entity.c index 52fa322f..aee2263e 100644 --- a/src/entity.c +++ b/src/entity.c @@ -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 }; diff --git a/src/entity.h b/src/entity.h index 1482a5ae..07a37ce7 100644 --- a/src/entity.h +++ b/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 */ diff --git a/src/game.c b/src/game.c index 081bfa7a..87dd593b 100644 --- a/src/game.c +++ b/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); } } diff --git a/src/gjk.h b/src/gjk.h index 17f1f7b2..c0e94f7d 100644 --- a/src/gjk.h +++ b/src/gjk.h @@ -29,7 +29,6 @@ struct gjk_extended_result { /* For debugging */ b32 solved; struct gjk_simplex simplex; - b32 velocity_intersects; struct gjk_prototype prototype; }; diff --git a/src/user.c b/src/user.c index 6dc2de5d..7b2c8fb6 100644 --- a/src/user.c +++ b/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;