Fix #6: Rendering Shared 3D Models #10
|
@ -330,6 +330,12 @@ version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic_refcell"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -708,12 +714,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.18"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
|
@ -1749,6 +1752,7 @@ name = "lyra-ecs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"atomic_refcell",
|
||||||
"lyra-ecs-derive",
|
"lyra-ecs-derive",
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"paste",
|
"paste",
|
||||||
|
@ -1844,6 +1848,7 @@ dependencies = [
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
|
"lyra-scene",
|
||||||
"mime",
|
"mime",
|
||||||
"notify",
|
"notify",
|
||||||
"notify-debouncer-full",
|
"notify-debouncer-full",
|
||||||
|
|
|
@ -367,8 +367,8 @@ impl World {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Ensure that all non-send resources are only accessible on the main thread.
|
// TODO: Ensure that all non-send resources are only accessible on the main thread.
|
||||||
/* unsafe impl Send for World {}
|
unsafe impl Send for World {}
|
||||||
unsafe impl Sync for World {} */
|
unsafe impl Sync for World {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
||||||
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
lyra-reflect = { path = "../lyra-reflect" }
|
lyra-reflect = { path = "../lyra-reflect" }
|
||||||
lyra-math = { path = "../lyra-math" }
|
lyra-math = { path = "../lyra-math" }
|
||||||
|
lyra-scene = { path = "../lyra-scene" }
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
base64 = "0.21.4"
|
base64 = "0.21.4"
|
||||||
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }
|
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }
|
||||||
|
|
|
@ -3,9 +3,10 @@ use std::{ffi::OsStr, path::{Path, PathBuf}, sync::Arc};
|
||||||
use glam::{Quat, Vec3};
|
use glam::{Quat, Vec3};
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use lyra_math::Transform;
|
use lyra_math::Transform;
|
||||||
|
use lyra_scene::{SceneGraph, SceneNode};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{gltf::GltfScene, loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage};
|
use crate::{loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage};
|
||||||
use super::{Gltf, GltfNode, Material, Mesh, MeshIndices, MeshVertexAttribute, VertexAttributeData};
|
use super::{Gltf, GltfNode, Material, Mesh, MeshIndices, MeshVertexAttribute, VertexAttributeData};
|
||||||
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
@ -64,7 +65,7 @@ impl ModelLoader {
|
||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
|
|
||||||
fn process_node(ctx: &mut GltfLoadContext, materials: &Vec<ResHandle<Material>>, gnode: gltf::Node<'_>) -> GltfNode {
|
fn process_node(ctx: &mut GltfLoadContext, materials: &Vec<ResHandle<Material>>, scene: &mut SceneGraph, scene_parent: &SceneNode, gnode: gltf::Node<'_>) -> GltfNode {
|
||||||
let mut node = GltfNode::default();
|
let mut node = GltfNode::default();
|
||||||
|
|
||||||
node.transform = {
|
node.transform = {
|
||||||
|
@ -75,6 +76,8 @@ impl ModelLoader {
|
||||||
};
|
};
|
||||||
node.name = gnode.name().map(str::to_string);
|
node.name = gnode.name().map(str::to_string);
|
||||||
|
|
||||||
|
let scene_node = scene.add_node_under(scene_parent, node.transform, ());
|
||||||
|
|
||||||
if let Some(mesh) = gnode.mesh() {
|
if let Some(mesh) = gnode.mesh() {
|
||||||
let mut new_mesh = Mesh::default();
|
let mut new_mesh = Mesh::default();
|
||||||
|
|
||||||
|
@ -127,11 +130,12 @@ impl ModelLoader {
|
||||||
|
|
||||||
let handle = ResHandle::new_ready(None, new_mesh);
|
let handle = ResHandle::new_ready(None, new_mesh);
|
||||||
ctx.resource_manager.store_uuid(handle.clone());
|
ctx.resource_manager.store_uuid(handle.clone());
|
||||||
node.mesh = Some(handle);
|
node.mesh = Some(handle.clone());
|
||||||
|
scene.insert(&scene_node, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in gnode.children() {
|
for child in gnode.children() {
|
||||||
let cmesh = ModelLoader::process_node(ctx, materials, child);
|
let cmesh = ModelLoader::process_node(ctx, materials, scene, &scene_node, child);
|
||||||
node.children.push(cmesh);
|
node.children.push(cmesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +214,21 @@ impl ResourceLoader for ModelLoader {
|
||||||
debug!("Loaded {} materials in {}s", materials.len(), mat_time.as_secs_f32());
|
debug!("Loaded {} materials in {}s", materials.len(), mat_time.as_secs_f32());
|
||||||
|
|
||||||
for (_idx, scene) in gltf.scenes().enumerate() {
|
for (_idx, scene) in gltf.scenes().enumerate() {
|
||||||
let start_inst = Instant::now();
|
let mut graph = SceneGraph::new();
|
||||||
|
let root_node = graph.root_node();
|
||||||
|
|
||||||
|
for node in scene.nodes() {
|
||||||
|
let n = ModelLoader::process_node(&mut context, &materials, &mut graph, &root_node, node);
|
||||||
|
|
||||||
|
if let Some(mesh) = n.mesh {
|
||||||
|
gltf_out.meshes.push(mesh.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let graph = ResHandle::new_ready(Some(path.as_str()), graph);
|
||||||
|
gltf_out.scenes.push(graph);
|
||||||
|
|
||||||
|
/* let start_inst = Instant::now();
|
||||||
let nodes: Vec<GltfNode> = scene.nodes()
|
let nodes: Vec<GltfNode> = scene.nodes()
|
||||||
.map(|node| ModelLoader::process_node(&mut context, &materials, node))
|
.map(|node| ModelLoader::process_node(&mut context, &materials, node))
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -228,7 +246,7 @@ impl ResourceLoader for ModelLoader {
|
||||||
nodes,
|
nodes,
|
||||||
};
|
};
|
||||||
let scene = ResHandle::new_ready(Some(path.as_str()), scene);
|
let scene = ResHandle::new_ready(Some(path.as_str()), scene);
|
||||||
gltf_out.scenes.push(scene);
|
gltf_out.scenes.push(scene); */
|
||||||
}
|
}
|
||||||
|
|
||||||
gltf_out.materials = materials;
|
gltf_out.materials = materials;
|
||||||
|
@ -249,6 +267,8 @@ impl ResourceLoader for ModelLoader {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use lyra_ecs::{query::Entities, relation::ChildOf};
|
||||||
|
|
||||||
use crate::tests::busy_wait_resource;
|
use crate::tests::busy_wait_resource;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -272,14 +292,30 @@ mod tests {
|
||||||
let scene = &gltf.scenes[0]
|
let scene = &gltf.scenes[0]
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
|
|
||||||
assert_eq!(scene.nodes.len(), 1);
|
let mut node = None;
|
||||||
let mnode = &scene.nodes[0];
|
scene.traverse_down(|no, _tran| {
|
||||||
|
node = Some(no.clone());
|
||||||
|
});
|
||||||
|
|
||||||
assert!(mnode.mesh.is_some());
|
let world = scene.world();
|
||||||
assert_eq!(mnode.transform, Transform::from_xyz(0.0, 0.0, 0.0));
|
let node = node.unwrap();
|
||||||
assert_eq!(mnode.children.len(), 0);
|
|
||||||
|
|
||||||
let mesh = mnode.mesh.as_ref().unwrap();
|
let data = world.view_one::<(&ResHandle<Mesh>, &Transform)>(node.entity()).get();
|
||||||
|
debug_assert!(data.is_some(), "The mesh was not loaded"); // transform will always be there
|
||||||
|
let data = data.unwrap();
|
||||||
|
|
||||||
|
// ensure there are no children of the node
|
||||||
|
assert_eq!(
|
||||||
|
world.view::<Entities>()
|
||||||
|
.relates_to::<ChildOf>(node.entity())
|
||||||
|
.into_iter()
|
||||||
|
.count(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(*data.1, Transform::from_xyz(0.0, 0.0, 0.0));
|
||||||
|
|
||||||
|
let mesh = data.0;
|
||||||
let mesh = mesh.data_ref().unwrap();
|
let mesh = mesh.data_ref().unwrap();
|
||||||
assert!(mesh.position().unwrap().len() > 0);
|
assert!(mesh.position().unwrap().len() > 0);
|
||||||
assert!(mesh.normals().unwrap().len() > 0);
|
assert!(mesh.normals().unwrap().len() > 0);
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub use loader::*;
|
||||||
|
|
||||||
pub mod material;
|
pub mod material;
|
||||||
use lyra_math::Transform;
|
use lyra_math::Transform;
|
||||||
|
use lyra_scene::SceneGraph;
|
||||||
use crate::ResourceData;
|
use crate::ResourceData;
|
||||||
pub use material::*;
|
pub use material::*;
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ use crate::ResHandle;
|
||||||
/// A loaded Gltf file
|
/// A loaded Gltf file
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Gltf {
|
pub struct Gltf {
|
||||||
pub scenes: Vec<ResHandle<GltfScene>>,
|
pub scenes: Vec<ResHandle<SceneGraph>>,
|
||||||
pub materials: Vec<ResHandle<Material>>,
|
pub materials: Vec<ResHandle<Material>>,
|
||||||
pub meshes: Vec<ResHandle<Mesh>>,
|
pub meshes: Vec<ResHandle<Mesh>>,
|
||||||
}
|
}
|
||||||
|
@ -55,14 +56,6 @@ impl ResourceData for Gltf {
|
||||||
impl Gltf {
|
impl Gltf {
|
||||||
/// Collects all Gltf meshes and gets their world Transform.
|
/// Collects all Gltf meshes and gets their world Transform.
|
||||||
pub fn collect_world_meshes(&self) -> Vec<(ResHandle<Mesh>, Transform)> {
|
pub fn collect_world_meshes(&self) -> Vec<(ResHandle<Mesh>, Transform)> {
|
||||||
let mut v = vec![];
|
todo!()
|
||||||
|
|
||||||
for scene in self.scenes.iter() {
|
|
||||||
let mut tmp = scene.data_ref()
|
|
||||||
.unwrap().collect_world_meshes();
|
|
||||||
v.append(&mut tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
v
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use lyra_math::Transform;
|
use lyra_math::Transform;
|
||||||
|
use lyra_scene::SceneGraph;
|
||||||
use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle};
|
use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle};
|
||||||
|
|
||||||
use super::Mesh;
|
use super::Mesh;
|
||||||
|
@ -33,8 +34,8 @@ impl ResourceData for GltfNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Scene in a Gltf file
|
// A Scene in a Gltf file
|
||||||
#[derive(Clone)]
|
/* #[derive(Clone)]
|
||||||
pub struct GltfScene {
|
pub struct GltfScene {
|
||||||
pub nodes: Vec<GltfNode>,
|
pub nodes: Vec<GltfNode>,
|
||||||
}
|
}
|
||||||
|
@ -92,4 +93,18 @@ impl GltfScene {
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
impl ResourceData for SceneGraph {
|
||||||
|
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -214,12 +214,12 @@ impl UntypedResHandle {
|
||||||
/// However, the only times it will be blocking is if another thread is reloading the resource
|
/// However, the only times it will be blocking is if another thread is reloading the resource
|
||||||
/// and has a write lock on the data. This means that most of the time, it is not blocking.
|
/// and has a write lock on the data. This means that most of the time, it is not blocking.
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct ResHandle<T: 'static> {
|
pub struct ResHandle<T: ResourceData> {
|
||||||
pub(crate) handle: UntypedResHandle,
|
pub(crate) handle: UntypedResHandle,
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Clone for ResHandle<T> {
|
impl<T: ResourceData> Clone for ResHandle<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
handle: self.handle.clone(),
|
handle: self.handle.clone(),
|
||||||
|
@ -228,7 +228,7 @@ impl<T> Clone for ResHandle<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Deref for ResHandle<T> {
|
impl<T: ResourceData> Deref for ResHandle<T> {
|
||||||
type Target = UntypedResHandle;
|
type Target = UntypedResHandle;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -236,7 +236,7 @@ impl<T> Deref for ResHandle<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DerefMut for ResHandle<T> {
|
impl<T: ResourceData> DerefMut for ResHandle<T> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.handle
|
&mut self.handle
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ impl<T: ResourceData> ResHandle<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send + Sync + 'static> ResourceStorage for ResHandle<T> {
|
impl<T: ResourceData> ResourceStorage for ResHandle<T> {
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ impl ResourceManager {
|
||||||
|
|
||||||
/// Request a resource without downcasting to a `ResHandle<T>`.
|
/// Request a resource without downcasting to a `ResHandle<T>`.
|
||||||
/// Whenever you're ready to downcast, you can do so like this:
|
/// Whenever you're ready to downcast, you can do so like this:
|
||||||
/// ```compile_fail
|
/// ```nobuild
|
||||||
/// let arc_any = res_arc.as_arc_any();
|
/// let arc_any = res_arc.as_arc_any();
|
||||||
/// let res: Arc<ResHandle<T>> = res.downcast::<ResHandle<T>>().expect("Failure to downcast resource");
|
/// let res: Arc<ResHandle<T>> = res.downcast::<ResHandle<T>>().expect("Failure to downcast resource");
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -221,7 +221,7 @@ impl ResourceManager {
|
||||||
///
|
///
|
||||||
/// The resource cannot be requested with [`ResourceManager::request`], it can only be
|
/// The resource cannot be requested with [`ResourceManager::request`], it can only be
|
||||||
/// retrieved with [`ResourceManager::request_uuid`].
|
/// retrieved with [`ResourceManager::request_uuid`].
|
||||||
pub fn store_uuid<T: Send + Sync + 'static>(&self, res: ResHandle<T>) {
|
pub fn store_uuid<T: ResourceData>(&self, res: ResHandle<T>) {
|
||||||
let mut state = self.state_mut();
|
let mut state = self.state_mut();
|
||||||
state.resources.insert(res.uuid().to_string(), Arc::new(res));
|
state.resources.insert(res.uuid().to_string(), Arc::new(res));
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ impl ResourceManager {
|
||||||
///
|
///
|
||||||
/// Returns `None` if the resource was not found. The resource must of had been
|
/// Returns `None` if the resource was not found. The resource must of had been
|
||||||
/// stored with [`ResourceManager::request`] to return `Some`.
|
/// stored with [`ResourceManager::request`] to return `Some`.
|
||||||
pub fn request_uuid<T: Send + Sync + 'static>(&self, uuid: &Uuid) -> Option<ResHandle<T>> {
|
pub fn request_uuid<T: ResourceData>(&self, uuid: &Uuid) -> Option<ResHandle<T>> {
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
match state.resources.get(&uuid.to_string())
|
match state.resources.get(&uuid.to_string())
|
||||||
.or_else(|| state.uuid_resources.get(&uuid))
|
.or_else(|| state.uuid_resources.get(&uuid))
|
||||||
|
@ -291,7 +291,7 @@ impl ResourceManager {
|
||||||
/// Requests bytes from the manager.
|
/// Requests bytes from the manager.
|
||||||
pub fn request_loaded_bytes<T>(&self, ident: &str) -> Result<Arc<ResHandle<T>>, RequestError>
|
pub fn request_loaded_bytes<T>(&self, ident: &str) -> Result<Arc<ResHandle<T>>, RequestError>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Any + 'static
|
T: ResourceData
|
||||||
{
|
{
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
match state.resources.get(&ident.to_string()) {
|
match state.resources.get(&ident.to_string()) {
|
||||||
|
@ -366,7 +366,7 @@ impl ResourceManager {
|
||||||
/// the handle.
|
/// the handle.
|
||||||
pub fn reload<T>(&self, resource: ResHandle<T>) -> Result<(), RequestError>
|
pub fn reload<T>(&self, resource: ResHandle<T>) -> Result<(), RequestError>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Any + 'static
|
T: ResourceData
|
||||||
{
|
{
|
||||||
let state = self.state();
|
let state = self.state();
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub trait WorldAssetExt {
|
||||||
/// automatically triggered if the resource is being watched.
|
/// automatically triggered if the resource is being watched.
|
||||||
fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError>
|
fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Any + 'static;
|
T: ResourceData;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorldAssetExt for World {
|
impl WorldAssetExt for World {
|
||||||
|
@ -67,7 +67,7 @@ impl WorldAssetExt for World {
|
||||||
|
|
||||||
fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError>
|
fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Any + 'static
|
T: ResourceData
|
||||||
{
|
{
|
||||||
let man = self.get_resource_or_default::<ResourceManager>();
|
let man = self.get_resource_or_default::<ResourceManager>();
|
||||||
man.reload(resource)
|
man.reload(resource)
|
||||||
|
|
|
@ -116,6 +116,13 @@ impl SceneGraph {
|
||||||
world_add_child_node(&mut self.world, parent, local_transform, bundle)
|
world_add_child_node(&mut self.world, parent, local_transform, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a component bundle to a SceneNode.
|
||||||
|
///
|
||||||
|
/// See [`lyra_ecs::World::insert`].
|
||||||
|
pub fn insert<B: Bundle>(&mut self, node: &SceneNode, bundle: B) {
|
||||||
|
self.world.insert(node.entity(), bundle);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_empty_node_under(&mut self, parent: &SceneNode, local_transform: Transform) -> SceneNode {
|
pub fn add_empty_node_under(&mut self, parent: &SceneNode, local_transform: Transform) -> SceneNode {
|
||||||
let e = self.world.spawn((SceneNodeFlag, local_transform));
|
let e = self.world.spawn((SceneNodeFlag, local_transform));
|
||||||
self.world.add_relation(e, ChildOf, parent.entity());
|
self.world.add_relation(e, ChildOf, parent.entity());
|
||||||
|
@ -155,6 +162,11 @@ impl SceneGraph {
|
||||||
pub fn root_node(&self) -> SceneNode {
|
pub fn root_node(&self) -> SceneNode {
|
||||||
self.root_node.clone()
|
self.root_node.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve a borrow of the world that backs the Scene
|
||||||
|
pub fn world(&self) -> &World {
|
||||||
|
&self.world
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a node under a parent node.
|
/// Add a node under a parent node.
|
||||||
|
|
Loading…
Reference in New Issue