Skip to main content

oxideav_obj/
encoder.rs

1//! [`Mesh3DEncoder`] adaptors for OBJ + standalone MTL outputs.
2//!
3//! These wrap the [`crate::obj::serialize_obj`] /
4//! [`crate::mtl::serialize_mtl`] free functions in the
5//! `oxideav-mesh3d` encoder trait shape, with optional configuration
6//! captured on the wrapper itself.
7
8use oxideav_mesh3d::{Mesh3DEncoder, Result, Scene3D};
9
10use crate::{mtl, obj};
11
12/// Wavefront OBJ encoder.
13///
14/// By default the encoder emits a standalone OBJ (no `mtllib`
15/// directive). Call [`ObjEncoder::with_mtl_basename`] to embed an
16/// `mtllib <basename>.mtl` line at the top, in which case the caller
17/// is responsible for writing the companion MTL file alongside (use
18/// [`crate::mtl::serialize_mtl`] for that).
19///
20/// The encoder is loss-tolerant — it preserves the OBJ-specific
21/// extras (`obj:groups`, `obj:smoothing_group`, `obj:original_face_arities`,
22/// `obj:usemtl`, `obj:mtllibs`) populated by the decoder, so a
23/// decode → encode → decode round-trip is structurally stable.
24#[derive(Debug, Default)]
25pub struct ObjEncoder {
26    mtl_basename: Option<String>,
27    negative_indices: bool,
28}
29
30impl ObjEncoder {
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Emit `mtllib <basename>.mtl` at the top of the output. The
36    /// basename is used verbatim (no `.mtl` extension is appended
37    /// twice; if you supply `"foo"` the directive becomes `mtllib foo.mtl`).
38    pub fn with_mtl_basename(mut self, basename: impl Into<String>) -> Self {
39        self.mtl_basename = Some(basename.into());
40        self
41    }
42
43    /// Emit face/line vertex indices in the relative negative-index
44    /// form (`f -3 -2 -1`) instead of the default absolute 1-based
45    /// form. Useful when the consumer wants to mirror an input that
46    /// originally used negative indices, since the parser accepts
47    /// both forms but loses the original spelling.
48    pub fn with_negative_indices(mut self, on: bool) -> Self {
49        self.negative_indices = on;
50        self
51    }
52}
53
54impl Mesh3DEncoder for ObjEncoder {
55    fn encode(&mut self, scene: &Scene3D) -> Result<Vec<u8>> {
56        obj::serialize_obj_with_options(
57            scene,
58            &obj::SerializeOptions {
59                mtl_basename: self.mtl_basename.as_deref(),
60                negative_indices: self.negative_indices,
61            },
62        )
63    }
64}
65
66/// Standalone MTL encoder — serialises only the [`Scene3D::materials`]
67/// vector (and any [`oxideav_mesh3d::Texture`]s referenced by them).
68#[derive(Debug, Default)]
69pub struct MtlEncoder {
70    _private: (),
71}
72
73impl MtlEncoder {
74    pub fn new() -> Self {
75        Self::default()
76    }
77}
78
79impl Mesh3DEncoder for MtlEncoder {
80    fn encode(&mut self, scene: &Scene3D) -> Result<Vec<u8>> {
81        mtl::serialize_mtl(&scene.materials, &scene.textures)
82    }
83}