diff --git a/crates/render_anim/src/occupancy/dyn_ent_phys.rs b/crates/render_anim/src/occupancy/dyn_ent_phys.rs index 82d095f..f082f70 100644 --- a/crates/render_anim/src/occupancy/dyn_ent_phys.rs +++ b/crates/render_anim/src/occupancy/dyn_ent_phys.rs @@ -17,6 +17,10 @@ const MSEC_STEP: i32 = 17; const MAX_ITERS: i32 = 2; const CONTENTS_SOLID: u32 = 1; +const CONTENTS_ITEMCLIP: u32 = 0x400; +const WAKE_MASKS: [u32; 2] = [CONTENTS_SOLID | CONTENTS_ITEMCLIP, CONTENTS_SOLID]; +const WAKE_LIFTS: [f32; 6] = [0.0, CONTACT_SLOP, 0.25, 0.5, 1.0, 2.0]; +const WAKE_FOOTPRINTS: [f32; 3] = [1.0, 0.75, 0.5]; const SLEEP_SPEED: f32 = 4.0; const GROUND_NZ: f32 = 0.7; const CONTACT_SLOP: f32 = 0.125; @@ -56,6 +60,7 @@ struct DynEntPhysBody { maxs: [f32; 3], bounce: f32, friction: f32, + mask: u32, on_ground: bool, asleep_ms: i32, } @@ -99,6 +104,7 @@ pub fn step_phys_world0( } let (mins, maxs) = xmodel_phys_hull(catalog, &inst.current_model); let add_vel = impulse.impulse / preset.mass; + let phys_clip = clip.0.as_deref(); world .bodies .entry(entity) @@ -106,15 +112,22 @@ pub fn step_phys_world0( body.vel += add_vel; body.asleep_ms = 0; }) - .or_insert_with(|| DynEntPhysBody { - origin: transform.translation, - vel: add_vel, - mins, - maxs, - bounce: preset.bounce, - friction: preset.friction, - on_ground: false, - asleep_ms: 0, + .or_insert_with(|| { + let pose = phys_clip + .and_then(|clip| clear_wake_pose(clip, transform.translation, mins, maxs)); + let (origin, mins, maxs, mask) = + pose.unwrap_or((transform.translation, mins, maxs, CONTENTS_SOLID)); + DynEntPhysBody { + origin, + vel: add_vel, + mins, + maxs, + bounce: preset.bounce, + friction: preset.friction, + mask, + on_ground: false, + asleep_ms: 0, + } }); } @@ -185,6 +198,39 @@ pub fn step_phys_world0( } } +fn clear_wake_pose( + clip: &ClipCollision, + origin: Vec3, + mins: [f32; 3], + maxs: [f32; 3], +) -> Option<(Vec3, [f32; 3], [f32; 3], u32)> { + for mask in WAKE_MASKS { + for scale in WAKE_FOOTPRINTS { + let (mins, maxs) = narrowed_footprint(mins, maxs, scale); + for lift in WAKE_LIFTS { + let at = (origin + Vec3::Z * lift).to_array(); + let probe = clip.sweep_box(at, at, mins, maxs, mask); + if !probe.startsolid && !probe.allsolid { + return Some((Vec3::from_array(at), mins, maxs, mask)); + } + } + } + } + None +} + +fn narrowed_footprint(mins: [f32; 3], maxs: [f32; 3], scale: f32) -> ([f32; 3], [f32; 3]) { + let mut lo = mins; + let mut hi = maxs; + for axis in 0..2 { + let mid = (mins[axis] + maxs[axis]) * 0.5; + let half = (maxs[axis] - mins[axis]) * 0.5 * scale; + lo[axis] = mid - half; + hi[axis] = mid + half; + } + (lo, hi) +} + fn integrate_free(body: &mut DynEntPhysBody, dt: f32) { body.vel.z += GRAVITY_Z * dt; body.origin += body.vel * dt; @@ -200,7 +246,7 @@ fn integrate_body(body: &mut DynEntPhysBody, clip: &ClipCollision, dt: f32) { end.to_array(), body.mins, body.maxs, - CONTENTS_SOLID, + body.mask, ); if hit.allsolid || hit.startsolid { body.vel = Vec3::ZERO;