diff --git a/src/config.h b/src/config.h index 85fc1ba5..2c2b3621 100644 --- a/src/config.h +++ b/src/config.h @@ -38,7 +38,7 @@ * E.g: At 1.5, the user thread will render 75ms back in time (if game thread runs at 50FPS) */ #define USER_INTERP_OFFSET_TICK_RATIO 1.1 -#define USER_INTERP_ENABLED 1 +#define USER_INTERP_ENABLED 0 /* ========================== * * Settings diff --git a/src/entity.h b/src/entity.h index 8008c29e..65ac76ac 100644 --- a/src/entity.h +++ b/src/entity.h @@ -312,6 +312,11 @@ INLINE b32 entity_has_prop(struct entity *ent, enum entity_prop prop) return !!(ent->props[index] & ((u64)1 << bit)); } +INLINE b32 entity_is_valid_and_active(struct entity *ent) +{ + return ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE); +} + /* ========================== * * Entity functions * ========================== */ diff --git a/src/game.c b/src/game.c index 27d845e0..b097f3db 100644 --- a/src/game.c +++ b/src/game.c @@ -251,24 +251,6 @@ INTERNAL void spawn_test_entities(void) * TESTING * ========================== */ -INTERNAL void pre_step(void) -{ - struct entity_store *store = G.tick.entity_store; - - /* TODO: Just do this during create contact manifolds call if both are outside of substep */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *manifold = &store->entities[entity_index]; - if (!(manifold->valid && entity_has_prop(manifold, ENTITY_PROP_ACTIVE))) continue; - if (!entity_has_prop(manifold, ENTITY_PROP_MANIFOLD)) continue; - - for (u32 i = 0; i < manifold->num_contacts; ++i) { - struct contact *contact = &manifold->contacts[i]; - contact->accumulated_impulse = 0; - } - } -} - - INTERNAL void create_contact_manifolds(void) @@ -284,7 +266,7 @@ INTERNAL void create_contact_manifolds(void) struct entity *root = G.root; 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_is_valid_and_active(e0)) continue; if (!entity_has_prop(e0, ENTITY_PROP_PHYSICAL)) continue; /* Calculate entity 0 shape */ @@ -305,7 +287,7 @@ INTERNAL void create_contact_manifolds(void) 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_is_valid_and_active(e1)) continue; if (!entity_has_prop(e1, ENTITY_PROP_PHYSICAL)) continue; /* TODO: Remove this (temporary stop to prevent double-manifold creation) */ @@ -332,14 +314,18 @@ INTERNAL void create_contact_manifolds(void) manifold_key = STRING_FROM_BUFFER(BUFFER_FROM_STRUCT(&manifold_hash)); } - struct entity *manifold = entity_nil(); + struct entity *manifold = NULL; struct entity_handle *entry = fixed_dict_get(&dict, manifold_key); if (entry) { - manifold = entity_from_handle(store, *entry); + struct entity *t = entity_from_handle(store, *entry); + if (entity_is_valid_and_active(t)) { + manifold = t; + } } + /* Ensure manifold hasn't already been computed this iteration */ - if (manifold->valid) { + if (manifold) { if (manifold->last_manifold_iteration == manifold_iteration) { /* Already iterated this manifold from The other entity's perspective, skip */ continue; @@ -369,14 +355,14 @@ INTERNAL void create_contact_manifolds(void) CT_ASSERT(ARRAY_COUNT(res.points) == 2); /* TODO: Remove this (debugging) */ - if (manifold->valid) { + if (manifold) { manifold->prototype = res.prototype; manifold->simplex = res.simplex; manifold->solved = res.solved; } if (res.num_points > 0) { - if (!manifold->valid) { + if (!manifold) { manifold = entity_alloc(root); manifold->manifold_e0 = e0->handle; manifold->manifold_e1 = e1->handle; @@ -428,32 +414,33 @@ INTERNAL void create_contact_manifolds(void) f32 sep = res_point->separation; u32 id = res_point->id; struct contact *contact = NULL; - { - for (u32 j = 0; j < manifold->num_contacts; ++j) { - struct contact *t = &manifold->contacts[j]; - if (t->id == id) { - /* Update existing */ - contact = t; - contact->persisted = true; - break; - } - } - if (!contact) { - /* Insert new */ - contact = &manifold->contacts[manifold->num_contacts++]; - MEMZERO_STRUCT(contact); - contact->id = id; + /* Match */ + for (u32 j = 0; j < manifold->num_contacts; ++j) { + struct contact *t = &manifold->contacts[j]; + if (t->id == id) { + contact = t; + break; } } + if (contact) { + /* Update existing */ + contact->accumulated_impulse = 0; + contact->persisted = true; + } else { + /* Insert new */ + contact = &manifold->contacts[manifold->num_contacts++]; + MEMZERO_STRUCT(contact); + contact->id = id; + } contact->point_local_e0 = xform_invert_mul_v2(e0_xf, point); contact->point_local_e1 = xform_invert_mul_v2(e1_xf, point); contact->starting_separation = sep; } - } else if (manifold->valid) { + } else if (manifold) { /* No longer colliding, delete manifold */ -#if 0 +#if 1 manifold->num_contacts = 0; entity_enable_prop(manifold, ENTITY_PROP_RELEASE); #else @@ -470,6 +457,45 @@ INTERNAL void create_contact_manifolds(void) +INTERNAL void warm_start_contacts(f32 dt) +{ + struct entity_store *store = G.tick.entity_store; + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *manifold = &store->entities[entity_index]; + if (!entity_is_valid_and_active(manifold)) continue; + if (!entity_has_prop(manifold, ENTITY_PROP_MANIFOLD)) continue; + + struct entity *e0 = entity_from_handle(store, manifold->manifold_e0); + struct entity *e1 = entity_from_handle(store, manifold->manifold_e1); + + if (entity_is_valid_and_active(e0) && entity_is_valid_and_active(e1)) { + struct v2 normal = manifold->manifold_normal; + struct xform e0_xf = entity_get_xform(e0); + struct xform e1_xf = entity_get_xform(e1); + + + /* Warm start */ + for (u32 i = 0; i < manifold->num_contacts; ++i) { + struct contact *contact = &manifold->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); + f32 separation = v2_dot(v2_sub(p1, p0), normal) + contact->starting_separation; + + (UNUSED)p0; + (UNUSED)p1; + (UNUSED)separation; + (UNUSED)dt; + + contact->accumulated_impulse = 0; + } + } + + } +} + + INTERNAL void solve_collisions(f32 dt, b32 apply_bias) @@ -478,7 +504,7 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) struct entity_store *store = G.tick.entity_store; for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *manifold = &store->entities[entity_index]; - if (!(manifold->valid && entity_has_prop(manifold, ENTITY_PROP_ACTIVE))) continue; + if (!entity_is_valid_and_active(manifold)) continue; if (!entity_has_prop(manifold, ENTITY_PROP_MANIFOLD)) continue; struct entity *e0 = entity_from_handle(store, manifold->manifold_e0); @@ -486,8 +512,8 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) u32 num_contacts = manifold->num_contacts; f32 inv_num_contacts = 1.f / num_contacts; - - if (num_contacts > 0 && e0->valid && e1->valid) { + (UNUSED)inv_num_contacts; + if (num_contacts > 0 && entity_is_valid_and_active(e0) && entity_is_valid_and_active(e1)) { struct xform e0_xf = entity_get_xform(e0); struct xform e1_xf = entity_get_xform(e1); @@ -519,11 +545,11 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) f32 bias = 0; if (apply_bias) { //f32 bias_factor = 0.2f; - f32 bias_factor = 0.2f; + f32 bias_factor = 0.1f; //f32 bias_factor = 0.025f; //f32 bias_slop = 0.0005f; - f32 bias_slop = 0.001f; + f32 bias_slop = 0.005f; //f32 bias_slop = 0.00f; bias = (bias_factor / dt) * max_f32((separation - bias_slop), 0); @@ -547,7 +573,6 @@ INTERNAL void solve_collisions(f32 dt, b32 apply_bias) /* (to be applied along n) */ f32 j = (vn + bias) / k; j *= inv_num_contacts; /* TODO: Is this the correct place to do this? */ - (UNUSED)inv_num_contacts; f32 old_accumulated_impulse = contact->accumulated_impulse; f32 new_accumulated_impulse = max_f32(contact->accumulated_impulse + j, 0); @@ -584,7 +609,7 @@ INTERNAL void integrate_positions(f32 dt) 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_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue; struct xform xf = entity_get_xform(ent); @@ -726,7 +751,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_TRIGGER_NEXT_TICK)) { entity_disable_prop(ent, ENTITY_PROP_TRIGGER_NEXT_TICK); @@ -742,7 +767,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (sprite_tag_is_nil(ent->sprite)) continue; /* Update animation */ @@ -794,7 +819,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_ATTACHED)) continue; struct entity *parent = entity_from_handle(store, ent->parent); @@ -819,7 +844,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { /* Process cmds */ @@ -880,7 +905,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_TEST)) continue; #if 0 @@ -935,11 +960,11 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED)) { struct entity *eq = entity_from_handle(store, ent->equipped); - if (eq->valid) { + if (entity_is_valid_and_active(eq)) { entity_enable_prop(eq, ENTITY_PROP_TRIGGERED_THIS_TICK); } } @@ -951,7 +976,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK)) continue; if ((time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; @@ -994,7 +1019,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) #if 0 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_is_valid_and_active(ent)) continue; struct v2 move = ent->control.move; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { @@ -1007,7 +1032,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) #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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { struct v2 move = ent->control.move; @@ -1024,7 +1049,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) #if 0 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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { struct xform xf = entity_get_xform(ent); @@ -1087,7 +1112,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) #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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { struct xform xf = entity_get_xform(ent); @@ -1178,7 +1203,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) /* 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_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue; #if 1 if (ent->linear_ground_friction != 0) { @@ -1238,7 +1263,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue; struct xform xf = entity_get_xform(ent); @@ -1265,22 +1290,23 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) { create_contact_manifolds(); - pre_step(); + //warm_start_contacts(dt); (UNUSED)create_contact_manifolds; (UNUSED)solve_collisions; (UNUSED)integrate_positions; + (UNUSED)warm_start_contacts; f32 substep_dt = dt / GAME_PHYSICS_SUBSTEPS; for (u32 i = 0; i < GAME_PHYSICS_SUBSTEPS; ++i) { - #if 1 +#if 1 solve_collisions(substep_dt, true); integrate_positions(substep_dt); solve_collisions(substep_dt, false); /* Relaxation */ - #else +#else solve_collisions(substep_dt, true); integrate_positions(substep_dt); - #endif +#endif } } @@ -1290,7 +1316,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_is_valid_and_active(ent)) continue; /* FIXME: Apply src entity velocity to bullet velocity */ @@ -1317,7 +1343,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) #if 0 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_is_valid_and_active(ent)) continue; /* Camera follow */ if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { @@ -1354,7 +1380,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) #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_is_valid_and_active(ent)) continue; /* Camera follow */ if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { @@ -1395,13 +1421,13 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) * Update sound emitters * ========================== */ - /* TODO: Sound entities should be created by game thread, but played by the - * user thread. This is so sounds play at the correct time on the user - * thread regardless of interp delay. */ + /* TODO: Sound entities should be created by game thread, but played by the + * user thread. This is so sounds play at the correct time on the user + * thread regardless of interp delay. */ 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_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_TEST_SOUND_EMITTER)) { struct mixer_desc desc = ent->sound_desc; diff --git a/src/gjk.c b/src/gjk.c index e825a132..f057fbbc 100644 --- a/src/gjk.c +++ b/src/gjk.c @@ -356,7 +356,7 @@ struct gjk_contact_points_result gjk_contact_points(struct v2_array shape0, stru DBGSTEP; { - const f32 wedge_epsilon = 0.001; + const f32 wedge_epsilon = 0.001f; /* shape0 a -> b winding = clockwise */ u32 id_a0; diff --git a/src/user.c b/src/user.c index ba3cabd0..46bd3c5c 100644 --- a/src/user.c +++ b/src/user.c @@ -1135,7 +1135,7 @@ INTERNAL void user_update(void) /* Draw normal */ { u32 color = COLOR_WHITE; - f32 len = 0.1; + f32 len = 0.1f; f32 arrow_thickness = 2; f32 arrow_height = 5; struct v2 start = xform_mul_v2(G.world_view, point); @@ -1155,8 +1155,8 @@ INTERNAL void user_update(void) ); struct string text = string_format(temp.arena, fmt, FMT_HEX(contact.id), - FMT_FLOAT(contact.accumulated_impulse), - FMT_FLOAT(contact.starting_separation)); + FMT_FLOAT_P(contact.accumulated_impulse, 3), + FMT_FLOAT_P(contact.starting_separation, 3)); draw_text(G.viewport_canvas, disp_font, v2_add(v2_round(xform_mul_v2(G.world_view, point)), V2(0, offset_px)), text); @@ -1267,8 +1267,7 @@ INTERNAL void user_update(void) input_move_dir = v2_mul(v2_norm(input_move_dir), move_speed); } struct v2 input_aim_pos = G.world_cursor; - //if (!G.debug_camera) { - { + if (!G.debug_camera) { queue_game_cmd(scratch.arena, &cmd_list, (struct game_cmd) { .kind = GAME_CMD_KIND_PLAYER_MOVE, .move_dir = input_move_dir,