Skip to main content

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}