use super::events::RuntimeEventBus;
use crate::camera::camera::{Camera, CameraTarget};
use crate::interaction::selection::{NodeId, Selection};
use crate::resources::mesh_store::MeshId;
#[derive(Default)]
pub struct TransformWriteback {
ops: Vec<NodeTransformOp>,
}
impl TransformWriteback {
pub fn set(&mut self, id: NodeId, transform: glam::Affine3A) {
self.ops.push(NodeTransformOp {
id,
transform,
preserve_scale: false,
});
}
pub fn set_preserve_scale(&mut self, id: NodeId, transform: glam::Affine3A) {
self.ops.push(NodeTransformOp {
id,
transform,
preserve_scale: true,
});
}
pub(super) fn into_ops(self) -> Vec<NodeTransformOp> {
self.ops
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeTransformOp {
pub id: NodeId,
pub transform: glam::Affine3A,
#[cfg_attr(feature = "serde", serde(default))]
pub preserve_scale: bool,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SelectionOp {
SelectOne(NodeId),
Toggle(NodeId),
Add(NodeId),
Remove(NodeId),
Extend(Vec<NodeId>),
Clear,
SelectAll(Vec<NodeId>),
}
impl SelectionOp {
pub fn apply_to(&self, selection: &mut Selection) {
match self {
SelectionOp::SelectOne(id) => selection.select_one(*id),
SelectionOp::Toggle(id) => selection.toggle(*id),
SelectionOp::Add(id) => selection.add(*id),
SelectionOp::Remove(id) => selection.remove(*id),
SelectionOp::Extend(ids) => selection.extend(ids.iter().copied()),
SelectionOp::Clear => selection.clear(),
SelectionOp::SelectAll(ids) => selection.select_all(ids.iter().copied()),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ContactEvent {
pub node_a: NodeId,
pub node_b: NodeId,
pub world_normal: glam::Vec3,
pub impulse: f32,
pub contact_point: glam::Vec3,
}
#[derive(Debug, Clone)]
pub enum CameraCommand {
SetCenter(glam::Vec3),
OffsetCenter(glam::Vec3),
SetDistance(f32),
SetOrientation(glam::Quat),
BlendToward {
target: CameraTarget,
weight: f32,
},
}
pub struct SkinnedMeshUpdate {
pub mesh_id: MeshId,
pub positions: Vec<[f32; 3]>,
pub normals: Vec<[f32; 3]>,
}
pub struct SkinnedPoseUpdate {
pub mesh_id: MeshId,
pub instance_id: u32,
pub joint_matrices: Vec<glam::Mat4>,
}
#[derive(Default)]
#[non_exhaustive]
pub struct RuntimeOutput {
pub node_transform_ops: Vec<NodeTransformOp>,
pub selection_ops: Vec<SelectionOp>,
pub contact_events: Vec<ContactEvent>,
pub camera_follow_target: Option<glam::Vec3>,
pub camera_commands: Vec<CameraCommand>,
pub events: RuntimeEventBus,
pub skinned_mesh_updates: Vec<SkinnedMeshUpdate>,
pub skinned_pose_updates: Vec<SkinnedPoseUpdate>,
}
impl RuntimeOutput {
pub fn apply_camera_commands(&self, camera: &mut Camera) {
for cmd in &self.camera_commands {
match cmd {
CameraCommand::SetCenter(c) => {
camera.center = *c;
}
CameraCommand::OffsetCenter(d) => {
camera.center += *d;
}
CameraCommand::SetDistance(d) => {
camera.set_distance(*d);
}
CameraCommand::SetOrientation(q) => {
camera.orientation = q.normalize();
}
CameraCommand::BlendToward { target, weight } => {
let w = weight.clamp(0.0, 1.0);
camera.center = camera.center.lerp(target.center, w);
camera.distance = camera.distance + (target.distance - camera.distance) * w;
camera.distance = camera.distance.max(0.001);
camera.orientation = camera
.orientation
.slerp(target.orientation.normalize(), w)
.normalize();
}
}
}
}
}