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