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.
12pub fn write_bwm_ascii<W: Write>(writer: &mut W, bwm: &Bwm) -> Result<(), BwmAsciiError> {
13 writeln!(writer, "node aabb")?;
14
15 // Position
16 writeln!(
17 writer,
18 " position {} {} {}",
19 bwm.position.x, bwm.position.y, bwm.position.z
20 )?;
21
22 // Orientation (Default identity as we don't store it)
23 writeln!(writer, " orientation 0.0 0.0 0.0 1.0")?;
24
25 // Vertices
26 writeln!(writer, " verts {}", bwm.vertices.len())?;
27 for v in &bwm.vertices {
28 writeln!(writer, " {} {} {}", v.x, v.y, v.z)?;
29 }
30
31 // Faces
32 writeln!(writer, " faces {}", bwm.faces.len())?;
33 for face in &bwm.faces {
34 // Output format: v1 v2 v3 adj1 adj2 adj3 adj4 material
35 // ASCII format requires 4 adjacency indices, but the engine recomputes them.
36 // We write `-1` placeholders.
37
38 writeln!(
39 writer,
40 " {} {} {} -1 -1 -1 -1 {}",
41 face.vertex_indices[0],
42 face.vertex_indices[1],
43 face.vertex_indices[2],
44 face.material_id
45 )?;
46 }
47
48 // AABB
49 // Only if AreaModel
50 if bwm.walkmesh_type.known() == Some(BwmType::AreaModel) && !bwm.aabb_nodes.is_empty() {
51 writeln!(writer, " aabb")?;
52 for node in &bwm.aabb_nodes {
53 // Output only leaf nodes (containing faces) to the ASCII AABB list.
54 // The game rebuilds the tree structure from these leaves on load.
55 if node.face_index != 0xFFFFFFFF {
56 // Write raw bounding box values. The game applies epsilon expansion on load.
57 writeln!(
58 writer,
59 " {} {} {} {} {} {} {}",
60 node.bb_min.x,
61 node.bb_min.y,
62 node.bb_min.z,
63 node.bb_max.x,
64 node.bb_max.y,
65 node.bb_max.z,
66 node.face_index
67 )?;
68 }
69 }
70 }
71
72 writeln!(writer, "endnode")?;
73
74 Ok(())
75}