use super::HierarchyNode;
use crate::ecs::bounding_volume::components::BoundingVolume;
use crate::ecs::grass::{GrassInteractor, GrassRegion};
use crate::ecs::navmesh::NavMeshAgent;
use crate::ecs::particles::components::ParticleEmitter;
use crate::ecs::physics::components::CharacterControllerComponent;
use crate::ecs::transform::components::IgnoreParentScale;
use crate::ecs::world::{CASTS_SHADOW, IGNORE_PARENT_SCALE};
use crate::prelude::*;
#[derive(Clone, serde::Serialize, serde::Deserialize)]
#[serde(crate = "serde")]
pub struct EntitySnapshot {
pub name: Option<Name>,
pub local_transform: Option<LocalTransform>,
#[serde(skip)]
pub global_transform: Option<GlobalTransform>,
#[serde(skip)]
pub parent_entity: Option<Entity>,
pub camera: Option<Camera>,
pub light: Option<Light>,
pub visibility: Option<Visibility>,
pub render_mesh: Option<RenderMesh>,
pub instanced_mesh: Option<InstancedMesh>,
pub material_ref: Option<MaterialRef>,
pub render_layer: Option<RenderLayer>,
pub script: Option<Script>,
pub text: Option<Text>,
pub hud_text: Option<HudText>,
pub lines: Option<Lines>,
pub sprite: Option<Sprite>,
pub sprite_animator: Option<SpriteAnimator>,
pub decal: Option<Decal>,
pub water: Option<Water>,
pub animation_player: Option<AnimationPlayer>,
pub bounding_volume: Option<BoundingVolume>,
pub casts_shadow: bool,
pub ignore_parent_scale: bool,
#[serde(skip)]
pub particle_emitter: Option<ParticleEmitter>,
#[serde(skip)]
pub grass_region: Option<GrassRegion>,
#[serde(skip)]
pub grass_interactor: Option<GrassInteractor>,
#[serde(skip)]
pub character_controller: Option<CharacterControllerComponent>,
#[serde(skip)]
pub navmesh_agent: Option<NavMeshAgent>,
}
impl EntitySnapshot {
pub fn capture(world: &World, entity: Entity) -> Self {
let parent_entity = world.get_parent(entity).and_then(|p| p.0);
Self {
name: world.get_name(entity).cloned(),
local_transform: world.get_local_transform(entity).cloned(),
global_transform: world.get_global_transform(entity).cloned(),
parent_entity,
camera: world.get_camera(entity).cloned(),
light: world.get_light(entity).cloned(),
visibility: world.get_visibility(entity).cloned(),
render_mesh: world.get_render_mesh(entity).cloned(),
instanced_mesh: world.get_instanced_mesh(entity).cloned(),
material_ref: world.get_material_ref(entity).cloned(),
render_layer: world.get_render_layer(entity).cloned(),
script: world.get_script(entity).cloned(),
text: world.get_text(entity).cloned(),
hud_text: world.get_hud_text(entity).cloned(),
lines: world.get_lines(entity).cloned(),
sprite: world.get_sprite(entity).cloned(),
sprite_animator: world.get_sprite_animator(entity).cloned(),
decal: world.get_decal(entity).cloned(),
water: world.get_water(entity).cloned(),
animation_player: world.get_animation_player(entity).cloned(),
bounding_volume: world.get_bounding_volume(entity).cloned(),
casts_shadow: world.entity_has_casts_shadow(entity),
ignore_parent_scale: world.entity_has_ignore_parent_scale(entity),
particle_emitter: world.get_particle_emitter(entity).cloned(),
grass_region: world.get_grass_region(entity).cloned(),
grass_interactor: world.get_grass_interactor(entity).cloned(),
character_controller: world.get_character_controller(entity).cloned(),
navmesh_agent: world.get_navmesh_agent(entity).cloned(),
}
}
}
pub fn capture_hierarchy(world: &World, entity: Entity) -> HierarchyNode {
let snapshot = EntitySnapshot::capture(world, entity);
let children = query_children(world, entity)
.into_iter()
.map(|child| capture_hierarchy(world, child))
.collect();
HierarchyNode {
entity,
snapshot,
children,
}
}
pub fn recreate_hierarchy_with_mapping(
world: &mut World,
node: &HierarchyNode,
parent: Option<Entity>,
) -> (Entity, std::collections::HashMap<Entity, Entity>) {
let mut mapping = std::collections::HashMap::new();
let root_entity = recreate_hierarchy_internal_with_mapping(world, node, parent, &mut mapping);
(root_entity, mapping)
}
fn recreate_hierarchy_internal_with_mapping(
world: &mut World,
node: &HierarchyNode,
parent: Option<Entity>,
mapping: &mut std::collections::HashMap<Entity, Entity>,
) -> Entity {
let new_entity = recreate_entity(world, &node.snapshot, parent);
mapping.insert(node.entity, new_entity);
for child_node in &node.children {
recreate_hierarchy_internal_with_mapping(world, child_node, Some(new_entity), mapping);
}
new_entity
}
pub fn recreate_entity(
world: &mut World,
snapshot: &EntitySnapshot,
parent: Option<Entity>,
) -> Entity {
let entities = EntityBuilder::new()
.with_local_transform(snapshot.local_transform.unwrap_or_default())
.with_global_transform(snapshot.global_transform.unwrap_or_default())
.spawn(world, 1);
let entity = entities[0];
if let Some(name) = &snapshot.name {
world.set_name(entity, name.clone());
}
if let Some(parent_entity) = parent
&& world.get_local_transform(parent_entity).is_some()
{
world.set_parent(entity, Parent(Some(parent_entity)));
}
if let Some(camera) = &snapshot.camera {
world.set_camera(entity, *camera);
}
if let Some(light) = &snapshot.light {
world.set_light(entity, light.clone());
}
if let Some(visibility) = &snapshot.visibility {
world.set_visibility(entity, visibility.clone());
}
if let Some(render_mesh) = &snapshot.render_mesh {
world.set_render_mesh(entity, render_mesh.clone());
world.resources.mesh_render_state.mark_entity_added(entity);
if let Some(&index) = world
.resources
.mesh_cache
.registry
.name_to_index
.get(&render_mesh.name)
{
world.resources.mesh_cache.registry.add_reference(index);
}
}
if let Some(material_ref) = &snapshot.material_ref {
world.set_material_ref(entity, material_ref.clone());
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_ref.name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
}
if let Some(instanced_mesh) = &snapshot.instanced_mesh {
world.set_instanced_mesh(entity, instanced_mesh.clone());
}
if let Some(render_layer) = &snapshot.render_layer {
world.set_render_layer(entity, *render_layer);
}
if let Some(script) = &snapshot.script {
world.set_script(entity, script.clone());
}
if let Some(text) = &snapshot.text {
world.set_text(entity, text.clone());
}
if let Some(hud_text) = &snapshot.hud_text {
world.set_hud_text(entity, hud_text.clone());
}
if let Some(lines) = &snapshot.lines {
world.set_lines(entity, lines.clone());
}
if let Some(sprite) = &snapshot.sprite {
world.set_sprite(entity, sprite.clone());
}
if let Some(sprite_animator) = &snapshot.sprite_animator {
world.set_sprite_animator(entity, sprite_animator.clone());
}
if let Some(decal) = &snapshot.decal {
world.set_decal(entity, decal.clone());
}
if let Some(water) = &snapshot.water {
world.set_water(entity, water.clone());
}
if let Some(animation_player) = &snapshot.animation_player {
world.set_animation_player(entity, animation_player.clone());
}
if let Some(bounding_volume) = &snapshot.bounding_volume {
world.set_bounding_volume(entity, *bounding_volume);
}
if snapshot.casts_shadow {
world.add_components(entity, CASTS_SHADOW);
}
if snapshot.ignore_parent_scale {
world.set_ignore_parent_scale(entity, IgnoreParentScale);
}
if let Some(particle_emitter) = &snapshot.particle_emitter {
world.set_particle_emitter(entity, particle_emitter.clone());
}
if let Some(grass_region) = &snapshot.grass_region {
world.set_grass_region(entity, grass_region.clone());
}
if let Some(grass_interactor) = &snapshot.grass_interactor {
world.set_grass_interactor(entity, grass_interactor.clone());
}
if let Some(character_controller) = &snapshot.character_controller {
world.set_character_controller(entity, character_controller.clone());
}
if let Some(navmesh_agent) = &snapshot.navmesh_agent {
world.set_navmesh_agent(entity, navmesh_agent.clone());
}
entity
}
pub fn apply_snapshot_to_entity(world: &mut World, entity: Entity, snapshot: &EntitySnapshot) {
macro_rules! apply_optional {
($field:ident, $set:ident, $remove:ident) => {
if let Some(value) = &snapshot.$field {
world.$set(entity, value.clone());
} else {
world.$remove(entity);
}
};
}
apply_optional!(name, set_name, remove_name);
apply_optional!(camera, set_camera, remove_camera);
apply_optional!(light, set_light, remove_light);
apply_optional!(visibility, set_visibility, remove_visibility);
apply_optional!(render_layer, set_render_layer, remove_render_layer);
apply_optional!(script, set_script, remove_script);
apply_optional!(text, set_text, remove_text);
apply_optional!(hud_text, set_hud_text, remove_hud_text);
apply_optional!(lines, set_lines, remove_lines);
apply_optional!(sprite, set_sprite, remove_sprite);
apply_optional!(sprite_animator, set_sprite_animator, remove_sprite_animator);
apply_optional!(decal, set_decal, remove_decal);
apply_optional!(water, set_water, remove_water);
apply_optional!(
animation_player,
set_animation_player,
remove_animation_player
);
apply_optional!(bounding_volume, set_bounding_volume, remove_bounding_volume);
apply_optional!(
particle_emitter,
set_particle_emitter,
remove_particle_emitter
);
apply_optional!(grass_region, set_grass_region, remove_grass_region);
apply_optional!(
grass_interactor,
set_grass_interactor,
remove_grass_interactor
);
apply_optional!(
character_controller,
set_character_controller,
remove_character_controller
);
apply_optional!(navmesh_agent, set_navmesh_agent, remove_navmesh_agent);
if let Some(render_mesh) = &snapshot.render_mesh {
world.set_render_mesh(entity, render_mesh.clone());
world.resources.mesh_render_state.mark_entity_added(entity);
} else {
world.remove_render_mesh(entity);
}
if let Some(material_ref) = &snapshot.material_ref {
world.set_material_ref(entity, material_ref.clone());
} else {
world.remove_material_ref(entity);
}
if let Some(instanced_mesh) = &snapshot.instanced_mesh {
world.set_instanced_mesh(entity, instanced_mesh.clone());
} else {
world.remove_instanced_mesh(entity);
}
if snapshot.casts_shadow {
world.add_components(entity, CASTS_SHADOW);
} else {
world.remove_components(entity, CASTS_SHADOW);
}
if snapshot.ignore_parent_scale {
world.set_ignore_parent_scale(entity, IgnoreParentScale);
} else {
world.remove_components(entity, IGNORE_PARENT_SCALE);
}
if let Some(local_transform) = &snapshot.local_transform {
world.set_local_transform(entity, *local_transform);
mark_local_transform_dirty(world, entity);
}
}