use std::{
any::TypeId,
collections::{HashMap, VecDeque},
marker::PhantomData,
ops::{Deref, DerefMut},
sync::Arc,
};
use parking_lot::{ArcRwLockReadGuard, ArcRwLockWriteGuard, RawRwLock, RwLock};
use crate::{
GameContext, Node,
asset::{Asset, AssetHandle, AssetLibrary, AssetState},
platform::SendSync,
prelude::{EventCtx, EventLabel, EventReceiver, Ready, node_transform::WorldTransform},
};
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub struct NodeId(u64);
impl Default for NodeId {
fn default() -> Self {
Self::new()
}
}
impl NodeId {
pub fn new() -> Self {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
NodeId(COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
pub struct SceneNode {
_id: NodeId,
name: String,
children: Vec<NodeId>,
parent: Option<NodeId>,
type_id: TypeId,
}
type NodeStorage = Arc<RwLock<Box<dyn Node>>>;
pub struct NodeHandle<'a, T: Node> {
id: NodeId,
scene: &'a Scene,
_ty: PhantomData<T>,
}
pub struct NodeReadGuard<T: Node> {
guard: ArcRwLockReadGuard<RawRwLock, Box<dyn Node>>,
_ty: PhantomData<T>,
}
pub struct NodeWriteGuard<T: Node> {
guard: ArcRwLockWriteGuard<RawRwLock, Box<dyn Node>>,
_ty: PhantomData<T>,
}
impl<T: Node> Deref for NodeReadGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.guard.as_any().downcast_ref::<T>().unwrap()
}
}
impl<T: Node> Deref for NodeWriteGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.guard.as_any().downcast_ref::<T>().unwrap()
}
}
impl<T: Node> DerefMut for NodeWriteGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.guard.as_any_mut().downcast_mut::<T>().unwrap()
}
}
impl<'a, T: Node> NodeHandle<'a, T> {
pub fn id(&self) -> NodeId {
self.id
}
pub fn name(&self) -> Option<String> {
self.scene.node_name(self.id)
}
pub fn children(&self) -> Vec<NodeId> {
self.scene.children(self.id)
}
pub fn spawn_child<C: Node>(&self, name: impl Into<String>, node: C) -> NodeHandle<'a, C> {
self.scene.spawn_as_child(name, node, self.id)
}
pub fn merge_scene(&self, other: Scene) -> Vec<NodeId> {
self.scene.merge_as_child(other, self.id)
}
pub fn merge_asset<A: SceneAsset>(&self, handle: AssetHandle<A>) {
self.scene.merge_asset_as_child(handle, self.id);
}
pub fn on<E: EventLabel>(
&self,
handler: impl FnMut(EventCtx<E, T>) + Send + Sync + 'static,
) -> &Self {
self.scene.on(self.id(), handler);
self
}
pub fn read(&self) -> NodeReadGuard<T> {
let node_lock = {
let nodes = self.scene.nodes.read();
Arc::clone(nodes.get(&self.id).expect("Node not found"))
};
let guard = RwLock::read_arc(&node_lock);
NodeReadGuard {
guard,
_ty: PhantomData,
}
}
pub fn write(&self) -> NodeWriteGuard<T> {
let node_lock = {
let nodes = self.scene.nodes.read();
Arc::clone(nodes.get(&self.id).expect("Node not found"))
};
let guard = RwLock::write_arc(&node_lock);
NodeWriteGuard {
guard,
_ty: PhantomData,
}
}
}
type PendingAssetEntry = (Box<dyn PendingSceneAsset>, Option<NodeId>);
pub struct Scene {
nodes: RwLock<HashMap<NodeId, NodeStorage>>,
heirarchy: RwLock<HashMap<NodeId, SceneNode>>,
events: RwLock<HashMap<NodeId, EventReceiver>>,
ready_queue: RwLock<VecDeque<NodeId>>,
pending_assets: RwLock<Vec<PendingAssetEntry>>,
}
impl Default for Scene {
fn default() -> Self {
Self::new()
}
}
impl<'a> Scene {
pub fn new() -> Self {
Self {
nodes: RwLock::new(HashMap::new()),
heirarchy: RwLock::new(HashMap::new()),
events: RwLock::new(HashMap::new()),
ready_queue: RwLock::new(VecDeque::new()),
pending_assets: RwLock::new(Vec::new()),
}
}
pub fn spawn<T: Node>(&'a self, name: impl Into<String>, node: T) -> NodeHandle<'a, T> {
self.spawn_with_parent(name, node, None)
}
pub fn spawn_as_child<T: Node>(
&'a self,
name: impl Into<String>,
node: T,
parent: NodeId,
) -> NodeHandle<'a, T> {
self.spawn_with_parent(name, node, Some(parent))
}
pub fn on<E: EventLabel, N: Node>(
&self,
node: NodeId,
handler: impl FnMut(EventCtx<E, N>) + SendSync + 'static,
) {
self.events
.write()
.entry(node)
.or_default()
.on::<E, N, _>(handler);
}
fn spawn_with_parent<T: Node>(
&'a self,
name: impl Into<String>,
node: T,
parent: Option<NodeId>,
) -> NodeHandle<'a, T> {
let id = NodeId::new();
let scene_node = SceneNode {
_id: id,
name: name.into(),
children: Vec::new(),
parent,
type_id: TypeId::of::<T>(),
};
{
let mut hierarchy = self.heirarchy.write();
if let Some(parent_id) = parent
&& let Some(parent_node) = hierarchy.get_mut(&parent_id)
{
parent_node.children.push(id);
}
hierarchy.insert(id, scene_node);
}
{
let mut nodes = self.nodes.write();
nodes.insert(id, Arc::new(RwLock::new(Box::new(node))));
}
{
let mut ready_queue = self.ready_queue.write();
ready_queue.push_back(id);
}
NodeHandle {
id,
scene: self,
_ty: PhantomData,
}
}
pub fn merge(&self, other: impl Into<Scene>) -> Vec<NodeId> {
self.merge_as_child_of(other.into(), None)
}
pub fn merge_as_child(&self, other: impl Into<Scene>, parent: NodeId) -> Vec<NodeId> {
self.merge_as_child_of(other.into(), Some(parent))
}
pub fn merge_asset<T: Asset + SceneAsset>(&self, handle: AssetHandle<T>) {
let pending = TypedPendingAsset { handle };
self.pending_assets.write().push((Box::new(pending), None));
}
pub fn merge_asset_as_child<T: Asset + SceneAsset>(
&self,
handle: AssetHandle<T>,
parent: NodeId,
) {
let pending = TypedPendingAsset { handle };
self.pending_assets
.write()
.push((Box::new(pending), Some(parent)));
}
fn merge_as_child_of(&self, other: Scene, parent: Option<NodeId>) -> Vec<NodeId> {
let mut other_hierarchy = other.heirarchy.write();
let mut other_nodes = other.nodes.write();
let mut other_events = other.events.write();
let root_ids: Vec<NodeId> = other_hierarchy
.iter()
.filter(|(_, node)| node.parent.is_none())
.map(|(id, _)| *id)
.collect();
{
let mut self_heirarchy = self.heirarchy.write();
let mut self_nodes = self.nodes.write();
let mut self_events = self.events.write();
for (id, mut scene_node) in other_hierarchy.drain() {
if scene_node.parent.is_none() {
scene_node.parent = parent;
}
self_heirarchy.insert(id, scene_node);
}
for (id, node_data) in other_nodes.drain() {
self_nodes.insert(id, node_data);
}
for (id, events) in other_events.drain() {
self_events.insert(id, events);
}
self.ready_queue
.write()
.append(&mut other.ready_queue.write());
self.pending_assets
.write()
.append(&mut other.pending_assets.write());
if let Some(parent_id) = parent
&& let Some(parent_node) = self_heirarchy.get_mut(&parent_id)
{
parent_node.children.extend(&root_ids);
}
}
root_ids
}
pub fn get<T: Node>(&'a self, id: NodeId) -> Option<NodeHandle<'a, T>> {
let hierarchy = self.heirarchy.read();
let scene_node = hierarchy.get(&id)?;
if scene_node.type_id != TypeId::of::<T>() {
return None;
}
Some(NodeHandle {
id,
scene: self,
_ty: PhantomData,
})
}
pub fn get_by_name<T: Node>(&'a self, name: &str) -> Option<NodeHandle<'a, T>> {
let hierarchy = self.heirarchy.read();
let type_id = TypeId::of::<T>();
for (id, scene_node) in hierarchy.iter() {
if scene_node.name == name && scene_node.type_id == type_id {
return Some(NodeHandle {
id: *id,
scene: self,
_ty: PhantomData,
});
}
}
None
}
pub fn parent(&self, id: NodeId) -> Option<NodeId> {
self.heirarchy.read().get(&id).and_then(|n| n.parent)
}
pub fn children(&self, id: NodeId) -> Vec<NodeId> {
self.heirarchy
.read()
.get(&id)
.map(|n| n.children.clone())
.unwrap_or_default()
}
pub fn node_name(&self, id: NodeId) -> Option<String> {
self.heirarchy.read().get(&id).map(|n| n.name.clone())
}
pub fn collect<T: Node>(&'a self) -> Vec<NodeHandle<'a, T>> {
let heirarchy = self.heirarchy.read();
let type_id = TypeId::of::<T>();
heirarchy
.iter()
.filter(|(_, node)| node.type_id == type_id)
.map(|(id, _)| NodeHandle {
id: *id,
scene: self,
_ty: PhantomData,
})
.collect()
}
pub fn root_ids(&self) -> Vec<NodeId> {
let hierarchy = self.heirarchy.read();
hierarchy
.iter()
.filter(|(_, node)| node.parent.is_none())
.map(|(id, _)| *id)
.collect()
}
pub fn emit<E: EventLabel>(&self, event: &E, ctx: &GameContext) {
for root_id in self.root_ids() {
self.emit_recursive(root_id, event, ctx);
}
}
fn emit_recursive<E: EventLabel>(&self, id: NodeId, event: &E, ctx: &GameContext) {
if let Some(events) = self.events.read().get(&id) {
events.trigger(event, self, id, ctx);
}
let children = self.children(id);
for child_id in children {
self.emit_recursive(child_id, event, ctx);
}
}
pub fn sync_world_transform(&self) {
for id in self.root_ids() {
self.sync_world_transform_recursive(id, WorldTransform::default());
}
}
fn sync_world_transform_recursive(&self, id: NodeId, parent_world: WorldTransform) {
let node_lock = {
let nodes = self.nodes.read();
nodes.get(&id).map(Arc::clone)
};
let Some(node_lock) = node_lock else {
return;
};
let mut node = node_lock.write();
node.get_transform().get_world_space(parent_world);
let current_world = *node.get_transform().world_space();
drop(node);
let children = self.children(id);
for child in children {
self.sync_world_transform_recursive(child, current_world);
}
}
pub(crate) fn pop_ready_queue(&self, ctx: &GameContext) {
while let Some(id) = self.ready_queue.write().pop_front() {
self.emit_to(id, &Ready, ctx);
}
}
pub fn emit_to<E: EventLabel>(&self, id: NodeId, event: &E, ctx: &GameContext) {
if let Some(events) = self.events.read().get(&id) {
events.trigger(event, self, id, ctx);
}
}
pub fn for_each<T: Node>(&self, f: &mut impl FnMut(&mut T)) {
let type_id = TypeId::of::<T>();
let node_locks: Vec<NodeStorage> = {
let hierarchy = self.heirarchy.read();
let nodes = self.nodes.read();
hierarchy
.iter()
.filter(|(_, node)| node.type_id == type_id)
.filter_map(|(id, _)| nodes.get(id).map(Arc::clone))
.collect()
};
for node_lock in node_locks {
let mut node = node_lock.write();
if let Some(concrete) = node.as_any_mut().downcast_mut::<T>() {
f(concrete);
}
}
}
pub fn for_each_ref<T: Node>(&self, f: &mut impl FnMut(&T)) {
let type_id = TypeId::of::<T>();
let node_locks: Vec<NodeStorage> = {
let hierarchy = self.heirarchy.read();
let nodes = self.nodes.read();
hierarchy
.iter()
.filter(|(_, node)| node.type_id == type_id)
.filter_map(|(id, _)| nodes.get(id).map(Arc::clone))
.collect()
};
for node_lock in node_locks {
let node = node_lock.read();
if let Some(concrete) = node.as_any().downcast_ref::<T>() {
f(concrete);
}
}
}
pub fn for_each_with_id<T: Node>(&self, f: &mut impl FnMut(NodeId, &mut T)) {
let type_id = TypeId::of::<T>();
let node_data: Vec<(NodeId, NodeStorage)> = {
let hierarchy = self.heirarchy.read();
let nodes = self.nodes.read();
hierarchy
.iter()
.filter(|(_, node)| node.type_id == type_id)
.filter_map(|(id, _)| nodes.get(id).map(|n| (*id, Arc::clone(n))))
.collect()
};
for (id, node_lock) in node_data {
let mut node = node_lock.write();
if let Some(concrete) = node.as_any_mut().downcast_mut::<T>() {
f(id, concrete);
}
}
}
pub fn poll_async(&mut self, assets: &AssetLibrary) {
let mut loaded_indices = Vec::new();
{
let pending = self.pending_assets.read();
if pending.is_empty() {
return;
}
log::trace!("Polling {} scene assets", pending.len());
for (i, (asset, parent)) in pending.iter().enumerate() {
if asset.poll_and_load(assets, self, *parent) {
loaded_indices.push(i);
}
}
}
if !loaded_indices.is_empty() {
let mut pending = self.pending_assets.write();
for &i in loaded_indices.iter().rev() {
log::info!("merging loaded scene into scene");
pending.swap_remove(i);
}
}
}
}
trait PendingSceneAsset: Send + Sync {
fn poll_and_load(&self, assets: &AssetLibrary, scene: &Scene, parent: Option<NodeId>) -> bool;
}
struct TypedPendingAsset<T: Asset + SceneAsset> {
handle: AssetHandle<T>,
}
impl<T: Asset + SceneAsset> PendingSceneAsset for TypedPendingAsset<T> {
fn poll_and_load(&self, assets: &AssetLibrary, scene: &Scene, parent: Option<NodeId>) -> bool {
match assets.get(&self.handle) {
AssetState::Loaded(asset) => {
asset.load(scene, parent);
true }
AssetState::Error(e) => {
log::error!("Failed to load scene asset: {:?}", e);
true }
AssetState::Loading => false, }
}
}
pub trait SceneAsset: Asset {
fn load(&self, scene: &Scene, parent: Option<NodeId>);
}
pub trait IntoScene {
fn into(self, assets: &AssetLibrary) -> Scene;
}
impl IntoScene for Scene {
fn into(self, _assets: &AssetLibrary) -> Scene {
self
}
}
pub trait SceneBuilder {
fn build(&mut self, assets: &AssetLibrary) -> Scene;
}
impl<T: SceneBuilder> IntoScene for T {
fn into(mut self, assets: &AssetLibrary) -> Scene {
self.build(assets)
}
}