use self::legacy::PhysicsBinding;
use crate::{
core::{
algebra::{Matrix4, Vector3},
inspect::{Inspect, PropertyInfo},
math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
pool::{ErasedHandle, Handle},
visitor::{Visit, VisitError, VisitResult, Visitor},
},
resource::model::Model,
scene::{graph::Graph, node::Node, transform::Transform},
};
use std::{
cell::Cell,
ops::{Deref, DerefMut},
};
use strum_macros::{AsRefStr, EnumString, EnumVariantNames};
pub(crate) mod legacy {
use crate::core::{
inspect::{Inspect, PropertyInfo},
visitor::{Visit, VisitResult, Visitor},
};
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Inspect)]
#[repr(u32)]
pub enum PhysicsBinding {
NodeWithBody = 0,
BodyWithNode = 1,
}
impl Default for PhysicsBinding {
fn default() -> Self {
Self::NodeWithBody
}
}
impl PhysicsBinding {
fn from_id(id: u32) -> Result<Self, String> {
match id {
0 => Ok(Self::NodeWithBody),
1 => Ok(Self::BodyWithNode),
_ => Err(format!("Invalid physics binding id {}!", id)),
}
}
}
impl Visit for PhysicsBinding {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut id = *self as u32;
id.visit(name, visitor)?;
if visitor.is_reading() {
*self = Self::from_id(id)?;
}
Ok(())
}
}
}
#[derive(Inspect, Default, Debug, Clone, Copy, PartialEq, Hash)]
pub struct LodControlledObject(pub Handle<Node>);
impl Deref for LodControlledObject {
type Target = Handle<Node>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LodControlledObject {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Visit for LodControlledObject {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.visit(name, visitor)
}
}
#[derive(Debug, Default, Clone, Visit, Inspect)]
pub struct LevelOfDetail {
begin: f32,
end: f32,
pub objects: Vec<LodControlledObject>,
}
impl LevelOfDetail {
pub fn new(begin: f32, end: f32, objects: Vec<LodControlledObject>) -> Self {
for object in objects.iter() {
assert!(object.is_some());
}
let begin = begin.min(end);
let end = end.max(begin);
Self {
begin: begin.min(1.0).max(0.0),
end: end.min(1.0).max(0.0),
objects,
}
}
pub fn set_begin(&mut self, percent: f32) {
self.begin = percent.min(1.0).max(0.0);
if self.begin > self.end {
std::mem::swap(&mut self.begin, &mut self.end);
}
}
pub fn begin(&self) -> f32 {
self.begin
}
pub fn set_end(&mut self, percent: f32) {
self.end = percent.min(1.0).max(0.0);
if self.end < self.begin {
std::mem::swap(&mut self.begin, &mut self.end);
}
}
pub fn end(&self) -> f32 {
self.end
}
}
#[derive(Debug, Default, Clone, Visit, Inspect)]
pub struct LodGroup {
pub levels: Vec<LevelOfDetail>,
}
#[derive(
Copy,
Clone,
PartialOrd,
PartialEq,
Ord,
Eq,
Debug,
Inspect,
AsRefStr,
EnumString,
EnumVariantNames,
)]
#[repr(u32)]
pub enum Mobility {
Static = 0,
Stationary = 1,
Dynamic = 2,
}
impl Visit for Mobility {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut id = *self as u32;
id.visit(name, visitor)?;
if visitor.is_reading() {
*self = match id {
0 => Self::Static,
1 => Self::Stationary,
2 => Self::Dynamic,
_ => return Err(VisitError::User(format!("Invalid mobility id {}!", id))),
};
}
Ok(())
}
}
#[derive(Debug, Visit, Inspect, Clone, AsRefStr, EnumString, EnumVariantNames)]
pub enum PropertyValue {
NodeHandle(Handle<Node>),
Handle(ErasedHandle),
String(String),
I64(i64),
U64(u64),
I32(i32),
U32(u32),
I16(i16),
U16(u16),
I8(i8),
U8(u8),
F32(f32),
F64(f64),
}
impl Default for PropertyValue {
fn default() -> Self {
Self::I8(0)
}
}
#[derive(Debug, Visit, Inspect, Default, Clone)]
pub struct Property {
pub name: String,
pub value: PropertyValue,
}
#[derive(Debug, Inspect)]
pub struct Base {
name: String,
pub(crate) local_transform: Transform,
visibility: bool,
#[inspect(skip)]
pub(in crate) global_visibility: Cell<bool>,
#[inspect(skip)]
pub(in crate) parent: Handle<Node>,
#[inspect(skip)]
pub(in crate) children: Vec<Handle<Node>>,
#[inspect(skip)]
pub(in crate) global_transform: Cell<Matrix4<f32>>,
#[inspect(skip)]
pub(in crate) inv_bind_pose_transform: Matrix4<f32>,
#[inspect(read_only)]
pub(in crate) resource: Option<Model>,
#[inspect(skip)]
pub(in crate) original_handle_in_resource: Handle<Node>,
#[inspect(read_only)]
pub(in crate) is_resource_instance_root: bool,
pub(in crate) lifetime: Option<f32>,
#[inspect(min_value = 0.0, max_value = 1.0, step = 0.1)]
depth_offset: f32,
lod_group: Option<LodGroup>,
mobility: Mobility,
tag: String,
pub properties: Vec<Property>,
#[inspect(skip)]
pub(in crate) transform_modified: Cell<bool>,
#[inspect(skip)]
pub(in crate) physics_binding: PhysicsBinding,
frustum_culling: bool,
}
impl Base {
pub fn set_name<N: AsRef<str>>(&mut self, name: N) -> &mut Self {
self.name = name.as_ref().to_owned();
self
}
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn name_owned(&self) -> String {
self.name.clone()
}
pub fn local_transform(&self) -> &Transform {
&self.local_transform
}
pub fn local_transform_mut(&mut self) -> &mut Transform {
self.transform_modified.set(true);
&mut self.local_transform
}
pub fn set_local_transform(&mut self, transform: Transform) -> &mut Self {
self.local_transform = transform;
self
}
pub fn find_properties_ref<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Property> {
self.properties.iter().filter(move |p| p.name == name)
}
pub fn find_first_property_ref(&self, name: &str) -> Option<&Property> {
self.properties.iter().find(|p| p.name == name)
}
pub fn set_lifetime(&mut self, time_seconds: Option<f32>) -> &mut Self {
self.lifetime = time_seconds;
self
}
pub fn lifetime(&self) -> Option<f32> {
self.lifetime
}
pub fn parent(&self) -> Handle<Node> {
self.parent
}
pub fn children(&self) -> &[Handle<Node>] {
self.children.as_slice()
}
pub fn global_transform(&self) -> Matrix4<f32> {
self.global_transform.get()
}
pub fn inv_bind_pose_transform(&self) -> Matrix4<f32> {
self.inv_bind_pose_transform
}
pub fn is_resource_instance_root(&self) -> bool {
self.is_resource_instance_root
}
pub fn resource(&self) -> Option<Model> {
self.resource.clone()
}
pub fn set_visibility(&mut self, visibility: bool) -> &mut Self {
self.visibility = visibility;
self
}
pub fn visibility(&self) -> bool {
self.visibility
}
#[inline]
pub fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
AxisAlignedBoundingBox::unit()
}
pub fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.local_bounding_box()
.transform(&self.global_transform())
}
pub fn set_mobility(&mut self, mobility: Mobility) -> &mut Self {
self.mobility = mobility;
self
}
pub fn mobility(&self) -> Mobility {
self.mobility
}
pub fn global_visibility(&self) -> bool {
self.global_visibility.get()
}
pub fn original_handle_in_resource(&self) -> Handle<Node> {
self.original_handle_in_resource
}
pub fn global_position(&self) -> Vector3<f32> {
self.global_transform.get().position()
}
pub fn look_vector(&self) -> Vector3<f32> {
self.global_transform.get().look()
}
pub fn side_vector(&self) -> Vector3<f32> {
self.global_transform.get().side()
}
pub fn up_vector(&self) -> Vector3<f32> {
self.global_transform.get().up()
}
pub fn set_depth_offset_factor(&mut self, factor: f32) {
self.depth_offset = factor.abs().min(1.0).max(0.0);
}
pub fn depth_offset_factor(&self) -> f32 {
self.depth_offset
}
pub fn set_lod_group(&mut self, lod_group: Option<LodGroup>) -> Option<LodGroup> {
std::mem::replace(&mut self.lod_group, lod_group)
}
pub fn take_lod_group(&mut self) -> Option<LodGroup> {
std::mem::take(&mut self.lod_group)
}
pub fn lod_group(&self) -> Option<&LodGroup> {
self.lod_group.as_ref()
}
pub fn lod_group_mut(&mut self) -> Option<&mut LodGroup> {
self.lod_group.as_mut()
}
pub fn tag(&self) -> &str {
&self.tag
}
pub fn tag_owned(&self) -> String {
self.tag.clone()
}
pub fn set_tag(&mut self, tag: String) {
self.tag = tag;
}
pub fn raw_copy(&self) -> Self {
Self {
name: self.name.clone(),
local_transform: self.local_transform.clone(),
global_transform: self.global_transform.clone(),
visibility: self.visibility,
global_visibility: self.global_visibility.clone(),
inv_bind_pose_transform: self.inv_bind_pose_transform,
resource: self.resource.clone(),
is_resource_instance_root: self.is_resource_instance_root,
lifetime: self.lifetime,
mobility: self.mobility,
tag: self.tag.clone(),
lod_group: self.lod_group.clone(),
properties: self.properties.clone(),
original_handle_in_resource: Default::default(),
parent: Default::default(),
children: Default::default(),
depth_offset: Default::default(),
transform_modified: Cell::new(false),
physics_binding: Default::default(),
frustum_culling: self.frustum_culling,
}
}
pub fn frustum_culling(&self) -> bool {
self.frustum_culling
}
pub fn set_frustum_culling(&mut self, frustum_culling: bool) {
self.frustum_culling = frustum_culling;
}
}
impl Default for Base {
fn default() -> Self {
BaseBuilder::new().build_base()
}
}
impl Visit for Base {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.name.visit("Name", visitor)?;
self.local_transform.visit("Transform", visitor)?;
self.visibility.visit("Visibility", visitor)?;
self.parent.visit("Parent", visitor)?;
self.children.visit("Children", visitor)?;
self.resource.visit("Resource", visitor)?;
self.is_resource_instance_root
.visit("IsResourceInstance", visitor)?;
self.lifetime.visit("Lifetime", visitor)?;
self.depth_offset.visit("DepthOffset", visitor)?;
self.lod_group.visit("LodGroup", visitor)?;
self.mobility.visit("Mobility", visitor)?;
self.original_handle_in_resource
.visit("Original", visitor)?;
self.tag.visit("Tag", visitor)?;
let _ = self.properties.visit("Properties", visitor);
let _ = self.physics_binding.visit("PhysicsBinding", visitor);
let _ = self.frustum_culling.visit("FrustumCulling", visitor);
visitor.leave_region()
}
}
pub struct BaseBuilder {
name: String,
visibility: bool,
local_transform: Transform,
children: Vec<Handle<Node>>,
lifetime: Option<f32>,
depth_offset: f32,
lod_group: Option<LodGroup>,
mobility: Mobility,
inv_bind_pose_transform: Matrix4<f32>,
tag: String,
frustum_culling: bool,
}
impl Default for BaseBuilder {
fn default() -> Self {
Self::new()
}
}
impl BaseBuilder {
pub fn new() -> Self {
Self {
name: Default::default(),
visibility: true,
local_transform: Default::default(),
children: Default::default(),
lifetime: None,
depth_offset: 0.0,
lod_group: None,
mobility: Mobility::Dynamic,
inv_bind_pose_transform: Matrix4::identity(),
tag: Default::default(),
frustum_culling: true,
}
}
pub fn with_mobility(mut self, mobility: Mobility) -> Self {
self.mobility = mobility;
self
}
pub fn with_name<P: AsRef<str>>(mut self, name: P) -> Self {
self.name = name.as_ref().to_owned();
self
}
pub fn with_visibility(mut self, visibility: bool) -> Self {
self.visibility = visibility;
self
}
pub fn with_local_transform(mut self, transform: Transform) -> Self {
self.local_transform = transform;
self
}
pub fn with_inv_bind_pose_transform(mut self, inv_bind_pose: Matrix4<f32>) -> Self {
self.inv_bind_pose_transform = inv_bind_pose;
self
}
pub fn with_children<'a, I: IntoIterator<Item = &'a Handle<Node>>>(
mut self,
children: I,
) -> Self {
for &child in children.into_iter() {
if child.is_some() {
self.children.push(child)
}
}
self
}
pub fn with_lifetime(mut self, time_seconds: f32) -> Self {
self.lifetime = Some(time_seconds);
self
}
pub fn with_depth_offset(mut self, offset: f32) -> Self {
self.depth_offset = offset;
self
}
pub fn with_lod_group(mut self, lod_group: LodGroup) -> Self {
self.lod_group = Some(lod_group);
self
}
pub fn with_tag(mut self, tag: String) -> Self {
self.tag = tag;
self
}
pub fn with_frustum_culling(mut self, frustum_culling: bool) -> Self {
self.frustum_culling = frustum_culling;
self
}
pub(in crate) fn build_base(self) -> Base {
Base {
name: self.name,
children: self.children,
local_transform: self.local_transform,
lifetime: self.lifetime,
visibility: self.visibility,
global_visibility: Cell::new(true),
parent: Handle::NONE,
global_transform: Cell::new(Matrix4::identity()),
inv_bind_pose_transform: self.inv_bind_pose_transform,
resource: None,
original_handle_in_resource: Handle::NONE,
is_resource_instance_root: false,
depth_offset: self.depth_offset,
lod_group: self.lod_group,
mobility: self.mobility,
tag: self.tag,
properties: Default::default(),
transform_modified: Cell::new(false),
physics_binding: Default::default(),
frustum_culling: self.frustum_culling,
}
}
pub fn build_node(self) -> Node {
Node::Base(self.build_base())
}
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}