use concinnity_physics::{LayerMask, Simulation};
use crate::components::{AudioOcclusionProbe, CameraProbe, GroundProbes};
use crate::ecs::PipelineContext;
use crate::math::sqrt;
use super::rig::RigPhysics;
const CAMERA_MARGIN: f32 = 0.15;
const CAMERA_MIN_DISTANCE: f32 = 0.3;
pub(crate) fn step_probes(
world: &Simulation,
ctx: &mut PipelineContext,
rigs: &[RigPhysics],
mask: LayerMask,
) {
let handle_of = |target| rigs.iter().find(|r| r.target == target).map(|r| r.handle);
for probes in ctx.query_mut::<GroundProbes>() {
let exclude = handle_of(probes.target);
for probe in &mut probes.probes {
probe.hit = world
.raycast(
probe.origin,
[0.0, -1.0, 0.0],
probe.max_dist,
exclude,
mask,
)
.map(|hit| (hit.point, hit.normal));
}
}
for probe in ctx.query_mut::<CameraProbe>() {
let dir = [
probe.desired[0] - probe.pivot[0],
probe.desired[1] - probe.pivot[1],
probe.desired[2] - probe.pivot[2],
];
let dist = sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
probe.clearance = world
.raycast(probe.pivot, dir, dist, handle_of(probe.target), mask)
.map(|hit| (hit.distance - CAMERA_MARGIN).max(CAMERA_MIN_DISTANCE));
}
for probe in ctx.query_mut::<AudioOcclusionProbe>() {
let dir = [
probe.to[0] - probe.from[0],
probe.to[1] - probe.from[1],
probe.to[2] - probe.from[2],
];
let dist = sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
probe.blocked = Some(world.raycast(probe.from, dir, dist, None, mask).is_some());
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use alloc::vec::Vec;
use concinnity_physics::ColliderShape;
use super::super::test_world::TestWorld;
#[test]
fn audio_probes_answer_blocked_and_clear() {
let mut world = Simulation::with_capacity(1);
world
.add_fixed(
&ColliderShape::Cuboid {
half_extents: [0.5, 5.0, 5.0],
},
[5.0, 0.0, 0.0],
[0.0; 3],
0.8,
LayerMask::ALL,
)
.expect("room in the pool");
world.step(1.0 / 60.0);
let mut w = TestWorld::new();
let probe = |to: [f32; 3]| AudioOcclusionProbe {
from: [0.0; 3],
to,
blocked: None,
};
w.components.push_typed(probe([10.0, 0.0, 0.0]));
w.components.push_typed(probe([0.0, 0.0, 10.0]));
w.components.push_typed(probe([0.0; 3]));
let mut ctx = w.ctx();
step_probes(&world, &mut ctx, &[], LayerMask::ALL);
let answers: Vec<Option<bool>> = ctx
.query::<AudioOcclusionProbe>()
.map(|p| p.blocked)
.collect();
assert_eq!(
answers,
vec![Some(true), Some(false), Some(false)],
"through the wall / past it / zero-length"
);
}
}