oxideav-mesh3d
Pure-Rust 3D scene + mesh typed model.
The shared data model that every OxideAV 3D-format crate
(oxideav-stl, oxideav-obj, oxideav-gltf, future
oxideav-fbx / oxideav-usd) decodes into and encodes from. The
type model is aligned with glTF 2.0 (Khronos KHR-public spec) as
the spec-stable common denominator: right-handed coordinates,
Y-up, -Z forward, metres, metallic-roughness PBR, xyzw quaternions.
Files coming from Z-up formats (STL/OBJ Wavefront) just set
[Scene3D::up_axis] to Axis::PosZ — the model stores the
orientation, no implicit re-projection happens.
Round 1 ships:
Scene3D— top-level container holdingVecs of nodes, meshes, materials, textures, skeletons, skins, animations, cameras, lights, plusup_axis/front_axis/unitmetadata and a free-formextras: HashMap<String, serde_json::Value>round-trip side-channel.Node+Transform { Matrix, Trs }with best-effort matrix↔TRS decompose.Mesh/Primitive/Topology(Triangles, TriangleStrip, TriangleFan, Lines, LineStrip, LineLoop, Points) /Indices(U16 or U32). Multi-channel UVs and vertex colours; optional skinning joint indices + weights.Material— full glTF 2.0 metallic-roughness PBR slots (base_color,metallic,roughness,normal,occlusion,emissive) plusAlphaMode { Opaque, Mask{cutoff}, Blend }anddouble_sided.Texture/ImageData { Embedded(VideoFrame), Source(Arc<dyn AssetSource>), External }/Samplerwith the usual mag/min filters and wrap modes. TheSourcevariant lets format crates pass a lazy reader through the type model without materialising aVec<u8>(round 2).Skeleton(joint nodes + inverse-bind matrices) +Skinbinding to a mesh.Animation/AnimationChannel/AnimationSampler/AnimationProperty { Translation, Rotation, Scale, MorphWeights }/Interpolation { Step, Linear, CubicSpline }.Camera { Perspective, Orthographic }andLight { Directional, Point, Spot }.Mesh3DDecoder/Mesh3DEncodertraits +Mesh3DRegistry(case-insensitive extension lookup) — mirrors the codec-registry pattern fromoxideav-core.
Round 2 adds (still pre-publish, BREAKING vs round 1):
AssetSourcetrait —Send + Sync + Debuglazy reference to a binary asset (texture, audio, anything blob-shaped).open()returns a streaming reader; optionalraw_storage()exposes the asset's stored bytes + a scheme identifier so a writer targeting the same scheme (USDZ → USDZ, GLB → GLB) can pass the payload through without re-encoding.RawStorage<'a> { scheme, bytes, uncompressed_size }andInMemoryAsset(trivial owning impl).- Audio surface —
AudioSource,AudioEmitter,SpatialAudio,AuralMode { SpatialNonAcoustic, SpatialAcoustic },DistanceModel { Linear, Inverse, Exponential }. Aligned with USDUsdMediaSpatialAudio+ glTFKHR_audio_emitter.Scene3Dgainsaudio_sources+audio_emittersarenas;Nodegainsaudio_emitter: Option<AudioEmitterId>. - BREAKING:
ImageData::Encoded { mime, bytes }removed in favour ofImageData::Source(Arc<dyn AssetSource>). Migration: wrap bytes inInMemoryAsset { mime, bytes }. TheTexture::from_encoded(mime, bytes)helper signature is unchanged — it now wraps internally.
No format support yet; sibling crates (oxideav-stl,
oxideav-obj, oxideav-gltf) plug in via Mesh3DRegistry once
this crate is published.
Round 3 candidates
oxideav-stl(binary + ASCII) — STL is the smallest realistic consumer of the type model and validates the encoder side immediately.oxideav-obj+ Wavefront MTL — exercises the multi-material primitive split + theMaterial::extrasround-trip path.oxideav-gltf(JSON + GLB) — proves the type model round-trips losslessly against its design source-of-truth, including theKHR_audio_emitterextension against the new audio types.oxideav-usdz— first real consumer of theraw_storage()pass-through path.- KHR extension surface (
KHR_materials_emissive_strength,KHR_materials_unlit,KHR_lights_punctual,KHR_audio_emitter) onceoxideav-gltflands.
Standalone build
oxideav-core is gated behind the default-on registry cargo
feature. Drop the framework dependency entirely with:
= { = "0.0", = false }
The typed model and trait definitions stay available — only the
embedded VideoFrame / AudioFrame variants
(ImageData::Embedded / AudioData::Embedded) disappear, and the
Error / Result aliases resolve to a crate-local enum instead
of oxideav_core::Error. AssetSource::open() returns a
crate-local ReadSeek trait alias with the same shape as
oxideav_core::ReadSeek, so the trait surface is identical
either way.
License
MIT — see LICENSE.