Skip to main content

draco_io/
lib.rs

1//! Format I/O layer for Draco geometry.
2//!
3//! `draco-io` maps external 3D formats onto the geometry types from
4//! `draco-core`. It handles file/container concerns such as OBJ, PLY, FBX,
5//! glTF, GLB, scene hierarchy, and `KHR_draco_mesh_compression`; raw `.drc`
6//! bitstream encoding and decoding stays in `draco-core`.
7//!
8//! # Supported Formats
9//!
10//! | Format | Read | Write | Draco compression |
11//! |--------|------|-------|-------------------|
12//! | OBJ    | yes  | yes   | no                |
13//! | PLY    | yes  | yes   | no                |
14//! | FBX    | yes  | yes   | no                |
15//! | glTF   | yes  | yes   | yes               |
16//! | GLB    | yes  | yes   | yes               |
17//!
18//! # Feature Model
19//!
20//! The default feature set enables all readers, all writers, optional
21//! compression support, and point-cloud Draco decoding. For smaller builds, use
22//! `default-features = false` and enable only the format features needed, such
23//! as `gltf-reader`, `gltf-writer`, `obj-reader`, or `ply-writer`.
24//!
25//! # Geometry Contract
26//!
27//! `draco-io` maps source formats onto the Draco geometry model. Meshes use
28//! triangle faces, `Position` is the required attribute, and `Normal`, `Color`,
29//! `TexCoord`, and `Generic` are preserved when the file format can represent
30//! them as Draco attributes. Scene support in the *geometry model* is limited to
31//! names, hierarchy, transforms, and mesh parts; materials, textures, cameras,
32//! lights, animation, skinning, structural metadata, and arbitrary format extras
33//! are not represented in that model.
34//!
35//! # Document-preserving glTF compression
36//!
37//! Decoding into the geometry model and re-emitting a fresh glTF necessarily
38//! drops anything the model does not represent (materials, textures, and so on).
39//! When the goal is to Draco-compress an existing glTF/GLB **in place**, use
40//! [`compress_gltf_bytes`]: it rewrites only the compressible mesh geometry and
41//! carries the rest of the document through untouched — materials, textures,
42//! images, samplers, cameras, nodes, animations, skins, `extras`, and unknown
43//! extensions all survive.
44//!
45//! # Unified Trait API
46//!
47//! All readers implement [`Reader`] and all writers implement [`Writer`]:
48//!
49//! ```ignore
50//! use draco_io::{Reader, Writer, ObjReader, ObjWriter};
51//!
52//! // Generic read function
53//! fn load<R: Reader>(path: &str) -> io::Result<Mesh> {
54//!     let mut reader = R::open(path)?;
55//!     reader.read_mesh()
56//! }
57//!
58//! // Generic write function
59//! fn save<W: Writer>(mut writer: W, mesh: &Mesh) -> io::Result<()> {
60//!     writer.add_mesh(mesh, Some("Model"))?;
61//!     writer.write("output.ext")
62//! }
63//!
64//! // Works with any format
65//! let mesh = load::<ObjReader>("input.obj")?;
66//! save(ObjWriter::new(), &mesh)?;
67//! save(PlyWriter::new(), &mesh)?;
68//! ```
69//!
70//! # Format-Specific Features
71//!
72//! While the trait provides a common interface, each writer has format-specific methods:
73//!
74//! ```ignore
75//! // OBJ: Named groups
76//! let mut obj = ObjWriter::new();
77//! obj.add_mesh(&mesh, Some("Cube"));
78//!
79//! // PLY: Point clouds with colors
80//! let mut ply = PlyWriter::new();
81//! ply.add_points_with_colors(&points, &colors);
82//!
83//! // FBX: Optional compression
84//! let mut fbx = FbxWriter::new().with_compression(true);
85//! fbx.add_mesh(&mesh, Some("Model"));
86//!
87//! // glTF: Custom quantization, multiple output formats
88//! let mut gltf = GltfWriter::new();
89//! gltf.add_draco_mesh(&mesh, Some("Model"), None)?;  // Use default quantization
90//! gltf.write_glb("output.glb")?;              // Binary GLB
91//! gltf.write_gltf("out.gltf", "out.bin")?;   // Separate files
92//! gltf.write_gltf_embedded("embedded.gltf")?; // Pure text
93//! ```
94//!
95//! # glTF/GLB with Draco Compression
96//!
97//! The `gltf_reader` and `gltf_writer` modules provide focused support for
98//! Draco triangle meshes through `KHR_draco_mesh_compression`. They validate
99//! the container enough to avoid silently dropping required glTF features; they
100//! are not a full glTF SDK.
101//!
102//! Three output formats are available:
103//!
104//! - **GLB**: Binary container (single .glb file)
105//! - **glTF + .bin**: JSON with separate binary file
106//! - **glTF (embedded)**: Pure text JSON with base64 data URIs
107//!
108//! ## Reading Draco-compressed glTF
109//!
110//! ```ignore
111//! use draco_io::gltf_reader::GltfReader;
112//!
113//! let reader = GltfReader::open("model.glb")?;
114//! for (info, mesh) in reader.decode_all_draco_meshes()? {
115//!     println!("Mesh '{}' has {} faces",
116//!         info.mesh_name.unwrap_or_default(),
117//!         mesh.num_faces());
118//! }
119//! ```
120//!
121//! ## Writing Draco-compressed GLB
122//!
123//! ```ignore
124//! use draco_io::gltf_writer::GltfWriter;
125//!
126//! let mut writer = GltfWriter::new();
127//! writer.add_draco_mesh(&mesh, Some("MyMesh"), None)?;  // Use default quantization
128//!
129//! // Option 1: Binary GLB (most compact)
130//! writer.write_glb("output.glb")?;
131//!
132//! // Option 2: Separate JSON and binary
133//! writer.write_gltf("output.gltf", "output.bin")?;
134//!
135//! // Option 3: Pure text with embedded data (no external files)
136//! writer.write_gltf_embedded("output.gltf")?;
137//! ```
138
139#![cfg_attr(docsrs, feature(doc_cfg))]
140
141// Reader modules.
142#[cfg(feature = "fbx-reader")]
143#[cfg_attr(docsrs, doc(cfg(feature = "fbx-reader")))]
144pub mod fbx_reader;
145// Reader-agnostic glTF geometry decode + shared error type. Available with the
146// reader or the writer, so the compressor reuses it without the reader.
147#[cfg(any(feature = "gltf-reader", feature = "gltf-writer"))]
148#[cfg_attr(
149    docsrs,
150    doc(cfg(any(feature = "gltf-reader", feature = "gltf-writer")))
151)]
152pub mod gltf_geometry;
153#[cfg(feature = "gltf-reader")]
154#[cfg_attr(docsrs, doc(cfg(feature = "gltf-reader")))]
155pub mod gltf_reader;
156#[cfg(feature = "obj-reader")]
157#[cfg_attr(docsrs, doc(cfg(feature = "obj-reader")))]
158pub mod obj_reader;
159#[cfg(feature = "ply-reader")]
160#[cfg_attr(docsrs, doc(cfg(feature = "ply-reader")))]
161pub mod ply_reader;
162
163// Writer modules.
164#[cfg(feature = "fbx-writer")]
165#[cfg_attr(docsrs, doc(cfg(feature = "fbx-writer")))]
166pub mod fbx_writer;
167#[cfg(feature = "gltf-writer")]
168#[cfg_attr(docsrs, doc(cfg(feature = "gltf-writer")))]
169/// Document-preserving glTF Draco compression (keeps materials, textures, etc.).
170///
171/// The in-memory [`gltf_compress::compress_gltf_value`] needs only the writer;
172/// the byte API ([`gltf_compress::compress_gltf_bytes`]) also needs the reader.
173pub mod gltf_compress;
174#[cfg(feature = "gltf-writer")]
175#[cfg_attr(docsrs, doc(cfg(feature = "gltf-writer")))]
176/// glTF/GLB writer with Draco mesh compression support.
177pub mod gltf_writer;
178#[cfg(feature = "obj-writer")]
179#[cfg_attr(docsrs, doc(cfg(feature = "obj-writer")))]
180pub mod obj_writer;
181#[cfg(feature = "ply-writer")]
182#[cfg_attr(docsrs, doc(cfg(feature = "ply-writer")))]
183pub mod ply_writer;
184
185/// Shared PLY storage-format enum.
186pub mod ply_format;
187// Traits module is always available
188pub mod traits;
189
190// Scene-graph layer (data model + traits) is only compiled for hierarchical
191// formats (glTF, FBX) that actually carry a scene.
192#[cfg(feature = "scene")]
193#[cfg_attr(docsrs, doc(cfg(feature = "scene")))]
194pub mod scene;
195
196// Re-export main types for convenience
197#[cfg(feature = "fbx-reader")]
198#[cfg_attr(docsrs, doc(cfg(feature = "fbx-reader")))]
199pub use fbx_reader::{FbxMemoryReader, FbxReader};
200#[cfg(feature = "fbx-writer")]
201#[cfg_attr(docsrs, doc(cfg(feature = "fbx-writer")))]
202pub use fbx_writer::FbxWriter;
203// Reader-agnostic geometry decode + shared error type (reader or writer).
204#[cfg(any(feature = "gltf-reader", feature = "gltf-writer"))]
205#[cfg_attr(
206    docsrs,
207    doc(cfg(any(feature = "gltf-reader", feature = "gltf-writer")))
208)]
209pub use gltf_geometry::{decode_geometry, AccessorSource, DecodedAccessor, GltfError};
210// In-memory compressor core: writer only.
211#[cfg(feature = "gltf-writer")]
212#[cfg_attr(docsrs, doc(cfg(feature = "gltf-writer")))]
213pub use gltf_compress::compress_gltf_value;
214// Byte compressor API: needs the reader to parse + resolve buffers.
215#[cfg(all(feature = "gltf-reader", feature = "gltf-writer"))]
216#[cfg_attr(
217    docsrs,
218    doc(cfg(all(feature = "gltf-reader", feature = "gltf-writer")))
219)]
220pub use gltf_compress::{compress_gltf_bytes, compress_gltf_bytes_with_base_path};
221#[cfg(feature = "gltf-reader")]
222#[cfg_attr(docsrs, doc(cfg(feature = "gltf-reader")))]
223pub use gltf_reader::{DracoPrimitiveInfo, GltfReader};
224#[cfg(feature = "gltf-writer")]
225#[cfg_attr(docsrs, doc(cfg(feature = "gltf-writer")))]
226pub use gltf_writer::{GltfWriteError, GltfWriter};
227#[cfg(feature = "obj-reader")]
228#[cfg_attr(docsrs, doc(cfg(feature = "obj-reader")))]
229pub use obj_reader::ObjReader;
230#[cfg(feature = "obj-writer")]
231#[cfg_attr(docsrs, doc(cfg(feature = "obj-writer")))]
232pub use obj_writer::ObjWriter;
233pub use ply_format::PlyFormat;
234#[cfg(feature = "ply-reader")]
235#[cfg_attr(docsrs, doc(cfg(feature = "ply-reader")))]
236pub use ply_reader::PlyReader;
237#[cfg(feature = "ply-writer")]
238#[cfg_attr(docsrs, doc(cfg(feature = "ply-writer")))]
239pub use ply_writer::PlyWriter;
240#[cfg(feature = "scene")]
241#[cfg_attr(docsrs, doc(cfg(feature = "scene")))]
242pub use scene::{
243    flatten_to_scene, MeshInstance, Scene, SceneNode, SceneReader, SceneWriter, Transform,
244};
245pub use traits::{PointCloudReader, PointCloudWriter, ReadFromBytes, Reader, WriteToBytes, Writer};