stardust_xr_asteroids/
lib.rs1#![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) },
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 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 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}