From cdc876e71f8d6c30794a0db4e42ffe1e2a0b94dd Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 19 Jan 2026 01:08:59 -0600 Subject: [PATCH] prediction testing --- src/pp/pp.c | 540 ++++++++++++++++++++++++++---------- src/pp/pp.h | 13 + src/pp/pp_sim/pp_sim_core.c | 21 +- src/pp/pp_vis/pp_vis_core.c | 39 ++- 4 files changed, 449 insertions(+), 164 deletions(-) diff --git a/src/pp/pp.c b/src/pp/pp.c index 342eab7f..ce9d5a40 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -1464,6 +1464,13 @@ void P_StepFrame(P_Frame *frame) } } + ////////////////////////////// + //- Query ents + + b32 is_predicting = P_tl.is_predicting; + P_Ent *local_player = P_EntFromKey(frame, P_tl.local_player); + P_Ent *local_guy = P_EntFromKey(frame, local_player->guy); + ////////////////////////////// //- Update double-buffered entity data @@ -1518,6 +1525,7 @@ void P_StepFrame(P_Frame *frame) for (P_Ent *guy = P_FirstEnt(frame); !P_IsEntNil(guy); guy = P_NextEnt(guy)) { + // if (guy->is_guy && (!is_predicting || guy == local_guy)) if (guy->is_guy) { P_Control control = guy->control; @@ -1570,22 +1578,205 @@ void P_StepFrame(P_Frame *frame) } - ////////////////////////////// - //- Generate guy-on-guy constraints - - - - - - - // TODO: Not like this - + //- Copy previous frame's constraints frame->constraints_count = MinI64(prev_frame->constraints_count, frame->max_constraints); CopyStructs(frame->constraints, prev_frame->constraints, frame->constraints_count); + i32 solver_steps_count = 4; + f32 solver_dt = sim_dt / solver_steps_count; + // Solid params + SoftSpring solid_spring = MakeSpring(TweakFloat("Contact spring hz", 25, 5, 100), TweakFloat("Contact spring damp", 10, 5, 100), solver_dt); + f32 solid_pushout_velocity = TweakFloat("Contact spring pushout", 3, 0, 50); + + // Gentle params + f32 gentle_pushout_factor = TweakFloat("Gentle pushout factor", 10, 0, 50); + + + + + + + + + + + // ////////////////////////////// + // //- Generate guy-on-guy constraints + + + + // // TODO: Not like this + + // for (P_Ent *ent0 = P_FirstEnt(frame); !P_IsEntNil(ent0); ent0 = P_NextEnt(ent0)) + // { + // if (ent0->is_guy) + // { + // P_Shape shape0 = P_WorldShapeFromEnt(ent0); + // for (P_Ent *ent1 = P_FirstEnt(frame); !P_IsEntNil(ent1); ent1 = P_NextEnt(ent1)) + // { + // if (ent1->is_guy && ent1->key.v > ent0->key.v) + // { + // P_Shape shape1 = P_WorldShapeFromEnt(ent1); + + // // TODO: World query + // P_CollisionResult collision = P_CollisionResultFromShapes(shape0, shape1); + // if (collision.collision_points_count > 0) + // { + // // FIXME: Key lookup + // P_Constraint *constraint = 0; + // { + // b32 match = 0; + // for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) + // { + // constraint = &frame->constraints[constraint_idx]; + // if (P_MatchKey(constraint->ent0, ent0->key) && P_MatchKey(constraint->ent1, ent1->key)) + // { + // match = 1; + // break; + // } + // } + // if (!match) + // { + // if (frame->constraints_count < frame->max_constraints) + // { + // constraint = &frame->constraints[frame->constraints_count]; + // frame->constraints_count += 1; + // ZeroStruct(constraint); + // } + // } + // } + // if (constraint) + // { + // constraint->flags = P_ConstraintFlag_Gentle | P_ConstraintFlag_NoWarmStart; + // constraint->last_touched_tick = frame->tick; + // constraint->normal = collision.collision_normal; + // // constraint->friction = SqrtF32(ent0->friction * ent1->friction); + // constraint->friction = 0; + + // // TODO: Real masses + // f32 inv_m0 = 10; + // f32 inv_m1 = 10; + // f32 inv_i0 = 0; + // f32 inv_i1 = 0; + + // // Treat non-predicted guys as infinite-mass + // // if (is_predicting && ent0 != local_guy) + // // { + // // inv_m0 = 0; + // // } + // // if (is_predicting && ent1 != local_guy) + // // { + // // inv_m1 = 0; + // // } + + // constraint->ent0 = ent0->key; + // constraint->ent1 = ent1->key; + // // constraint->static_center1 = shape1.center_of_mass; + + // constraint->inv_m0 = inv_m0; + // constraint->inv_m1 = inv_m1; + // constraint->inv_i0 = inv_i0; + // constraint->inv_i1 = inv_i1; + + // // Delete old contacts that are no longer present + // for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) + // { + // P_ContactPoint *contact = &constraint->points[contact_point_idx]; + // u32 id = contact->id; + // b32 match = 0; + // for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) + // { + // if (collision.collision_points[collision_point_idx].id == id) + // { + // match = 1; + // break; + // } + // } + // if (!match) + // { + // // Delete contact by replacing with last in array + // *contact = constraint->points[constraint->points_count - 1]; + // constraint->points_count -= 1; + // contact_point_idx -= 1; + // } + // } + + // // Create / update contacts from collision + // for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) + // { + // P_CollisionPoint collision_point = collision.collision_points[collision_point_idx]; + + // u32 id = collision_point.id; + // P_ContactPoint *contact = 0; + // { + // for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) + // { + // P_ContactPoint *tmp = &constraint->points[contact_point_idx]; + // if (tmp->id == id) + // { + // contact = tmp; + // break; + // } + // } + // if (!contact) + // { + // contact = &constraint->points[constraint->points_count]; + // constraint->points_count += 1; + // ZeroStruct(contact); + // } + // } + // contact->id = id; + + // Vec2 vcp0 = SubVec2(collision_point.p, shape0.center_of_mass); + // Vec2 vcp1 = SubVec2(collision_point.p, shape1.center_of_mass); + + // contact->vcp0 = vcp0; + // contact->vcp1 = vcp1; + // contact->starting_separation = collision_point.separation; + + // // Debug draw + // // { + // // // P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); + // // // P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); + // // Vec2 normal = constraint->normal; + // // Vec2 center0 = Zi; + // // Vec2 center1 = Zi; + // // if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; + // // if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; + // // Vec2 p0 = AddVec2(center0, vcp0); + // // Vec2 p1 = AddVec2(center1, vcp1); + // // P_DebugDrawPoint(p0, Color_Cyan); + // // P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); + // // } + // } + // } + // } + // } + // } + // } + // } + + + + + + + + + + + + + + ////////////////////////////// + //- Generate solid constraints + + + + // TODO: Not like this for (P_Ent *ent0 = P_FirstEnt(frame); !P_IsEntNil(ent0); ent0 = P_NextEnt(ent0)) { @@ -1594,7 +1785,7 @@ void P_StepFrame(P_Frame *frame) P_Shape shape0 = P_WorldShapeFromEnt(ent0); for (P_Ent *ent1 = P_FirstEnt(frame); !P_IsEntNil(ent1); ent1 = P_NextEnt(ent1)) { - if (ent1->is_guy && ent1 > ent0) + if (ent1->is_guy && ent1->key.v > ent0->key.v) { P_Shape shape1 = P_WorldShapeFromEnt(ent1); @@ -1627,6 +1818,7 @@ void P_StepFrame(P_Frame *frame) } if (constraint) { + constraint->flags = P_ConstraintFlag_Solid; constraint->last_touched_tick = frame->tick; constraint->normal = collision.collision_normal; // constraint->friction = SqrtF32(ent0->friction * ent1->friction); @@ -1638,6 +1830,17 @@ void P_StepFrame(P_Frame *frame) f32 inv_i0 = 0; f32 inv_i1 = 0; + // Treat non-predicted guys as infinite-mass + // if (is_predicting && ent0 != local_guy) + // { + // inv_m0 = 0; + // } + // if (is_predicting && ent1 != local_guy) + // { + // inv_m1 = 0; + // } + + constraint->ent0 = ent0->key; constraint->ent1 = ent1->key; // constraint->static_center1 = shape1.center_of_mass; @@ -1703,20 +1906,20 @@ void P_StepFrame(P_Frame *frame) contact->vcp1 = vcp1; contact->starting_separation = collision_point.separation; - // // Debug draw - // { - // // P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); - // // P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); - // Vec2 normal = constraint->normal; - // Vec2 center0 = Zi; - // Vec2 center1 = Zi; - // if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; - // if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; - // Vec2 p0 = AddVec2(center0, vcp0); - // Vec2 p1 = AddVec2(center1, vcp1); - // P_DebugDrawPoint(p0, Color_Cyan); - // P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); - // } + // Debug draw + { + // P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); + // P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); + Vec2 normal = constraint->normal; + Vec2 center0 = Zi; + Vec2 center1 = Zi; + if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; + if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; + Vec2 p0 = AddVec2(center0, vcp0); + Vec2 p1 = AddVec2(center1, vcp1); + P_DebugDrawPoint(p0, Color_Cyan); + P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); + } } } } @@ -1725,6 +1928,9 @@ void P_StepFrame(P_Frame *frame) } } + + + ////////////////////////////// //- Prune constraints @@ -1761,10 +1967,6 @@ void P_StepFrame(P_Frame *frame) ////////////////////////////// //- Run solver steps - i32 solver_steps_count = 4; - f32 solver_dt = sim_dt / solver_steps_count; - f32 contact_spring_hz = TweakFloat("Contact spring hz", 25, 5, 100); - f32 contact_spring_damp = TweakFloat("Contact spring damp", 10, 5, 100); for (i32 solver_step_idx = 0; solver_step_idx < solver_steps_count; ++solver_step_idx) { ////////////////////////////// @@ -1810,41 +2012,44 @@ void P_StepFrame(P_Frame *frame) for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) { P_Constraint *constraint = &frame->constraints[constraint_idx]; - - P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); - P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); - - Vec2 v0 = ent0->solved_v; - Vec2 v1 = ent1->solved_v; - f32 w0 = ent0->solved_w; - f32 w1 = ent1->solved_w; - - Vec2 normal = constraint->normal; - Vec2 tangent = PerpVec2(normal); - for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) + if (!(constraint->flags & P_ConstraintFlag_NoWarmStart)) { - P_ContactPoint *contact = &constraint->points[contact_idx]; - Vec2 vcp0 = contact->vcp0; - Vec2 vcp1 = contact->vcp1; + P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); + P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); - Vec2 impulse = AddVec2(MulVec2(normal, contact->solved_normal_impulse), MulVec2(tangent, contact->solved_tangent_impulse)); - // impulse = MulVec2(impulse, inv_num_points); + Vec2 v0 = ent0->solved_v; + Vec2 v1 = ent1->solved_v; + f32 w0 = ent0->solved_w; + f32 w1 = ent1->solved_w; - v0 = SubVec2(v0, MulVec2(impulse, constraint->inv_m0)); - v1 = AddVec2(v1, MulVec2(impulse, constraint->inv_m1)); - w0 -= WedgeVec2(vcp0, impulse) * constraint->inv_i0; - w1 += WedgeVec2(vcp1, impulse) * constraint->inv_i1; - } + Vec2 normal = constraint->normal; + Vec2 tangent = PerpVec2(normal); - if (!P_IsEntNil(ent0)) - { - ent0->solved_v = v0; - ent0->solved_w = w0; - } - if (!P_IsEntNil(ent1)) - { - ent1->solved_v = v1; - ent1->solved_w = w1; + for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) + { + P_ContactPoint *contact = &constraint->points[contact_idx]; + Vec2 vcp0 = contact->vcp0; + Vec2 vcp1 = contact->vcp1; + + Vec2 impulse = AddVec2(MulVec2(normal, contact->solved_normal_impulse), MulVec2(tangent, contact->solved_tangent_impulse)); + // impulse = MulVec2(impulse, inv_num_points); + + v0 = SubVec2(v0, MulVec2(impulse, constraint->inv_m0)); + v1 = AddVec2(v1, MulVec2(impulse, constraint->inv_m1)); + w0 -= WedgeVec2(vcp0, impulse) * constraint->inv_i0; + w1 += WedgeVec2(vcp1, impulse) * constraint->inv_i1; + } + + if (!P_IsEntNil(ent0)) + { + ent0->solved_v = v0; + ent0->solved_w = w0; + } + if (!P_IsEntNil(ent1)) + { + ent1->solved_v = v1; + ent1->solved_w = w1; + } } } @@ -1858,6 +2063,9 @@ void P_StepFrame(P_Frame *frame) P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); + Vec2 normal = constraint->normal; + Vec2 tangent = PerpVec2(normal); + f32 inv_m0 = constraint->inv_m0; f32 inv_m1 = constraint->inv_m1; f32 inv_i0 = constraint->inv_i0; @@ -1878,113 +2086,157 @@ void P_StepFrame(P_Frame *frame) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; } - // Normal impulse - Vec2 normal = constraint->normal; - for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) + //- Solve solid constraint + if (constraint->flags & P_ConstraintFlag_Solid) { - P_ContactPoint *contact = &constraint->points[contact_idx]; - Vec2 vcp0 = contact->vcp0; - Vec2 vcp1 = contact->vcp1; - Vec2 p0 = AddVec2(center0, vcp0); - Vec2 p1 = AddVec2(center1, vcp1); - f32 separation = DotVec2(SubVec2(p1, p0), normal) + contact->starting_separation; - - f32 velocity_bias = 0.0; - f32 mass_scale = 1.0; - f32 impulse_scale = 0.0; - - // TODO: Do a relaxation pass without bias - b32 apply_bias = 1; - if (separation > 0.0) + // Normal impulse + for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) { - // Speculative - velocity_bias = separation / solver_dt; - } - else if (apply_bias) - { - // Soft constraint - SoftSpring softness = MakeSpring(contact_spring_hz, contact_spring_damp, solver_dt); - // f32 pushout_velocity = constraint->pushout_velocity; - f32 pushout_velocity = 3.0; - mass_scale = softness.mass_scale; - impulse_scale = softness.impulse_scale; - velocity_bias = MaxF32(softness.bias_rate * separation, -pushout_velocity); + P_ContactPoint *contact = &constraint->points[contact_idx]; + Vec2 vcp0 = contact->vcp0; + Vec2 vcp1 = contact->vcp1; + Vec2 p0 = AddVec2(center0, vcp0); + Vec2 p1 = AddVec2(center1, vcp1); + f32 separation = DotVec2(SubVec2(p1, p0), normal) + contact->starting_separation; + + f32 velocity_bias = 0.0; + f32 mass_scale = 1.0; + f32 impulse_scale = 0.0; + + // TODO: Do a relaxation pass without bias + b32 apply_bias = 1; + if (separation > 0.0) + { + // Speculative + velocity_bias = separation / solver_dt; + } + else if (apply_bias) + { + // Soft constraint + SoftSpring softness = solid_spring; + f32 pushout_velocity = solid_pushout_velocity; + mass_scale = softness.mass_scale; + impulse_scale = softness.impulse_scale; + velocity_bias = MaxF32(softness.bias_rate * separation, -pushout_velocity); + } + + Vec2 vel0 = AddVec2(v0, MulPerpVec2(vcp0, w0)); + Vec2 vel1 = AddVec2(v1, MulPerpVec2(vcp1, w1)); + Vec2 vrel = SubVec2(vel0, vel1); + + f32 k = contact->inv_normal_mass; + + // To be applied along normal + f32 vn = DotVec2(vrel, normal); + f32 j = ((k * mass_scale) * (vn - velocity_bias)) - (contact->solved_normal_impulse * impulse_scale); + + f32 old_impulse = contact->solved_normal_impulse; + f32 new_impulse = MaxF32(old_impulse + j, 0); + f32 delta = new_impulse - old_impulse; + contact->solved_normal_impulse = new_impulse; + + Vec2 impulse = MulVec2(normal, delta); + v0 = SubVec2(v0, MulVec2(impulse, inv_m0)); + v1 = AddVec2(v1, MulVec2(impulse, inv_m1)); + w0 -= WedgeVec2(vcp0, impulse) * inv_i0; + w1 += WedgeVec2(vcp1, impulse) * inv_i1; } - Vec2 vel0 = AddVec2(v0, MulPerpVec2(vcp0, w0)); - Vec2 vel1 = AddVec2(v1, MulPerpVec2(vcp1, w1)); - Vec2 vrel = SubVec2(vel0, vel1); + // Tangent impulse + for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) + { + P_ContactPoint *contact = &constraint->points[contact_idx]; + Vec2 vcp0 = contact->vcp0; + Vec2 vcp1 = contact->vcp1; - f32 k = contact->inv_normal_mass; + Vec2 vel0 = AddVec2(v0, MulPerpVec2(vcp0, w0)); + Vec2 vel1 = AddVec2(v1, MulPerpVec2(vcp1, w1)); + Vec2 vrel = SubVec2(vel0, vel1); - // (to be applied along n) - f32 vn = DotVec2(vrel, normal); - f32 j = ((k * mass_scale) * (vn - velocity_bias)) - (contact->solved_normal_impulse * impulse_scale); + f32 k = contact->inv_tangent_mass; - f32 old_impulse = contact->solved_normal_impulse; - f32 new_impulse = MaxF32(old_impulse + j, 0); - f32 delta = new_impulse - old_impulse; - contact->solved_normal_impulse = new_impulse; + // To be applied along tangent + f32 vt = DotVec2(vrel, tangent); + f32 j = vt * k; - Vec2 impulse = MulVec2(normal, delta); - v0 = SubVec2(v0, MulVec2(impulse, inv_m0)); - v1 = AddVec2(v1, MulVec2(impulse, inv_m1)); - w0 -= WedgeVec2(vcp0, impulse) * inv_i0; - w1 += WedgeVec2(vcp1, impulse) * inv_i1; + f32 max_friction = constraint->friction * contact->solved_normal_impulse; + f32 old_impulse = contact->solved_tangent_impulse; + f32 new_impulse = ClampF32(old_impulse + j, -max_friction, max_friction); + f32 delta = new_impulse - old_impulse; + contact->solved_tangent_impulse = new_impulse; + + Vec2 impulse = MulVec2(tangent, delta); + v0 = SubVec2(v0, MulVec2(impulse, inv_m0)); + v1 = AddVec2(v1, MulVec2(impulse, inv_m1)); + w0 -= WedgeVec2(vcp0, impulse) * inv_i0; + w1 += WedgeVec2(vcp1, impulse) * inv_i1; + } + + if (!P_IsEntNil(ent0)) + { + ent0->solved_v = v0; + ent0->solved_w = w0; + } + if (!P_IsEntNil(ent1)) + { + ent1->solved_v = v1; + ent1->solved_w = w1; + } } - // Tangent impulse - Vec2 tangent = PerpVec2(normal); - for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) + //- Solve gentle constraint + if (constraint->flags & P_ConstraintFlag_Gentle) { - P_ContactPoint *contact = &constraint->points[contact_idx]; - Vec2 vcp0 = contact->vcp0; - Vec2 vcp1 = contact->vcp1; + // Normal impulse + for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) + { + P_ContactPoint *contact = &constraint->points[contact_idx]; + Vec2 vcp0 = contact->vcp0; + Vec2 vcp1 = contact->vcp1; + Vec2 p0 = AddVec2(center0, vcp0); + Vec2 p1 = AddVec2(center1, vcp1); + f32 separation = DotVec2(SubVec2(p1, p0), normal) + contact->starting_separation; - Vec2 vel0 = AddVec2(v0, MulPerpVec2(vcp0, w0)); - Vec2 vel1 = AddVec2(v1, MulPerpVec2(vcp1, w1)); - Vec2 vrel = SubVec2(vel0, vel1); + f32 j = -separation * solver_dt * gentle_pushout_factor; - f32 k = contact->inv_tangent_mass; + f32 old_impulse = contact->solved_normal_impulse; + f32 new_impulse = MaxF32(old_impulse + j, 0); + f32 delta = new_impulse - old_impulse; + contact->solved_normal_impulse = new_impulse; - // (to be applied along t) - f32 vt = DotVec2(vrel, tangent); - f32 j = vt * k; + Vec2 impulse = MulVec2(normal, delta); + v0 = SubVec2(v0, MulVec2(impulse, inv_m0)); + v1 = AddVec2(v1, MulVec2(impulse, inv_m1)); + w0 -= WedgeVec2(vcp0, impulse) * inv_i0; + w1 += WedgeVec2(vcp1, impulse) * inv_i1; + } - f32 max_friction = constraint->friction * contact->solved_normal_impulse; - f32 old_impulse = contact->solved_tangent_impulse; - f32 new_impulse = ClampF32(old_impulse + j, -max_friction, max_friction); - f32 delta = new_impulse - old_impulse; - contact->solved_tangent_impulse = new_impulse; - - Vec2 impulse = MulVec2(tangent, delta); - v0 = SubVec2(v0, MulVec2(impulse, inv_m0)); - v1 = AddVec2(v1, MulVec2(impulse, inv_m1)); - w0 -= WedgeVec2(vcp0, impulse) * inv_i0; - w1 += WedgeVec2(vcp1, impulse) * inv_i1; - } - - if (!P_IsEntNil(ent0)) - { - ent0->solved_v = v0; - ent0->solved_w = w0; - } - if (!P_IsEntNil(ent1)) - { - ent1->solved_v = v1; - ent1->solved_w = w1; + if (!P_IsEntNil(ent0)) + { + ent0->solved_v = v0; + ent0->solved_w = w0; + } + if (!P_IsEntNil(ent1)) + { + ent1->solved_v = v1; + ent1->solved_w = w1; + } } } + ////////////////////////////// //- Integrate velocities for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { - Xform xf = ent->xf; - xf.og = AddVec2(xf.og, MulVec2(ent->solved_v, solver_dt)); - xf = RotateXform(xf, ent->solved_w * solver_dt); - ent->xf = xf; + // if (!is_predicting || ent == local_guy) + { + Xform xf = ent->xf; + xf.og = AddVec2(xf.og, MulVec2(ent->solved_v, solver_dt)); + xf = RotateXform(xf, ent->solved_w * solver_dt); + ent->xf = xf; + } } } diff --git a/src/pp/pp.h b/src/pp/pp.h index fb4a6a09..46da3c42 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -164,6 +164,14 @@ Struct(P_EntBin) //////////////////////////////////////////////////////////// //~ Constraint types +Enum(P_ConstraintFlag) +{ + P_ConstraintFlag_None = 0, + P_ConstraintFlag_Solid = (1 << 0), + P_ConstraintFlag_Gentle = (1 << 1), + P_ConstraintFlag_NoWarmStart = (1 << 2), +}; + Struct(P_ContactPoint) { Vec2 vcp0; @@ -179,6 +187,8 @@ Struct(P_ContactPoint) Struct(P_Constraint) { + P_ConstraintFlag flags; + i64 last_touched_tick; P_Key ent0; P_Key ent1; @@ -388,6 +398,9 @@ Struct(P_Ctx) Struct(P_ThreadLocalCtx) { + b32 is_predicting; + P_Key local_player; + //- Per-thread debug info Arena *debug_arena; b32 debug_draw_enabled; diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index addf1a66..5432c00d 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -431,14 +431,23 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Apply dummy controls - for (P_Ent *dummy = P_FirstEnt(world_frame); !P_IsEntNil(dummy); dummy = P_NextEnt(dummy)) { - if (dummy->is_dummy) + b32 dummy_movement_enabled = TweakBool("Dummy movement enabled", 0); + for (P_Ent *dummy = P_FirstEnt(world_frame); !P_IsEntNil(dummy); dummy = P_NextEnt(dummy)) { - i64 alive_time_ns = time_ns - dummy->created_at_ns; - i64 frequency_ns = NsFromSeconds(0.1); - - dummy->control.move.x = SinF32((f64)alive_time_ns / (f64)frequency_ns); + if (dummy->is_dummy) + { + if (dummy_movement_enabled) + { + i64 alive_time_ns = time_ns - dummy->created_at_ns; + i64 frequency_ns = NsFromSeconds(0.1); + dummy->control.move.x = SinF32((f64)alive_time_ns / (f64)frequency_ns); + } + else + { + ZeroStruct(&dummy->control.move); + } + } } } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index a45b12a1..f4db2532 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -337,8 +337,9 @@ void V_TickForever(WaveLaneCtx *lane) Arena *perm = PermArena(); G_ArenaHandle gpu_perm = G_PermArena(); P_tl.debug_arena = AcquireArena(Gibi(64)); - P_tl.debug_tint = VEC4(0, 0, 1, 0.75); + P_tl.debug_tint = VEC4(0, 0.4, 0.9, 0.75); P_tl.out_msgs_arena = AcquireArena(Gibi(64)); + P_tl.is_predicting = 1; const i32 world_pitch = P_WorldPitch; const f32 zoom_rate = 1.50; @@ -366,11 +367,13 @@ void V_TickForever(WaveLaneCtx *lane) i64 max_local_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND; P_Control *local_controls = PushStructs(perm, P_Control, max_local_controls); - i64 known_sim_tick = 0; i64 remote_ack_received_at_ns = 0; i64 remote_ack = 0; i64 prev_snapshot_sent_at_ns = 0; + i64 prev_known_sim_tick = 0; + i64 known_sim_tick = 0; + Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); @@ -2887,6 +2890,7 @@ void V_TickForever(WaveLaneCtx *lane) if (src_frame->tick == src_tick) { V.player_key = player_key; + P_tl.local_player = player_key; P_Frame *dst_frame = P_FrameFromTick(sim_world, dst_tick); if (P_IsFrameNil(dst_frame)) @@ -3223,6 +3227,15 @@ void V_TickForever(WaveLaneCtx *lane) + i64 first_predict_tick = 0; + i64 last_predict_tick = 0; + { + // FIXME: Not like this + i64 max_predict_ticks = SIM_TICKS_PER_SECOND; + last_predict_tick = frame->predict_to; + first_predict_tick = known_sim_tick - 2; + first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); + } // Predict P_Frame *predict_frame = &P_NilFrame; @@ -3234,22 +3247,12 @@ void V_TickForever(WaveLaneCtx *lane) } predict_world->seed = sim_world->seed; - - - // FIXME: Not like this - i64 max_predict_ticks = SIM_TICKS_PER_SECOND; - - i64 last_predict_tick = frame->predict_to; - i64 first_predict_tick = known_sim_tick - 2; - first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); - P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick); for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) { predict_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_tick); - P_Ent *predict_player = P_EntFromKey(predict_frame, local_player->key); if (!P_IsEntNil(predict_player)) { @@ -3259,10 +3262,18 @@ void V_TickForever(WaveLaneCtx *lane) predict_player->control = *predict_control; } } + // for (P_Ent *ent = P_FirstEnt(predict_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + // { + // if (ent != predict_player) + // { + // ZeroStruct(&ent->control); + // } + // } - P_tl.debug_draw_enabled = 0; + b32 old_debug_draw = P_tl.debug_draw_enabled; + P_tl.debug_draw_enabled = TweakBool("Debug draw intermediate prediction steps", 0); P_StepFrame(predict_frame); - P_tl.debug_draw_enabled = 1; + P_tl.debug_draw_enabled = old_debug_draw; } predict_frame = predict_world->last_frame;