prediction testing
This commit is contained in:
parent
a529ea8c5b
commit
cdc876e71f
540
src/pp/pp.c
540
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
src/pp/pp.h
13
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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user