begin moving simulation logic to shared predictable implementation

This commit is contained in:
jacob 2026-01-13 14:47:50 -06:00
parent f5a9733525
commit 3ad4579bb9
4 changed files with 778 additions and 1069 deletions

View File

@ -63,6 +63,16 @@ P_Key P_RandKey(void)
return result;
}
u64 P_RandU64FromEnt(P_Ent *ent)
{
u64 result = MixU64s(ent->key.v, ent->rand_seq);
if (!P_IsEntNil(ent))
{
ent->rand_seq += 1;
}
return result;
}
////////////////////////////////////////////////////////////
//~ Tile helpers
@ -83,7 +93,6 @@ String P_NameFromTileKind(P_TileKind kind)
return result;
}
////////////////////////////////////////////////////////////
//~ Shape helpers
@ -1317,7 +1326,6 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick)
for (P_Ent *src = P_FirstEnt(src_frame); !P_IsEntNil(src); src = P_NextEnt(src))
{
// FIXME: Pull from freelist
P_Ent *dst = world->first_free_ent;
if (dst)
{
@ -1340,8 +1348,737 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick)
////////////////////////////////////////////////////////////
//~ Step
P_Frame *P_StepWorld(P_World *world, P_Frame *prev_frame, P_CmdList cmds)
void P_StepFrame(P_Frame *frame, P_CmdList cmds)
{
P_Frame *result = &P_NilFrame;
return result;
TempArena scratch = BeginScratchNoConflict();
P_World *world = frame->world;
P_Frame *prev_frame = frame->prev;
i64 sim_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
f64 sim_dt = SecondsFromNs(sim_dt_ns);
//////////////////////////////
//- Update double-buffered entity data
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
ent->prev_xf = ent->xf;
}
//////////////////////////////
//- Update ent controls
// FIXME: Only apply relevant cmds based on tick
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
ent->fire_presses = 0;
}
for (P_CmdNode *cmd_node = cmds.first; cmd_node; cmd_node = cmd_node->next)
{
P_Cmd cmd = cmd_node->cmd;
if (cmd.kind == P_CmdKind_Control)
{
P_Ent *target = P_EntFromKey(frame, cmd.target);
if (!P_IsEntNil(target))
{
target->move = ClampVec2Len(cmd.move, 1);
target->look = cmd.look;
target->fire_held = cmd.fire_held;
target->fire_presses += cmd.fire_presses;
}
}
}
//////////////////////////////
//- Spawn entities
// {
// //////////////////////////////
// //- Push bullets
// for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// {
// if (ent->fire_held)
// {
// if (ent->has_weapon)
// {
// }
// }
// }
// }
//////////////////////////////
//- Integrate control forces
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
// Xform xf = ent->xf;
// Xform desired_xf = xf;
// if (!IsVec2Zero(ent->look))
// {
// desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->look));
// }
// f32 move_speed = TweakFloat("Player move speed", 6.5, 0, 20);
// desired_xf.og = AddVec2(xf.og, MulVec2(ent->move, move_speed * sim_dt));
// Vec2 pos_diff = SubVec2(desired_xf.og, xf.og);
// f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf));
// ent->solved_v = pos_diff;
// ent->solved_w = angle_diff;
{
// f32 damp_vel = damp_force * sim_dt;
if (Vec2Len(ent->solved_v) > 0.001)
{
f32 damp_force = TweakFloat("Player damp force", 50, 0, 100);
Vec2 damp = MulVec2(NegVec2(ent->solved_v), damp_force * sim_dt);
ent->solved_v = AddVec2(ent->solved_v, damp);
}
else
{
ent->solved_v = VEC2(0, 0);
}
}
{
f32 move_force = TweakFloat("Player move force", 400, 0, 400);
f32 max_speed = TweakFloat("Player max speed", 10, 0, 20);
Vec2 new_velocity = ent->solved_v;
new_velocity = AddVec2(new_velocity, MulVec2(ent->move, move_force * sim_dt));
// if (Vec2Len(new_velocity) > max_speed)
// {
// new_velocity = Vec2WithLen(new_velocity, max_speed);
// }
ent->solved_v = new_velocity;
}
}
//////////////////////////////
//- Generate player wall constraints
// TODO: Not like this
// i64 max_constraints = 4096;
// i64 constraints_count = 0;
// P_Constraint *constraints = PushStructs(scratch.arena, P_Constraint, max_constraints);
PERSIST i64 max_constraints = 4096;
PERSIST i64 constraints_count = 0;
PERSIST P_Constraint *constraints = 0;
if (!constraints)
{
constraints = PushStructs(scratch.arena, P_Constraint, max_constraints);
}
for (P_Ent *ent0 = P_FirstEnt(frame); !P_IsEntNil(ent0); ent0 = P_NextEnt(ent0))
{
P_Shape shape0 = P_WorldShapeFromEnt(ent0);
for (P_Ent *ent1 = P_FirstEnt(frame); !P_IsEntNil(ent1); ent1 = P_NextEnt(ent1))
{
if (ent1 > ent0)
{
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 < constraints_count; ++constraint_idx)
{
constraint = &constraints[constraint_idx];
if (P_MatchKey(constraint->ent0, ent0->key) && P_MatchKey(constraint->ent1, ent1->key))
{
match = 1;
break;
}
}
if (!match)
{
if (constraints_count < max_constraints)
{
constraint = &constraints[constraints_count];
constraints_count += 1;
ZeroStruct(constraint);
}
}
}
if (constraint)
{
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;
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);
// }
}
}
}
}
}
}
//////////////////////////////
//- Prune constraints
{
i64 constraint_idx = 0;
while (constraint_idx < constraints_count)
{
P_Constraint *constraint = &constraints[constraint_idx];
b32 prune = 1;
if (constraint->last_touched_tick == frame->tick)
{
P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1);
if (!P_IsEntNil(ent0) && !P_IsEntNil(ent1))
{
prune = 0;
}
}
if (prune)
{
// Prune by replacing with last constraint
// TODO: Investigate whether the reordering here can degrade stability
P_Constraint *last_constraint = &constraints[constraints_count - 1];
*constraint = *last_constraint;
constraints_count -= 1;
}
else
{
constraint_idx += 1;
}
}
}
//////////////////////////////
//- 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)
{
//////////////////////////////
//- Prepare constraints
for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx)
{
P_Constraint *constraint = &constraints[constraint_idx];
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;
f32 inv_i1 = constraint->inv_i1;
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;
// Compute normal mass
{
f32 vcp0_wedge = WedgeVec2(vcp0, normal);
f32 vcp1_wedge = WedgeVec2(vcp1, normal);
f32 k = (inv_m0 + inv_m1) + (inv_i0 * vcp0_wedge * vcp0_wedge) + (inv_i1 * vcp1_wedge * vcp1_wedge);
contact->inv_normal_mass = k > 0.0f ? 1.0f / k : 0.0f;
}
// Compute tangent mass
{
f32 vcp0_wedge = WedgeVec2(vcp0, tangent);
f32 vcp1_wedge = WedgeVec2(vcp1, tangent);
f32 k = (inv_m0 + inv_m1) + (inv_i0 * vcp0_wedge * vcp0_wedge) + (inv_i1 * vcp1_wedge * vcp1_wedge);
contact->inv_tangent_mass = k > 0.0f ? 1.0f / k : 0.0f;
}
}
}
//////////////////////////////
//- Warm start constraints
for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx)
{
P_Constraint *constraint = &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)
{
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;
}
}
//////////////////////////////
//- Solve constraints
for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx)
{
P_Constraint *constraint = &constraints[constraint_idx];
P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1);
f32 inv_m0 = constraint->inv_m0;
f32 inv_m1 = constraint->inv_m1;
f32 inv_i0 = constraint->inv_i0;
f32 inv_i1 = constraint->inv_i1;
Vec2 v0 = ent0->solved_v;
Vec2 v1 = ent1->solved_v;
f32 w0 = ent0->solved_w;
f32 w1 = ent1->solved_w;
Vec2 center0 = constraint->static_center0;
Vec2 center1 = constraint->static_center1;
if (!P_IsEntNil(ent0))
{
center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
}
if (!P_IsEntNil(ent1))
{
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)
{
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;
// TDOO: 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 = 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);
}
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 n)
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;
}
// Tangent impulse
Vec2 tangent = PerpVec2(normal);
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 vel0 = AddVec2(v0, MulPerpVec2(vcp0, w0));
Vec2 vel1 = AddVec2(v1, MulPerpVec2(vcp1, w1));
Vec2 vrel = SubVec2(vel0, vel1);
f32 k = contact->inv_tangent_mass;
// (to be applied along t)
f32 vt = DotVec2(vrel, tangent);
f32 j = vt * k;
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;
}
}
//////////////////////////////
//- 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;
}
}
//////////////////////////////
//- Move bullets
for (P_Ent *bullet = P_FirstEnt(frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{
if (bullet->is_bullet)
{
Vec2 start = bullet->bullet_start;
Vec2 end = bullet->bullet_end;
Vec2 vel = SubVec2(end, start);
bullet->bullet_start = end;
bullet->bullet_end = AddVec2(end, vel);
}
}
//////////////////////////////
//- Spawn new bullets
// TODO: Remove this
{
P_EntList bullets_to_spawn = Zi;
for (P_Ent *firer = P_FirstEnt(frame); !P_IsEntNil(firer); firer = P_NextEnt(firer))
{
if (firer->fire_held)
// if (firer->fire_presses)
{
// i64 fire_delta_ns = frame->time_ns - firer->last_fire_ns;
// i64 single_bullet_delta_ns = NsFromSeconds(1) / firer->fire_rate;
// i64 tick_bullets_count = sim_dt * firer->fire_rate;
f32 fire_rate = 50;
f32 bullets_per_fire = 1;
// f32 spread = Tau * 0.05;
f32 spread = Tau * 0.01;
f32 tweak_speed = TweakFloat("Bullet speed", 100, 1, 100);
b32 can_fire = (firer->last_fire_ns + NsFromSeconds(1.0 / fire_rate)) <= frame->time_ns;
if (can_fire)
{
i64 tick_bullets_count = bullets_per_fire;
if (tick_bullets_count > 0)
{
P_Shape firer_world_shape = P_WorldShapeFromEnt(firer);
Vec2 pos = P_EdgePointFromShape(firer_world_shape, firer->look);
for (i64 bullet_idx = 0; bullet_idx < tick_bullets_count; ++bullet_idx)
{
P_Ent *bullet = P_PushTempEnt(scratch.arena, &bullets_to_spawn);
bullet->is_bullet = 1;
bullet->key = P_RandKey();
f32 rand_speed = ((f32)P_RandU64FromEnt(firer) / (f32)0xFFFFFFFFFFFFFFFFull) - 0.5;
f32 rand_angle = ((f32)P_RandU64FromEnt(firer) / (f32)0xFFFFFFFFFFFFFFFFull) - 0.5;
f32 speed = tweak_speed * sim_dt;
f32 angle = AngleFromVec2(firer->look);
speed += (speed * 0.5) * rand_speed;
angle += rand_angle * spread;
Vec2 dir = Vec2FromAngle(angle);
bullet->bullet_start = pos;
bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(dir, speed));
bullet->bullet_firer = firer->key;
}
}
firer->last_fire_ns = frame->time_ns;
}
}
}
P_SpawnEntsFromList(frame, bullets_to_spawn);
}
//////////////////////////////
//- Update bullet hits
// TODO: Not like this
// TODO: Separate 'hits' from bullets, so that bullets can have multiple hits
for (P_Ent *bullet = P_FirstEnt(frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{
if (bullet->is_bullet)
{
bullet->has_hit = 0;
Vec2 ray_start = bullet->bullet_start;
Vec2 ray_end = bullet->bullet_end;
Vec2 ray_dir = SubVec2(ray_end, ray_start);
// TODO: Real raycast query
P_Ent *closest_victim = &P_NilEnt;
P_RaycastResult victim_raycast = Zi;
{
f32 closest_len_sq = Inf;
for (P_Ent *victim = P_FirstEnt(frame); !P_IsEntNil(victim); victim = P_NextEnt(victim))
{
if (victim->is_player && !P_MatchKey(victim->key, bullet->bullet_firer))
{
P_Shape victim_world_shape = P_WorldShapeFromEnt(victim);
P_RaycastResult entrance_raycast = P_RaycastShape(victim_world_shape, ray_start, ray_dir);
Vec2 entrance = entrance_raycast.p;
if (entrance_raycast.is_intersecting)
{
P_RaycastResult exit_raycast = P_RaycastShape(victim_world_shape, ray_start, NegVec2(ray_dir));
Vec2 exit = exit_raycast.p;
f32 da = DotVec2(ray_dir, SubVec2(entrance, ray_start));
f32 db = DotVec2(ray_dir, SubVec2(exit, ray_start));
if (db > 0 && (da <= Vec2LenSq(ray_dir) || da <= 0))
{
f32 len_sq = Vec2LenSq(SubVec2(entrance_raycast.p, ray_start));
if (len_sq < closest_len_sq)
{
closest_len_sq = len_sq;
closest_victim = victim;
victim_raycast = entrance_raycast;
}
}
}
}
}
}
if (!P_IsEntNil(closest_victim))
{
bullet->has_hit = 1;
bullet->hit_entry = victim_raycast.p;
bullet->hit_entry_normal = victim_raycast.normal;
// bullet->bullet_end = bullet->hit_entry;
bullet->exists = 0;
}
Rng2 bounds = Zi;
bounds.p0 = VEC2(-P_WorldPitch / 2, -P_WorldPitch / 2);
bounds.p1 = VEC2(P_WorldPitch / 2, P_WorldPitch / 2);
if (
bullet->bullet_start.x < bounds.p0.x || bullet->bullet_start.y < bounds.p0.y ||
bullet->bullet_start.x > bounds.p1.x || bullet->bullet_start.y > bounds.p1.y
)
{
bullet->exists = 0;
}
}
}
//////////////////////////////
//- Debug draw entities
if (P_tl.debug_draw_enabled)
{
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
P_Shape world_shape = P_WorldShapeFromEnt(ent);
// Draw aabb
{
Vec4 color = VEC4(0.4, 0.2, 0.2, 1);
Rng2 bb = P_BoundingBoxFromShape(world_shape);
P_DebugDrawRect(bb, color);
}
// Draw shape
{
// Vec4 color = Color_Cyan;
Vec4 color = VEC4(0.2, 0.4, 0.2, 1);
P_DebugDrawShape(world_shape, color);
}
// Draw rot
{
Vec4 color = VEC4(0.8, 0.8, 0.8, 1);
Vec2 p0 = world_shape.centroid;
Vec2 p1 = P_EdgePointFromShape(world_shape, UpFromXform(ent->xf));
P_DebugDrawLine(p0, p1, color);
}
// Draw look
{
Vec4 color = VEC4(0.4, 0.8, 0.4, 1);
Vec2 p0 = world_shape.centroid;
Vec2 p1 = P_EdgePointFromShape(world_shape, ent->look);
P_DebugDrawLine(p0, p1, color);
}
}
}
//////////////////////////////
//- End frame
frame->time_ns += sim_dt_ns;
EndScratch(scratch);
}

View File

@ -84,6 +84,7 @@ Struct(P_Ent)
//- Persistent data
P_Key key;
u64 rand_seq;
//////////////////////////////
//- Build data
@ -183,6 +184,7 @@ Struct(P_World)
Arena *statics_arena;
u64 seed;
RandState rand;
P_Ent *first_free_ent;
@ -468,6 +470,7 @@ b32 P_IsFrameNil(P_Frame *frame);
b32 P_MatchKey(P_Key a, P_Key b);
P_Key P_RandKey(void);
u64 P_RandU64FromEnt(P_Ent *ent);
#define P_FmtKey(key) FmtHandle((key).v)
@ -539,4 +542,4 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick);
////////////////////////////////////////////////////////////
//~ Step
P_Frame *P_StepWorld(P_World *world, P_Frame *prev_frame, P_CmdList cmds);
void P_StepFrame(P_Frame *frame, P_CmdList cmds);

