1use std::collections::BTreeSet;
2use std::sync::atomic::AtomicBool;
3use std::sync::{Arc, Mutex};
4
5use self::bounds::union_optional;
6use self::diagnostic_overlays::diagnostic_overlay;
7use self::handedness::reject_unproven_left_handed_mesh_import;
8use self::types::{ImportBuild, ImportedNode, PendingSkinBinding, mesh_node_kind};
9use self::units::convert_marker_units;
10pub(super) use self::variants::MeshVariantRecord;
11use super::transforms::compose_transform;
12use super::{
13 ConnectorMetadata, ConnectorPolarity, ConnectorRollPolicy, NodeKey, NodeKind, Scene,
14 SceneSkinBinding, Transform,
15};
16use crate::animation::{AnimationClip, AnimationClipKey};
17use crate::assets::SceneAsset;
18use crate::diagnostics::{ImportDiagnosticOverlay, ImportDiagnosticOverlayKind, InstantiateError};
19
20mod accessors;
21mod bounds;
22mod diagnostic_overlays;
23mod handedness;
24mod load;
25mod lookups;
26mod options;
27mod types;
28mod units;
29mod variants;
30
31#[derive(Debug, Clone)]
32pub struct SceneImport {
33 roots: Vec<NodeKey>,
34 records: Vec<ImportedNode>,
35 anchors: Vec<ImportAnchor>,
36 connectors: Vec<ImportConnector>,
37 clips: Vec<ImportClip>,
38 diagnostic_overlays: Vec<ImportDiagnosticOverlay>,
39 source_units: SourceUnits,
40 source_coordinate_system: SourceCoordinateSystem,
41 live: Arc<AtomicBool>,
42 pub(super) material_variants: Vec<String>,
44 pub(super) active_variant: Arc<Mutex<Option<u32>>>,
45 pub(super) variant_records: Vec<MeshVariantRecord>,
46}
47
48#[derive(Debug, Clone)]
49pub struct ImportAnchor {
50 name: String,
51 node: NodeKey,
52 placement_node: NodeKey,
53 transform: Transform,
54 placement_transform: Transform,
55 tags: BTreeSet<String>,
56 label: Option<String>,
57 source_units: SourceUnits,
58 source_coordinate_system: SourceCoordinateSystem,
59 live: Arc<AtomicBool>,
60}
61
62#[derive(Debug, Clone)]
63pub struct ImportConnector {
64 name: String,
65 kind: Option<String>,
66 allowed_mates: Vec<String>,
67 tags: BTreeSet<String>,
68 snap_tolerance: Option<f32>,
69 clearance_hint: Option<f32>,
70 roll_policy: ConnectorRollPolicy,
71 polarity: Option<ConnectorPolarity>,
72 metadata: Option<ConnectorMetadata>,
73 node: NodeKey,
74 placement_node: NodeKey,
75 transform: Transform,
76 placement_transform: Transform,
77 source_units: SourceUnits,
78 source_coordinate_system: SourceCoordinateSystem,
79 live: Arc<AtomicBool>,
80}
81
82#[derive(Debug, Clone, PartialEq)]
83pub struct ImportAnchorDebugMetadata {
84 name: String,
85 node: NodeKey,
86 transform: Transform,
87}
88
89#[derive(Debug, Clone, PartialEq)]
90pub struct ImportClip {
91 clip: AnimationClip,
92}
93
94#[derive(Debug, Clone, PartialEq)]
95pub struct ImportPivot {
96 name: Option<String>,
97 node: NodeKey,
98 transform: Transform,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
102pub struct ImportOptions {
103 source_units: SourceUnits,
104 source_coordinate_system: SourceCoordinateSystem,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108pub enum SourceUnits {
109 #[default]
110 Meters,
111 Centimeters,
112 Millimeters,
113 Inches,
114 Feet,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
118pub enum SourceCoordinateSystem {
119 #[default]
120 GltfYUpRightHanded,
121 YUpLeftHanded,
122 ZUpRightHanded,
123 ZUpLeftHanded,
124}
125
126impl Scene {
127 pub fn instantiate(
128 &mut self,
129 scene_asset: &SceneAsset,
130 ) -> Result<SceneImport, InstantiateError> {
131 self.instantiate_with(scene_asset, ImportOptions::gltf_default())
132 }
133
134 pub fn instantiate_with(
135 &mut self,
136 scene_asset: &SceneAsset,
137 options: ImportOptions,
138 ) -> Result<SceneImport, InstantiateError> {
139 reject_unproven_left_handed_mesh_import(scene_asset, options)?;
140 let nodes = scene_asset.nodes();
141 let mut child_indices = BTreeSet::new();
142 for node in nodes {
143 child_indices.extend(node.children().iter().copied());
144 }
145
146 let roots = (0..nodes.len())
147 .filter(|index| !child_indices.contains(index))
148 .collect::<Vec<_>>();
149 let mut import = SceneImport {
150 roots: Vec::new(),
151 records: Vec::new(),
152 anchors: Vec::new(),
153 connectors: Vec::new(),
154 clips: Vec::new(),
155 diagnostic_overlays: Vec::new(),
156 source_units: options.source_units(),
157 source_coordinate_system: options.source_coordinate_system(),
158 live: Arc::new(AtomicBool::new(true)),
159 material_variants: scene_asset.material_variants().to_vec(),
160 active_variant: Arc::new(Mutex::new(None)),
161 variant_records: Vec::new(),
162 };
163 let mut pending_skin_bindings = Vec::new();
164 for source_index in roots {
165 let mut build = ImportBuild {
166 scene_asset,
167 options,
168 import_live: &import.live,
169 records: &mut import.records,
170 anchors: &mut import.anchors,
171 connectors: &mut import.connectors,
172 diagnostic_overlays: &mut import.diagnostic_overlays,
173 pending_skin_bindings: &mut pending_skin_bindings,
174 variant_records: &mut import.variant_records,
175 };
176 let node = self.instantiate_scene_asset_node(
177 source_index,
178 self.root,
179 None,
180 None,
181 Transform::IDENTITY,
182 &mut build,
183 )?;
184 import.roots.push(node);
185 }
186 self.resolve_import_skin_bindings(
187 scene_asset,
188 &import.records,
189 pending_skin_bindings.as_slice(),
190 )?;
191 import.clips = scene_asset
192 .clips()
193 .iter()
194 .map(|clip| {
195 let rebased = clip.clip().rebind(
196 AnimationClipKey::fresh(),
197 |source_index| {
198 import
199 .records
200 .iter()
201 .find(|record| record.source_index == source_index)
202 .map(|record| record.node)
203 },
204 |target, value| options.convert_animation_vec3(target, value),
205 );
206 ImportClip { clip: rebased }
207 })
208 .collect();
209 Ok(import)
210 }
211
212 fn instantiate_scene_asset_node(
213 &mut self,
214 source_index: usize,
215 parent: NodeKey,
216 imported_parent: Option<NodeKey>,
217 import_root: Option<NodeKey>,
218 root_from_parent: Transform,
219 build: &mut ImportBuild<'_>,
220 ) -> Result<NodeKey, InstantiateError> {
221 let source_node = build.scene_asset.nodes().get(source_index).ok_or(
222 InstantiateError::InvalidChildIndex {
223 parent: source_index,
224 child: source_index,
225 },
226 )?;
227 let transform = build.options.convert_transform(source_node.transform());
228 let meshes = source_node.meshes();
229 let skin = source_node.skin();
230 let bounds = meshes.iter().fold(None, |bounds, mesh| {
231 Some(union_optional(bounds, mesh.bounds()))
232 });
233 let node = match (meshes, source_node.light()) {
234 ([mesh], _) => {
235 let node = self.insert_node(parent, mesh_node_kind(mesh), transform);
236 if let Ok(node) = node {
237 self.set_initial_morph_weights(node, mesh.morph_weights());
238 if let Some(skin) = skin {
239 build.pending_skin_bindings.push(PendingSkinBinding {
240 node,
241 source_node: source_index,
242 skin,
243 });
244 }
245 if !mesh.material_variant_bindings().is_empty() {
246 build.variant_records.push(MeshVariantRecord {
247 node,
248 default_material: mesh.material(),
249 bindings: mesh.material_variant_bindings().to_vec(),
250 });
251 }
252 }
253 node
254 }
255 ([_, _, ..], _) => {
256 let node = self.insert_node(parent, NodeKind::Empty, transform);
257 if let Ok(parent) = node {
258 for mesh in meshes {
259 let child = self
260 .insert_node(parent, mesh_node_kind(mesh), Transform::IDENTITY)
261 .expect("multi-primitive parent was inserted by this scene");
262 self.node_bounds.insert(child, mesh.bounds());
263 self.set_initial_morph_weights(child, mesh.morph_weights());
264 if let Some(skin) = skin {
265 build.pending_skin_bindings.push(PendingSkinBinding {
266 node: child,
267 source_node: source_index,
268 skin,
269 });
270 }
271 if !mesh.material_variant_bindings().is_empty() {
272 build.variant_records.push(MeshVariantRecord {
273 node: child,
274 default_material: mesh.material(),
275 bindings: mesh.material_variant_bindings().to_vec(),
276 });
277 }
278 }
279 }
280 node
281 }
282 ([], Some(light)) => match light.light() {
283 super::Light::Directional(light) => self
284 .directional_light(light)
285 .parent(parent)
286 .transform(transform)
287 .add(),
288 super::Light::Point(light) => self
289 .point_light(light)
290 .parent(parent)
291 .transform(transform)
292 .add(),
293 super::Light::Spot(light) => self
294 .spot_light(light)
295 .parent(parent)
296 .transform(transform)
297 .add(),
298 },
299 ([], None) => self.insert_node(parent, NodeKind::Empty, transform),
300 }
301 .expect("import parent was inserted by this scene");
302 build.records.push(ImportedNode {
303 source_index,
304 node,
305 parent: imported_parent,
306 name: source_node.name().map(str::to_string),
307 bounds,
308 });
309 let placement_node = import_root.unwrap_or(node);
310 let root_from_node = match import_root {
311 Some(_) => compose_transform(root_from_parent, transform),
312 None => Transform::IDENTITY,
313 };
314 let label = source_node.name().map(str::to_string);
315 let overlay_options = build.options;
316 build.diagnostic_overlays.push(diagnostic_overlay(
317 overlay_options,
318 ImportDiagnosticOverlayKind::Origin,
319 node,
320 transform,
321 None,
322 label.clone(),
323 ));
324 build.diagnostic_overlays.push(diagnostic_overlay(
325 overlay_options,
326 ImportDiagnosticOverlayKind::Axes,
327 node,
328 transform,
329 None,
330 label.clone(),
331 ));
332 if let Some(bounds) = bounds {
333 self.node_bounds.insert(node, bounds);
334 build.diagnostic_overlays.push(diagnostic_overlay(
335 overlay_options,
336 ImportDiagnosticOverlayKind::Bounds,
337 node,
338 Transform::IDENTITY,
339 Some(bounds),
340 label.clone(),
341 ));
342 }
343 let mut anchor_names = BTreeSet::new();
344 for anchor in source_node.anchors() {
345 if let Some(reason) = anchor.invalid_reason() {
346 return Err(InstantiateError::InvalidAnchorExtras {
347 node: source_node.name().unwrap_or("<unnamed>").to_string(),
348 reason: reason.to_string(),
349 });
350 }
351 if !anchor_names.insert(anchor.name()) {
352 return Err(InstantiateError::InvalidAnchorExtras {
353 node: source_node.name().unwrap_or("<unnamed>").to_string(),
354 reason: format!("duplicate anchor '{}'", anchor.name()),
355 });
356 }
357 let anchor_units = anchor
358 .source_units()
359 .unwrap_or(build.options.source_units());
360 let anchor_transform = convert_marker_units(
361 anchor.transform(),
362 anchor_units,
363 build.options.source_units(),
364 );
365 let anchor_connection_transform = build
366 .options
367 .source_coordinate_system()
368 .convert_connector_transform(anchor_transform);
369 build.anchors.push(ImportAnchor {
370 name: anchor.name().to_string(),
371 node,
372 placement_node,
373 transform: anchor_transform,
374 placement_transform: compose_transform(root_from_node, anchor_connection_transform),
375 tags: anchor.tags().clone(),
376 label: anchor.label().map(str::to_string),
377 source_units: anchor_units,
378 source_coordinate_system: build.options.source_coordinate_system(),
379 live: Arc::clone(build.import_live),
380 });
381 build.diagnostic_overlays.push(diagnostic_overlay(
382 overlay_options,
383 ImportDiagnosticOverlayKind::Anchor,
384 node,
385 anchor_transform,
386 None,
387 Some(anchor.name().to_string()),
388 ));
389 if anchor.name() == "pivot" {
390 build.diagnostic_overlays.push(diagnostic_overlay(
391 overlay_options,
392 ImportDiagnosticOverlayKind::Pivot,
393 node,
394 anchor_transform,
395 None,
396 Some(anchor.name().to_string()),
397 ));
398 }
399 }
400 let mut connector_names = BTreeSet::new();
401 for connector in source_node.connectors() {
402 if let Some(reason) = connector.invalid_reason() {
403 return Err(InstantiateError::InvalidAnchorExtras {
404 node: source_node.name().unwrap_or("<unnamed>").to_string(),
405 reason: reason.to_string(),
406 });
407 }
408 if !connector_names.insert(connector.name()) {
409 return Err(InstantiateError::InvalidAnchorExtras {
410 node: source_node.name().unwrap_or("<unnamed>").to_string(),
411 reason: format!("duplicate connector '{}'", connector.name()),
412 });
413 }
414 let connector_transform = connector.transform();
415 let connector_connection_transform = build
416 .options
417 .source_coordinate_system()
418 .convert_connector_transform(connector_transform);
419 build.connectors.push(ImportConnector {
420 name: connector.name().to_string(),
421 kind: connector.kind().map(str::to_string),
422 allowed_mates: connector
423 .allowed_mates()
424 .into_iter()
425 .map(str::to_string)
426 .collect(),
427 tags: connector.tags().clone(),
428 snap_tolerance: connector.snap_tolerance(),
429 clearance_hint: connector.clearance_hint(),
430 roll_policy: connector.roll_policy(),
431 polarity: connector.polarity(),
432 metadata: connector.metadata().cloned(),
433 node,
434 placement_node,
435 transform: connector_transform,
436 placement_transform: compose_transform(
437 root_from_node,
438 connector_connection_transform,
439 ),
440 source_units: build.options.source_units(),
441 source_coordinate_system: build.options.source_coordinate_system(),
442 live: Arc::clone(build.import_live),
443 });
444 build.diagnostic_overlays.push(diagnostic_overlay(
445 overlay_options,
446 ImportDiagnosticOverlayKind::Connector,
447 node,
448 connector_transform,
449 None,
450 Some(connector.name().to_string()),
451 ));
452 }
453 for child in source_node.children() {
454 if build.scene_asset.nodes().get(*child).is_none() {
455 return Err(InstantiateError::InvalidChildIndex {
456 parent: source_index,
457 child: *child,
458 });
459 }
460 self.instantiate_scene_asset_node(
461 *child,
462 node,
463 Some(node),
464 Some(placement_node),
465 root_from_node,
466 build,
467 )?;
468 }
469 Ok(node)
470 }
471
472 fn resolve_import_skin_bindings(
473 &mut self,
474 scene_asset: &SceneAsset,
475 records: &[ImportedNode],
476 pending: &[PendingSkinBinding],
477 ) -> Result<(), InstantiateError> {
478 for pending in pending {
479 let skin = scene_asset.skins().get(pending.skin).ok_or(
480 InstantiateError::InvalidSkinIndex {
481 node: pending.source_node,
482 skin: pending.skin,
483 },
484 )?;
485 let joints = skin
486 .joints()
487 .iter()
488 .map(|source_joint| {
489 records
490 .iter()
491 .find(|record| record.source_index == *source_joint)
492 .map(|record| record.node)
493 .ok_or(InstantiateError::InvalidSkinJointIndex {
494 skin: pending.skin,
495 joint: *source_joint,
496 })
497 })
498 .collect::<Result<Vec<_>, _>>()?;
499 self.set_initial_skin_binding(
500 pending.node,
501 SceneSkinBinding::new(joints, skin.inverse_bind_matrices().to_vec()),
502 );
503 }
504 Ok(())
505 }
506}