use crate::data::attachment::quad_corner::{BLX, BLY, BRX, BRY, ULX, ULY, URX, URY};
use crate::data::{Attachment, SlotId};
use crate::skeleton::Skeleton;
const QUAD_REGION_UVS: [f32; 8] = [1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0];
const QUAD_TRIANGLES: [u16; 6] = [0, 1, 2, 2, 3, 0];
#[derive(Debug, Default, Clone)]
pub struct RegionGeometry {
world: Vec<f32>,
region_uvs: Vec<f32>,
triangles: Vec<u16>,
}
impl RegionGeometry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn update(&mut self, skeleton: &Skeleton, slot: SlotId) -> bool {
self.world.clear();
self.region_uvs.clear();
self.triangles.clear();
let s = &skeleton.slots[slot.index()];
let pose = s.applied();
let Some(attachment) = pose.attachment else {
return false;
};
match skeleton.attachment(attachment) {
Attachment::Region(region) => {
let off = region
.sequence
.offsets(region.sequence.resolve_index(pose.sequence_index));
let b = skeleton.bones[s.bone.index()].applied();
for (x, y) in [(BRX, BRY), (BLX, BLY), (ULX, ULY), (URX, URY)] {
self.world.push(off[x] * b.a + off[y] * b.b + b.world_x);
self.world.push(off[x] * b.c + off[y] * b.d + b.world_y);
}
self.region_uvs.extend_from_slice(&QUAD_REGION_UVS);
self.triangles.extend_from_slice(&QUAD_TRIANGLES);
}
Attachment::Mesh(mesh) => {
let n = mesh.vertex_data.world_vertices_length as usize;
if mesh.region_uvs.len() < n || mesh.triangles.len() < 3 {
return false;
}
self.world.resize(n, 0.0);
skeleton.compute_world_vertices(
&mesh.vertex_data,
slot,
0,
n,
&mut self.world,
0,
2,
);
self.region_uvs.extend_from_slice(&mesh.region_uvs[..n]);
self.triangles.extend_from_slice(&mesh.triangles);
}
_ => return false,
}
true
}
#[must_use]
pub fn world_vertices(&self) -> &[f32] {
&self.world
}
#[must_use]
pub fn region_uvs(&self) -> &[f32] {
&self.region_uvs
}
#[must_use]
pub fn map(&self, u: f32, v: f32) -> Option<(f32, f32)> {
let (uvs, world) = (&self.region_uvs, &self.world);
let mut best = None;
let mut best_min = f32::MIN;
for t in self.triangles.as_chunks::<3>().0 {
let (i0, i1, i2) = (usize::from(t[0]), usize::from(t[1]), usize::from(t[2]));
let (u0, v0) = (uvs[i0 * 2], uvs[i0 * 2 + 1]);
let (u1, v1) = (uvs[i1 * 2], uvs[i1 * 2 + 1]);
let (u2, v2) = (uvs[i2 * 2], uvs[i2 * 2 + 1]);
let det = (u1 - u0) * (v2 - v0) - (u2 - u0) * (v1 - v0);
if det.abs() < 1e-12 {
continue;
}
let l1 = ((u - u0) * (v2 - v0) - (u2 - u0) * (v - v0)) / det;
let l2 = ((u1 - u0) * (v - v0) - (u - u0) * (v1 - v0)) / det;
let l0 = 1.0 - l1 - l2;
let m = l0.min(l1).min(l2);
if m > best_min {
best_min = m;
best = Some((
l0 * world[i0 * 2] + l1 * world[i1 * 2] + l2 * world[i2 * 2],
l0 * world[i0 * 2 + 1] + l1 * world[i1 * 2 + 1] + l2 * world[i2 * 2 + 1],
));
}
if m >= 0.0 {
break;
}
}
best
}
}