#![warn(missing_docs)]
use super::collider::BitMask;
use crate::{
asset::{untyped::UntypedResource, Resource},
core::{
algebra::{Matrix4, Vector2},
math::{aabb::AxisAlignedBoundingBox, frustum::Frustum},
pool::Handle,
reflect::prelude::*,
uuid::Uuid,
uuid_provider, variable,
variable::mark_inheritable_properties_non_modified,
visitor::{Visit, VisitResult, Visitor},
ComponentProvider, NameProvider,
},
graph::SceneGraphNode,
renderer::bundle::RenderContext,
resource::model::{Model, ModelResource},
scene::{
self,
animation::{absm::AnimationBlendingStateMachine, AnimationPlayer},
base::Base,
camera::Camera,
debug::SceneDrawingContext,
decal::Decal,
dim2::{self, rectangle::Rectangle},
graph::{self, Graph, GraphUpdateSwitches, NodePool},
light::{directional::DirectionalLight, point::PointLight, spot::SpotLight},
mesh::Mesh,
navmesh::NavigationalMesh,
particle_system::ParticleSystem,
pivot::Pivot,
ragdoll::Ragdoll,
sound::{context::SoundContext, listener::Listener, Sound},
sprite::Sprite,
terrain::Terrain,
Scene,
},
};
use fyrox_core::{define_as_any_trait, pool::ObjectOrVariantHelper};
use std::{
any::{Any, TypeId},
fmt::Debug,
marker::PhantomData,
ops::{Deref, DerefMut},
};
pub mod constructor;
pub mod container;
define_as_any_trait!(NodeAsAny => BaseNodeTrait);
pub trait BaseNodeTrait: NodeAsAny + Debug + Deref<Target = Base> + DerefMut + Send {
fn clone_box(&self) -> Node;
}
impl<T> BaseNodeTrait for T
where
T: Clone + NodeTrait + 'static,
{
fn clone_box(&self) -> Node {
Node(Box::new(self.clone()))
}
}
pub struct SyncContext<'a, 'b> {
pub nodes: &'a NodePool,
pub physics: &'a mut graph::physics::PhysicsWorld,
pub physics2d: &'a mut dim2::physics::PhysicsWorld,
pub sound_context: &'a mut SoundContext,
pub switches: Option<&'b GraphUpdateSwitches>,
}
pub struct UpdateContext<'a> {
pub frame_size: Vector2<f32>,
pub dt: f32,
pub nodes: &'a mut NodePool,
pub physics: &'a mut graph::physics::PhysicsWorld,
pub physics2d: &'a mut dim2::physics::PhysicsWorld,
pub sound_context: &'a mut SoundContext,
}
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
pub enum RdcControlFlow {
Continue,
Break,
}
pub trait NodeTrait: BaseNodeTrait + Reflect + Visit + ComponentProvider {
fn summary(&self) -> String {
use std::fmt::Write;
let mut result = String::new();
let type_name = self
.type_name()
.strip_prefix("fyrox_impl::scene::")
.unwrap_or(self.type_name());
write!(result, "{} {}<{}>", self.handle(), self.name(), type_name,).unwrap();
if self.children().len() == 1 {
result.push_str(" 1 child");
} else if self.children().len() > 1 {
write!(result, " {} children", self.children().len()).unwrap();
}
if self.script_count() > 0 {
write!(result, " {} scripts", self.script_count()).unwrap();
}
if self.is_resource_instance_root() {
result.push_str(" root");
}
let origin = self.original_handle_in_resource();
if origin.is_some() {
write!(result, " from:{}", origin).unwrap();
}
if let Some(r) = self.resource() {
write!(result, " {}", r.summary()).unwrap();
}
result
}
fn local_bounding_box(&self) -> AxisAlignedBoundingBox;
fn world_bounding_box(&self) -> AxisAlignedBoundingBox;
fn id(&self) -> Uuid;
fn on_removed_from_graph(&mut self, #[allow(unused_variables)] graph: &mut Graph) {}
fn on_unlink(&mut self, #[allow(unused_variables)] graph: &mut Graph) {}
fn sync_native(
&self,
#[allow(unused_variables)] self_handle: Handle<Node>,
#[allow(unused_variables)] context: &mut SyncContext,
) {
}
fn on_global_transform_changed(
&self,
#[allow(unused_variables)] new_global_transform: &Matrix4<f32>,
#[allow(unused_variables)] context: &mut SyncContext,
) {
}
fn on_local_transform_changed(&self, #[allow(unused_variables)] context: &mut SyncContext) {}
fn is_alive(&self) -> bool {
true
}
fn update(&mut self, #[allow(unused_variables)] context: &mut UpdateContext) {}
fn collect_render_data(
&self,
#[allow(unused_variables)] ctx: &mut RenderContext,
) -> RdcControlFlow {
RdcControlFlow::Continue
}
#[inline]
fn should_be_rendered(&self, frustum: Option<&Frustum>, render_mask: BitMask) -> bool {
if *self.render_mask & render_mask == BitMask::none() {
return false;
}
if !self.global_visibility() {
return false;
}
if !self.is_globally_enabled() {
return false;
}
if self.frustum_culling() {
if let Some(frustum) = frustum {
if !frustum.is_intersects_aabb(&self.world_bounding_box()) {
return false;
}
}
}
true
}
fn debug_draw(&self, #[allow(unused_variables)] ctx: &mut SceneDrawingContext) {}
fn validate(&self, #[allow(unused_variables)] scene: &Scene) -> Result<(), String> {
Ok(())
}
}
impl<T: NodeTrait> ObjectOrVariantHelper<Node, T> for PhantomData<T> {
fn convert_to_dest_type_helper(node: &Node) -> Option<&T> {
(node.0.deref() as &dyn ComponentProvider).component_ref()
}
fn convert_to_dest_type_helper_mut(node: &mut Node) -> Option<&mut T> {
(node.0.deref_mut() as &mut dyn ComponentProvider).component_mut()
}
}
#[derive(Debug)]
pub struct Node(pub(crate) Box<dyn NodeTrait>);
impl<T: NodeTrait> From<T> for Node {
fn from(value: T) -> Self {
Self(Box::new(value))
}
}
impl Clone for Node {
fn clone(&self) -> Self {
self.0.clone_box()
}
}
impl SceneGraphNode for Node {
type Base = Base;
type SceneGraph = Graph;
type ResourceData = Model;
fn base(&self) -> &Self::Base {
self.0.deref()
}
fn set_base(&mut self, base: Self::Base) {
***self = base;
}
fn is_resource_instance_root(&self) -> bool {
self.is_resource_instance_root
}
fn original_handle_in_resource(&self) -> Handle<Self> {
self.original_handle_in_resource
}
fn set_original_handle_in_resource(&mut self, handle: Handle<Self>) {
self.original_handle_in_resource = handle;
}
fn resource(&self) -> Option<Resource<Self::ResourceData>> {
self.resource.clone()
}
fn self_handle(&self) -> Handle<Self> {
self.handle()
}
fn parent(&self) -> Handle<Self> {
self.parent
}
fn children(&self) -> &[Handle<Self>] {
&self.children
}
fn children_mut(&mut self) -> &mut [Handle<Self>] {
&mut self.children
}
fn instance_id(&self) -> Uuid {
self.instance_id.0
}
}
impl NameProvider for Node {
fn name(&self) -> &str {
&self.0.name
}
}
impl ComponentProvider for Node {
fn query_component_ref(&self, type_id: TypeId) -> Option<&dyn Any> {
self.0.query_component_ref(type_id)
}
fn query_component_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.0.query_component_mut(type_id)
}
}
uuid_provider!(Node = "a9bc5231-155c-4564-b0ca-f23972673925");
impl Deref for Node {
type Target = dyn NodeTrait;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl DerefMut for Node {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
#[macro_export]
macro_rules! define_is_as {
($typ:ty => fn $is:ident, fn $as_ref:ident, fn $as_mut:ident) => {
#[inline]
pub fn $is(&self) -> bool {
self.cast::<$typ>().is_some()
}
#[inline]
pub fn $as_ref(&self) -> &$typ {
self.cast::<$typ>()
.unwrap_or_else(|| panic!("Cast to {} failed!", stringify!($kind)))
}
#[inline]
pub fn $as_mut(&mut self) -> &mut $typ {
self.cast_mut::<$typ>()
.unwrap_or_else(|| panic!("Cast to {} failed!", stringify!($kind)))
}
};
}
impl Node {
#[inline]
pub fn new<T: NodeTrait>(node: T) -> Self {
Self(Box::new(node))
}
#[inline]
pub fn cast<T: NodeTrait>(&self) -> Option<&T> {
NodeAsAny::as_any(self.0.deref()).downcast_ref::<T>()
}
#[inline]
pub fn cast_mut<T: NodeTrait>(&mut self) -> Option<&mut T> {
NodeAsAny::as_any_mut(self.0.deref_mut()).downcast_mut::<T>()
}
pub(crate) fn mark_inheritable_variables_as_modified(&mut self) {
variable::mark_inheritable_properties_modified(self, &[TypeId::of::<UntypedResource>()])
}
pub(crate) fn set_inheritance_data(
&mut self,
original_handle: Handle<Node>,
model: ModelResource,
) {
self.resource = Some(model.clone());
self.is_resource_instance_root = false;
self.as_reflect_mut(&mut |reflect| {
mark_inheritable_properties_non_modified(reflect, &[TypeId::of::<UntypedResource>()])
});
self.original_handle_in_resource = original_handle;
}
define_is_as!(Mesh => fn is_mesh, fn as_mesh, fn as_mesh_mut);
define_is_as!(Pivot => fn is_pivot, fn as_pivot, fn as_pivot_mut);
define_is_as!(Camera => fn is_camera, fn as_camera, fn as_camera_mut);
define_is_as!(SpotLight => fn is_spot_light, fn as_spot_light, fn as_spot_light_mut);
define_is_as!(PointLight => fn is_point_light, fn as_point_light, fn as_point_light_mut);
define_is_as!(DirectionalLight => fn is_directional_light, fn as_directional_light, fn as_directional_light_mut);
define_is_as!(ParticleSystem => fn is_particle_system, fn as_particle_system, fn as_particle_system_mut);
define_is_as!(Sprite => fn is_sprite, fn as_sprite, fn as_sprite_mut);
define_is_as!(Terrain => fn is_terrain, fn as_terrain, fn as_terrain_mut);
define_is_as!(Decal => fn is_decal, fn as_decal, fn as_decal_mut);
define_is_as!(Rectangle => fn is_rectangle, fn as_rectangle, fn as_rectangle_mut);
define_is_as!(scene::rigidbody::RigidBody => fn is_rigid_body, fn as_rigid_body, fn as_rigid_body_mut);
define_is_as!(scene::collider::Collider => fn is_collider, fn as_collider, fn as_collider_mut);
define_is_as!(scene::joint::Joint => fn is_joint, fn as_joint, fn as_joint_mut);
define_is_as!(dim2::rigidbody::RigidBody => fn is_rigid_body2d, fn as_rigid_body2d, fn as_rigid_body2d_mut);
define_is_as!(dim2::collider::Collider => fn is_collider2d, fn as_collider2d, fn as_collider2d_mut);
define_is_as!(dim2::joint::Joint => fn is_joint2d, fn as_joint2d, fn as_joint2d_mut);
define_is_as!(Sound => fn is_sound, fn as_sound, fn as_sound_mut);
define_is_as!(Listener => fn is_listener, fn as_listener, fn as_listener_mut);
define_is_as!(NavigationalMesh => fn is_navigational_mesh, fn as_navigational_mesh, fn as_navigational_mesh_mut);
define_is_as!(AnimationBlendingStateMachine => fn is_absm, fn as_absm, fn as_absm_mut);
define_is_as!(AnimationPlayer => fn is_animation_player, fn as_animation_player, fn as_animation_player_mut);
define_is_as!(Ragdoll => fn is_ragdoll, fn as_ragdoll, fn as_ragdoll_mut);
}
impl Visit for Node {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.visit(name, visitor)
}
}
impl Reflect for Node {
fn source_path() -> &'static str {
file!()
}
fn derived_types() -> &'static [TypeId] {
&[]
}
fn query_derived_types(&self) -> &'static [TypeId] {
Self::derived_types()
}
fn type_name(&self) -> &'static str {
self.0.deref().type_name()
}
fn doc(&self) -> &'static str {
self.0.deref().doc()
}
fn assembly_name(&self) -> &'static str {
self.0.deref().assembly_name()
}
fn type_assembly_name() -> &'static str {
env!("CARGO_PKG_NAME")
}
fn fields_ref(&self, func: &mut dyn FnMut(&[FieldRef])) {
self.0.deref().fields_ref(func)
}
fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [FieldMut])) {
self.0.deref_mut().fields_mut(func)
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
Reflect::into_any(self.0)
}
fn as_any(&self, func: &mut dyn FnMut(&dyn Any)) {
Reflect::as_any(self.0.deref(), func)
}
fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn Any)) {
Reflect::as_any_mut(self.0.deref_mut(), func)
}
fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
self.0.deref().as_reflect(func)
}
fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
self.0.deref_mut().as_reflect_mut(func)
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
self.0.deref_mut().set(value)
}
fn set_field(
&mut self,
field: &str,
value: Box<dyn Reflect>,
func: &mut dyn FnMut(Result<Box<dyn Reflect>, SetFieldError>),
) {
self.0.deref_mut().set_field(field, value, func)
}
fn field(&self, name: &str, func: &mut dyn FnMut(Option<&dyn Reflect>)) {
self.0.deref().field(name, func)
}
fn field_mut(&mut self, name: &str, func: &mut dyn FnMut(Option<&mut dyn Reflect>)) {
self.0.deref_mut().field_mut(name, func)
}
fn try_clone_box(&self) -> Option<Box<dyn Reflect>> {
Some(Box::new(self.clone()))
}
}
#[cfg(test)]
mod test {
use crate::{
asset::manager::ResourceManager,
core::{
algebra::{Matrix4, Vector3},
futures::executor::block_on,
impl_component_provider,
reflect::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::{prelude::*, Visitor},
SafeLock, TypeUuidProvider,
},
engine::{self, SerializationContext},
resource::model::{Model, ModelResourceExtension},
scene::{
base::BaseBuilder,
mesh::{
surface::{SurfaceBuilder, SurfaceData, SurfaceResource},
MeshBuilder,
},
pivot::PivotBuilder,
transform::TransformBuilder,
Scene,
},
script::ScriptTrait,
};
use fyrox_graph::SceneGraph;
use fyrox_resource::io::FsResourceIo;
use fyrox_resource::untyped::ResourceKind;
use std::{fs, path::Path, sync::Arc};
#[derive(Debug, Clone, Reflect, Visit, Default)]
struct MyScript {
some_field: InheritableVariable<String>,
some_collection: InheritableVariable<Vec<u32>>,
}
impl_component_provider!(MyScript);
impl TypeUuidProvider for MyScript {
fn type_uuid() -> Uuid {
uuid!("d3f66902-803f-4ace-8170-0aa485d98b40")
}
}
impl ScriptTrait for MyScript {}
fn create_scene() -> Scene {
let mut scene = Scene::new();
let mesh;
PivotBuilder::new(
BaseBuilder::new()
.with_name("Pivot")
.with_script(MyScript {
some_field: "Foobar".to_string().into(),
some_collection: vec![1, 2, 3].into(),
})
.with_child({
mesh = MeshBuilder::new(
BaseBuilder::new().with_name("Mesh").with_local_transform(
TransformBuilder::new()
.with_local_position(Vector3::new(3.0, 2.0, 1.0))
.build(),
),
)
.with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok(
Uuid::new_v4(),
ResourceKind::Embedded,
SurfaceData::make_cone(16, 1.0, 1.0, &Matrix4::identity()),
))
.build()])
.build(&mut scene.graph);
mesh
}),
)
.build(&mut scene.graph);
let mesh = &scene.graph[mesh];
assert_eq!(mesh.surfaces().len(), 1);
assert!(mesh.surfaces()[0].bones.is_modified());
assert!(mesh.surfaces()[0].data.is_modified());
assert!(mesh.surfaces()[0].material.is_modified());
scene
}
fn save_scene(scene: &mut Scene, path: &Path) {
let mut visitor = Visitor::new();
scene.save("Scene", &mut visitor).unwrap();
visitor.save_ascii_to_file(path).unwrap();
}
#[test]
fn test_property_inheritance() {
if !Path::new("test_output").exists() {
fs::create_dir_all("test_output").unwrap();
}
let root_asset_path = Path::new("test_output/root.rgs");
let derived_asset_path = Path::new("test_output/derived.rgs");
{
let mut scene = create_scene();
save_scene(&mut scene, root_asset_path);
}
let resource_manager =
ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
resource_manager
.state()
.resource_registry
.safe_lock()
.set_path("test_output/resources.registry");
let serialization_context = SerializationContext::new();
serialization_context
.script_constructors
.add::<MyScript>("MyScript");
assert!(serialization_context
.script_constructors
.map()
.iter()
.any(|s| s.1.source_path == file!()));
engine::initialize_resource_manager_loaders(
&resource_manager,
Arc::new(serialization_context),
Default::default(),
Default::default(),
);
resource_manager.update_or_load_registry();
let root_asset = block_on(resource_manager.request::<Model>(root_asset_path)).unwrap();
{
let mut derived = Scene::new();
root_asset.instantiate(&mut derived);
let pivot = derived.graph.find_by_name_from_root("Pivot").unwrap().0;
let mesh = derived.graph.find_by_name_from_root("Mesh").unwrap().0;
let pivot = &mut derived.graph[pivot];
pivot
.local_transform_mut()
.set_position(Vector3::new(1.0, 2.0, 3.0));
let my_script = pivot.try_get_script_mut::<MyScript>().unwrap();
my_script.some_collection.push(4);
let mesh = derived.graph[mesh].as_mesh_mut();
assert_eq!(
**mesh.local_transform().position(),
Vector3::new(3.0, 2.0, 1.0)
);
assert_eq!(mesh.surfaces().len(), 1);
assert!(!mesh.surfaces()[0].bones.is_modified());
assert!(!mesh.surfaces()[0].data.is_modified());
assert!(!mesh.surfaces()[0].material.is_modified());
mesh.set_cast_shadows(false);
save_scene(&mut derived, derived_asset_path);
let registry = resource_manager.state().resource_registry.clone();
let mut registry = registry.safe_lock();
let mut ctx = registry.modify();
ctx.write_metadata(Uuid::new_v4(), derived_asset_path.to_path_buf())
.unwrap();
}
{
let derived_asset =
block_on(resource_manager.request::<Model>(derived_asset_path)).unwrap();
let derived_data = derived_asset.data_ref();
let derived_scene = derived_data.get_scene();
let pivot = derived_scene
.graph
.find_by_name_from_root("Pivot")
.unwrap()
.0;
let mesh = derived_scene
.graph
.find_by_name_from_root("Mesh")
.unwrap()
.0;
let pivot = &derived_scene.graph[pivot];
let my_script = pivot.try_get_script::<MyScript>().unwrap();
assert_eq!(
**pivot.local_transform().position(),
Vector3::new(1.0, 2.0, 3.0)
);
assert_eq!(*my_script.some_field, "Foobar");
assert_eq!(*my_script.some_collection, &[1, 2, 3, 4]);
let mesh = derived_scene.graph[mesh].as_mesh();
assert!(!mesh.cast_shadows());
assert_eq!(mesh.surfaces().len(), 1);
assert!(!mesh.surfaces()[0].bones.is_modified());
assert!(!mesh.surfaces()[0].data.is_modified());
assert!(!mesh.surfaces()[0].material.is_modified());
assert_eq!(
**mesh.local_transform().position(),
Vector3::new(3.0, 2.0, 1.0)
);
}
}
}