1use optic_core::ATTRType;
2
3use crate::asset::attr::DataType;
4
5#[derive(Clone, Debug, PartialEq)]
11pub enum ATTRName {
12 Custom(String),
13 Pos2D,
14 Pos3D,
15 Col,
16 UVM,
17 Nrm,
18 Ind,
19 Rot3D,
20 Rot2D,
21 Scale3D,
22 Scale2D,
23}
24
25impl ATTRName {
26 pub fn as_string(&self) -> String {
28 match self {
29 ATTRName::Pos2D => "pos2d".into(),
30 ATTRName::Pos3D => "pos3d".into(),
31 ATTRName::Col => "color".into(),
32 ATTRName::UVM => "uv map".into(),
33 ATTRName::Nrm => "normals".into(),
34 ATTRName::Ind => "indices".into(),
35 ATTRName::Rot3D => "rotation 3d".into(),
36 ATTRName::Rot2D => "rotation 2d".into(),
37 ATTRName::Scale3D => "scale 3d".into(),
38 ATTRName::Scale2D => "scale 2d".into(),
39 ATTRName::Custom(n) => format!("{n}(custom)"),
40 }
41 }
42}
43
44#[derive(Clone, Debug)]
49pub struct ATTRInfo {
50 pub name: ATTRName,
51 pub typ: ATTRType,
52 pub byte_count: usize,
53 pub elem_count: usize,
54}
55
56impl ATTRInfo {
57 pub fn empty() -> Self {
59 Self {
60 name: ATTRName::Pos3D,
61 typ: ATTRType::F32,
62 byte_count: 0,
63 elem_count: 0,
64 }
65 }
66
67 pub fn fmt_as_string(&self) -> String {
69 let typ_str = match self.typ {
70 ATTRType::U8 => "u8",
71 ATTRType::I8 => "i8",
72 ATTRType::U16 => "u16",
73 ATTRType::I16 => "i16",
74 ATTRType::U32 => "u32",
75 ATTRType::I32 => "i32",
76 ATTRType::F32 => "f32",
77 ATTRType::F64 => "f64",
78 };
79 if self.elem_count == 1 {
80 typ_str.to_string()
81 } else {
82 format!("[{typ_str};{}]", self.elem_count)
83 }
84 }
85}
86
87macro_rules! attr {
88 ($(#[$meta:meta])* $attr:ident, $typ:ty, $name:expr) => {
89 $(#[$meta])*
90 #[derive(Debug, Clone)]
91 pub struct $attr {
92 pub data: Vec<$typ>,
93 pub info: ATTRInfo,
94 }
95
96 impl $attr {
97 pub fn empty() -> Self {
98 let mut info = ATTRInfo::empty();
99 info.typ = <$typ>::ATTR_FORMAT;
100 info.byte_count = <$typ>::BYTE_COUNT;
101 info.elem_count = <$typ>::ELEM_COUNT;
102 info.name = $name;
103 Self { data: Vec::new(), info }
104 }
105
106 pub fn from(vec: Vec<$typ>) -> Self {
107 let mut attr = Self::empty();
108 for elem in vec {
109 attr.data.push(elem);
110 }
111 attr
112 }
113
114 pub fn from_array(array: &[$typ]) -> Self {
115 Self::from(Vec::from(array))
116 }
117
118 pub fn push(&mut self, elem: $typ) {
119 self.data.push(elem);
120 }
121
122 pub fn is_empty(&self) -> bool {
123 self.data.is_empty()
124 }
125 }
126 };
127}
128
129attr!(Pos3DATTR, [f32; 3], ATTRName::Pos3D);
131attr!(Pos2DATTR, [f32; 2], ATTRName::Pos2D);
133attr!(ColATTR, [f32; 4], ATTRName::Col);
135attr!(UVMATTR, [f32; 2], ATTRName::UVM);
137attr!(NrmATTR, [f32; 3], ATTRName::Nrm);
139attr!(IndATTR, u32, ATTRName::Ind);
141attr!(Rot3DATTR, [f32; 4], ATTRName::Rot3D);
143attr!(Rot2DATTR, f32, ATTRName::Rot2D);
145attr!(Scale3DATTR, [f32; 3], ATTRName::Scale3D);
147attr!(Scale2DATTR, [f32; 2], ATTRName::Scale2D);
149
150#[derive(Debug)]
164pub struct CustomATTR {
165 pub data: Vec<u8>,
166 pub info: ATTRInfo,
167}
168
169impl CustomATTR {
170 pub fn empty<D: DataType>(name: &str) -> Self {
172 let mut info = ATTRInfo::empty();
173 info.typ = D::ATTR_FORMAT;
174 info.byte_count = D::BYTE_COUNT;
175 info.elem_count = D::ELEM_COUNT;
176 info.name = ATTRName::Custom(name.to_string());
177 Self { data: Vec::new(), info }
178 }
179
180 pub fn from<D: DataType>(name: &str, vec: Vec<D>) -> Self {
182 let mut attr = Self::empty::<D>(name);
183 for elem in vec {
184 let bytes = elem.u8ify();
185 attr.data.extend_from_slice(&bytes);
186 }
187 attr
188 }
189
190 pub fn from_array<D: DataType + Clone>(name: &str, array: &[D]) -> Self {
192 Self::from(name, Vec::from(array))
193 }
194
195 pub fn push<D: DataType>(&mut self, elem: D) {
197 let bytes = elem.u8ify();
198 self.data.extend_from_slice(&bytes);
199 }
200
201 pub fn is_empty(&self) -> bool {
203 self.data.is_empty()
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn attr_info_empty() {
213 let info = ATTRInfo::empty();
214 assert_eq!(info.byte_count, 0);
215 assert_eq!(info.elem_count, 0);
216 }
217
218 #[test]
219 fn attr_info_fmt_as_string() {
220 let mut info = ATTRInfo::empty();
221 info.typ = ATTRType::F32;
222 info.elem_count = 3;
223 assert_eq!(info.fmt_as_string(), "[f32;3]");
224 info.elem_count = 1;
225 assert_eq!(info.fmt_as_string(), "f32");
226 }
227
228 #[test]
229 fn attr_name_as_string() {
230 assert_eq!(ATTRName::Pos2D.as_string(), "pos2d");
231 assert_eq!(ATTRName::Pos3D.as_string(), "pos3d");
232 assert_eq!(ATTRName::Col.as_string(), "color");
233 assert_eq!(ATTRName::UVM.as_string(), "uv map");
234 assert_eq!(ATTRName::Nrm.as_string(), "normals");
235 assert_eq!(ATTRName::Ind.as_string(), "indices");
236 assert_eq!(ATTRName::Rot3D.as_string(), "rotation 3d");
237 assert_eq!(ATTRName::Rot2D.as_string(), "rotation 2d");
238 assert_eq!(ATTRName::Scale3D.as_string(), "scale 3d");
239 assert_eq!(ATTRName::Scale2D.as_string(), "scale 2d");
240 let custom = ATTRName::Custom("user_data".into());
241 assert_eq!(custom.as_string(), "user_data(custom)");
242 }
243
244 #[test]
245 fn pos3d_attr() {
246 let mut attr = Pos3DATTR::empty();
247 assert!(attr.is_empty());
248 attr.push([1.0, 2.0, 3.0]);
249 assert!(!attr.is_empty());
250 assert_eq!(attr.data.len(), 1);
251 assert_eq!(attr.data[0], [1.0, 2.0, 3.0]);
252 }
253
254 #[test]
255 fn pos3d_from_array() {
256 let attr = Pos3DATTR::from_array(&[[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]);
257 assert_eq!(attr.data.len(), 2);
258 assert_eq!(attr.info.name.as_string(), "pos3d");
259 }
260
261 #[test]
262 fn pos2d_attr() {
263 let mut attr = Pos2DATTR::empty();
264 attr.push([0.5, 0.5]);
265 assert_eq!(attr.data[0], [0.5, 0.5]);
266 }
267
268 #[test]
269 fn color_attr() {
270 let mut attr = ColATTR::empty();
271 attr.push([1.0, 0.0, 0.0, 1.0]);
272 assert_eq!(attr.data[0], [1.0, 0.0, 0.0, 1.0]);
273 }
274
275 #[test]
276 fn uvm_attr() {
277 let attr = UVMATTR::from_array(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]);
278 assert_eq!(attr.data.len(), 4);
279 }
280
281 #[test]
282 fn nrm_attr() {
283 let attr = NrmATTR::from_array(&[[0.0, 1.0, 0.0]]);
284 assert_eq!(attr.data[0], [0.0, 1.0, 0.0]);
285 }
286
287 #[test]
288 fn ind_attr() {
289 let mut attr = IndATTR::empty();
290 attr.push(0);
291 attr.push(1);
292 attr.push(2);
293 assert_eq!(attr.data, vec![0, 1, 2]);
294 }
295
296 #[test]
297 fn rot3d_attr() {
298 let mut attr = Rot3DATTR::empty();
299 attr.push([0.0, 0.0, 0.0, 1.0]);
300 assert_eq!(attr.data[0], [0.0, 0.0, 0.0, 1.0]);
301 assert_eq!(attr.info.elem_count, 4);
302 assert_eq!(attr.info.byte_count, 4);
303 }
304
305 #[test]
306 fn rot2d_attr() {
307 let mut attr = Rot2DATTR::empty();
308 attr.push(1.5708);
309 assert!((attr.data[0] - 1.5708).abs() < 0.001);
310 assert_eq!(attr.info.elem_count, 1);
311 }
312
313 #[test]
314 fn scale3d_attr() {
315 let attr = Scale3DATTR::from_array(&[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]]);
316 assert_eq!(attr.data.len(), 2);
317 assert_eq!(attr.info.name.as_string(), "scale 3d");
318 }
319
320 #[test]
321 fn custom_attr_empty() {
322 let attr = CustomATTR::empty::<[f32; 3]>("weights");
323 assert!(attr.is_empty());
324 assert_eq!(attr.info.name.as_string(), "weights(custom)");
325 assert_eq!(attr.info.typ, ATTRType::F32);
326 assert_eq!(attr.info.elem_count, 3);
327 }
328
329 #[test]
330 fn custom_attr_from_array() {
331 let attr = CustomATTR::from_array::<[f32; 3]>("bone_weights", &[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]);
332 assert!(!attr.is_empty());
333 assert_eq!(attr.data.len(), 24); }
335
336 #[test]
337 fn custom_attr_push() {
338 let mut attr = CustomATTR::empty::<u32>("ids");
339 attr.push(42u32);
340 attr.push(99u32);
341 assert_eq!(attr.data.len(), 8); }
343}