1use crate::gfx::render_types::{LineVertex, TextDrawCall};
9use crate::gfx::view_modes::{ShowFlags, ViewMode};
10use crate::render::scene_flow::SceneControl;
11use alloc::vec::Vec;
12
13type Mat4 = [[f32; 4]; 4];
14
15#[derive(Clone, Copy, Debug)]
17pub struct FrameScalars {
18 pub elapsed: f32,
20 pub fov_y_radians: f32,
22 pub near: f32,
24 pub far: f32,
26 pub view: Mat4,
28 pub cam_pos: [f32; 3],
30 pub view_mode: ViewMode,
32 pub show: ShowFlags,
34 pub world_hidden: bool,
36 pub menu_active: bool,
38}
39
40impl Default for FrameScalars {
41 fn default() -> Self {
42 Self {
43 elapsed: 0.0,
44 fov_y_radians: core::f32::consts::FRAC_PI_4,
45 near: 0.05,
46 far: 200.0,
47 view: crate::gfx::transform::IDENTITY,
48 cam_pos: [0.0; 3],
49 view_mode: ViewMode::default(),
50 show: ShowFlags::default(),
51 world_hidden: false,
52 menu_active: false,
53 }
54 }
55}
56
57#[derive(Clone, Copy, Debug, Default)]
60pub struct UiIntents {
61 pub cursor_hidden: bool,
63 pub menu_mode: Option<bool>,
65 pub camera_capture: Option<bool>,
67}
68
69#[derive(Debug, Default)]
73pub struct SpanBuffer<T> {
74 values: Vec<T>,
75 spans: Vec<(usize, u32, u32)>,
76}
77
78impl<T: Copy> SpanBuffer<T> {
79 pub fn clear(&mut self) {
81 self.values.clear();
82 self.spans.clear();
83 }
84
85 pub fn is_empty(&self) -> bool {
87 self.spans.is_empty()
88 }
89
90 pub fn push(&mut self, slot: usize, values: &[T]) {
92 let start = self.values.len() as u32;
93 self.values.extend_from_slice(values);
94 self.spans.push((slot, start, values.len() as u32));
95 }
96
97 pub fn iter(&self) -> impl Iterator<Item = (usize, &[T])> {
99 self.spans
100 .iter()
101 .map(|&(slot, start, len)| (slot, &self.values[start as usize..(start + len) as usize]))
102 }
103}
104
105#[derive(Clone, Copy, Debug, PartialEq)]
108pub enum SceneOp {
109 SetFade(f32),
111 Visibility {
113 draw_idx: usize,
115 visible: bool,
117 },
118}
119
120pub struct SceneOpRecorder<'a>(pub &'a mut Vec<SceneOp>);
123
124impl SceneControl for SceneOpRecorder<'_> {
125 fn update_visibility(&mut self, draw_idx: usize, visible: bool) {
126 self.0.push(SceneOp::Visibility { draw_idx, visible });
127 }
128
129 fn set_fade(&mut self, fade: f32) {
130 self.0.push(SceneOp::SetFade(fade));
131 }
132}
133
134#[derive(Default)]
136pub struct RenderSnapshot {
137 pub frame: FrameScalars,
139 pub ui: UiIntents,
141 pub ops: crate::render::ops::RenderOps,
145 pub models: Vec<(u32, Mat4)>,
148 pub skinned_models: Vec<(u32, Mat4)>,
150 pub poses: SpanBuffer<Mat4>,
152 pub morphs: SpanBuffer<f32>,
154 pub text_calls: Vec<TextDrawCall>,
159 pub lines: Vec<LineVertex>,
161 pub scene_ops: Vec<SceneOp>,
163}
164
165impl RenderSnapshot {
166 pub fn clear(&mut self) {
168 self.frame = FrameScalars::default();
169 self.ui = UiIntents::default();
170 self.ops.clear();
171 self.models.clear();
172 self.skinned_models.clear();
173 self.poses.clear();
174 self.morphs.clear();
175 self.lines.clear();
176 self.scene_ops.clear();
177 }
178}
179
180const _: () = {
182 const fn require_send<T: Send + 'static>() {}
183 require_send::<RenderSnapshot>()
184};
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 use alloc::vec;
191 #[test]
192 fn span_buffer_round_trips_slots_in_push_order() {
193 let mut spans: SpanBuffer<u32> = SpanBuffer::default();
194 assert!(spans.is_empty());
195 spans.push(7, &[1, 2, 3]);
196 spans.push(2, &[9]);
197 let collected: Vec<(usize, Vec<u32>)> =
198 spans.iter().map(|(slot, v)| (slot, v.to_vec())).collect();
199 assert_eq!(collected, vec![(7, vec![1, 2, 3]), (2, vec![9])]);
200 }
201
202 #[test]
203 fn span_buffer_clear_keeps_capacity() {
204 let mut spans: SpanBuffer<u32> = SpanBuffer::default();
205 spans.push(0, &[1, 2, 3, 4]);
206 spans.push(1, &[5, 6]);
207 let values_ptr = spans.values.as_ptr();
208 spans.clear();
209 assert!(spans.is_empty());
210 spans.push(0, &[1, 2, 3]);
211 assert_eq!(
212 spans.values.as_ptr(),
213 values_ptr,
214 "values buffer reallocated"
215 );
216 }
217
218 #[test]
219 fn recorder_captures_fade_and_visibility_in_order() {
220 let mut ops = Vec::new();
221 {
222 let mut recorder = SceneOpRecorder(&mut ops);
223 recorder.set_fade(0.5);
224 recorder.update_visibility(3, false);
225 recorder.update_visibility(4, true);
226 }
227 assert_eq!(
228 ops,
229 vec![
230 SceneOp::SetFade(0.5),
231 SceneOp::Visibility {
232 draw_idx: 3,
233 visible: false
234 },
235 SceneOp::Visibility {
236 draw_idx: 4,
237 visible: true
238 },
239 ]
240 );
241 }
242
243 #[test]
244 fn snapshot_clear_resets_contents_and_keeps_capacity() {
245 let mut snap = RenderSnapshot::default();
246 snap.models.push((1, [[0.0; 4]; 4]));
247 snap.scene_ops.push(SceneOp::SetFade(1.0));
248 snap.frame.elapsed = 5.0;
249 let models_ptr = snap.models.as_ptr();
250 snap.clear();
251 assert!(snap.models.is_empty());
252 assert!(snap.scene_ops.is_empty());
253 assert_eq!(snap.frame.elapsed, 0.0);
254 snap.models.push((2, [[0.0; 4]; 4]));
255 assert_eq!(
256 snap.models.as_ptr(),
257 models_ptr,
258 "models buffer reallocated"
259 );
260 }
261}