use nightshade::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Bounds {
pub center: Vec3,
pub radius: f32,
}
pub fn bounds(world: &World, entity: Entity) -> Option<Bounds> {
let bounding_volume = world.core.get_bounding_volume(entity)?;
let global = world.core.get_global_transform(entity)?;
let transformed = bounding_volume.transform(&global.0);
Some(Bounds {
center: transformed.obb.center,
radius: transformed.sphere_radius,
})
}
pub fn bounds_of(world: &World, entities: &[Entity]) -> Option<Bounds> {
let mut combined: Option<Bounds> = None;
for &entity in entities {
if let Some(next) = bounds(world, entity) {
combined = Some(match combined {
Some(current) => merge(current, next),
None => next,
});
}
}
combined
}
pub fn frame_entities(world: &mut World, entities: &[Entity]) {
let Some(bounds) = bounds_of(world, entities) else {
return;
};
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(orbit) = world.core.get_pan_orbit_camera_mut(camera) {
orbit.target_focus = bounds.center;
orbit.target_radius = (bounds.radius * 2.5).max(0.5);
}
}
fn merge(a: Bounds, b: Bounds) -> Bounds {
let offset = b.center - a.center;
let distance = offset.magnitude();
if distance + b.radius <= a.radius {
return a;
}
if distance + a.radius <= b.radius {
return b;
}
if distance < f32::EPSILON {
return Bounds {
center: a.center,
radius: a.radius.max(b.radius),
};
}
let radius = (a.radius + distance + b.radius) * 0.5;
let center = a.center + offset * ((radius - a.radius) / distance);
Bounds { center, radius }
}