use crate::ecs::material::components::Material;
use crate::ecs::physics::components::{ColliderComponent, ColliderShape, RigidBodyComponent};
use crate::ecs::physics::types::{InteractionGroups, LockedAxes, RigidBodyType};
use crate::ecs::script::components::Script;
use crate::ecs::world::components::{
BoundingVolume, Camera, LocalTransform, Name, RenderMesh, Visibility,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PrefabBodyType {
Dynamic,
Kinematic,
Static,
}
pub fn prefab_body_type_to_rigid_body_type(body_type: PrefabBodyType) -> RigidBodyType {
match body_type {
PrefabBodyType::Dynamic => RigidBodyType::Dynamic,
PrefabBodyType::Kinematic => RigidBodyType::KinematicPositionBased,
PrefabBodyType::Static => RigidBodyType::Fixed,
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabRigidBody {
pub body_type: PrefabBodyType,
pub mass: f32,
}
impl Default for PrefabRigidBody {
fn default() -> Self {
Self {
body_type: PrefabBodyType::Dynamic,
mass: 1.0,
}
}
}
pub fn prefab_rigid_body_to_component(prefab: &PrefabRigidBody) -> RigidBodyComponent {
RigidBodyComponent {
handle: None,
body_type: prefab_body_type_to_rigid_body_type(prefab.body_type),
translation: [0.0, 0.0, 0.0],
rotation: [0.0, 0.0, 0.0, 1.0],
linvel: [0.0, 0.0, 0.0],
angvel: [0.0, 0.0, 0.0],
mass: prefab.mass,
locked_axes: LockedAxes::empty(),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabCollider {
pub shape: ColliderShape,
pub friction: f32,
pub restitution: f32,
pub density: f32,
pub is_sensor: bool,
}
impl Default for PrefabCollider {
fn default() -> Self {
Self {
shape: ColliderShape::Cuboid {
hx: 0.5,
hy: 0.5,
hz: 0.5,
},
friction: 0.5,
restitution: 0.0,
density: 1.0,
is_sensor: false,
}
}
}
pub fn prefab_collider_to_component(prefab: &PrefabCollider) -> ColliderComponent {
ColliderComponent {
handle: None,
shape: prefab.shape.clone(),
friction: prefab.friction,
restitution: prefab.restitution,
density: prefab.density,
is_sensor: prefab.is_sensor,
collision_groups: InteractionGroups::all(),
solver_groups: InteractionGroups::all(),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prefab {
pub name: String,
pub root_nodes: Vec<PrefabNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabNode {
pub local_transform: LocalTransform,
pub components: PrefabComponents,
pub children: Vec<PrefabNode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_index: Option<usize>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PrefabSource {
pub prefab_name: String,
pub source_path: Option<String>,
}
pub fn prefab_source_new(prefab_name: impl Into<String>) -> PrefabSource {
PrefabSource {
prefab_name: prefab_name.into(),
source_path: None,
}
}
pub fn prefab_source_with_path(
prefab_name: impl Into<String>,
source_path: impl Into<String>,
) -> PrefabSource {
PrefabSource {
prefab_name: prefab_name.into(),
source_path: Some(source_path.into()),
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PrefabComponents {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(skip_serializing_if = "Option::is_none")]
pub render_mesh: Option<RenderMesh>,
#[serde(skip_serializing_if = "Option::is_none")]
pub material: Option<Material>,
#[serde(skip_serializing_if = "Option::is_none")]
pub camera: Option<Camera>,
#[serde(skip_serializing_if = "Option::is_none")]
pub visibility: Option<Visibility>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bounding_volume: Option<BoundingVolume>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rigid_body: Option<PrefabRigidBody>,
#[serde(skip_serializing_if = "Option::is_none")]
pub collider: Option<PrefabCollider>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script: Option<Script>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub skin_index: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabInstance {
pub prefab_id: PrefabId,
pub instance_id: uuid::Uuid,
pub overrides: PrefabOverrides,
pub node_path: NodePath,
}
impl Default for PrefabInstance {
fn default() -> Self {
Self {
prefab_id: PrefabId::default(),
instance_id: uuid::Uuid::nil(),
overrides: PrefabOverrides::default(),
node_path: node_path_root(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
pub struct PrefabId {
pub name: String,
pub source_path: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PrefabOverrides {
#[serde(skip_serializing_if = "TransformOverride::is_none")]
pub transform_override: TransformOverride,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub component_overrides: Vec<ComponentOverride>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub added_components: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub removed_components: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub child_additions: Vec<ChildAddition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub child_removals: Vec<NodePath>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TransformOverride {
#[serde(skip_serializing_if = "Option::is_none")]
pub translation: Option<[f32; 3]>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rotation: Option<[f32; 4]>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scale: Option<[f32; 3]>,
}
impl TransformOverride {
fn is_none(&self) -> bool {
self.translation.is_none() && self.rotation.is_none() && self.scale.is_none()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentOverride {
pub component_type: String,
pub overridden_fields: Vec<FieldOverride>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldOverride {
pub field_name: String,
pub value: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChildAddition {
pub name: Option<String>,
pub transform: LocalTransform,
pub components: PrefabComponents,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct NodePath {
pub segments: Vec<NodePathSegment>,
}
pub fn node_path_root() -> NodePath {
NodePath {
segments: Vec::new(),
}
}
impl std::fmt::Display for NodePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.segments.is_empty() {
write!(f, "/")
} else {
for segment in &self.segments {
write!(f, "/{}", segment.name)?;
if segment.index > 0 {
write!(f, "[{}]", segment.index)?;
}
}
Ok(())
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct NodePathSegment {
pub name: String,
pub index: usize,
}