use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::ecs::bounding_volume::components::BoundingVolume;
use crate::ecs::decal::Decal;
use crate::ecs::graphics::resources::Atmosphere;
use crate::ecs::grass::{GrassInteractor, GrassRegion};
use crate::ecs::lines::components::Line;
use crate::ecs::navmesh::NavMeshAgent;
use crate::ecs::render_layer::RenderLayer;
use crate::ecs::script::components::Script;
use crate::ecs::text::Text;
use crate::ecs::transform::LocalTransform;
use crate::ecs::water::Water;
use super::animation::SceneAnimationPlayer;
use super::asset_uuid::AssetUuid;
use super::audio::{EmbeddedAudio, EmbeddedTexture, SceneAudioSource, ScenePrefabInstance};
use super::character_controller::SceneCharacterController;
use super::lighting::{SceneCamera, SceneLight};
use super::material::SceneMaterial;
use super::mesh::SceneInstancedMesh;
use super::navigation::SceneNavMesh;
use super::particles::SceneParticleEmitter;
use super::physics::{SceneJointConnection, ScenePhysics};
use super::settings::SceneSettings;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MetadataValue {
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Array(Vec<MetadataValue>),
Map(HashMap<String, MetadataValue>),
}
pub const SCENE_FORMAT_VERSION: u32 = 1;
pub const ENGINE_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum AssetMode {
#[default]
Referenced,
Embedded,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneHeader {
pub format_version: u32,
pub engine_version: String,
pub name: String,
pub asset_mode: AssetMode,
}
impl Default for SceneHeader {
fn default() -> Self {
Self {
format_version: SCENE_FORMAT_VERSION,
engine_version: ENGINE_VERSION.to_string(),
name: String::new(),
asset_mode: AssetMode::Referenced,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct ChunkId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct LayerId(pub u32);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneLayerConfig {
pub id: LayerId,
pub name: String,
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub order: i32,
}
impl SceneLayerConfig {
pub fn new(id: u32, name: impl Into<String>) -> Self {
Self {
id: LayerId(id),
name: name.into(),
enabled: true,
order: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneChunkConfig {
pub id: ChunkId,
pub name: String,
pub bounds_min: [f32; 3],
pub bounds_max: [f32; 3],
#[serde(default)]
pub loaded: bool,
#[serde(default)]
pub load_distance: f32,
#[serde(default)]
pub unload_distance: f32,
}
impl SceneChunkConfig {
pub fn new(
id: u32,
name: impl Into<String>,
bounds_min: [f32; 3],
bounds_max: [f32; 3],
) -> Self {
Self {
id: ChunkId(id),
name: name.into(),
bounds_min,
bounds_max,
loaded: true,
load_distance: 100.0,
unload_distance: 150.0,
}
}
}
impl Default for SceneChunkConfig {
fn default() -> Self {
Self {
id: ChunkId(0),
name: String::new(),
bounds_min: [-f32::INFINITY, -f32::INFINITY, -f32::INFINITY],
bounds_max: [f32::INFINITY, f32::INFINITY, f32::INFINITY],
loaded: true,
load_distance: 100.0,
unload_distance: 150.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SceneChunk {
pub config: SceneChunkConfig,
pub entities: Vec<SceneEntity>,
#[serde(default)]
pub joints: Vec<SceneJointConnection>,
}
impl SceneChunk {
pub fn new(config: SceneChunkConfig) -> Self {
Self {
config,
entities: Vec::new(),
joints: Vec::new(),
}
}
pub fn add_entity(&mut self, mut entity: SceneEntity) -> AssetUuid {
entity.chunk_id = Some(self.config.id);
let uuid = entity.uuid;
self.entities.push(entity);
uuid
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Scene {
pub header: SceneHeader,
pub atmosphere: Atmosphere,
#[serde(default)]
pub hdr_skybox: Option<SceneHdrSkybox>,
#[serde(default)]
pub settings: SceneSettings,
pub entities: Vec<SceneEntity>,
#[serde(default)]
pub joints: Vec<SceneJointConnection>,
#[serde(default)]
pub layers: Vec<SceneLayerConfig>,
#[serde(default)]
pub chunks: Vec<SceneChunkConfig>,
#[serde(default)]
pub embedded_textures: HashMap<AssetUuid, EmbeddedTexture>,
#[serde(default)]
pub embedded_audio: HashMap<AssetUuid, EmbeddedAudio>,
#[serde(default)]
pub metadata: HashMap<String, MetadataValue>,
#[serde(default)]
pub navmesh: Option<SceneNavMesh>,
#[serde(skip)]
pub spawn_order: Vec<AssetUuid>,
#[serde(skip)]
pub uuid_index: HashMap<AssetUuid, usize>,
#[serde(skip)]
pub chunk_index: HashMap<ChunkId, Vec<usize>>,
}
impl Scene {
pub fn new(name: impl Into<String>) -> Self {
Self {
header: SceneHeader {
format_version: SCENE_FORMAT_VERSION,
engine_version: ENGINE_VERSION.to_string(),
name: name.into(),
asset_mode: AssetMode::Referenced,
},
..Default::default()
}
}
pub fn add_entity(&mut self, entity: SceneEntity) -> AssetUuid {
let uuid = entity.uuid;
let index = self.entities.len();
self.uuid_index.insert(uuid, index);
self.entities.push(entity);
uuid
}
pub fn find_entity(&self, uuid: AssetUuid) -> Option<&SceneEntity> {
if let Some(&index) = self.uuid_index.get(&uuid) {
self.entities.get(index)
} else {
self.entities.iter().find(|entity| entity.uuid == uuid)
}
}
pub fn find_entity_mut(&mut self, uuid: AssetUuid) -> Option<&mut SceneEntity> {
if let Some(&index) = self.uuid_index.get(&uuid) {
self.entities.get_mut(index)
} else {
self.entities.iter_mut().find(|entity| entity.uuid == uuid)
}
}
pub fn root_entities(&self) -> impl Iterator<Item = &SceneEntity> {
self.entities
.iter()
.filter(|entity| entity.parent.is_none())
}
pub fn children_of(&self, parent_uuid: AssetUuid) -> impl Iterator<Item = &SceneEntity> {
self.entities
.iter()
.filter(move |entity| entity.parent == Some(parent_uuid))
}
pub fn add_joint(&mut self, joint: SceneJointConnection) {
self.joints.push(joint);
}
pub fn rebuild_uuid_index(&mut self) {
self.uuid_index.clear();
self.chunk_index.clear();
for (index, entity) in self.entities.iter().enumerate() {
self.uuid_index.insert(entity.uuid, index);
if let Some(chunk_id) = entity.chunk_id {
self.chunk_index.entry(chunk_id).or_default().push(index);
}
}
}
pub fn compute_spawn_order(&mut self) {
let uuid_set: std::collections::HashSet<AssetUuid> =
self.entities.iter().map(|entity| entity.uuid).collect();
let mut result: Vec<AssetUuid> = Vec::with_capacity(self.entities.len());
let mut visited: std::collections::HashSet<AssetUuid> = std::collections::HashSet::new();
let mut in_stack: std::collections::HashSet<AssetUuid> = std::collections::HashSet::new();
let entity_map: HashMap<AssetUuid, &SceneEntity> = self
.entities
.iter()
.map(|entity| (entity.uuid, entity))
.collect();
fn visit(
uuid: AssetUuid,
entity_map: &HashMap<AssetUuid, &SceneEntity>,
uuid_set: &std::collections::HashSet<AssetUuid>,
visited: &mut std::collections::HashSet<AssetUuid>,
in_stack: &mut std::collections::HashSet<AssetUuid>,
result: &mut Vec<AssetUuid>,
) {
if visited.contains(&uuid) {
return;
}
if in_stack.contains(&uuid) {
return;
}
in_stack.insert(uuid);
if let Some(entity) = entity_map.get(&uuid)
&& let Some(parent_uuid) = entity.parent
&& uuid_set.contains(&parent_uuid)
{
visit(parent_uuid, entity_map, uuid_set, visited, in_stack, result);
}
in_stack.remove(&uuid);
visited.insert(uuid);
result.push(uuid);
}
for entity in &self.entities {
visit(
entity.uuid,
&entity_map,
&uuid_set,
&mut visited,
&mut in_stack,
&mut result,
);
}
self.spawn_order = result;
}
pub fn remove_entity(&mut self, uuid: AssetUuid) -> Option<SceneEntity> {
if let Some(index) = self.uuid_index.remove(&uuid) {
let entity = self.entities.remove(index);
for (existing_uuid, existing_index) in self.uuid_index.iter_mut() {
if *existing_index > index {
*existing_index -= 1;
}
let _ = existing_uuid;
}
Some(entity)
} else if let Some(pos) = self.entities.iter().position(|entity| entity.uuid == uuid) {
Some(self.entities.remove(pos))
} else {
None
}
}
pub fn add_layer(&mut self, layer: SceneLayerConfig) {
self.layers.push(layer);
}
pub fn add_chunk(&mut self, chunk: SceneChunkConfig) {
self.chunks.push(chunk);
}
pub fn get_layer(&self, layer_id: LayerId) -> Option<&SceneLayerConfig> {
self.layers.iter().find(|layer| layer.id == layer_id)
}
pub fn get_chunk(&self, chunk_id: ChunkId) -> Option<&SceneChunkConfig> {
self.chunks.iter().find(|chunk| chunk.id == chunk_id)
}
pub fn entities_in_layer(&self, layer_id: LayerId) -> impl Iterator<Item = &SceneEntity> {
self.entities
.iter()
.filter(move |entity| entity.layer == Some(layer_id))
}
pub fn entities_in_chunk(&self, chunk_id: ChunkId) -> impl Iterator<Item = &SceneEntity> {
self.chunk_index
.get(&chunk_id)
.into_iter()
.flatten()
.filter_map(|&index| self.entities.get(index))
}
pub fn extract_chunk(&self, chunk_id: ChunkId) -> SceneChunk {
let config = self
.get_chunk(chunk_id)
.cloned()
.unwrap_or_else(|| SceneChunkConfig {
id: chunk_id,
..Default::default()
});
let entities: Vec<SceneEntity> = self
.entities
.iter()
.filter(|entity| entity.chunk_id == Some(chunk_id))
.cloned()
.collect();
let entity_uuids: std::collections::HashSet<AssetUuid> =
entities.iter().map(|entity| entity.uuid).collect();
let joints: Vec<SceneJointConnection> = self
.joints
.iter()
.filter(|joint| {
entity_uuids.contains(&joint.parent_entity)
|| entity_uuids.contains(&joint.child_entity)
})
.cloned()
.collect();
SceneChunk {
config,
entities,
joints,
}
}
pub fn merge_chunk(&mut self, chunk: SceneChunk) {
if !self.chunks.iter().any(|c| c.id == chunk.config.id) {
self.chunks.push(chunk.config);
}
for entity in chunk.entities {
self.add_entity(entity);
}
for joint in chunk.joints {
self.joints.push(joint);
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SceneHdrSkybox {
Embedded { data: Vec<u8> },
Reference { path: String },
Asset { uuid: AssetUuid },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneEntity {
pub uuid: AssetUuid,
#[serde(default)]
pub parent: Option<AssetUuid>,
#[serde(default)]
pub name: Option<String>,
pub transform: LocalTransform,
#[serde(default)]
pub layer: Option<LayerId>,
#[serde(default)]
pub chunk_id: Option<ChunkId>,
#[serde(default)]
pub components: SceneComponents,
}
impl SceneEntity {
pub fn new() -> Self {
Self {
uuid: AssetUuid::new(),
parent: None,
name: None,
transform: LocalTransform::default(),
layer: None,
chunk_id: None,
components: SceneComponents::default(),
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_transform(mut self, transform: LocalTransform) -> Self {
self.transform = transform;
self
}
pub fn with_parent(mut self, parent: AssetUuid) -> Self {
self.parent = Some(parent);
self
}
pub fn with_layer(mut self, layer: LayerId) -> Self {
self.layer = Some(layer);
self
}
pub fn with_chunk(mut self, chunk_id: ChunkId) -> Self {
self.chunk_id = Some(chunk_id);
self
}
pub fn with_components(mut self, components: SceneComponents) -> Self {
self.components = components;
self
}
pub fn with_mesh(mut self, mesh: SceneMesh) -> Self {
self.components.mesh = Some(mesh);
self
}
pub fn with_light(mut self, light: SceneLight) -> Self {
self.components.light = Some(light);
self
}
pub fn with_camera(mut self, camera: SceneCamera) -> Self {
self.components.camera = Some(camera);
self
}
pub fn with_casts_shadow(mut self, casts_shadow: bool) -> Self {
self.components.casts_shadow = casts_shadow;
self
}
pub fn with_visible(mut self, visible: bool) -> Self {
self.components.visible = visible;
self
}
}
impl Default for SceneEntity {
fn default() -> Self {
Self::new()
}
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneComponents {
#[serde(default)]
pub mesh: Option<SceneMesh>,
#[serde(default)]
pub instanced_mesh: Option<SceneInstancedMesh>,
#[serde(default)]
pub light: Option<SceneLight>,
#[serde(default)]
pub camera: Option<SceneCamera>,
#[serde(default)]
pub physics: Option<ScenePhysics>,
#[serde(default)]
pub script: Option<Script>,
#[serde(default)]
pub audio: Option<SceneAudioSource>,
#[serde(default)]
pub prefab: Option<ScenePrefabInstance>,
#[serde(default)]
pub bounding_volume: Option<BoundingVolume>,
#[serde(default)]
pub animation_player: Option<SceneAnimationPlayer>,
#[serde(default)]
pub lines: Vec<Line>,
#[serde(default = "default_true")]
pub casts_shadow: bool,
#[serde(default = "default_true")]
pub visible: bool,
#[serde(default)]
pub particle_emitter: Option<SceneParticleEmitter>,
#[serde(default)]
pub decal: Option<Decal>,
#[serde(default)]
pub water: Option<Water>,
#[serde(default)]
pub grass_region: Option<GrassRegion>,
#[serde(default)]
pub grass_interactor: Option<GrassInteractor>,
#[serde(default)]
pub render_layer: Option<RenderLayer>,
#[serde(default)]
pub text: Option<Text>,
#[serde(default)]
pub character_controller: Option<SceneCharacterController>,
#[serde(default)]
pub navmesh_agent: Option<NavMeshAgent>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub metadata: HashMap<String, MetadataValue>,
}
impl Default for SceneComponents {
fn default() -> Self {
Self {
mesh: None,
instanced_mesh: None,
light: None,
camera: None,
physics: None,
script: None,
audio: None,
prefab: None,
bounding_volume: None,
animation_player: None,
lines: Vec::new(),
casts_shadow: true,
visible: true,
particle_emitter: None,
decal: None,
water: None,
grass_region: None,
grass_interactor: None,
render_layer: None,
text: None,
character_controller: None,
navmesh_agent: None,
tags: Vec::new(),
metadata: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneMesh {
#[serde(default)]
pub mesh_uuid: Option<AssetUuid>,
#[serde(default)]
pub mesh_name: Option<String>,
#[serde(default)]
pub material: Option<SceneMaterial>,
}
impl SceneMesh {
pub fn from_uuid(uuid: AssetUuid) -> Self {
Self {
mesh_uuid: Some(uuid),
mesh_name: None,
material: None,
}
}
pub fn from_name(name: impl Into<String>) -> Self {
Self {
mesh_uuid: None,
mesh_name: Some(name.into()),
material: None,
}
}
pub fn with_material(mut self, material: SceneMaterial) -> Self {
self.material = Some(material);
self
}
}