File diff suppressed because it is too large Load Diff

View File

@ -1574,7 +1574,7 @@ void V_TickForever(WaveLaneCtx *lane)
V.dragging_window = 0;
}
// TODO: Add window to free list
// TODO: Add window to freelist
// TODO: Remove panel if windowless
DllQueueRemoveNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
panel->windows_count -= 1;
@ -2639,7 +2639,8 @@ void V_TickForever(WaveLaneCtx *lane)
frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0);
if (frame->predict_to != prev_frame->predict_to)
b32 should_send_sim_cmds = frame->predict_to != prev_frame->predict_to;
if (should_send_sim_cmds)
{
// Push control cmd
{
@ -2665,21 +2666,8 @@ void V_TickForever(WaveLaneCtx *lane)
}
}
UnlockTicketMutex(&P.sim_input_back_tm);
if (V.sim_cmds.first)
{
V.sim_cmds.last->next = V.first_free_sim_cmd_node;
V.first_free_sim_cmd_node = V.sim_cmds.first;
}
V.sim_cmds.first = 0;
V.sim_cmds.last = 0;
V.sim_cmds.count = 0;
}
//////////////////////////////
//- Predict
@ -2771,6 +2759,22 @@ void V_TickForever(WaveLaneCtx *lane)
// }
//////////////////////////////
//- Reset queued sim cmds
if (should_send_sim_cmds)
{
if (V.sim_cmds.first)
{
V.sim_cmds.last->next = V.first_free_sim_cmd_node;
V.first_free_sim_cmd_node = V.sim_cmds.first;
}
V.sim_cmds.first = 0;
V.sim_cmds.last = 0;
V.sim_cmds.count = 0;
}
@ -3528,6 +3532,7 @@ void V_TickForever(WaveLaneCtx *lane)
SllStackPush(sim_world->first_free_ent, ent);
}
}
//////////////////////////////
//- End frame