use crate::ecs::animation::components::{AnimationClip, AnimationPlayer};
use crate::ecs::bounding_volume::components::BoundingVolume;
use crate::ecs::generational_registry::registry_entry_by_name;
use crate::ecs::material::resources::material_registry_insert;
use crate::ecs::morph::components::MorphWeights;
#[cfg(feature = "assets")]
use crate::ecs::prefab::commands::gltf_import::GltfSkin;
#[cfg(feature = "assets")]
use crate::ecs::skin::components::{Joint, Skin};
use crate::ecs::world::components::MaterialRef;
use crate::ecs::world::components::{GlobalTransform, LocalTransformDirty, Parent};
use crate::ecs::world::{
ANIMATION_PLAYER, BOUNDING_VOLUME, CAMERA, CASTS_SHADOW, COLLIDER, GLOBAL_TRANSFORM,
LOCAL_TRANSFORM, LOCAL_TRANSFORM_DIRTY, MATERIAL_REF, MORPH_WEIGHTS, NAME, PARENT, RENDER_MESH,
RIGID_BODY, SCRIPT, VISIBILITY, Vec3, World,
};
#[cfg(feature = "assets")]
use crate::ecs::world::{JOINT, PREFAB_SOURCE, SKIN};
use crate::render::wgpu::texture_cache::texture_cache_add_reference;
use freecs::Entity;
use std::collections::HashMap;
#[cfg(feature = "assets")]
use std::sync::Arc;
use super::super::components::{Prefab, PrefabNode};
fn get_bounding_volume_from_mesh_name(world: &World, mesh_name: &str) -> Option<BoundingVolume> {
let mesh = registry_entry_by_name(&world.resources.mesh_cache.registry, mesh_name)?;
mesh.bounding_volume
}
pub fn spawn_prefab(world: &mut World, prefab: &Prefab, offset: Vec3) -> Entity {
let parent_entity = world.spawn_entities(
NAME | LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY,
1,
)[0];
world.core.set_name(
parent_entity,
crate::ecs::world::components::Name(prefab.name.clone()),
);
world.core.set_local_transform(
parent_entity,
crate::ecs::world::components::LocalTransform {
translation: offset,
rotation: nalgebra_glm::Quat::identity(),
scale: nalgebra_glm::vec3(1.0, 1.0, 1.0),
},
);
world
.core
.set_global_transform(parent_entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(parent_entity, LocalTransformDirty);
for root_node in &prefab.root_nodes {
spawn_prefab_node(world, root_node, Vec3::zeros(), Some(parent_entity));
}
parent_entity
}
pub fn spawn_prefab_node(
world: &mut World,
node: &PrefabNode,
offset: Vec3,
parent_entity: Option<Entity>,
) -> Entity {
let calculated_bounding_volume = if node.components.bounding_volume.is_none() {
if let Some(render_mesh) = &node.components.render_mesh {
get_bounding_volume_from_mesh_name(world, &render_mesh.name)
} else {
None
}
} else {
None
};
let mut component_mask = LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY;
if node.components.name.is_some() {
component_mask |= NAME;
}
if node.components.render_mesh.is_some() {
component_mask |= RENDER_MESH | CASTS_SHADOW;
}
if node.components.material.is_some() {
component_mask |= MATERIAL_REF;
}
if node.components.camera.is_some() {
component_mask |= CAMERA;
}
if node.components.visibility.is_some() {
component_mask |= VISIBILITY;
}
if node.components.bounding_volume.is_some() || calculated_bounding_volume.is_some() {
component_mask |= BOUNDING_VOLUME;
}
if node.components.rigid_body.is_some() {
component_mask |= RIGID_BODY;
}
if node.components.collider.is_some() {
component_mask |= COLLIDER;
}
if node.components.script.is_some() {
component_mask |= SCRIPT;
}
if parent_entity.is_some() {
component_mask |= PARENT;
}
let entity = world.spawn_entities(component_mask, 1)[0];
let mut local_transform = node.local_transform;
if parent_entity.is_none() {
local_transform.translation += offset;
}
world.core.set_local_transform(entity, local_transform);
world
.core
.set_global_transform(entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(entity, LocalTransformDirty);
if let Some(name) = &node.components.name {
world.core.set_name(entity, name.clone());
}
if let Some(script) = &node.components.script {
world.core.set_script(entity, script.clone());
}
if let Some(render_mesh) = &node.components.render_mesh {
if !world
.resources
.mesh_cache
.registry
.name_to_index
.contains_key(&render_mesh.name)
{
tracing::error!(
"Mesh '{}' not found in mesh_cache when spawning entity {:?}. Skipping RenderMesh component.",
render_mesh.name,
entity
);
} else {
if let Some(&index) = world
.resources
.mesh_cache
.registry
.name_to_index
.get(&render_mesh.name)
{
world.resources.mesh_cache.registry.add_reference(index);
}
world.core.set_render_mesh(entity, render_mesh.clone());
world.resources.mesh_render_state.mark_entity_added(entity);
world
.core
.set_casts_shadow(entity, crate::ecs::shadow::components::CastsShadow);
}
}
if let Some(material) = &node.components.material {
if let Some(base_texture) = &material.base_texture {
texture_cache_add_reference(&mut world.resources.texture_cache, base_texture);
}
if let Some(emissive_texture) = &material.emissive_texture {
texture_cache_add_reference(&mut world.resources.texture_cache, emissive_texture);
}
let node_name = node
.components
.name
.as_ref()
.map(|n| n.0.as_str())
.unwrap_or("unnamed");
let material_name = format!("Prefab_{}_{}", node_name, entity.id);
material_registry_insert(
&mut world.resources.material_registry,
material_name.clone(),
material.clone(),
);
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world
.core
.set_material_ref(entity, MaterialRef::new(material_name));
}
if let Some(camera) = &node.components.camera {
world.core.set_camera(entity, *camera);
}
if let Some(visibility) = &node.components.visibility {
world.core.set_visibility(entity, visibility.clone());
}
if let Some(bounding_volume) = &node.components.bounding_volume {
world.core.set_bounding_volume(entity, *bounding_volume);
} else if let Some(calculated_bv) = calculated_bounding_volume {
world.core.set_bounding_volume(entity, calculated_bv);
}
#[cfg(feature = "physics")]
if let Some(prefab_rigid_body) = &node.components.rigid_body {
super::spawn_physics::apply_prefab_physics(
world,
entity,
prefab_rigid_body,
node.components.collider.as_ref(),
local_transform,
);
}
if let Some(parent) = parent_entity {
if world.core.get_local_transform(parent).is_some() {
world.core.set_parent(entity, Parent(Some(parent)));
world
.resources
.children_cache
.entry(parent)
.or_default()
.push(entity);
} else {
tracing::warn!(
"Parent entity {:?} does not exist or lacks LocalTransform component when spawning entity {:?}",
parent,
entity
);
}
}
for child_node in &node.children {
spawn_prefab_node(world, child_node, offset, Some(entity));
}
entity
}
pub fn spawn_prefab_with_animations(
world: &mut World,
prefab: &Prefab,
animations: &[AnimationClip],
offset: Vec3,
) -> Entity {
let parent_entity = world.spawn_entities(
NAME | LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY,
1,
)[0];
world.core.set_name(
parent_entity,
crate::ecs::world::components::Name(prefab.name.clone()),
);
world.core.set_local_transform(
parent_entity,
crate::ecs::world::components::LocalTransform {
translation: offset,
rotation: nalgebra_glm::Quat::identity(),
scale: nalgebra_glm::vec3(1.0, 1.0, 1.0),
},
);
world
.core
.set_global_transform(parent_entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(parent_entity, LocalTransformDirty);
let mut node_index_to_entity: HashMap<usize, Entity> = HashMap::new();
let mut bone_name_to_entity: HashMap<String, Entity> = HashMap::new();
for root_node in &prefab.root_nodes {
spawn_prefab_node_with_mapping(
world,
root_node,
Vec3::zeros(),
Some(parent_entity),
&mut node_index_to_entity,
&mut bone_name_to_entity,
);
}
if !animations.is_empty() {
let mut animation_player = AnimationPlayer {
clips: animations.to_vec(),
current_clip: if animations.is_empty() { None } else { Some(0) },
time: 0.0,
speed: 1.0,
looping: true,
playing: true,
play_all: false,
node_index_to_entity,
bone_name_to_entity,
blend_from_clip: None,
blend_from_time: 0.0,
blend_factor: 1.0,
blend_duration: 0.0,
};
if !animation_player.clips.is_empty() {
animation_player.play(0);
}
world.core.add_components(parent_entity, ANIMATION_PLAYER);
world
.core
.set_animation_player(parent_entity, animation_player);
}
parent_entity
}
fn spawn_prefab_node_with_mapping(
world: &mut World,
node: &PrefabNode,
offset: Vec3,
parent_entity: Option<Entity>,
node_index_to_entity: &mut HashMap<usize, Entity>,
bone_name_to_entity: &mut HashMap<String, Entity>,
) -> Entity {
let calculated_bounding_volume = if node.components.bounding_volume.is_none() {
if let Some(render_mesh) = &node.components.render_mesh {
get_bounding_volume_from_mesh_name(world, &render_mesh.name)
} else {
None
}
} else {
None
};
let mut component_mask = LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY;
if node.components.name.is_some() {
component_mask |= NAME;
}
if node.components.render_mesh.is_some() {
component_mask |= RENDER_MESH | CASTS_SHADOW;
}
if node.components.material.is_some() {
component_mask |= MATERIAL_REF;
}
if node.components.camera.is_some() {
component_mask |= CAMERA;
}
if node.components.visibility.is_some() {
component_mask |= VISIBILITY;
}
if node.components.bounding_volume.is_some() || calculated_bounding_volume.is_some() {
component_mask |= BOUNDING_VOLUME;
}
if node.components.rigid_body.is_some() {
component_mask |= RIGID_BODY;
}
if node.components.collider.is_some() {
component_mask |= COLLIDER;
}
if node.components.script.is_some() {
component_mask |= SCRIPT;
}
if parent_entity.is_some() {
component_mask |= PARENT;
}
let entity = world.spawn_entities(component_mask, 1)[0];
if let Some(node_index) = node.node_index {
node_index_to_entity.insert(node_index, entity);
}
if let Some(name) = &node.components.name {
bone_name_to_entity.insert(name.0.clone(), entity);
}
let mut local_transform = node.local_transform;
if parent_entity.is_none() {
local_transform.translation += offset;
}
world.core.set_local_transform(entity, local_transform);
world
.core
.set_global_transform(entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(entity, LocalTransformDirty);
if let Some(name) = &node.components.name {
world.core.set_name(entity, name.clone());
}
if let Some(script) = &node.components.script {
world.core.set_script(entity, script.clone());
}
if let Some(render_mesh) = &node.components.render_mesh {
if !world
.resources
.mesh_cache
.registry
.name_to_index
.contains_key(&render_mesh.name)
{
tracing::error!(
"Mesh '{}' not found in mesh_cache when spawning entity {:?}. Skipping RenderMesh component.",
render_mesh.name,
entity
);
} else {
if let Some(&index) = world
.resources
.mesh_cache
.registry
.name_to_index
.get(&render_mesh.name)
{
world.resources.mesh_cache.registry.add_reference(index);
}
world.core.set_render_mesh(entity, render_mesh.clone());
world.resources.mesh_render_state.mark_entity_added(entity);
world
.core
.set_casts_shadow(entity, crate::ecs::shadow::components::CastsShadow);
}
}
if let Some(material) = &node.components.material {
if let Some(base_texture) = &material.base_texture {
texture_cache_add_reference(&mut world.resources.texture_cache, base_texture);
}
if let Some(emissive_texture) = &material.emissive_texture {
texture_cache_add_reference(&mut world.resources.texture_cache, emissive_texture);
}
let node_name = node
.components
.name
.as_ref()
.map(|n| n.0.as_str())
.unwrap_or("unnamed");
let material_name = format!("Prefab_{}_{}", node_name, entity.id);
material_registry_insert(
&mut world.resources.material_registry,
material_name.clone(),
material.clone(),
);
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world
.core
.set_material_ref(entity, MaterialRef::new(material_name));
}
if let Some(camera) = &node.components.camera {
world.core.set_camera(entity, *camera);
}
if let Some(visibility) = &node.components.visibility {
world.core.set_visibility(entity, visibility.clone());
}
if let Some(bounding_volume) = &node.components.bounding_volume {
world.core.set_bounding_volume(entity, *bounding_volume);
} else if let Some(calculated_bv) = calculated_bounding_volume {
world.core.set_bounding_volume(entity, calculated_bv);
}
#[cfg(feature = "physics")]
if let Some(prefab_rigid_body) = &node.components.rigid_body {
super::spawn_physics::apply_prefab_physics(
world,
entity,
prefab_rigid_body,
node.components.collider.as_ref(),
local_transform,
);
}
if let Some(parent) = parent_entity {
if world.core.get_local_transform(parent).is_some() {
world.core.set_parent(entity, Parent(Some(parent)));
world
.resources
.children_cache
.entry(parent)
.or_default()
.push(entity);
} else {
tracing::warn!(
"Parent entity {:?} does not exist or lacks LocalTransform component when spawning entity {:?}",
parent,
entity
);
}
}
for child_node in &node.children {
spawn_prefab_node_with_mapping(
world,
child_node,
offset,
Some(entity),
node_index_to_entity,
bone_name_to_entity,
);
}
entity
}
#[cfg(feature = "assets")]
pub fn spawn_prefab_with_source(
world: &mut World,
prefab: &Prefab,
animations: &[AnimationClip],
skins: &[GltfSkin],
offset: Vec3,
source_path: Option<&str>,
) -> Entity {
let parent_entity = world.spawn_entities(
NAME | LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY | PREFAB_SOURCE,
1,
)[0];
world.core.set_name(
parent_entity,
crate::ecs::world::components::Name(prefab.name.clone()),
);
world.core.set_local_transform(
parent_entity,
crate::ecs::world::components::LocalTransform {
translation: offset,
rotation: nalgebra_glm::Quat::identity(),
scale: nalgebra_glm::vec3(1.0, 1.0, 1.0),
},
);
world
.core
.set_global_transform(parent_entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(parent_entity, LocalTransformDirty);
let prefab_source = if let Some(path) = source_path {
super::super::components::prefab_source_with_path(&prefab.name, path)
} else {
super::super::components::prefab_source_new(&prefab.name)
};
world.core.set_prefab_source(parent_entity, prefab_source);
let mut node_index_to_entity: HashMap<usize, Entity> = HashMap::new();
let mut bone_name_to_entity: HashMap<String, Entity> = HashMap::new();
let mut entity_to_skin_index: HashMap<Entity, usize> = HashMap::new();
for root_node in &prefab.root_nodes {
spawn_prefab_node_with_skin_mapping(
world,
root_node,
Vec3::zeros(),
Some(parent_entity),
&mut node_index_to_entity,
&mut bone_name_to_entity,
&mut entity_to_skin_index,
);
}
tracing::info!(
"spawn_prefab_with_source: {} node mappings, {} bone names, {} entities need skins",
node_index_to_entity.len(),
bone_name_to_entity.len(),
entity_to_skin_index.len()
);
for (&entity, &skin_index) in &entity_to_skin_index {
if skin_index < skins.len() {
let gltf_skin = &skins[skin_index];
let joints: Vec<Entity> = gltf_skin
.joints
.iter()
.filter_map(|&joint_node_index| {
node_index_to_entity.get(&joint_node_index).copied()
})
.collect();
tracing::info!(
" Skin {}: expected {} joints, found {} (name: {:?})",
skin_index,
gltf_skin.joints.len(),
joints.len(),
gltf_skin.name
);
if joints.len() < gltf_skin.joints.len() {
let missing: Vec<_> = gltf_skin
.joints
.iter()
.filter(|idx| !node_index_to_entity.contains_key(*idx))
.take(5)
.collect();
tracing::warn!(" Missing joint node indices (first 5): {:?}", missing);
}
for (joint_index, &joint_entity) in joints.iter().enumerate() {
world.core.add_components(joint_entity, JOINT);
world.core.set_joint(
joint_entity,
Joint {
index_in_skin: joint_index,
name: None,
},
);
}
let skin = Skin {
joints,
inverse_bind_matrices: Arc::new(gltf_skin.inverse_bind_matrices.clone()),
name: gltf_skin.name.clone(),
};
world.core.add_components(entity, SKIN);
world.core.set_skin(entity, skin);
}
}
if !animations.is_empty() {
tracing::info!("Setting up AnimationPlayer with {} clips", animations.len());
for (clip_idx, clip) in animations.iter().enumerate() {
tracing::info!(
" Clip {} '{}': {} channels, duration: {}s",
clip_idx,
clip.name,
clip.channels.len(),
clip.duration
);
for (ch_idx, channel) in clip.channels.iter().take(5).enumerate() {
let found_by_name = channel
.target_bone_name
.as_ref()
.and_then(|name| bone_name_to_entity.get(name));
let found_by_idx = node_index_to_entity.get(&channel.target_node);
tracing::info!(
" Ch{}: node={}, bone={:?}, found_name={}, found_idx={}",
ch_idx,
channel.target_node,
channel.target_bone_name,
found_by_name.is_some(),
found_by_idx.is_some()
);
}
if clip.channels.len() > 5 {
tracing::info!(" ... and {} more channels", clip.channels.len() - 5);
}
}
let mut animation_player = AnimationPlayer {
clips: animations.to_vec(),
current_clip: if animations.is_empty() { None } else { Some(0) },
time: 0.0,
speed: 1.0,
looping: true,
playing: true,
play_all: false,
node_index_to_entity,
bone_name_to_entity,
blend_from_clip: None,
blend_from_time: 0.0,
blend_factor: 1.0,
blend_duration: 0.0,
};
if !animation_player.clips.is_empty() {
animation_player.play(0);
}
world.core.add_components(parent_entity, ANIMATION_PLAYER);
world
.core
.set_animation_player(parent_entity, animation_player);
}
parent_entity
}
#[cfg(feature = "assets")]
pub fn spawn_prefab_with_skins(
world: &mut World,
prefab: &Prefab,
animations: &[AnimationClip],
skins: &[GltfSkin],
offset: Vec3,
) -> Entity {
let parent_entity = world.spawn_entities(
NAME | LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY,
1,
)[0];
world.core.set_name(
parent_entity,
crate::ecs::world::components::Name(prefab.name.clone()),
);
world.core.set_local_transform(
parent_entity,
crate::ecs::world::components::LocalTransform {
translation: offset,
rotation: nalgebra_glm::Quat::identity(),
scale: nalgebra_glm::vec3(1.0, 1.0, 1.0),
},
);
world
.core
.set_global_transform(parent_entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(parent_entity, LocalTransformDirty);
let mut node_index_to_entity: HashMap<usize, Entity> = HashMap::new();
let mut bone_name_to_entity: HashMap<String, Entity> = HashMap::new();
let mut entity_to_skin_index: HashMap<Entity, usize> = HashMap::new();
for root_node in &prefab.root_nodes {
spawn_prefab_node_with_skin_mapping(
world,
root_node,
Vec3::zeros(),
Some(parent_entity),
&mut node_index_to_entity,
&mut bone_name_to_entity,
&mut entity_to_skin_index,
);
}
for (&entity, &skin_index) in &entity_to_skin_index {
if skin_index < skins.len() {
let gltf_skin = &skins[skin_index];
let joints: Vec<Entity> = gltf_skin
.joints
.iter()
.filter_map(|&joint_node_index| {
node_index_to_entity.get(&joint_node_index).copied()
})
.collect();
for (joint_index, &joint_entity) in joints.iter().enumerate() {
world.core.add_components(joint_entity, JOINT);
world.core.set_joint(
joint_entity,
Joint {
index_in_skin: joint_index,
name: None,
},
);
}
let skin = Skin {
joints,
inverse_bind_matrices: Arc::new(gltf_skin.inverse_bind_matrices.clone()),
name: gltf_skin.name.clone(),
};
world.core.add_components(entity, SKIN);
world.core.set_skin(entity, skin);
}
}
if !animations.is_empty() {
let mut animation_player = AnimationPlayer {
clips: animations.to_vec(),
current_clip: if animations.is_empty() { None } else { Some(0) },
time: 0.0,
speed: 1.0,
looping: true,
playing: true,
play_all: false,
node_index_to_entity,
bone_name_to_entity,
blend_from_clip: None,
blend_from_time: 0.0,
blend_factor: 1.0,
blend_duration: 0.0,
};
if !animation_player.clips.is_empty() {
animation_player.play(0);
}
world.core.add_components(parent_entity, ANIMATION_PLAYER);
world
.core
.set_animation_player(parent_entity, animation_player);
}
parent_entity
}
#[cfg(feature = "assets")]
fn spawn_prefab_node_with_skin_mapping(
world: &mut World,
node: &PrefabNode,
offset: Vec3,
parent_entity: Option<Entity>,
node_index_to_entity: &mut HashMap<usize, Entity>,
bone_name_to_entity: &mut HashMap<String, Entity>,
entity_to_skin_index: &mut HashMap<Entity, usize>,
) -> Entity {
let calculated_bounding_volume = if node.components.bounding_volume.is_none() {
if let Some(render_mesh) = &node.components.render_mesh {
get_bounding_volume_from_mesh_name(world, &render_mesh.name)
} else {
None
}
} else {
None
};
let mut component_mask = LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY;
if node.components.name.is_some() {
component_mask |= NAME;
}
if node.components.render_mesh.is_some() {
component_mask |= RENDER_MESH | CASTS_SHADOW;
}
if node.components.material.is_some() {
component_mask |= MATERIAL_REF;
}
if node.components.camera.is_some() {
component_mask |= CAMERA;
}
if node.components.visibility.is_some() {
component_mask |= VISIBILITY;
}
if node.components.bounding_volume.is_some() || calculated_bounding_volume.is_some() {
component_mask |= BOUNDING_VOLUME;
}
if node.components.rigid_body.is_some() {
component_mask |= RIGID_BODY;
}
if node.components.collider.is_some() {
component_mask |= COLLIDER;
}
if node.components.script.is_some() {
component_mask |= SCRIPT;
}
if parent_entity.is_some() {
component_mask |= PARENT;
}
let entity = world.spawn_entities(component_mask, 1)[0];
if let Some(node_index) = node.node_index {
node_index_to_entity.insert(node_index, entity);
}
if let Some(name) = &node.components.name {
bone_name_to_entity.insert(name.0.clone(), entity);
}
if let Some(skin_index) = node.components.skin_index {
entity_to_skin_index.insert(entity, skin_index);
}
let mut local_transform = node.local_transform;
if parent_entity.is_none() {
local_transform.translation += offset;
}
world.core.set_local_transform(entity, local_transform);
world
.core
.set_global_transform(entity, GlobalTransform::default());
world
.core
.set_local_transform_dirty(entity, LocalTransformDirty);
if let Some(name) = &node.components.name {
world.core.set_name(entity, name.clone());
}
if let Some(script) = &node.components.script {
world.core.set_script(entity, script.clone());
}
if let Some(render_mesh) = &node.components.render_mesh {
if !world
.resources
.mesh_cache
.registry
.name_to_index
.contains_key(&render_mesh.name)
{
tracing::error!(
"Mesh '{}' not found in mesh_cache when spawning entity {:?}. Skipping RenderMesh component.",
render_mesh.name,
entity
);
} else {
if let Some(&index) = world
.resources
.mesh_cache
.registry
.name_to_index
.get(&render_mesh.name)
{
world.resources.mesh_cache.registry.add_reference(index);
}
world.core.set_render_mesh(entity, render_mesh.clone());
world.resources.mesh_render_state.mark_entity_added(entity);
world
.core
.set_casts_shadow(entity, crate::ecs::shadow::components::CastsShadow);
let morph_weights_to_add =
registry_entry_by_name(&world.resources.mesh_cache.registry, &render_mesh.name)
.and_then(|mesh| mesh.morph_targets.as_ref())
.map(|morph_data| {
MorphWeights::new(
morph_data.default_weights.clone(),
render_mesh.name.clone(),
)
});
if let Some(morph_weights) = morph_weights_to_add {
world.core.add_components(entity, MORPH_WEIGHTS);
world.core.set_morph_weights(entity, morph_weights);
}
}
}
if let Some(material) = &node.components.material {
if let Some(base_texture) = &material.base_texture {
texture_cache_add_reference(&mut world.resources.texture_cache, base_texture);
}
if let Some(emissive_texture) = &material.emissive_texture {
texture_cache_add_reference(&mut world.resources.texture_cache, emissive_texture);
}
let node_name = node
.components
.name
.as_ref()
.map(|n| n.0.as_str())
.unwrap_or("unnamed");
let material_name = format!("Prefab_{}_{}", node_name, entity.id);
material_registry_insert(
&mut world.resources.material_registry,
material_name.clone(),
material.clone(),
);
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world
.core
.set_material_ref(entity, MaterialRef::new(material_name));
}
if let Some(camera) = &node.components.camera {
world.core.set_camera(entity, *camera);
}
if let Some(visibility) = &node.components.visibility {
world.core.set_visibility(entity, visibility.clone());
}
if let Some(bounding_volume) = &node.components.bounding_volume {
world.core.set_bounding_volume(entity, *bounding_volume);
} else if let Some(calculated_bv) = calculated_bounding_volume {
world.core.set_bounding_volume(entity, calculated_bv);
}
#[cfg(feature = "physics")]
if let Some(prefab_rigid_body) = &node.components.rigid_body {
super::spawn_physics::apply_prefab_physics(
world,
entity,
prefab_rigid_body,
node.components.collider.as_ref(),
local_transform,
);
}
if let Some(parent) = parent_entity {
if world.core.get_local_transform(parent).is_some() {
world.core.set_parent(entity, Parent(Some(parent)));
world
.resources
.children_cache
.entry(parent)
.or_default()
.push(entity);
} else {
tracing::warn!(
"Parent entity {:?} does not exist or lacks LocalTransform component when spawning entity {:?}",
parent,
entity
);
}
}
for child_node in &node.children {
spawn_prefab_node_with_skin_mapping(
world,
child_node,
offset,
Some(entity),
node_index_to_entity,
bone_name_to_entity,
entity_to_skin_index,
);
}
entity
}