1#![allow(dead_code)]
4
5#[derive(Clone, Debug)]
9pub struct Ac3dSurface {
10 pub flags: u32,
11 pub vertices: Vec<(u32, f32, f32)>, }
13
14#[derive(Clone, Debug)]
16pub struct Ac3dMaterial {
17 pub name: String,
18 pub rgb: [f32; 3],
19 pub amb: [f32; 3],
20 pub emis: [f32; 3],
21 pub spec: [f32; 3],
22 pub shi: u32,
23 pub trans: f32,
24}
25
26impl Default for Ac3dMaterial {
27 fn default() -> Self {
28 Self {
29 name: "default".to_string(),
30 rgb: [0.8, 0.8, 0.8],
31 amb: [0.2, 0.2, 0.2],
32 emis: [0.0, 0.0, 0.0],
33 spec: [0.5, 0.5, 0.5],
34 shi: 10,
35 trans: 0.0,
36 }
37 }
38}
39
40#[derive(Clone, Debug)]
42pub struct Ac3dObject {
43 pub name: String,
44 pub positions: Vec<[f32; 3]>,
45 pub surfaces: Vec<Ac3dSurface>,
46 pub mat_index: u32,
47}
48
49#[derive(Clone, Debug, Default)]
51pub struct Ac3dExport {
52 pub materials: Vec<Ac3dMaterial>,
53 pub objects: Vec<Ac3dObject>,
54}
55
56pub fn new_ac3d_export() -> Ac3dExport {
58 Ac3dExport::default()
59}
60
61pub fn ac3d_add_material(doc: &mut Ac3dExport, mat: Ac3dMaterial) -> u32 {
63 let idx = doc.materials.len() as u32;
64 doc.materials.push(mat);
65 idx
66}
67
68pub fn ac3d_add_mesh(
70 doc: &mut Ac3dExport,
71 name: &str,
72 positions: Vec<[f32; 3]>,
73 indices: &[u32],
74 mat_index: u32,
75) {
76 let surfaces: Vec<Ac3dSurface> = indices
77 .chunks(3)
78 .map(|t| Ac3dSurface {
79 flags: 0,
80 vertices: t.iter().map(|&i| (i, 0.0, 0.0)).collect(),
81 })
82 .collect();
83 doc.objects.push(Ac3dObject {
84 name: name.to_string(),
85 positions,
86 surfaces,
87 mat_index,
88 });
89}
90
91pub fn ac3d_material_count(doc: &Ac3dExport) -> usize {
93 doc.materials.len()
94}
95
96pub fn ac3d_object_count(doc: &Ac3dExport) -> usize {
98 doc.objects.len()
99}
100
101pub fn render_ac3d(doc: &Ac3dExport) -> String {
103 let mut out = String::from("AC3Db\n");
104 for mat in &doc.materials {
105 out.push_str(&format!(
106 "MATERIAL \"{}\" rgb {:.4} {:.4} {:.4} amb {:.4} {:.4} {:.4} emis {:.4} {:.4} {:.4} spec {:.4} {:.4} {:.4} shi {} trans {:.4}\n",
107 mat.name,
108 mat.rgb[0], mat.rgb[1], mat.rgb[2],
109 mat.amb[0], mat.amb[1], mat.amb[2],
110 mat.emis[0], mat.emis[1], mat.emis[2],
111 mat.spec[0], mat.spec[1], mat.spec[2],
112 mat.shi, mat.trans
113 ));
114 }
115 out.push_str("OBJECT world\nkids ");
116 out.push_str(&format!("{}\n", doc.objects.len()));
117 for obj in &doc.objects {
118 out.push_str("OBJECT poly\n");
119 out.push_str(&format!("name \"{}\"\n", obj.name));
120 out.push_str(&format!("numvert {}\n", obj.positions.len()));
121 for p in &obj.positions {
122 out.push_str(&format!("{:.6} {:.6} {:.6}\n", p[0], p[1], p[2]));
123 }
124 out.push_str(&format!("numsurf {}\n", obj.surfaces.len()));
125 for surf in &obj.surfaces {
126 out.push_str(&format!("SURF 0x{:02X}\n", surf.flags));
127 out.push_str(&format!("mat {}\n", obj.mat_index));
128 out.push_str(&format!("refs {}\n", surf.vertices.len()));
129 for &(idx, u, v) in &surf.vertices {
130 out.push_str(&format!("{} {:.4} {:.4}\n", idx, u, v));
131 }
132 }
133 out.push_str("kids 0\n");
134 }
135 out
136}
137
138pub fn ac3d_size_estimate(doc: &Ac3dExport) -> usize {
140 render_ac3d(doc).len()
141}
142
143pub fn validate_ac3d(doc: &Ac3dExport) -> bool {
145 for obj in &doc.objects {
146 let n = obj.positions.len() as u32;
147 for surf in &obj.surfaces {
148 for &(idx, _, _) in &surf.vertices {
149 if idx >= n {
150 return false;
151 }
152 }
153 }
154 }
155 true
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 fn simple_doc() -> Ac3dExport {
163 let mut d = new_ac3d_export();
164 let mat_idx = ac3d_add_material(&mut d, Ac3dMaterial::default());
165 ac3d_add_mesh(
166 &mut d,
167 "tri",
168 vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
169 &[0, 1, 2],
170 mat_idx,
171 );
172 d
173 }
174
175 #[test]
176 fn material_count() {
177 let d = simple_doc();
178 assert_eq!(ac3d_material_count(&d), 1);
179 }
180
181 #[test]
182 fn object_count() {
183 let d = simple_doc();
184 assert_eq!(ac3d_object_count(&d), 1);
185 }
186
187 #[test]
188 fn render_starts_with_ac3d() {
189 let d = simple_doc();
190 assert!(render_ac3d(&d).starts_with("AC3Db\n"));
191 }
192
193 #[test]
194 fn render_contains_material() {
195 let d = simple_doc();
196 assert!(render_ac3d(&d).contains("MATERIAL"));
197 }
198
199 #[test]
200 fn render_contains_numvert() {
201 let d = simple_doc();
202 assert!(render_ac3d(&d).contains("numvert 3"));
203 }
204
205 #[test]
206 fn validate_valid() {
207 let d = simple_doc();
208 assert!(validate_ac3d(&d));
209 }
210
211 #[test]
212 fn size_estimate_positive() {
213 let d = simple_doc();
214 assert!(ac3d_size_estimate(&d) > 0);
215 }
216
217 #[test]
218 fn empty_doc_valid() {
219 let d = new_ac3d_export();
220 assert!(validate_ac3d(&d));
221 }
222}