draco_io/scene.rs
1//! Scene-graph layer: data model and traits for formats that represent hierarchy.
2//!
3//! Only hierarchical formats (glTF, FBX) implement [`SceneReader`] /
4//! [`SceneWriter`]. Flat formats (OBJ, PLY) are intentionally **not** forced to
5//! fabricate a scene graph they do not have. When a caller wants a uniform
6//! scene from a flat format, [`flatten_to_scene`] performs the degenerate
7//! wrapping explicitly at the call site.
8
9use std::io;
10
11use draco_core::mesh::Mesh;
12
13use crate::traits::{Reader, Writer};
14
15/// Simple transform placeholder (4x4 row-major matrix).
16#[derive(Debug, Clone)]
17pub struct Transform {
18 /// Row-major 4x4 transform matrix.
19 pub matrix: [[f32; 4]; 4],
20}
21
22/// One mesh instance attached to a scene node.
23///
24/// The mesh data is owned by the instance for now; `transform` is the optional
25/// local transform applied to this specific instance when it cannot be folded
26/// into the containing node.
27#[derive(Debug, Clone)]
28pub struct MeshInstance {
29 /// Optional instance name.
30 pub name: Option<String>,
31 /// Mesh data for this instance.
32 pub mesh: Mesh,
33 /// Optional local transform for this instance.
34 pub transform: Option<Transform>,
35}
36
37/// A node in a scene graph. Nodes can contain mesh instances and children.
38#[derive(Debug, Clone)]
39pub struct SceneNode {
40 /// Optional node name.
41 pub name: Option<String>,
42 /// Optional local transform for this node.
43 pub transform: Option<Transform>,
44 /// Mesh instances attached directly to this node.
45 pub mesh_instances: Vec<MeshInstance>,
46 /// Child nodes.
47 pub children: Vec<SceneNode>,
48}
49
50impl SceneNode {
51 /// Create an empty scene node.
52 pub fn new(name: Option<String>) -> Self {
53 Self {
54 name,
55 transform: None,
56 mesh_instances: Vec::new(),
57 children: Vec::new(),
58 }
59 }
60
61 /// Create a scene node populated with mesh instances.
62 pub fn with_mesh_instances(name: Option<String>, mesh_instances: Vec<MeshInstance>) -> Self {
63 Self {
64 name,
65 transform: None,
66 mesh_instances,
67 children: Vec::new(),
68 }
69 }
70}
71
72/// A simple scene container.
73#[derive(Debug, Clone)]
74pub struct Scene {
75 /// Optional scene name.
76 pub name: Option<String>,
77 /// Root nodes forming a hierarchy.
78 pub root_nodes: Vec<SceneNode>,
79}
80
81impl Scene {
82 /// Create an empty scene.
83 pub fn new(name: Option<String>) -> Self {
84 Self {
85 name,
86 root_nodes: Vec::new(),
87 }
88 }
89
90 /// Create a flat scene containing one root node with the given mesh instances.
91 pub fn from_mesh_instances(name: Option<String>, mesh_instances: Vec<MeshInstance>) -> Self {
92 Self {
93 root_nodes: vec![SceneNode::with_mesh_instances(name.clone(), mesh_instances)],
94 name,
95 }
96 }
97}
98
99/// Trait for readers that can return full scene information (meshes + metadata).
100///
101/// This is a capability extension over the base [`Reader`] trait and is only
102/// implemented by formats that natively carry a scene graph (glTF, FBX). Flat
103/// formats deliberately do not implement it; use [`flatten_to_scene`] instead.
104pub trait SceneReader: Reader {
105 /// Read a single scene from the source/file.
106 fn read_scene(&mut self) -> io::Result<Scene>;
107
108 /// Read all scenes (default: single scene wrapper).
109 fn read_scenes(&mut self) -> io::Result<Vec<Scene>> {
110 Ok(vec![self.read_scene()?])
111 }
112}
113
114/// Trait for writers that can output full scene graphs (nodes + hierarchy + transforms).
115///
116/// This mirrors [`SceneReader`]: formats implementing this trait can accept one
117/// scene via [`SceneWriter::add_scene`] or many scenes via
118/// [`SceneWriter::add_scenes`]. Actual file output is still performed through
119/// the base [`Writer`] trait.
120///
121/// # Example
122///
123/// ```ignore
124/// use draco_io::{SceneWriter, Writer, GltfWriter, Scene};
125///
126/// let scene = Scene::new(Some("MyScene".to_string()));
127///
128/// let mut writer = GltfWriter::new();
129/// writer.add_scene(&scene)?;
130/// writer.write("output.glb")?; // Writer::write defaults to GLB for GltfWriter
131/// ```
132pub trait SceneWriter: Writer {
133 /// Add a scene graph to be written.
134 fn add_scene(&mut self, scene: &Scene) -> io::Result<()>;
135
136 /// Add all scenes (default: add one scene).
137 fn add_scenes(&mut self, scenes: &[Scene]) -> io::Result<()> {
138 for scene in scenes {
139 self.add_scene(scene)?;
140 }
141 Ok(())
142 }
143}
144
145/// Wrap the flat mesh list of any [`Reader`] into a single-node [`Scene`].
146///
147/// This is the honest adapter for formats without a native scene graph (OBJ,
148/// PLY): it makes the degenerate wrapping explicit at the call site instead of
149/// every flat format pretending it has hierarchy. Hierarchical formats should
150/// implement [`SceneReader`] and return their real graph instead.
151pub fn flatten_to_scene<R: Reader>(reader: &mut R, name: Option<String>) -> io::Result<Scene> {
152 let meshes = reader.read_meshes()?;
153 let mesh_instances = meshes
154 .into_iter()
155 .map(|mesh| MeshInstance {
156 name: None,
157 mesh,
158 transform: None,
159 })
160 .collect();
161 Ok(Scene::from_mesh_instances(name, mesh_instances))
162}