use bevy::{
platform::collections::{HashMap, HashSet},
prelude::*,
};
use crate::{
plugins::{render::plugin::FrameComparison, schedule::plugin::SimulationCycle},
prelude::Point,
};
#[derive(Component)]
pub struct AttachmentPoint(pub u64);
#[derive(Component, Clone, Copy, Debug)]
pub struct MeshOffset {
pub translation: Vec3,
pub rotation: Quat,
pub anchors: [Vec3; 3],
}
pub struct AttachmentPlugin;
impl Plugin for AttachmentPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
reposition_models.in_set(SimulationCycle::Preparation3),
);
}
}
pub fn reposition_models(
points: Query<
(Entity, &AttachmentPoint, &Transform, &Point),
(With<Point>, Without<SceneRoot>),
>,
mut meshes: Query<
(&AttachmentPoint, &mut Transform, &mut MeshOffset),
(With<SceneRoot>, Without<Point>),
>,
state: Res<FrameComparison>,
mut initialized: Local<HashSet<u64>>, ) {
let mut current_map: HashMap<u64, Vec<(Entity, Vec3)>> = HashMap::new();
for (entity, ap, _tf, pt) in points.iter() {
current_map
.entry(ap.0)
.or_default()
.push((entity, pt.position));
}
let mut seen_mesh_ids: Vec<u64> = Vec::new();
for (ap, mut mesh_tf, mut offset) in meshes.iter_mut() {
let id = ap.0;
seen_mesh_ids.push(id);
if !initialized.contains(&id) {
if let Some(vec) = current_map.get(&id) {
if vec.len() == 3 {
let mut sorted = vec.clone();
sorted.sort_by_key(|(e, _)| *e);
offset.anchors = [sorted[0].1, sorted[1].1, sorted[2].1];
}
initialized.insert(id);
}
continue;
}
if !state.changed {
continue;
}
if let Some(vec) = current_map.get(&id) {
match vec.len() {
3 => {
let mut sorted = vec.clone();
sorted.sort_by_key(|(e, _)| *e);
let p0 = sorted[0].1;
let p1 = sorted[1].1;
let p2 = sorted[2].1;
let from_refs = [&offset.anchors[0], &offset.anchors[1], &offset.anchors[2]];
let to_refs = [&p0, &p1, &p2];
let (delta_rot, delta_trans) = compute_rigid_transform(from_refs, to_refs);
let new_rot = (delta_rot * offset.rotation).normalize();
let new_pos = delta_trans + new_rot * offset.translation;
mesh_tf.rotation = new_rot;
mesh_tf.translation = new_pos;
}
1 => {
let p = vec[0].1;
mesh_tf.rotation = offset.rotation;
mesh_tf.translation = p + offset.rotation * offset.translation;
}
_ => {
mesh_tf.rotation = offset.rotation;
mesh_tf.translation = offset.translation;
}
}
}
}
initialized.retain(|&id| seen_mesh_ids.contains(&id));
}
pub fn compute_rigid_transform(
source_points: [&Vec3; 3],
target_points: [&Vec3; 3],
) -> (Quat, Vec3) {
let source_centroid = (*source_points[0] + *source_points[1] + *source_points[2]) / 3.0;
let target_centroid = (*target_points[0] + *target_points[1] + *target_points[2]) / 3.0;
let from_edge = (*source_points[1] - *source_points[0]).normalize();
let from_normal = from_edge
.cross(*source_points[2] - *source_points[0])
.normalize();
let to_edge = (*target_points[1] - *target_points[0]).normalize();
let to_normal = to_edge
.cross(*target_points[2] - *target_points[0])
.normalize();
let align_normals = Quat::from_rotation_arc(from_normal, to_normal).normalize();
let rotated_from_edge = align_normals * from_edge;
let align_edges = Quat::from_rotation_arc(rotated_from_edge, to_edge).normalize();
let rotation = align_edges * align_normals;
let translation = target_centroid - rotation * source_centroid;
(rotation, translation)
}