damascene_core/scene/data.rs
1//! `Scene3DData`: the backend-neutral payload of a `DrawOp::Scene3D`.
2//!
3//! Assembled fresh each frame from the El tree's marks (cheap — it holds
4//! `Arc`-cloned [geometry handles](crate::scene::GeometryHandle), not
5//! geometry copies) plus the resolved camera, light rig, and style. The
6//! backend walks these draw lists, uploads any geometry whose revision
7//! advanced, and renders. See `docs/SCENE3D_PLAN.md`.
8
9// Lock in full per-item documentation for this module (issue #73).
10#![warn(missing_docs)]
11
12use glam::Mat4;
13
14use crate::scene::bounds::Aabb;
15use crate::scene::camera::ResolvedCamera;
16use crate::scene::geometry::{LinesHandle, MeshHandle, PointsHandle};
17use crate::scene::style::{LightRig, LineStyle, Material, PointStyle, SceneStyle};
18
19/// A mesh mark: geometry handle + object→world transform + material.
20#[derive(Clone, Debug)]
21pub struct MeshDraw {
22 /// Shared handle to the mesh geometry (uploaded once per revision).
23 pub geometry: MeshHandle,
24 /// Object→world transform applied to the geometry.
25 pub transform: Mat4,
26 /// Surface material; alpha < 1 routes through the translucent path.
27 pub material: Material,
28}
29
30/// A point/scatter mark: geometry handle + transform + style (per-point
31/// colour is in the geometry).
32#[derive(Clone, Debug)]
33pub struct PointDraw {
34 /// Shared handle to the point geometry (uploaded once per revision).
35 pub geometry: PointsHandle,
36 /// Object→world transform applied to the points.
37 pub transform: Mat4,
38 /// Marker size, shape, and size mode shared by every point in the mark.
39 pub style: PointStyle,
40 /// Per-point text labels / hover tooltips. `None` = unlabelled. CPU-only
41 /// presentation (not uploaded); see [`PointLabels`](crate::scene::PointLabels).
42 pub labels: Option<crate::scene::labels::PointLabels>,
43 /// Presentation hint: these discs exist only to patch the GPU line
44 /// pipeline's butt-cap joins (one disc per polyline vertex, sized to
45 /// the line width). Vector backends that draw round joins natively —
46 /// the SVG bundle fallback — skip these draws entirely; they are
47 /// visually redundant there and dominate output size. `false` for
48 /// real data markers (scatter, chart3d points).
49 pub line_joins: bool,
50}
51
52/// A line mark: geometry handle + transform + style (per-segment colour is
53/// in the geometry).
54#[derive(Clone, Debug)]
55pub struct LineDraw {
56 /// Shared handle to the line geometry (uploaded once per revision).
57 pub geometry: LinesHandle,
58 /// Object→world transform applied to the segments.
59 pub transform: Mat4,
60 /// Width, pattern, and size mode shared by every segment in the mark.
61 pub style: LineStyle,
62}
63
64/// Everything a backend needs to render one scene, all backend-neutral.
65#[derive(Clone, Debug)]
66pub struct Scene3DData {
67 /// Mesh marks; opaque ones draw first, translucent ones back-to-front.
68 pub meshes: Vec<MeshDraw>,
69 /// Point/scatter marks, drawn after meshes so data is never veiled.
70 pub points: Vec<PointDraw>,
71 /// Line marks, drawn after meshes so data is never veiled.
72 pub lines: Vec<LineDraw>,
73 /// The resolved (auto-framed) camera for this frame.
74 pub camera: ResolvedCamera,
75 /// The key + hemispheric-ambient light rig shading lit materials.
76 pub lights: LightRig,
77 /// Scene-level styling: grid, background, MSAA, axis visibility.
78 pub style: SceneStyle,
79 /// Whether the backend should capture this scene's depth buffer for
80 /// label occlusion. Set when the scene has scene-anchored labels (axis
81 /// labels today); lets label-free scenes skip the resolve + read-back
82 /// cost. See [`SceneDepthMap`](crate::scene::SceneDepthMap).
83 pub capture_depth: bool,
84}
85
86impl Scene3DData {
87 /// Combined world-space bounds of all marks — each handle's local
88 /// bounds transformed by its mark transform, unioned. Empty when there
89 /// is no geometry.
90 ///
91 /// Computed from the draw lists *before* assembling `Scene3DData`,
92 /// because the camera is resolved (auto-framed) against these bounds
93 /// and then stored in `camera`. Taking slices lets callers compute it
94 /// at that point.
95 pub fn content_bounds(meshes: &[MeshDraw], points: &[PointDraw], lines: &[LineDraw]) -> Aabb {
96 let mut bb = Aabb::EMPTY;
97 for m in meshes {
98 bb = bb.union(m.geometry.bounds().transformed(m.transform));
99 }
100 for p in points {
101 bb = bb.union(p.geometry.bounds().transformed(p.transform));
102 }
103 for l in lines {
104 bb = bb.union(l.geometry.bounds().transformed(l.transform));
105 }
106 bb
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::scene::geometry::{PointData, PointsHandle, ScenePoint};
114 use glam::Vec3;
115
116 #[test]
117 fn content_bounds_unions_and_applies_transforms() {
118 let pts = PointsHandle::new(PointData {
119 points: vec![
120 ScenePoint {
121 position: Vec3::splat(-1.0),
122 color: [1.0; 4],
123 },
124 ScenePoint {
125 position: Vec3::splat(1.0),
126 color: [1.0; 4],
127 },
128 ],
129 });
130 let draw = PointDraw {
131 geometry: pts,
132 transform: Mat4::from_translation(Vec3::new(5.0, 0.0, 0.0)),
133 style: PointStyle::default(),
134 labels: None,
135 line_joins: false,
136 };
137 let bb = Scene3DData::content_bounds(&[], std::slice::from_ref(&draw), &[]);
138 assert!(bb.is_valid());
139 assert_eq!(bb.min, Vec3::new(4.0, -1.0, -1.0));
140 assert_eq!(bb.max, Vec3::new(6.0, 1.0, 1.0));
141 }
142
143 #[test]
144 fn content_bounds_empty_with_no_marks() {
145 assert!(!Scene3DData::content_bounds(&[], &[], &[]).is_valid());
146 }
147}