1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy)]
9pub struct DualQuat {
10 pub q0: [f32; 4],
11 pub qe: [f32; 4],
12}
13
14impl DualQuat {
15 pub fn identity() -> Self {
16 DualQuat {
17 q0: [0.0, 0.0, 0.0, 1.0],
18 qe: [0.0; 4],
19 }
20 }
21}
22
23#[derive(Debug, Clone)]
25pub struct DqsVertex {
26 pub bone_indices: [usize; 4],
27 pub weights: [f32; 4],
28}
29
30impl Default for DqsVertex {
31 fn default() -> Self {
32 DqsVertex {
33 bone_indices: [0; 4],
34 weights: [0.0; 4],
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct DualQuaternionSkin {
42 pub vertices: Vec<DqsVertex>,
43 pub bone_dqs: Vec<DualQuat>,
44}
45
46impl DualQuaternionSkin {
47 pub fn new(vertex_count: usize, bone_count: usize) -> Self {
48 DualQuaternionSkin {
49 vertices: (0..vertex_count).map(|_| DqsVertex::default()).collect(),
50 bone_dqs: (0..bone_count).map(|_| DualQuat::identity()).collect(),
51 }
52 }
53}
54
55pub fn new_dqs(vertex_count: usize, bone_count: usize) -> DualQuaternionSkin {
57 DualQuaternionSkin::new(vertex_count, bone_count)
58}
59
60#[allow(clippy::too_many_arguments)]
62pub fn dqs_set_vertex(
63 dqs: &mut DualQuaternionSkin,
64 vertex: usize,
65 b0: usize,
66 w0: f32,
67 b1: usize,
68 w1: f32,
69 b2: usize,
70 w2: f32,
71) {
72 if vertex < dqs.vertices.len() {
73 dqs.vertices[vertex] = DqsVertex {
74 bone_indices: [b0, b1, b2, 0],
75 weights: [w0, w1, w2, 0.0],
76 };
77 }
78}
79
80pub fn dqs_set_bone(dqs: &mut DualQuaternionSkin, bone: usize, dq: DualQuat) {
82 if bone < dqs.bone_dqs.len() {
83 dqs.bone_dqs[bone] = dq;
84 }
85}
86
87pub fn dqs_normalize(dq: &DualQuat) -> DualQuat {
89 let len =
90 (dq.q0[0] * dq.q0[0] + dq.q0[1] * dq.q0[1] + dq.q0[2] * dq.q0[2] + dq.q0[3] * dq.q0[3])
91 .sqrt();
92 if len < 1e-9 {
93 return DualQuat::identity();
94 }
95 DualQuat {
96 q0: [
97 dq.q0[0] / len,
98 dq.q0[1] / len,
99 dq.q0[2] / len,
100 dq.q0[3] / len,
101 ],
102 qe: [
103 dq.qe[0] / len,
104 dq.qe[1] / len,
105 dq.qe[2] / len,
106 dq.qe[3] / len,
107 ],
108 }
109}
110
111pub fn dqs_vertex_count(dqs: &DualQuaternionSkin) -> usize {
113 dqs.vertices.len()
114}
115
116pub fn dqs_bone_count(dqs: &DualQuaternionSkin) -> usize {
118 dqs.bone_dqs.len()
119}
120
121pub fn dqs_to_json(dqs: &DualQuaternionSkin) -> String {
123 format!(
124 r#"{{"vertices":{},"bones":{}}}"#,
125 dqs.vertices.len(),
126 dqs.bone_dqs.len()
127 )
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_new_dqs_vertex_count() {
136 let d = new_dqs(15, 4);
137 assert_eq!(dqs_vertex_count(&d), 15 ,);
138 }
139
140 #[test]
141 fn test_new_dqs_bone_count() {
142 let d = new_dqs(5, 8);
143 assert_eq!(dqs_bone_count(&d), 8 ,);
144 }
145
146 #[test]
147 fn test_identity_dq_real_part() {
148 let dq = DualQuat::identity();
149 assert!((dq.q0[3] - 1.0).abs() < 1e-5, );
150 }
151
152 #[test]
153 fn test_normalize_identity_stays_identity() {
154 let dq = DualQuat::identity();
155 let n = dqs_normalize(&dq);
156 assert!((n.q0[3] - 1.0).abs() < 1e-5, );
157 }
158
159 #[test]
160 fn test_set_bone_updates() {
161 let mut d = new_dqs(2, 2);
162 let dq = DualQuat {
163 q0: [0.0, 0.0, 0.707, 0.707],
164 qe: [0.0; 4],
165 };
166 dqs_set_bone(&mut d, 0, dq);
167 assert!((d.bone_dqs[0].q0[2] - 0.707).abs() < 1e-3, );
168 }
169
170 #[test]
171 fn test_set_bone_out_of_bounds_ignored() {
172 let mut d = new_dqs(2, 2);
173 dqs_set_bone(&mut d, 99, DualQuat::identity());
174 assert_eq!(dqs_bone_count(&d), 2 ,);
175 }
176
177 #[test]
178 fn test_set_vertex_out_of_bounds_ignored() {
179 let mut d = new_dqs(2, 2);
180 dqs_set_vertex(&mut d, 99, 0, 1.0, 0, 0.0, 0, 0.0);
181 assert_eq!(dqs_vertex_count(&d), 2 ,);
182 }
183
184 #[test]
185 fn test_to_json_contains_vertices() {
186 let d = new_dqs(6, 3);
187 let j = dqs_to_json(&d);
188 assert!(j.contains("vertices") ,);
189 }
190
191 #[test]
192 fn test_identity_dual_part_zero() {
193 let dq = DualQuat::identity();
194 for &v in &dq.qe {
195 assert!((v).abs() < 1e-6, );
196 }
197 }
198
199 #[test]
200 fn test_normalize_zero_length_returns_identity() {
201 let dq = DualQuat {
202 q0: [0.0; 4],
203 qe: [0.0; 4],
204 };
205 let n = dqs_normalize(&dq);
206 assert!((n.q0[3] - 1.0).abs() < 1e-5, );
207 }
208}