use concinnity_core::components::{Camera3D, FrameInput, Transform};
use concinnity_core::ecs::World;
use concinnity_core::ecs::{FrameTime, PickIndex};
use concinnity_host::thread::asset_id;
use crate::editor::hook::EditorHook;
use crate::editor::hook::camera_pose;
use crate::editor::viewport::framing::{self, CameraPose};
const GLIDE_SECS: f32 = 0.25;
pub(in crate::editor::hook) struct CameraGlide {
pub(super) from: CameraPose,
pub(super) to: CameraPose,
pub(super) secs: f32,
}
impl EditorHook {
pub(in crate::editor::hook) fn selection_bounds(
&self,
world: &World,
) -> Option<([f32; 3], [f32; 3])> {
let index = world.resource::<PickIndex>();
let boxes = self.selection.iter().filter_map(|handle| {
let name = self.handle_name(handle)?;
let id = asset_id::lookup(&name)?;
if let Some(e) = index.and_then(|i| i.entries.iter().find(|e| e.asset_id == id)) {
return Some((e.bb_min, e.bb_max));
}
let entity = super::billboard::entity_by_name(world, &name)?;
Some(framing::pad_point(world.get::<Transform>(entity)?.position))
});
framing::union_bounds(boxes)
}
pub(in crate::editor::hook) fn frame_selection(&mut self, vp: [f32; 2], world: &World) {
let Some((mn, mx)) = self.selection_bounds(world) else {
return;
};
let Some(cam) = world.query::<Camera3D>().next() else {
return;
};
let aspect = if vp[1] > 0.0 { vp[0] / vp[1] } else { 1.0 };
let Some(current) = camera_pose::read(world) else {
return;
};
let (center, radius) = framing::bounding_sphere(mn, mx);
let to = framing::frame_pose(
¤t,
center,
radius,
cam.fov_y_degrees.to_radians(),
aspect,
);
self.start_glide(current, to);
}
pub(in crate::editor::hook) fn start_glide(&mut self, from: CameraPose, to: CameraPose) {
self.glide = Some(CameraGlide {
from,
to,
secs: 0.0,
});
}
pub(in crate::editor::hook) fn drive_glide(&mut self, input: &FrameInput, world: &mut World) {
if self.glide.is_none() {
return;
}
let steered = input.forward
|| input.backward
|| input.left
|| input.right
|| input.mouse_dx != 0.0
|| input.mouse_dy != 0.0;
if steered {
self.end_glide();
return;
}
let dt = world
.resource::<FrameTime>()
.copied()
.unwrap_or_default()
.dt
.max(0.0);
let Some(glide) = &mut self.glide else {
return;
};
glide.secs += dt;
let t = glide.secs / GLIDE_SECS;
let pose = framing::lerp_pose(&glide.from, &glide.to, framing::ease(t));
camera_pose::write(world, &pose);
if t >= 1.0 {
self.end_glide();
}
}
pub(super) fn end_glide(&mut self) {
self.glide = None;
}
}