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}