rakata_formats/bwm/ascii_writer.rs
1//! BWM ASCII writer implementation.
2//!
3//! This module provides an ASCII serializer for the KotOR walkmesh format,
4//! compatible with the game's `LoadMeshText` parser.
5
6use std::io::Write;
7
8use super::ascii_reader::BwmAsciiError;
9use super::{Bwm, BwmType};
10
11/// Writes a BWM to an ASCII writer.
12///
13/// # Errors
14///
15/// [`BwmAsciiError::Io`] when the writer fails. The ASCII grammar has no
16/// widths to overflow and the adjacency columns are written as `-1`
17/// placeholders, so nothing in the walkmesh itself can be refused here.
18pub fn write_bwm_ascii<W: Write>(writer: &mut W, bwm: &Bwm) -> Result<(), BwmAsciiError> {
19 writeln!(writer, "node aabb")?;
20
21 // Position
22 writeln!(
23 writer,
24 " position {} {} {}",
25 bwm.position.x, bwm.position.y, bwm.position.z
26 )?;
27
28 // Identity, since the binary form carries no orientation to round-trip.
29 writeln!(writer, " orientation 0.0 0.0 0.0 1.0")?;
30
31 // Vertices
32 writeln!(writer, " verts {}", bwm.vertices.len())?;
33 for v in &bwm.vertices {
34 writeln!(writer, " {} {} {}", v.x, v.y, v.z)?;
35 }
36
37 // Faces
38 writeln!(writer, " faces {}", bwm.faces.len())?;
39 for face in &bwm.faces {
40 // Output format: v1 v2 v3 adj1 adj2 adj3 adj4 material
41 // ASCII format requires 4 adjacency indices, but the engine recomputes
42 // them, so `-1` placeholders go out instead.
43
44 writeln!(
45 writer,
46 " {} {} {} -1 -1 -1 -1 {}",
47 face.vertex_indices[0],
48 face.vertex_indices[1],
49 face.vertex_indices[2],
50 face.material_id
51 )?;
52 }
53
54 // AABB
55 // Only if AreaModel
56 if bwm.walkmesh_type.known() == Some(BwmType::AreaModel) && !bwm.aabb_nodes.is_empty() {
57 writeln!(writer, " aabb")?;
58 for node in &bwm.aabb_nodes {
59 // Output only leaf nodes (containing faces) to the ASCII AABB list.
60 // The game rebuilds the tree structure from these leaves on load.
61 if node.face_index != 0xFFFFFFFF {
62 // Write raw bounding box values. The game applies epsilon expansion on load.
63 writeln!(
64 writer,
65 " {} {} {} {} {} {} {}",
66 node.bb_min.x,
67 node.bb_min.y,
68 node.bb_min.z,
69 node.bb_max.x,
70 node.bb_max.y,
71 node.bb_max.z,
72 node.face_index
73 )?;
74 }
75 }
76 }
77
78 writeln!(writer, "endnode")?;
79
80 Ok(())
81}