Skip to main content

stardust_xr_asteroids/
lib.rs

1#![allow(clippy::too_many_arguments)]
2
3pub mod client;
4mod context;
5mod custom;
6mod dynamic_element;
7mod element;
8pub mod elements;
9mod inner;
10mod mapped;
11mod resource;
12mod task;
13mod util;
14
15use crate::task::{FinishedTaskCallback, RootTasker};
16use bumpalo::{Bump, boxed::Box};
17use element::ElementDiffer;
18use inner::ElementInnerMap;
19use mapped::Mapped;
20use resource::ResourceRegistry;
21use stardust_xr_fusion::{root::FrameInfo, spatial::SpatialRef};
22use std::{path::PathBuf, sync::mpsc};
23
24pub use client::ClientState;
25pub use context::*;
26pub use custom::*;
27pub use dynamic_element::*;
28pub use element::{Element, gen_inner_key};
29pub use task::Tasker;
30pub use util::*;
31
32pub trait ValidState: Sized + Send + Sync + 'static {}
33impl<T: Sized + Send + Sync + 'static> ValidState for T {}
34
35pub trait Reify: ValidState + Sized + Send + Sync + 'static {
36	fn reify(&self, context: &Context, tasks: impl Tasker<Self>) -> impl Element<Self>;
37
38	fn reify_substate<
39		SuperState: ValidState,
40		Mapper: Fn(&mut SuperState) -> Option<&mut Self> + Clone + Send + Sync + 'static,
41	>(
42		&self,
43		context: &Context,
44		tasks: impl Tasker<SuperState>,
45		mapper: Mapper,
46	) -> Mapped<SuperState, Self, Mapper, impl Element<Self>> {
47		let tasks = tasks.clone().map::<Self, Mapper>(mapper.clone());
48		self.reify(context, tasks).map(mapper)
49	}
50}
51
52pub struct Projector<State: Reify>(Option<ProjectorInner<State>>);
53impl<State: Reify> Projector<State> {
54	pub fn create(
55		state: &State,
56		context: &Context,
57		tasker: RootTasker<State>,
58		rx: mpsc::Receiver<FinishedTaskCallback<State>>,
59		parent_spatial: SpatialRef,
60		root_element_path: PathBuf,
61	) -> Projector<State> {
62		let mut inner_map = ElementInnerMap::default();
63		let mut resource_registry = ResourceRegistry::default();
64
65		let blueprint = state.reify(context, tasker.clone());
66		blueprint.create_inner_recursive(
67			0,
68			context,
69			&parent_spatial,
70			&root_element_path,
71			&mut inner_map,
72			&mut resource_registry,
73		);
74		let bump = Bump::new();
75
76		Self(Some(ProjectorInner::new(
77			parent_spatial,
78			inner_map,
79			resource_registry,
80			tasker,
81			rx,
82			root_element_path,
83			bump,
84			move |bump| unsafe {
85				let concrete = Box::new_in(blueprint, bump);
86				let raw = Box::into_raw(concrete);
87				Box::from_raw(raw) // coerce the type manually since bumpalo can't implement `CoerceUnsized`
88			},
89		)))
90	}
91
92	#[tracing::instrument(level = "debug", skip_all)]
93	pub fn update(&mut self, context: &Context, state: &mut State) {
94		let Some(mut projector) = self.0.take() else {
95			tracing::warn!("Projector not found on update... how??");
96			return;
97		};
98		projector.with_task_callback_rx_mut(|task_callback_rx| {
99			while let Ok(task_callback_rx) = task_callback_rx.try_recv() {
100				(task_callback_rx)(state);
101			}
102		});
103		let blueprint = state.reify(context, projector.borrow_root_tasker().clone());
104		projector.with_mut(|fields| {
105			blueprint.dynamic_diff(
106				0,
107				fields.old.as_ref(),
108				context,
109				fields.root,
110				fields.root_element_path,
111				fields.inner_map,
112				&mut *fields.resource_registry,
113			);
114		});
115
116		// Move out fields by destructuring
117		let ouroboros_impl_projector_inner::Heads {
118			mut bump,
119			root_element_path,
120			resource_registry,
121			root_tasker,
122			task_callback_rx,
123			inner_map,
124			root,
125			..
126		} = projector.into_heads();
127		bump.reset();
128		self.0.replace(ProjectorInner::new(
129			root,
130			inner_map,
131			resource_registry,
132			root_tasker,
133			task_callback_rx,
134			root_element_path,
135			bump,
136			move |bump| unsafe {
137				let old_concrete = Box::new_in(blueprint, bump);
138				let old_raw = Box::into_raw(old_concrete);
139				// coerce the type manually since bumpalo can't implement `CoerceUnsized`
140				Box::from_raw(old_raw)
141			},
142		));
143	}
144	pub fn frame(&mut self, context: &Context, info: &FrameInfo, state: &mut State) {
145		let Some(projector) = self.0.as_mut() else {
146			tracing::warn!("Projector not found on frame... how??");
147			return;
148		};
149		projector.with_mut(|fields| {
150			fields
151				.old
152				.dynamic_frame_recursive(context, info, state, fields.inner_map);
153		});
154	}
155}
156
157#[ouroboros::self_referencing]
158struct ProjectorInner<State: Reify> {
159	root: SpatialRef,
160	inner_map: ElementInnerMap,
161	resource_registry: ResourceRegistry,
162	root_tasker: RootTasker<State>,
163	task_callback_rx: mpsc::Receiver<task::FinishedTaskCallback<State>>,
164	root_element_path: PathBuf,
165	bump: Bump,
166	#[borrows(bump)]
167	#[covariant]
168	old: Box<'this, dyn DynamicDiffer<State>>,
169}