Skip to main content

draco_gltf/
lib.rs

1//! Full, lossless glTF 2.0 / pinned 2.1-draft model with Draco geometry.
2//!
3//! [`Document`] is the public scene model, and all unknown JSON remains part
4//! of that model.
5
6#![deny(missing_docs)]
7
8use std::path::Path;
9
10use thiserror::Error;
11
12#[cfg(feature = "geometry")]
13mod accessor;
14#[cfg(feature = "draco-encode")]
15mod compression;
16/// Lossless document model and typed views for glTF scenes.
17pub mod document;
18#[cfg(feature = "geometry")]
19pub use accessor::{AccessorData, DocumentAccessorSource};
20#[cfg(feature = "draco-encode")]
21pub use compression::{CompressionMode, CompressionOptions, CompressionReport};
22mod json;
23pub use document::{
24    Accessor, AccessorIndex, Animation, AnimationIndex, BoundingVolume, Buffer, BufferIndex,
25    BufferView, BufferViewIndex, Camera, CameraIndex, ComponentType, Document, ExternalAsset,
26    ExternalAssetIndex, File, FileIndex, Image, ImageIndex, Material, MaterialIndex, Mesh,
27    MeshIndex, Node, NodeIndex, PrimitiveIndex, PrimitiveRef, Sampler, SamplerIndex, Scene,
28    SceneIndex, Shape, ShapeIndex, Skin, SkinIndex, Texture, TextureIndex, ValidationProfile,
29};
30#[cfg(feature = "geometry")]
31mod packed;
32#[cfg(feature = "geometry")]
33pub use packed::{GeometryError, PackedAttribute, PackedGeometry, PackedIndices, PrimitiveMode};
34#[cfg(feature = "write")]
35mod writer;
36/// Lossless JSON value used by the document model.
37pub use json::Value as JsonValue;
38#[cfg(feature = "write")]
39pub use writer::{GeometryEncoding, GeometryWriteOptions, GeometryWriteReport, PreserveReason};
40/// Extension contracts and resource storage used by document transforms.
41pub mod extensions;
42pub use extensions::{
43    BinaryFreeExtension, DracoExtension, ExtensionHandler, ExtensionRegistry,
44    ExtensionValidationContext, MeshGpuInstancingExtension, ResourceStore,
45    StructuralMetadataExtension, BINARY_FREE_EXTENSIONS, EXT_MESHOPT_COMPRESSION,
46    EXT_MESH_GPU_INSTANCING, EXT_STRUCTURAL_METADATA, KHR_DRACO_MESH_COMPRESSION,
47    KHR_MESHOPT_COMPRESSION,
48};
49mod import;
50#[cfg(not(target_arch = "wasm32"))]
51pub use import::open;
52pub use import::{
53    parse, parse_with_options, GltfOutput, GltfResource, Import, DEFAULT_EXTERNAL_ASSET_DEPTH,
54};
55
56pub use draco_io::{
57    ExternalFilePolicy, FileResourceResolver, GlbRangeReader, GltfContainerFormat, GltfError,
58    ResourceLimits, ResourceResolver,
59};
60
61/// Container representation selected when serializing an import.
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub enum OutputFormat {
64    /// Preserve the input container kind.
65    SameAsInput,
66    /// Emit a JSON glTF document and no materialized companion buffers.
67    GltfJson,
68    /// Emit a GLB version 2 container.
69    GlbV2,
70    /// Emit a draft GLB version 3 container.
71    GlbV3,
72}
73
74/// Errors returned by glTF parsing and Draco operations.
75#[derive(Debug, Error)]
76pub enum Error {
77    /// An operating-system or stream error.
78    #[error("IO error: {0}")]
79    Io(#[from] std::io::Error),
80    /// The JSON chunk could not be parsed.
81    #[error("JSON error: {0}")]
82    Json(String),
83    /// Draco bitstream decoding failed.
84    #[error("Draco decode error: {0}")]
85    Decode(#[from] draco_core::DracoError),
86    /// A low-level container or resource operation failed.
87    #[error("draco-io error: {0}")]
88    DracoIo(#[from] GltfError),
89    /// Materialized primitive geometry is invalid or unsupported.
90    #[cfg(feature = "geometry")]
91    #[error("geometry error: {0}")]
92    Geometry(#[from] GeometryError),
93    /// An extension contract rejected the document or transform.
94    #[error("extension error: {0}")]
95    Extension(String),
96    /// Document validation failed.
97    #[error("glTF validation failed: {0:?}")]
98    Validation(Vec<String>),
99    /// A configured resource or graph quota was exceeded.
100    #[error("resource quota exceeded: {0}")]
101    ResourceLimit(String),
102}
103/// Result type returned by this crate.
104pub type Result<T> = std::result::Result<T, Error>;
105
106/// Options controlling document loading, resource resolution and validation.
107pub struct ImportOptions<'a> {
108    /// Base directory used for relative external resources.
109    pub base_path: Option<&'a Path>,
110    /// Policy applied by the default filesystem resolver.
111    pub external_file_policy: ExternalFilePolicy,
112    /// Optional caller-provided synchronous resource resolver.
113    pub resolver: Option<&'a dyn ResourceResolver>,
114    /// Resource and graph quotas applied during loading.
115    pub limits: ResourceLimits,
116    /// Profile used for basic checks and strict validation when enabled.
117    pub profile: ValidationProfile,
118    /// Extension handlers available to validation and transforms.
119    pub extensions: ExtensionRegistry,
120}
121impl Default for ImportOptions<'_> {
122    fn default() -> Self {
123        Self {
124            base_path: None,
125            external_file_policy: ExternalFilePolicy::Deny,
126            resolver: None,
127            limits: ResourceLimits::default(),
128            profile: ValidationProfile::Gltf21Draft,
129            extensions: ExtensionRegistry::default(),
130        }
131    }
132}
133
134#[cfg(not(target_arch = "wasm32"))]
135/// Opens a glTF or GLB file using the draft validation profile.
136pub fn import(path: impl AsRef<Path>) -> Result<Import> {
137    open(path, ValidationProfile::Gltf21Draft)
138}
139
140/// Parses glTF or GLB bytes using the draft validation profile.
141///
142/// ```
143/// # use draco_gltf::import_slice;
144/// let input = br#"{"asset":{"version":"2.0"},"meshes":[]}"#;
145/// let scene = import_slice(input, None)?;
146/// assert_eq!(scene.document.meshes().len(), 0);
147/// # Ok::<(), draco_gltf::Error>(())
148/// ```
149pub fn import_slice(bytes: &[u8], base: Option<&Path>) -> Result<Import> {
150    let options = ImportOptions {
151        base_path: base,
152        external_file_policy: if base.is_some() {
153            ExternalFilePolicy::Allow
154        } else {
155            ExternalFilePolicy::Deny
156        },
157        ..ImportOptions::default()
158    };
159    import_slice_with_options(bytes, &options)
160}
161
162/// Parses glTF or GLB bytes with explicit loading options.
163pub fn import_slice_with_options(bytes: &[u8], options: &ImportOptions<'_>) -> Result<Import> {
164    let file_resolver = options
165        .base_path
166        .map(|base| FileResourceResolver::new(base, options.external_file_policy));
167    let resolver = options.resolver.or_else(|| {
168        file_resolver
169            .as_ref()
170            .map(|value| value as &dyn ResourceResolver)
171    });
172    parse_with_options(
173        bytes,
174        options.base_path,
175        resolver,
176        &options.limits,
177        options.profile,
178        &options.extensions,
179    )
180}
181
182/// Applies the available checks for the pinned draft profile.
183///
184/// Enable `strict-validation` for complete reference and scene-graph checks.
185pub fn validate(document: &Document) -> Result<()> {
186    document.validate(ValidationProfile::Gltf21Draft)
187}
188
189#[cfg(test)]
190mod document_tests;