oxihuman_export/
geometry_cache_v2_export.rs1#![allow(dead_code)]
4
5pub const GCV2_MAGIC: &[u8; 4] = b"GCV2";
9pub const GCV2_VERSION: u32 = 2;
11
12#[allow(dead_code)]
14#[derive(Debug, Clone)]
15pub struct GeoCacheV2Frame {
16 pub time: f32,
17 pub positions: Vec<[f32; 3]>,
18 pub normals: Vec<[f32; 3]>,
19 pub uvs: Vec<[f32; 2]>,
20}
21
22#[allow(dead_code)]
24#[derive(Debug, Clone)]
25pub struct GeoCacheV2Export {
26 pub frames: Vec<GeoCacheV2Frame>,
27 pub vertex_count: usize,
28}
29
30#[allow(dead_code)]
32pub fn new_geo_cache_v2(vertex_count: usize) -> GeoCacheV2Export {
33 GeoCacheV2Export {
34 frames: Vec::new(),
35 vertex_count,
36 }
37}
38
39#[allow(dead_code)]
41pub fn add_geo_v2_frame(
42 cache: &mut GeoCacheV2Export,
43 time: f32,
44 positions: Vec<[f32; 3]>,
45 normals: Vec<[f32; 3]>,
46 uvs: Vec<[f32; 2]>,
47) {
48 cache.frames.push(GeoCacheV2Frame {
49 time,
50 positions,
51 normals,
52 uvs,
53 });
54}
55
56#[allow(dead_code)]
58pub fn geo_v2_frame_count(cache: &GeoCacheV2Export) -> usize {
59 cache.frames.len()
60}
61
62#[allow(dead_code)]
64pub fn geo_v2_duration(cache: &GeoCacheV2Export) -> f32 {
65 cache.frames.iter().map(|f| f.time).fold(0.0_f32, f32::max)
66}
67
68#[allow(dead_code)]
70pub fn validate_geo_cache_v2(cache: &GeoCacheV2Export) -> bool {
71 cache.frames.iter().all(|f| {
72 f.positions.len() == cache.vertex_count
73 && (f.normals.is_empty() || f.normals.len() == cache.vertex_count)
74 && (f.uvs.is_empty() || f.uvs.len() == cache.vertex_count)
75 })
76}
77
78#[allow(dead_code)]
80pub fn geo_v2_size_bytes(cache: &GeoCacheV2Export) -> usize {
81 cache
82 .frames
83 .iter()
84 .map(|f| f.positions.len() * 12 + f.normals.len() * 12 + f.uvs.len() * 8)
85 .sum::<usize>()
86 + 8
87}
88
89#[allow(dead_code)]
91pub fn geo_v2_header_bytes() -> Vec<u8> {
92 let mut h = GCV2_MAGIC.to_vec();
93 h.extend_from_slice(&GCV2_VERSION.to_le_bytes());
94 h
95}
96
97#[allow(dead_code)]
99pub fn geo_cache_v2_to_json(cache: &GeoCacheV2Export) -> String {
100 format!(
101 "{{\"version\":{},\"vertex_count\":{},\"frame_count\":{}}}",
102 GCV2_VERSION,
103 cache.vertex_count,
104 geo_v2_frame_count(cache)
105 )
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 fn simple_cache() -> GeoCacheV2Export {
113 let mut c = new_geo_cache_v2(3);
114 add_geo_v2_frame(
115 &mut c,
116 0.0,
117 vec![[0.0; 3]; 3],
118 vec![[0.0, 0.0, 1.0]; 3],
119 vec![[0.0; 2]; 3],
120 );
121 c
122 }
123
124 #[test]
125 fn test_new_cache() {
126 let c = new_geo_cache_v2(10);
127 assert_eq!(c.vertex_count, 10);
128 assert_eq!(geo_v2_frame_count(&c), 0);
129 }
130
131 #[test]
132 fn test_add_frame() {
133 let c = simple_cache();
134 assert_eq!(geo_v2_frame_count(&c), 1);
135 }
136
137 #[test]
138 fn test_geo_v2_duration() {
139 let mut c = new_geo_cache_v2(3);
140 add_geo_v2_frame(&mut c, 1.5, vec![[0.0; 3]; 3], vec![], vec![]);
141 assert!((geo_v2_duration(&c) - 1.5).abs() < 1e-6);
142 }
143
144 #[test]
145 fn test_validate_valid() {
146 let c = simple_cache();
147 assert!(validate_geo_cache_v2(&c));
148 }
149
150 #[test]
151 fn test_validate_wrong_vertex_count() {
152 let mut c = new_geo_cache_v2(3);
153 add_geo_v2_frame(&mut c, 0.0, vec![[0.0; 3]; 2], vec![], vec![]);
154 assert!(!validate_geo_cache_v2(&c));
155 }
156
157 #[test]
158 fn test_geo_v2_size_bytes() {
159 let c = simple_cache();
160 let sz = geo_v2_size_bytes(&c);
161 assert!(sz > 0);
162 }
163
164 #[test]
165 fn test_header_bytes() {
166 let h = geo_v2_header_bytes();
167 assert!(h.starts_with(b"GCV2"));
168 }
169
170 #[test]
171 fn test_geo_cache_v2_to_json() {
172 let c = simple_cache();
173 let j = geo_cache_v2_to_json(&c);
174 assert!(j.contains("\"version\":2"));
175 }
176
177 #[test]
178 fn test_duration_empty() {
179 let c = new_geo_cache_v2(0);
180 assert!((geo_v2_duration(&c)).abs() < 1e-9);
181 }
182
183 #[test]
184 fn test_magic_bytes() {
185 assert_eq!(GCV2_MAGIC, b"GCV2");
186 }
187}