Skip to main content

rpfm_lib/files/bmd/common/
mod.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Common data structures shared across BMD format files.
12//!
13//! This module provides reusable geometric and transformation primitives used throughout
14//! BMD (Battle Map Definition) files and related formats. These structures are public to
15//! allow reuse in other file format modules.
16//!
17//! # Geometric Primitives
18//!
19//! ## Points
20//! - [`Point2d`] - 2D point (x, y)
21//! - [`Point3d`] - 3D point (x, y, z)
22//!
23//! ## Shapes
24//! - [`Rectangle`] - 2D axis-aligned bounding box
25//! - [`Cube`] - 3D axis-aligned bounding box
26//! - [`Outline2d`] - 2D polyline outline
27//! - [`Outline3d`] - 3D polyline outline
28//! - [`Polygon2d`] - 2D polygon with arbitrary vertices
29//!
30//! ## Colors
31//! - [`ColourRGB`] - RGB color (floating-point components)
32//! - [`ColourRGBA`] - RGBA color (8-bit components)
33//!
34//! ## Transformations
35//! - [`Transform3x4`] - 3x4 transformation matrix (rotation + translation)
36//! - [`Transform4x4`] - 4x4 transformation matrix (full affine transform)
37//! - [`Quaternion`] - Rotation quaternion (i, j, k, w)
38//! - [`Matrix`] - Trait for matrix operations and conversions
39//!
40//! # Matrix Trait
41//!
42//! The [`Matrix`] trait provides common operations for transformation matrices:
43//! - Element accessors (`m00()`, `m01()`, etc.)
44//! - Rotation matrix extraction
45//! - Scale extraction and application
46//! - Euler angle conversion
47//! - Identity matrix creation
48//!
49//! # Usage
50//!
51//! ```ignore
52//! use rpfm_lib::files::bmd::common::{Point3d, Transform4x4, Matrix};
53//!
54//! // Create a 3D point
55//! let point = Point3d::new(10.0, 20.0, 30.0);
56//!
57//! // Create an identity transform
58//! let transform = Transform4x4::identity();
59//!
60//! // Extract rotation angles
61//! let rotation_matrix = transform.rotation_matrix();
62//! let (x, y, z) = Transform4x4::rotation_matrix_to_euler_angles(rotation_matrix, true);
63//! ```
64//!
65//! # Submodules
66//!
67//! - [`building_link`] - Building linkage data structures
68//! - [`building_reference`] - Building reference data structures
69//! - [`flags`] - Flag definitions
70//! - [`properties`] - Property data structures
71
72use getset::*;
73use serde_derive::{Serialize, Deserialize};
74
75use std::ops::Sub;
76
77use crate::binary::{ReadBytes, WriteBytes};
78use crate::error::Result;
79use crate::files::{Decodeable, EncodeableExtraData, Encodeable};
80
81use super::*;
82
83pub mod building_link;
84pub mod building_reference;
85pub mod flags;
86pub mod properties;
87
88//---------------------------------------------------------------------------//
89//                              Enum & Structs
90//---------------------------------------------------------------------------//
91
92/// RGB color with floating-point components.
93///
94/// Used for lighting and material colors in BMD files. Each component is a 32-bit
95/// floating-point value typically in the range [0.0, 1.0], though values outside
96/// this range are supported for HDR lighting.
97///
98/// # Fields
99///
100/// - `r`: Red component
101/// - `g`: Green component
102/// - `b`: Blue component
103///
104/// # Example
105///
106/// ```ignore
107/// use rpfm_lib::files::bmd::common::ColourRGB;
108///
109/// let mut color = ColourRGB::default();
110/// color.set_r(1.0);  // Full red
111/// color.set_g(0.5);  // Half green
112/// color.set_b(0.0);  // No blue
113/// ```
114#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
115#[getset(get = "pub", get_mut = "pub", set = "pub")]
116pub struct ColourRGB {
117    /// Red component (typically 0.0-1.0).
118    r: f32,
119
120    /// Green component (typically 0.0-1.0).
121    g: f32,
122
123    /// Blue component (typically 0.0-1.0).
124    b: f32,
125}
126
127/// RGBA color with 8-bit components.
128///
129/// Used for color data requiring alpha (transparency) channel. Each component is
130/// an unsigned 8-bit integer in the range [0, 255].
131///
132/// # Fields
133///
134/// - `r`: Red component (0-255)
135/// - `g`: Green component (0-255)
136/// - `b`: Blue component (0-255)
137/// - `a`: Alpha (opacity) component (0-255, where 255 is fully opaque)
138///
139/// # Example
140///
141/// ```ignore
142/// use rpfm_lib::files::bmd::common::ColourRGBA;
143///
144/// let mut color = ColourRGBA::default();
145/// color.set_r(255);  // Full red
146/// color.set_g(128);  // Half green
147/// color.set_b(0);    // No blue
148/// color.set_a(255);  // Fully opaque
149/// ```
150#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
151#[getset(get = "pub", get_mut = "pub", set = "pub")]
152pub struct ColourRGBA {
153    /// Red component (0-255).
154    r: u8,
155
156    /// Green component (0-255).
157    g: u8,
158
159    /// Blue component (0-255).
160    b: u8,
161
162    /// Alpha (opacity) component (0-255, where 255 is fully opaque).
163    a: u8,
164}
165
166/// 3D axis-aligned bounding box (AABB).
167///
168/// Represents a rectangular volume aligned with coordinate axes, defined by minimum
169/// and maximum corners. Used for spatial bounds, collision volumes, and culling.
170///
171/// # Fields
172///
173/// - `min_x`, `min_y`, `min_z`: Minimum corner coordinates
174/// - `max_x`, `max_y`, `max_z`: Maximum corner coordinates
175///
176/// # Example
177///
178/// ```ignore
179/// use rpfm_lib::files::bmd::common::Cube;
180///
181/// let mut cube = Cube::default();
182/// cube.set_min_x(-10.0);
183/// cube.set_max_x(10.0);
184/// // Creates a 20x20x20 cube centered at origin
185/// ```
186#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
187#[getset(get = "pub", get_mut = "pub", set = "pub")]
188pub struct Cube {
189    /// Minimum X coordinate.
190    min_x: f32,
191
192    /// Minimum Y coordinate.
193    min_y: f32,
194
195    /// Minimum Z coordinate.
196    min_z: f32,
197
198    /// Maximum X coordinate.
199    max_x: f32,
200
201    /// Maximum Y coordinate.
202    max_y: f32,
203
204    /// Maximum Z coordinate.
205    max_z: f32,
206}
207
208/// 2D polyline outline.
209///
210/// Represents a sequence of connected 2D points forming an open or closed outline.
211/// Used for area boundaries, deployment zones, and other 2D regions.
212///
213/// # Fields
214///
215/// - `outline`: Ordered list of 2D points
216#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
217#[getset(get = "pub", get_mut = "pub", set = "pub")]
218pub struct Outline2d {
219    /// Ordered list of 2D points forming the outline.
220    outline: Vec<Point2d>,
221}
222
223/// 3D polyline outline.
224///
225/// Represents a sequence of connected 3D points forming an open or closed outline.
226/// Used for 3D boundaries, paths, and spatial regions.
227///
228/// # Fields
229///
230/// - `outline`: Ordered list of 3D points
231#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
232#[getset(get = "pub", get_mut = "pub", set = "pub")]
233pub struct Outline3d {
234    /// Ordered list of 3D points forming the outline.
235    outline: Vec<Point3d>,
236}
237
238/// 2D point in Cartesian coordinates.
239///
240/// Represents a position in 2D space. Used for map coordinates, UI positions,
241/// and texture coordinates.
242///
243/// # Fields
244///
245/// - `x`: Horizontal coordinate
246/// - `y`: Vertical coordinate
247#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
248#[getset(get = "pub", get_mut = "pub", set = "pub")]
249pub struct Point2d {
250    /// X (horizontal) coordinate.
251    x: f32,
252
253    /// Y (vertical) coordinate.
254    y: f32,
255}
256
257/// 3D point in Cartesian coordinates.
258///
259/// Represents a position in 3D space.
260///
261/// # Fields
262///
263/// - `x`: X-axis coordinate
264/// - `y`: Y-axis coordinate
265/// - `z`: Z-axis coordinate
266///
267/// # Example
268///
269/// ```ignore
270/// use rpfm_lib::files::bmd::common::Point3d;
271///
272/// let p1 = Point3d::new(10.0, 20.0, 30.0);
273/// let p2 = Point3d::new(5.0, 5.0, 5.0);
274/// let diff = p1 - p2;  // Vector from p2 to p1
275/// ```
276#[derive(Default, PartialEq, Copy, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
277#[getset(get = "pub", get_mut = "pub", set = "pub")]
278pub struct Point3d {
279    /// X-axis coordinate.
280    x: f32,
281
282    /// Y-axis coordinate.
283    y: f32,
284
285    /// Z-axis coordinate.
286    z: f32,
287}
288
289/// 2D polygon with arbitrary vertices.
290///
291/// Represents a closed 2D polygon defined by an ordered list of vertices.
292/// Used for complex area definitions and spatial regions.
293///
294/// # Fields
295///
296/// - `points`: Ordered list of polygon vertices
297#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
298#[getset(get = "pub", get_mut = "pub", set = "pub")]
299pub struct Polygon2d {
300    /// Ordered list of polygon vertices.
301    points: Vec<Point2d>
302}
303
304/// Rotation quaternion.
305///
306/// Represents a 3D rotation using quaternion representation (i, j, k, w).
307/// Quaternions provide smooth interpolation and avoid gimbal lock.
308///
309/// # Fields
310///
311/// - `i`, `j`, `k`: Imaginary components
312/// - `w`: Real (scalar) component
313///
314/// # Quaternion Format
315///
316/// Standard quaternion format: `q = w + xi + yj + zk`
317///
318/// # Example
319///
320/// ```ignore
321/// use rpfm_lib::files::bmd::common::Quaternion;
322///
323/// let mut quat = Quaternion::default();
324/// quat.set_w(1.0);  // Identity rotation
325/// ```
326#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
327#[getset(get = "pub", get_mut = "pub", set = "pub")]
328pub struct Quaternion {
329    /// Imaginary i component.
330    i: f32,
331
332    /// Imaginary j component.
333    j: f32,
334
335    /// Imaginary k component.
336    k: f32,
337
338    /// Real (scalar) w component.
339    w: f32,
340}
341
342/// 2D axis-aligned rectangle.
343///
344/// Represents a rectangular area aligned with coordinate axes, defined by
345/// minimum and maximum corner coordinates. Used for 2D bounds and regions.
346///
347/// # Fields
348///
349/// - `min_x`, `min_y`: Minimum corner coordinates
350/// - `max_x`, `max_y`: Maximum corner coordinates
351#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
352#[getset(get = "pub", get_mut = "pub", set = "pub")]
353pub struct Rectangle {
354    /// Minimum X coordinate.
355    min_x: f32,
356
357    /// Minimum Y coordinate.
358    min_y: f32,
359
360    /// Maximum X coordinate.
361    max_x: f32,
362
363    /// Maximum Y coordinate.
364    max_y: f32,
365}
366
367/// 3x4 transformation matrix.
368///
369/// Represents a 3D affine transformation with rotation and translation but no
370/// perspective. The matrix is stored in column-major order and contains:
371/// - 3x3 rotation/scale submatrix (top-left)
372/// - 3x1 translation vector (bottom row)
373///
374/// # Matrix Layout
375///
376/// ```text
377/// [ m00  m01  m02 ]
378/// [ m10  m11  m12 ]
379/// [ m20  m21  m22 ]
380/// [ m30  m31  m32 ]
381/// ```
382///
383/// # Note
384///
385/// This struct does not have automatic getters for matrix elements. Use the
386/// [`Matrix`] trait methods (m00(), m01(), etc.) to access elements.
387///
388/// # Example
389///
390/// ```ignore
391/// use rpfm_lib::files::bmd::common::{Transform3x4, Matrix};
392///
393/// let transform = Transform3x4::identity();
394/// let m00 = transform.m00();  // Access via Matrix trait
395/// ```
396#[derive(Default, PartialEq, Clone, Debug, MutGetters, Setters, Serialize, Deserialize)]
397#[getset(get_mut = "pub", set = "pub")]
398pub struct Transform3x4{
399    /// Matrix element at row 0, column 0.
400    m00: f32,
401    /// Matrix element at row 0, column 1.
402    m01: f32,
403    /// Matrix element at row 0, column 2.
404    m02: f32,
405    /// Matrix element at row 1, column 0.
406    m10: f32,
407    /// Matrix element at row 1, column 1.
408    m11: f32,
409    /// Matrix element at row 1, column 2.
410    m12: f32,
411    /// Matrix element at row 2, column 0.
412    m20: f32,
413    /// Matrix element at row 2, column 1.
414    m21: f32,
415    /// Matrix element at row 2, column 2.
416    m22: f32,
417    /// Matrix element at row 3, column 0 (translation X).
418    m30: f32,
419    /// Matrix element at row 3, column 1 (translation Y).
420    m31: f32,
421    /// Matrix element at row 3, column 2 (translation Z).
422    m32: f32,
423}
424
425/// 4x4 transformation matrix.
426///
427/// Represents a full 3D affine transformation including rotation, scale,
428/// translation, and perspective. The matrix is stored in column-major order.
429///
430/// # Matrix Layout
431///
432/// ```text
433/// [ m00  m01  m02  m03 ]
434/// [ m10  m11  m12  m13 ]
435/// [ m20  m21  m22  m23 ]
436/// [ m30  m31  m32  m33 ]
437/// ```
438///
439/// # Note
440///
441/// This struct does not have automatic getters for matrix elements. Use the
442/// [`Matrix`] trait methods (m00(), m01(), etc.) to access elements.
443///
444/// # Conversions
445///
446/// - Can be converted from/to [`Cube`] for bounding box storage
447///
448/// # Example
449///
450/// ```ignore
451/// use rpfm_lib::files::bmd::common::{Transform4x4, Matrix};
452///
453/// let transform = Transform4x4::identity();
454/// let rotation = transform.rotation_matrix();
455/// let (rx, ry, rz) = Transform4x4::rotation_matrix_to_euler_angles(rotation, true);
456/// ```
457#[derive(Default, PartialEq, Clone, Debug, MutGetters, Setters, Serialize, Deserialize)]
458#[getset(get_mut = "pub", set = "pub")]
459pub struct Transform4x4 {
460    /// Matrix element at row 0, column 0.
461    m00: f32,
462    /// Matrix element at row 0, column 1.
463    m01: f32,
464    /// Matrix element at row 0, column 2.
465    m02: f32,
466    /// Matrix element at row 0, column 3.
467    m03: f32,
468    /// Matrix element at row 1, column 0.
469    m10: f32,
470    /// Matrix element at row 1, column 1.
471    m11: f32,
472    /// Matrix element at row 1, column 2.
473    m12: f32,
474    /// Matrix element at row 1, column 3.
475    m13: f32,
476    /// Matrix element at row 2, column 0.
477    m20: f32,
478    /// Matrix element at row 2, column 1.
479    m21: f32,
480    /// Matrix element at row 2, column 2.
481    m22: f32,
482    /// Matrix element at row 2, column 3.
483    m23: f32,
484    /// Matrix element at row 3, column 0 (translation X).
485    m30: f32,
486    /// Matrix element at row 3, column 1 (translation Y).
487    m31: f32,
488    /// Matrix element at row 3, column 2 (translation Z).
489    m32: f32,
490    /// Matrix element at row 3, column 3 (homogeneous coordinate).
491    m33: f32,
492}
493
494//---------------------------------------------------------------------------//
495//                           Implementations
496//---------------------------------------------------------------------------//
497
498impl Decodeable for ColourRGB {
499
500    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
501        Ok(Self {
502            r: data.read_f32()?,
503            g: data.read_f32()?,
504            b: data.read_f32()?,
505        })
506    }
507}
508
509impl Encodeable for ColourRGB {
510
511    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
512        buffer.write_f32(self.r)?;
513        buffer.write_f32(self.g)?;
514        buffer.write_f32(self.b)?;
515
516        Ok(())
517    }
518}
519
520impl Decodeable for ColourRGBA {
521
522    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
523        Ok(Self {
524            r: data.read_u8()?,
525            g: data.read_u8()?,
526            b: data.read_u8()?,
527            a: data.read_u8()?,
528        })
529    }
530}
531
532impl Encodeable for ColourRGBA {
533
534    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
535        buffer.write_u8(self.r)?;
536        buffer.write_u8(self.g)?;
537        buffer.write_u8(self.b)?;
538        buffer.write_u8(self.a)?;
539
540        Ok(())
541    }
542}
543
544impl Decodeable for Cube {
545
546    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
547        Ok(Self {
548            min_x: data.read_f32()?,
549            min_y: data.read_f32()?,
550            min_z: data.read_f32()?,
551            max_x: data.read_f32()?,
552            max_y: data.read_f32()?,
553            max_z: data.read_f32()?,
554        })
555    }
556}
557
558impl Encodeable for Cube {
559
560    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
561        buffer.write_f32(self.min_x)?;
562        buffer.write_f32(self.min_y)?;
563        buffer.write_f32(self.min_z)?;
564        buffer.write_f32(self.max_x)?;
565        buffer.write_f32(self.max_y)?;
566        buffer.write_f32(self.max_z)?;
567
568        Ok(())
569    }
570}
571
572impl Decodeable for Outline2d {
573
574    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
575        let mut decoded = Self::default();
576
577        for _ in 0..data.read_u32()? {
578            decoded.outline.push(Point2d::decode(data, extra_data)?);
579        }
580
581        Ok(decoded)
582    }
583}
584
585impl Encodeable for Outline2d {
586
587    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
588        buffer.write_u32(self.outline.len() as u32)?;
589
590        for point in &mut self.outline {
591            point.encode(buffer, extra_data)?;
592        }
593
594        Ok(())
595    }
596}
597
598impl Decodeable for Outline3d {
599
600    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
601        let mut decoded = Self::default();
602
603        for _ in 0..data.read_u32()? {
604            decoded.outline.push(Point3d::decode(data, extra_data)?);
605        }
606
607        Ok(decoded)
608    }
609}
610
611impl Encodeable for Outline3d {
612
613    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
614        buffer.write_u32(self.outline.len() as u32)?;
615
616        for point in &mut self.outline {
617            point.encode(buffer, extra_data)?;
618        }
619
620        Ok(())
621    }
622}
623
624impl Decodeable for Point2d {
625
626    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
627        Ok(Self {
628            x: data.read_f32()?,
629            y: data.read_f32()?,
630        })
631    }
632}
633
634impl Encodeable for Point2d {
635
636    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
637        buffer.write_f32(self.x)?;
638        buffer.write_f32(self.y)?;
639
640        Ok(())
641    }
642}
643
644impl Decodeable for Point3d {
645
646    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
647        Ok(Self {
648            x: data.read_f32()?,
649            y: data.read_f32()?,
650            z: data.read_f32()?,
651        })
652    }
653}
654
655impl Encodeable for Point3d {
656
657    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
658        buffer.write_f32(self.x)?;
659        buffer.write_f32(self.y)?;
660        buffer.write_f32(self.z)?;
661
662        Ok(())
663    }
664}
665
666impl Decodeable for Polygon2d {
667
668    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
669        let mut decoded = Self::default();
670
671        for _ in 0..data.read_u32()? {
672            decoded.points.push(Point2d::decode(data, extra_data)?);
673        }
674
675        Ok(decoded)
676    }
677}
678
679impl Encodeable for Polygon2d {
680
681    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
682        buffer.write_u32(self.points.len() as u32)?;
683        for point in &mut self.points {
684            point.encode(buffer, extra_data)?;
685        }
686
687        Ok(())
688    }
689}
690
691impl Decodeable for Quaternion {
692
693    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
694        Ok(Self {
695            i: data.read_f32()?,
696            j: data.read_f32()?,
697            k: data.read_f32()?,
698            w: data.read_f32()?,
699        })
700    }
701}
702
703impl Encodeable for Quaternion {
704
705    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
706        buffer.write_f32(self.i)?;
707        buffer.write_f32(self.j)?;
708        buffer.write_f32(self.k)?;
709        buffer.write_f32(self.w)?;
710
711        Ok(())
712    }
713}
714
715impl Decodeable for Rectangle {
716
717    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
718        Ok(Self {
719            min_x: data.read_f32()?,
720            min_y: data.read_f32()?,
721            max_x: data.read_f32()?,
722            max_y: data.read_f32()?,
723        })
724    }
725}
726
727impl Encodeable for Rectangle {
728
729    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
730        buffer.write_f32(self.min_x)?;
731        buffer.write_f32(self.min_y)?;
732        buffer.write_f32(self.max_x)?;
733        buffer.write_f32(self.max_y)?;
734
735        Ok(())
736    }
737}
738
739impl Decodeable for Transform3x4 {
740
741    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
742        Ok(Self {
743            m00: data.read_f32()?,
744            m01: data.read_f32()?,
745            m02: data.read_f32()?,
746            m10: data.read_f32()?,
747            m11: data.read_f32()?,
748            m12: data.read_f32()?,
749            m20: data.read_f32()?,
750            m21: data.read_f32()?,
751            m22: data.read_f32()?,
752            m30: data.read_f32()?,
753            m31: data.read_f32()?,
754            m32: data.read_f32()?,
755        })
756    }
757}
758
759impl Encodeable for Transform3x4 {
760
761    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
762        buffer.write_f32(self.m00)?;
763        buffer.write_f32(self.m01)?;
764        buffer.write_f32(self.m02)?;
765        buffer.write_f32(self.m10)?;
766        buffer.write_f32(self.m11)?;
767        buffer.write_f32(self.m12)?;
768        buffer.write_f32(self.m20)?;
769        buffer.write_f32(self.m21)?;
770        buffer.write_f32(self.m22)?;
771        buffer.write_f32(self.m30)?;
772        buffer.write_f32(self.m31)?;
773        buffer.write_f32(self.m32)?;
774
775        Ok(())
776    }
777}
778
779impl Decodeable for Transform4x4 {
780
781    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
782        Ok(Self {
783            m00: data.read_f32()?,
784            m01: data.read_f32()?,
785            m02: data.read_f32()?,
786            m03: data.read_f32()?,
787            m10: data.read_f32()?,
788            m11: data.read_f32()?,
789            m12: data.read_f32()?,
790            m13: data.read_f32()?,
791            m20: data.read_f32()?,
792            m21: data.read_f32()?,
793            m22: data.read_f32()?,
794            m23: data.read_f32()?,
795            m30: data.read_f32()?,
796            m31: data.read_f32()?,
797            m32: data.read_f32()?,
798            m33: data.read_f32()?,
799        })
800    }
801}
802
803impl Encodeable for Transform4x4 {
804
805    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
806        buffer.write_f32(self.m00)?;
807        buffer.write_f32(self.m01)?;
808        buffer.write_f32(self.m02)?;
809        buffer.write_f32(self.m03)?;
810        buffer.write_f32(self.m10)?;
811        buffer.write_f32(self.m11)?;
812        buffer.write_f32(self.m12)?;
813        buffer.write_f32(self.m13)?;
814        buffer.write_f32(self.m20)?;
815        buffer.write_f32(self.m21)?;
816        buffer.write_f32(self.m22)?;
817        buffer.write_f32(self.m23)?;
818        buffer.write_f32(self.m30)?;
819        buffer.write_f32(self.m31)?;
820        buffer.write_f32(self.m32)?;
821        buffer.write_f32(self.m33)?;
822
823        Ok(())
824    }
825}
826
827/// Common operations for transformation matrices.
828///
829/// This trait abstracts behavior shared between [`Transform3x4`] and [`Transform4x4`],
830/// providing matrix element access and transformation utilities.
831///
832/// # Provided Methods
833///
834/// - **Element Access**: m00() through m33() - Access individual matrix elements
835/// - **Rotation**: `rotation_matrix()` - Extract 3x3 rotation submatrix
836/// - **Scaling**: `extract_scales()`, `apply_scales()`, `normalize_rotation_matrix()`
837/// - **Euler Angles**: `rotation_matrix_to_euler_angles()`, `euler_angles_to_rotation_matrix()`
838/// - **Identity**: `identity()` - Create identity transform
839///
840/// # Rotation Order
841///
842/// Euler angle conversions use 'xyz' extrinsic rotation order (roll-pitch-yaw).
843///
844/// # Example
845///
846/// ```ignore
847/// use rpfm_lib::files::bmd::common::{Transform4x4, Matrix};
848///
849/// let transform = Transform4x4::identity();
850///
851/// // Extract rotation
852/// let rotation = transform.rotation_matrix();
853/// let scales = Transform4x4::extract_scales(rotation);
854///
855/// // Convert to Euler angles (in degrees)
856/// let (x, y, z) = Transform4x4::rotation_matrix_to_euler_angles(rotation, true);
857/// println!("Rotation: X={}, Y={}, Z={}", x, y, z);
858/// ```
859pub trait Matrix {
860    /// Returns matrix element at row 0, column 0.
861    fn m00(&self) -> f32;
862    /// Returns matrix element at row 0, column 1.
863    fn m01(&self) -> f32;
864    /// Returns matrix element at row 0, column 2.
865    fn m02(&self) -> f32;
866    /// Returns matrix element at row 0, column 3 (0.0 for 3x4 matrices).
867    fn m03(&self) -> f32;
868    /// Returns matrix element at row 1, column 0.
869    fn m10(&self) -> f32;
870    /// Returns matrix element at row 1, column 1.
871    fn m11(&self) -> f32;
872    /// Returns matrix element at row 1, column 2.
873    fn m12(&self) -> f32;
874    /// Returns matrix element at row 1, column 3 (0.0 for 3x4 matrices).
875    fn m13(&self) -> f32;
876    /// Returns matrix element at row 2, column 0.
877    fn m20(&self) -> f32;
878    /// Returns matrix element at row 2, column 1.
879    fn m21(&self) -> f32;
880    /// Returns matrix element at row 2, column 2.
881    fn m22(&self) -> f32;
882    /// Returns matrix element at row 2, column 3 (0.0 for 3x4 matrices).
883    fn m23(&self) -> f32;
884    /// Returns matrix element at row 3, column 0 (translation X).
885    fn m30(&self) -> f32;
886    /// Returns matrix element at row 3, column 1 (translation Y).
887    fn m31(&self) -> f32;
888    /// Returns matrix element at row 3, column 2 (translation Z).
889    fn m32(&self) -> f32;
890    /// Returns matrix element at row 3, column 3 (1.0 for 3x4 matrices).
891    fn m33(&self) -> f32;
892
893    /// Extracts the 3x3 rotation submatrix.
894    ///
895    /// Converts from CA's column-major serialization to standard row-major
896    /// rotation matrix representation.
897    ///
898    /// # Returns
899    ///
900    /// 3x3 rotation matrix as nalgebra `Matrix3<f64>`.
901    ///
902    /// # Reference
903    ///
904    /// See: <https://developer.unigine.com/forum/uploads/monthly_2020_05/image.png.674c8b961433f2a7a62c54bc55cb599c.png>
905    fn rotation_matrix(&self) -> Matrix3<f64> {
906
907        // Fix order of the elements here
908        Matrix3::new(
909            self.m00() as f64, self.m10() as f64, self.m20() as f64,
910            self.m01() as f64, self.m11() as f64, self.m21() as f64,
911            self.m02() as f64, self.m12() as f64, self.m22() as f64
912        )
913    }
914
915    /// Extracts scale factors from a rotation matrix.
916    ///
917    /// Computes the scale of each axis by taking the norm of each column vector.
918    ///
919    /// # Parameters
920    ///
921    /// - `matrix`: 3x3 rotation/scale matrix
922    ///
923    /// # Returns
924    ///
925    /// Tuple of (scale_x, scale_y, scale_z)
926    ///
927    /// # Note
928    ///
929    /// **Does not support negative scales.** Negative scales will be treated as positive.
930    ///
931    /// # Reference
932    ///
933    /// See: <https://math.stackexchange.com/a/1463487>
934    fn extract_scales(matrix: Matrix3<f64>) -> (f64, f64, f64) {
935        let scale = (
936            matrix.column(0).norm(),
937            matrix.column(1).norm(),
938            matrix.column(2).norm()
939        );
940        scale
941    }
942
943    /// Applies scale factors to a rotation matrix.
944    ///
945    /// Scales each column of the matrix by the corresponding scale factor.
946    ///
947    /// # Parameters
948    ///
949    /// - `matrix`: 3x3 rotation matrix (should be normalized)
950    /// - `scales`: Tuple of (scale_x, scale_y, scale_z)
951    ///
952    /// # Returns
953    ///
954    /// Scaled rotation matrix
955    fn apply_scales(matrix: Matrix3<f64>, scales: (f64, f64, f64)) -> Matrix3<f64> {
956        Matrix3::new(
957            matrix[(0, 0)] * scales.0, matrix[(0, 1)] * scales.1, matrix[(0, 2)] * scales.2,
958            matrix[(1, 0)] * scales.0, matrix[(1, 1)] * scales.1, matrix[(1, 2)] * scales.2,
959            matrix[(2, 0)] * scales.0, matrix[(2, 1)] * scales.1, matrix[(2, 2)] * scales.2,
960        )
961    }
962
963    /// Normalizes a rotation matrix by removing scale factors.
964    ///
965    /// Divides each column by the corresponding scale factor to produce a pure
966    /// rotation matrix.
967    ///
968    /// # Parameters
969    ///
970    /// - `matrix`: 3x3 rotation/scale matrix
971    /// - `scales`: Tuple of (scale_x, scale_y, scale_z) to remove
972    ///
973    /// # Returns
974    ///
975    /// Normalized rotation matrix (orthonormal)
976    fn normalize_rotation_matrix(matrix: Matrix3<f64>, scales: (f64, f64, f64)) -> Matrix3<f64> {
977        Matrix3::new(
978            matrix[(0, 0)] / scales.0, matrix[(0, 1)] / scales.1, matrix[(0, 2)] / scales.2,
979            matrix[(1, 0)] / scales.0, matrix[(1, 1)] / scales.1, matrix[(1, 2)] / scales.2,
980            matrix[(2, 0)] / scales.0, matrix[(2, 1)] / scales.1, matrix[(2, 2)] / scales.2,
981        )
982    }
983
984    /// Converts a rotation matrix to Euler angles.
985    ///
986    /// Uses 'xyz' extrinsic rotation order (roll-pitch-yaw).
987    ///
988    /// # Parameters
989    ///
990    /// - `matrix`: 3x3 rotation matrix
991    /// - `degrees`: If true, return angles in degrees; if false, in radians
992    ///
993    /// # Returns
994    ///
995    /// Tuple of (x_rotation, y_rotation, z_rotation) in specified units
996    ///
997    /// # Example (Python equivalent using scipy)
998    ///
999    /// ```python
1000    /// from scipy.spatial.transform import Rotation as R
1001    /// r = R.from_euler("xyz", [-130.0, 80.0, -30.0], degrees=True)
1002    /// m = r.as_matrix()
1003    /// r = R.from_matrix(m)
1004    /// angles = r.as_euler("xyz", degrees=True)
1005    /// ```
1006    fn rotation_matrix_to_euler_angles(matrix: Matrix3<f64>, degrees: bool) -> (f64, f64, f64) {
1007        let rotation = Rotation3::from_matrix_unchecked(matrix);
1008        let euler = rotation.euler_angles();
1009        if degrees {
1010            (
1011                euler.0.to_degrees(),
1012                euler.1.to_degrees(),
1013                euler.2.to_degrees(),
1014            )
1015        } else {
1016           (euler.0, euler.1, euler.2)
1017        }
1018    }
1019
1020    /// Converts Euler angles to a rotation matrix.
1021    ///
1022    /// Uses 'xyz' extrinsic rotation order (roll-pitch-yaw).
1023    ///
1024    /// # Parameters
1025    ///
1026    /// - `angles`: Tuple of (x_rotation, y_rotation, z_rotation)
1027    /// - `degrees`: If true, angles are in degrees; if false, in radians
1028    ///
1029    /// # Returns
1030    ///
1031    /// 3x3 rotation matrix with values near zero cleaned up (< 1e-5 set to 0.0)
1032    fn euler_angles_to_rotation_matrix(angles: (f64, f64, f64), degrees: bool) -> Matrix3<f64> {
1033        let _angles = if degrees {
1034            (
1035                angles.0.to_radians(),
1036                angles.1.to_radians(),
1037                angles.2.to_radians(),
1038            )
1039        } else {
1040            angles
1041        };
1042        let rotation = Rotation3::from_euler_angles(_angles.0, _angles.1, _angles.2);
1043        let mut matrix : Matrix3<f64> = rotation.into();
1044
1045        // Clean up near-zero values for prettier output
1046        matrix.iter_mut().for_each(|element| {
1047            if element.abs() < 1e-5 {
1048                *element = 0.0;
1049            }
1050        });
1051        matrix
1052    }
1053
1054    /// Creates an identity transformation matrix.
1055    ///
1056    /// # Returns
1057    ///
1058    /// Identity matrix (no rotation, no translation, unit scale)
1059    fn identity() -> Self;
1060}
1061
1062impl Matrix for Transform3x4 {
1063    fn m00(&self) -> f32 {
1064        self.m00
1065    }
1066    fn m01(&self) -> f32 {
1067        self.m01
1068    }
1069    fn m02(&self) -> f32 {
1070        self.m02
1071    }
1072    fn m03(&self) -> f32 {
1073        0.0
1074    }
1075    fn m10(&self) -> f32 {
1076        self.m10
1077    }
1078    fn m11(&self) -> f32 {
1079        self.m11
1080    }
1081    fn m12(&self) -> f32 {
1082        self.m12
1083    }
1084    fn m13(&self) -> f32 {
1085        0.0
1086    }
1087    fn m20(&self) -> f32 {
1088        self.m20
1089    }
1090    fn m21(&self) -> f32 {
1091        self.m21
1092    }
1093    fn m22(&self) -> f32 {
1094        self.m22
1095    }
1096    fn m23(&self) -> f32 {
1097        0.0
1098    }
1099    fn m30(&self) -> f32 {
1100        self.m30
1101    }
1102    fn m31(&self) -> f32 {
1103        self.m31
1104    }
1105    fn m32(&self) -> f32 {
1106        self.m32
1107    }
1108    fn m33(&self) -> f32 {
1109        1.0
1110    }
1111
1112    fn identity() -> Self {
1113        Self {
1114            m00: 1.0,
1115            m01: 0.0,
1116            m02: 0.0,
1117            m10: 0.0,
1118            m11: 1.0,
1119            m12: 0.0,
1120            m20: 0.0,
1121            m21: 0.0,
1122            m22: 1.0,
1123            m30: 0.0,
1124            m31: 0.0,
1125            m32: 0.0,
1126        }
1127    }
1128}
1129
1130impl Matrix for Transform4x4 {
1131    fn m00(&self) -> f32 {
1132        self.m00
1133    }
1134    fn m01(&self) -> f32 {
1135        self.m01
1136    }
1137    fn m02(&self) -> f32 {
1138        self.m02
1139    }
1140    fn m03(&self) -> f32 {
1141        self.m03
1142    }
1143    fn m10(&self) -> f32 {
1144        self.m10
1145    }
1146    fn m11(&self) -> f32 {
1147        self.m11
1148    }
1149    fn m12(&self) -> f32 {
1150        self.m12
1151    }
1152    fn m13(&self) -> f32 {
1153        self.m13
1154    }
1155    fn m20(&self) -> f32 {
1156        self.m20
1157    }
1158    fn m21(&self) -> f32 {
1159        self.m21
1160    }
1161    fn m22(&self) -> f32 {
1162        self.m22
1163    }
1164    fn m23(&self) -> f32 {
1165        self.m23
1166    }
1167    fn m30(&self) -> f32 {
1168        self.m30
1169    }
1170    fn m31(&self) -> f32 {
1171        self.m31
1172    }
1173    fn m32(&self) -> f32 {
1174        self.m32
1175    }
1176    fn m33(&self) -> f32 {
1177        self.m33
1178    }
1179
1180    fn identity() -> Self {
1181        Self {
1182            m00: 1.0,
1183            m01: 0.0,
1184            m02: 0.0,
1185            m03: 0.0,
1186            m10: 0.0,
1187            m11: 1.0,
1188            m12: 0.0,
1189            m13: 0.0,
1190            m20: 0.0,
1191            m21: 0.0,
1192            m22: 1.0,
1193            m23: 0.0,
1194            m30: 0.0,
1195            m31: 0.0,
1196            m32: 0.0,
1197            m33: 1.0,
1198        }
1199    }
1200}
1201
1202impl Point3d {
1203    /// Creates a new 3D point with the specified coordinates.
1204    ///
1205    /// # Parameters
1206    ///
1207    /// - `x`: X-axis coordinate
1208    /// - `y`: Y-axis coordinate
1209    /// - `z`: Z-axis coordinate
1210    ///
1211    /// # Returns
1212    ///
1213    /// New `Point3d` instance
1214    ///
1215    /// # Example
1216    ///
1217    /// ```ignore
1218    /// use rpfm_lib::files::bmd::common::Point3d;
1219    ///
1220    /// let point = Point3d::new(10.0, 20.0, 30.0);
1221    /// assert_eq!(*point.x(), 10.0);
1222    /// ```
1223    pub fn new(x: f32, y: f32, z: f32) -> Self {
1224        Self { x, y, z }
1225    }
1226}
1227
1228impl Sub for Point3d {
1229    type Output = Self;
1230
1231    /// Subtracts two 3D points to produce a vector.
1232    ///
1233    /// # Example
1234    ///
1235    /// ```ignore
1236    /// use rpfm_lib::files::bmd::common::Point3d;
1237    ///
1238    /// let p1 = Point3d::new(10.0, 20.0, 30.0);
1239    /// let p2 = Point3d::new(5.0, 10.0, 15.0);
1240    /// let diff = p1 - p2;  // Results in (5.0, 10.0, 15.0)
1241    /// ```
1242    fn sub(self, rhs: Self) -> Self::Output {
1243        Self {
1244            x: self.x - rhs.x,
1245            y: self.y - rhs.y,
1246            z: self.z - rhs.z,
1247        }
1248    }
1249}
1250
1251impl From<Cube> for Transform4x4 {
1252    fn from(value: Cube) -> Self {
1253        Self {
1254            m00: value.min_x,
1255            m01: value.min_y,
1256            m02: value.min_z,
1257            m10: value.max_x,
1258            m11: value.max_y,
1259            m12: value.max_z,
1260            ..Default::default()
1261        }
1262    }
1263}
1264
1265impl From<Transform4x4> for Cube {
1266    fn from(value: Transform4x4) -> Self {
1267        Self {
1268            min_x: value.m00,
1269            min_y: value.m01,
1270            min_z: value.m02,
1271            max_x: value.m10,
1272            max_y: value.m11,
1273            max_z: value.m12
1274        }
1275    }
1276}