1use std::path::PathBuf;
2
3use super::*;
4use crate::ShadingModel;
5
6pub(super) fn build(doc: &mut Document<'_>, dir: Option<&Path>) -> common::Scene {
7 let mut meshes = Vec::with_capacity(doc.library_geometries.geometries.len());
8 let mut materials = Vec::with_capacity(doc.library_geometries.geometries.len());
9 let mut instance_geometry_map = HashMap::new();
10 let mut instance_material_map = HashMap::new();
11
12 if let Some(root) = &doc.scene.instance_visual_scene {
13 if let Some(root) = doc
14 .library_visual_scenes
15 .nodes
16 .iter_mut()
17 .find(|n| n.id == Some(root.url.as_str()))
18 {
19 root.transform *= Matrix4x4::new(
20 doc.asset.unit,
21 0.,
22 0.,
23 0.,
24 0.,
25 doc.asset.unit,
26 0.,
27 0.,
28 0.,
29 0.,
30 doc.asset.unit,
31 0.,
32 0.,
33 0.,
34 0.,
35 1.,
36 );
37 }
38 }
39
40 for node in &doc.library_visual_scenes.nodes {
41 for instance_geometry in &node.instance_geometry {
42 let mut transform = node.transform;
43 let mut parent = node.parent;
44 while let Some(p) = parent {
45 let node = &doc.library_visual_scenes.nodes[p];
46 transform *= node.transform;
47 parent = node.parent;
48 }
49 let transform = if transform.is_identity() {
50 None
51 } else {
52 Some(transform)
53 };
54 instance_geometry_map.insert(instance_geometry.url.as_str(), transform);
55 for (symbol, instance_material) in &instance_geometry.materials {
56 match doc.get(&instance_material.target) {
57 Some(material) => {
58 match doc.get(&material.instance_effect.url) {
59 Some(effect) => {
60 instance_material_map.insert(*symbol, (material.name, effect));
61 }
62 None => {
63 }
68 }
69 }
70 None => {
71 }
76 }
77 }
78 }
79 }
80 for geometry in doc.library_geometries.geometries.values() {
81 if let Some(&transform) = instance_geometry_map.get(geometry.id) {
82 meshes.push(build_mesh(doc, geometry, transform));
83 } else {
84 meshes.push(build_mesh(doc, geometry, None));
85 }
86 let mut material = None;
87 for mat in geometry.mesh.primitives.iter().filter_map(|m| m.material) {
88 if let Some(&(name, effect)) = instance_material_map.get(mat) {
89 material = Some(build_material(doc, name, effect, dir));
90 break;
92 }
93 }
94 match material {
95 Some(material) => materials.push(material),
96 None => materials.push(common::Material::default()),
97 }
98 }
99
100 common::Scene { materials, meshes }
101}
102
103fn build_mesh(
104 doc: &Document<'_>,
105 geometry: &Geometry<'_>,
106 transform: Option<Matrix4x4>,
107) -> common::Mesh {
108 let mut mesh = common::Mesh {
109 name: geometry.id.to_owned(),
110 ..Default::default()
111 };
112
113 for prim in (iter::Mesh { doc, xml: geometry }).primitives() {
114 #[allow(clippy::cast_possible_truncation)]
115 let prev_positions_len = mesh.vertices.len() as u32;
116 let p: Vec<_> = prim.positions().collect();
117 let n: Vec<_> = prim.normals().collect();
118 let t: Vec<_> = prim.texcoords(0).collect();
119 let c: Vec<_> = prim.colors().collect();
120 let positions_indices = prim.vertex_indices();
121 let mut normal_indices = prim.normal_indices();
122 let mut texcoord_indices = prim.texcoord_indices(0);
123 let mut color_indices = prim.color_indices();
124 let mut idx = 0;
125
126 for vertex_idx in positions_indices {
127 for vertex_idx in vertex_idx {
128 let mut v = [
129 p[vertex_idx as usize][0],
130 p[vertex_idx as usize][1],
131 p[vertex_idx as usize][2],
132 ];
133 if let Some(transform) = transform {
134 v *= transform;
135 }
136 mesh.vertices.push(v);
137 }
138 if !n.is_empty() {
139 if let Some(normal_idx) = normal_indices.next() {
140 for normal_idx in normal_idx {
141 mesh.normals.push([
142 n[normal_idx as usize][0],
143 n[normal_idx as usize][1],
144 n[normal_idx as usize][2],
145 ]);
146 }
147 } else {
148 panic!()
149 }
150 }
151 if !t.is_empty() {
152 if let Some(texcoord_idx) = texcoord_indices.next() {
153 for texcoord_idx in texcoord_idx {
154 mesh.texcoords[0]
155 .push([t[texcoord_idx as usize][0], t[texcoord_idx as usize][1]]);
156 }
157 } else {
158 panic!()
159 }
160 }
161 if !c.is_empty() {
162 if let Some(rgb_idx) = color_indices.next() {
163 for rgb_idx in rgb_idx {
164 mesh.colors[0].push([
165 c[rgb_idx as usize][0],
166 c[rgb_idx as usize][1],
167 c[rgb_idx as usize][2],
168 1.,
169 ]);
170 }
171 } else {
172 panic!()
173 }
174 }
175 mesh.faces.push([
176 prev_positions_len + idx,
177 prev_positions_len + (idx + 1),
178 prev_positions_len + (idx + 2),
179 ]);
180 idx += 3;
181 }
182 }
183
184 mesh
185}
186
187fn build_material(
188 doc: &Document<'_>,
189 name: Option<&str>,
190 effect: &Effect<'_>,
191 dir: Option<&Path>,
192) -> common::Material {
193 fn texture(
194 doc: &Document<'_>,
195 effect: &Effect<'_>,
196 texture: &Texture<'_>,
197 dir: Option<&Path>,
198 ) -> Option<PathBuf> {
199 if texture.texture.is_empty() {
200 return None;
201 }
202 let mut image = None;
203 if let Some(sampler) = effect.profile.samplers.get(texture.texture) {
204 if let Some(surface) = effect.profile.surfaces.get(sampler.source) {
205 if let Some(i) = doc.get(&surface.init_from) {
206 image = Some(i);
207 }
208 }
209 }
210 if image.is_none() {
211 if let Some(i) = doc.library_images.images.get(&texture.texture) {
212 image = Some(i);
213 }
214 }
215 if let Some(image) = image {
216 match &image.source {
217 ImageSource::Data(_data) => {} ImageSource::InitFrom(mut p) => {
219 if p.is_empty() {
221 return None;
222 }
223 match dir {
224 Some(dir) => {
225 p = p.strip_prefix("file://").unwrap_or(p);
227 let tmp: String;
228 if p.contains('\\') {
229 tmp = p.replace('\\', "/");
230 p = &*tmp;
231 }
232 if p.starts_with("/..") {
233 p = p.strip_prefix('/').unwrap_or(p);
234 }
235 let p = dir.join(p);
236 if p.exists() {
237 return Some(p);
238 }
239 }
240 None => return Some(p.into()),
241 }
242 }
243 ImageSource::Skip => {}
244 }
245 }
246 None
247 }
248
249 let mut mat = common::Material::default();
250
251 if let Some(name) = name {
252 mat.name = name.to_owned();
253 }
254
255 mat.shading_model = match effect.profile.technique.ty {
256 _ if effect.profile.technique.faceted => Some(ShadingModel::Flat),
257 ShadeType::Constant => Some(ShadingModel::NoShading),
258 ShadeType::Lambert => Some(ShadingModel::Gouraud),
259 ShadeType::Blinn => Some(ShadingModel::Blinn),
260 ShadeType::Phong => Some(ShadingModel::Phong),
261 };
262
263 mat.color.ambient = Some(effect.profile.technique.ambient.color);
267 mat.color.diffuse = Some(effect.profile.technique.diffuse.color);
268 mat.color.specular = Some(effect.profile.technique.specular.color);
269 mat.color.emissive = Some(effect.profile.technique.emission.color);
270 mat.color.reflective = Some(effect.profile.technique.reflective.color);
271
272 mat.shininess = Some(effect.profile.technique.shininess);
273 mat.reflectivity = Some(effect.profile.technique.reflectivity);
274 mat.index_of_refraction = Some(effect.profile.technique.index_of_refraction);
275
276 let mut transparency = effect.profile.technique.transparency;
278 let mut transparent = effect.profile.technique.transparent.color;
279 if transparency >= 0. && transparency <= 1. {
280 if effect.profile.technique.rgb_transparency {
281 transparency *=
282 0.212671 * transparent[0] + 0.715160 * transparent[1] + 0.072169 * transparent[2];
283 transparent[3] = 1.;
284 mat.color.transparent = Some(transparent);
285 } else {
286 transparency *= transparent[3];
287 }
288 if effect.profile.technique.invert_transparency {
289 transparency = 1. - transparency;
290 }
291 if effect.profile.technique.has_transparency || transparency < 1. {
292 mat.opacity = Some(transparency);
293 }
294 }
295
296 if let Some(p) = texture(doc, effect, &effect.profile.technique.ambient.texture, dir) {
297 mat.texture.lightmap = Some(p);
299 }
300 if let Some(p) = texture(doc, effect, &effect.profile.technique.emission.texture, dir) {
301 mat.texture.emissive = Some(p);
302 }
303 if let Some(p) = texture(doc, effect, &effect.profile.technique.specular.texture, dir) {
304 mat.texture.specular = Some(p);
305 }
306 if let Some(p) = texture(doc, effect, &effect.profile.technique.diffuse.texture, dir) {
307 mat.texture.diffuse = Some(p);
308 }
309 if let Some(p) = texture(doc, effect, &effect.profile.technique.bump, dir) {
310 mat.texture.normal = Some(p);
311 }
312 if let Some(p) = texture(
313 doc,
314 effect,
315 &effect.profile.technique.transparent.texture,
316 dir,
317 ) {
318 mat.texture.opacity = Some(p);
319 }
320 if let Some(p) = texture(
321 doc,
322 effect,
323 &effect.profile.technique.reflective.texture,
324 dir,
325 ) {
326 mat.texture.reflection = Some(p);
327 }
328
329 mat
330}