Path: blob/main/crates/bevy_gltf/src/loader/gltf_ext/scene.rs
6598 views
use bevy_ecs::name::Name;1use bevy_math::{Mat4, Vec3};2use bevy_transform::components::Transform;34use gltf::scene::Node;56use fixedbitset::FixedBitSet;7use itertools::Itertools;89#[cfg(feature = "bevy_animation")]10use bevy_platform::collections::{HashMap, HashSet};1112use crate::{13convert_coordinates::{ConvertCameraCoordinates as _, ConvertCoordinates as _},14GltfError,15};1617pub(crate) fn node_name(node: &Node) -> Name {18let name = node19.name()20.map(ToString::to_string)21.unwrap_or_else(|| format!("GltfNode{}", node.index()));22Name::new(name)23}2425/// Calculate the transform of gLTF [`Node`].26///27/// This should be used instead of calling [`gltf::scene::Transform::matrix()`]28/// on [`Node::transform()`](gltf::Node::transform) directly because it uses optimized glam types and29/// if `libm` feature of `bevy_math` crate is enabled also handles cross30/// platform determinism properly.31pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transform {32let transform = match node.transform() {33gltf::scene::Transform::Matrix { matrix } => {34Transform::from_matrix(Mat4::from_cols_array_2d(&matrix))35}36gltf::scene::Transform::Decomposed {37translation,38rotation,39scale,40} => Transform {41translation: Vec3::from(translation),42rotation: bevy_math::Quat::from_array(rotation),43scale: Vec3::from(scale),44},45};46if convert_coordinates {47if node.camera().is_some() || node.light().is_some() {48transform.convert_camera_coordinates()49} else {50transform.convert_coordinates()51}52} else {53transform54}55}5657#[cfg_attr(58not(target_arch = "wasm32"),59expect(60clippy::result_large_err,61reason = "need to be signature compatible with `load_gltf`"62)63)]64/// Check if [`Node`] is part of cycle65pub(crate) fn check_is_part_of_cycle(66node: &Node,67visited: &mut FixedBitSet,68) -> Result<(), GltfError> {69// Do we have a cycle?70if visited.contains(node.index()) {71return Err(GltfError::CircularChildren(format!(72"glTF nodes form a cycle: {} -> {}",73visited.ones().map(|bit| bit.to_string()).join(" -> "),74node.index()75)));76}7778// Recurse.79visited.insert(node.index());80for kid in node.children() {81check_is_part_of_cycle(&kid, visited)?;82}83visited.remove(node.index());8485Ok(())86}8788#[cfg(feature = "bevy_animation")]89pub(crate) fn collect_path(90node: &Node,91current_path: &[Name],92paths: &mut HashMap<usize, (usize, Vec<Name>)>,93root_index: usize,94visited: &mut HashSet<usize>,95) {96let mut path = current_path.to_owned();97path.push(node_name(node));98visited.insert(node.index());99for child in node.children() {100if !visited.contains(&child.index()) {101collect_path(&child, &path, paths, root_index, visited);102}103}104paths.insert(node.index(), (root_index, path));105}106107108