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 pub sky_rot: [[f32; 4]; 3],
41 pub directional: Option<crate::render::lights::DirectionalLightSet>,
45}
46
47impl Default for FrameScalars {
48 fn default() -> Self {
49 Self {
50 elapsed: 0.0,
51 fov_y_radians: core::f32::consts::FRAC_PI_4,
52 near: 0.05,
53 far: 200.0,
54 view: crate::gfx::transform::IDENTITY,
55 cam_pos: [0.0; 3],
56 view_mode: ViewMode::default(),
57 show: ShowFlags::default(),
58 world_hidden: false,
59 menu_active: false,
60 sky_rot: crate::sky::SkyOrientation::IDENTITY_ROWS,
61 directional: None,
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, Default)]
69pub struct UiIntents {
70 pub cursor_hidden: bool,
72 pub menu_mode: Option<bool>,
74 pub camera_capture: Option<bool>,
76}
77
78#[derive(Debug, Default)]
82pub struct SpanBuffer<T> {
83 values: Vec<T>,
84 spans: Vec<(usize, u32, u32)>,
85}
86
87impl<T: Copy> SpanBuffer<T> {
88 pub fn clear(&mut self) {
90 self.values.clear();
91 self.spans.clear();
92 }
93
94 pub fn is_empty(&self) -> bool {
96 self.spans.is_empty()
97 }
98
99 pub fn push(&mut self, slot: usize, values: &[T]) {
101 let start = self.values.len() as u32;
102 self.values.extend_from_slice(values);
103 self.spans.push((slot, start, values.len() as u32));
104 }
105
106 pub fn iter(&self) -> impl Iterator<Item = (usize, &[T])> {
108 self.spans
109 .iter()
110 .map(|&(slot, start, len)| (slot, &self.values[start as usize..(start + len) as usize]))
111 }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq)]
117pub enum SceneOp {
118 SetFade(f32),
120 Visibility {
122 draw_idx: usize,
124 visible: bool,
126 },
127}
128
129pub struct SceneOpRecorder<'a>(pub &'a mut Vec<SceneOp>);
132
133impl SceneControl for SceneOpRecorder<'_> {
134 fn update_visibility(&mut self, draw_idx: usize, visible: bool) {
135 self.0.push(SceneOp::Visibility { draw_idx, visible });
136 }
137
138 fn set_fade(&mut self, fade: f32) {
139 self.0.push(SceneOp::SetFade(fade));
140 }
141}
142
143#[derive(Default)]
145pub struct RenderSnapshot {
146 pub frame: FrameScalars,
148 pub ui: UiIntents,
150 pub ops: crate::render::ops::RenderOps,
154 pub models: Vec<(u32, Mat4)>,
157 pub skinned_models: Vec<(u32, Mat4)>,
159 pub poses: SpanBuffer<Mat4>,
161 pub morphs: SpanBuffer<f32>,
163 pub text_calls: Vec<TextDrawCall>,
168 pub lines: Vec<LineVertex>,
170 pub scene_ops: Vec<SceneOp>,
172}
173
174impl RenderSnapshot {
175 pub fn clear(&mut self) {
177 self.frame = FrameScalars::default();
178 self.ui = UiIntents::default();
179 self.ops.clear();
180 self.models.clear();
181 self.skinned_models.clear();
182 self.poses.clear();
183 self.morphs.clear();
184 self.lines.clear();
185 self.scene_ops.clear();
186 }
187}
188
189const _: () = {
191 const fn require_send<T: Send + 'static>() {}
192 require_send::<RenderSnapshot>()
193};
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 use alloc::vec;
200 #[test]
201 fn span_buffer_round_trips_slots_in_push_order() {
202 let mut spans: SpanBuffer<u32> = SpanBuffer::default();
203 assert!(spans.is_empty());
204 spans.push(7, &[1, 2, 3]);
205 spans.push(2, &[9]);
206 let collected: Vec<(usize, Vec<u32>)> =
207 spans.iter().map(|(slot, v)| (slot, v.to_vec())).collect();
208 assert_eq!(collected, vec![(7, vec![1, 2, 3]), (2, vec![9])]);
209 }
210
211 #[test]
212 fn span_buffer_clear_keeps_capacity() {
213 let mut spans: SpanBuffer<u32> = SpanBuffer::default();
214 spans.push(0, &[1, 2, 3, 4]);
215 spans.push(1, &[5, 6]);
216 let values_ptr = spans.values.as_ptr();
217 spans.clear();
218 assert!(spans.is_empty());
219 spans.push(0, &[1, 2, 3]);
220 assert_eq!(
221 spans.values.as_ptr(),
222 values_ptr,
223 "values buffer reallocated"
224 );
225 }
226
227 #[test]
228 fn recorder_captures_fade_and_visibility_in_order() {
229 let mut ops = Vec::new();
230 {
231 let mut recorder = SceneOpRecorder(&mut ops);
232 recorder.set_fade(0.5);
233 recorder.update_visibility(3, false);
234 recorder.update_visibility(4, true);
235 }
236 assert_eq!(
237 ops,
238 vec![
239 SceneOp::SetFade(0.5),
240 SceneOp::Visibility {
241 draw_idx: 3,
242 visible: false
243 },
244 SceneOp::Visibility {
245 draw_idx: 4,
246 visible: true
247 },
248 ]
249 );
250 }
251
252 #[test]
253 fn snapshot_clear_resets_contents_and_keeps_capacity() {
254 let mut snap = RenderSnapshot::default();
255 snap.models.push((1, [[0.0; 4]; 4]));
256 snap.scene_ops.push(SceneOp::SetFade(1.0));
257 snap.frame.elapsed = 5.0;
258 let models_ptr = snap.models.as_ptr();
259 snap.clear();
260 assert!(snap.models.is_empty());
261 assert!(snap.scene_ops.is_empty());
262 assert_eq!(snap.frame.elapsed, 0.0);
263 snap.models.push((2, [[0.0; 4]; 4]));
264 assert_eq!(
265 snap.models.as_ptr(),
266 models_ptr,
267 "models buffer reallocated"
268 );
269 }
270}