oxihuman_morph/
omega_skin.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum OmegaMode {
10 Linear,
11 DualQuat,
12 Blended,
13}
14
15#[derive(Debug, Clone)]
17pub struct OmegaVertex {
18 pub bone_index: usize,
19 pub omega_weight: f32,
20 pub lbs_weight: f32,
21}
22
23#[derive(Debug, Clone)]
25pub struct OmegaSkin {
26 pub vertices: Vec<OmegaVertex>,
27 pub mode: OmegaMode,
28 pub blend_factor: f32,
29}
30
31impl OmegaSkin {
32 pub fn new(vertex_count: usize) -> Self {
33 OmegaSkin {
34 vertices: (0..vertex_count)
35 .map(|_| OmegaVertex {
36 bone_index: 0,
37 omega_weight: 1.0,
38 lbs_weight: 1.0,
39 })
40 .collect(),
41 mode: OmegaMode::Blended,
42 blend_factor: 0.5,
43 }
44 }
45}
46
47pub fn new_omega_skin(vertex_count: usize) -> OmegaSkin {
49 OmegaSkin::new(vertex_count)
50}
51
52pub fn omega_set_blend(skin: &mut OmegaSkin, factor: f32) {
54 skin.blend_factor = factor.clamp(0.0, 1.0);
55}
56
57pub fn omega_set_mode(skin: &mut OmegaSkin, mode: OmegaMode) {
59 skin.mode = mode;
60}
61
62pub fn omega_vertex_count(skin: &OmegaSkin) -> usize {
64 skin.vertices.len()
65}
66
67pub fn omega_effective_weight(skin: &OmegaSkin, vertex: usize) -> f32 {
69 if vertex >= skin.vertices.len() {
70 return 0.0;
71 }
72 let v = &skin.vertices[vertex];
73 match skin.mode {
74 OmegaMode::Linear => v.lbs_weight,
75 OmegaMode::DualQuat => v.omega_weight,
76 OmegaMode::Blended => {
77 v.lbs_weight * (1.0 - skin.blend_factor) + v.omega_weight * skin.blend_factor
78 }
79 }
80}
81
82pub fn omega_to_json(skin: &OmegaSkin) -> String {
84 format!(
85 r#"{{"mode":"{}","blend":{:.4},"vertices":{}}}"#,
86 match skin.mode {
87 OmegaMode::Linear => "linear",
88 OmegaMode::DualQuat => "dual_quat",
89 OmegaMode::Blended => "blended",
90 },
91 skin.blend_factor,
92 skin.vertices.len()
93 )
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_new_omega_skin_vertex_count() {
102 let s = new_omega_skin(10);
103 assert_eq!(
104 omega_vertex_count(&s),
105 10, );
107 }
108
109 #[test]
110 fn test_default_mode_blended() {
111 let s = new_omega_skin(5);
112 assert_eq!(
113 s.mode,
114 OmegaMode::Blended, );
116 }
117
118 #[test]
119 fn test_set_blend_clamps() {
120 let mut s = new_omega_skin(3);
121 omega_set_blend(&mut s, 2.0);
122 assert!((s.blend_factor - 1.0).abs() < 1e-5, );
123 }
124
125 #[test]
126 fn test_set_mode_linear() {
127 let mut s = new_omega_skin(3);
128 omega_set_mode(&mut s, OmegaMode::Linear);
129 assert_eq!(s.mode, OmegaMode::Linear ,);
130 }
131
132 #[test]
133 fn test_effective_weight_linear_mode() {
134 let mut s = new_omega_skin(2);
135 s.vertices[0].lbs_weight = 0.8;
136 omega_set_mode(&mut s, OmegaMode::Linear);
137 let w = omega_effective_weight(&s, 0);
138 assert!((w - 0.8).abs() < 1e-5, );
139 }
140
141 #[test]
142 fn test_effective_weight_dq_mode() {
143 let mut s = new_omega_skin(2);
144 s.vertices[0].omega_weight = 0.9;
145 omega_set_mode(&mut s, OmegaMode::DualQuat);
146 let w = omega_effective_weight(&s, 0);
147 assert!((w - 0.9).abs() < 1e-5 ,);
148 }
149
150 #[test]
151 fn test_effective_weight_out_of_bounds() {
152 let s = new_omega_skin(2);
153 let w = omega_effective_weight(&s, 99);
154 assert!((w).abs() < 1e-6 ,);
155 }
156
157 #[test]
158 fn test_to_json_contains_mode() {
159 let s = new_omega_skin(3);
160 let j = omega_to_json(&s);
161 assert!(j.contains("mode") ,);
162 }
163
164 #[test]
165 fn test_default_blend_factor_half() {
166 let s = new_omega_skin(2);
167 assert!((s.blend_factor - 0.5).abs() < 1e-5, );
168 }
169
170 #[test]
171 fn test_blended_mode_midpoint() {
172 let mut s = new_omega_skin(1);
173 s.vertices[0].lbs_weight = 0.0;
174 s.vertices[0].omega_weight = 1.0;
175 omega_set_blend(&mut s, 0.5);
176 let w = omega_effective_weight(&s, 0);
177 assert!((w - 0.5).abs() < 1e-5, );
178 }
179}