1use std::mem::MaybeUninit;
2
3use super::{CoordinateOrientation, FVec3, FVec4};
4
5#[doc(alias = "C3D_Mtx")]
14#[derive(Clone, Copy)]
15#[repr(transparent)]
16pub struct Matrix4(citro3d_sys::C3D_Mtx);
17
18impl Matrix4 {
19 pub fn from_cells_wzyx(cells: [f32; 16]) -> Self {
24 Self(citro3d_sys::C3D_Mtx { m: cells })
25 }
26 pub fn from_rows(rows: [FVec4; 4]) -> Self {
28 Self(citro3d_sys::C3D_Mtx {
29 r: rows.map(|r| r.0),
30 })
31 }
32 pub fn from_raw(value: citro3d_sys::C3D_Mtx) -> Self {
34 Self(value)
35 }
36
37 pub fn as_raw(&self) -> &citro3d_sys::C3D_Mtx {
38 &self.0
39 }
40
41 pub fn as_raw_mut(&mut self) -> &mut citro3d_sys::C3D_Mtx {
42 &mut self.0
43 }
44
45 pub fn into_raw(self) -> citro3d_sys::C3D_Mtx {
46 self.0
47 }
48
49 pub fn rows_wzyx(self) -> [FVec4; 4] {
51 unsafe { self.0.r }.map(FVec4::from_raw)
52 }
53
54 pub fn rows_xyzw(self) -> [[f32; 4]; 4] {
56 self.rows_wzyx().map(|r| [r.x(), r.y(), r.z(), r.w()])
57 }
58 #[doc(alias = "Mtx_Zeros")]
60 pub fn zero() -> Self {
61 let mut out = MaybeUninit::uninit();
63 unsafe {
64 citro3d_sys::Mtx_Zeros(out.as_mut_ptr());
65 Self::from_raw(out.assume_init())
66 }
67 }
68
69 #[doc(alias = "Mtx_Transpose")]
71 pub fn transpose(mut self) -> Matrix4 {
72 unsafe {
73 citro3d_sys::Mtx_Transpose(self.as_raw_mut());
74 }
75 Matrix4::from_raw(self.into_raw())
76 }
77
78 #[doc(alias = "Mtx_Translate")]
89 pub fn translate(&mut self, x: f32, y: f32, z: f32) {
90 unsafe { citro3d_sys::Mtx_Translate(self.as_raw_mut(), x, y, z, false) }
91 }
92
93 #[doc(alias = "Mtx_Scale")]
95 pub fn scale(&mut self, x: f32, y: f32, z: f32) {
96 unsafe { citro3d_sys::Mtx_Scale(self.as_raw_mut(), x, y, z) }
97 }
98
99 #[doc(alias = "Mtx_Rotate")]
101 pub fn rotate(&mut self, axis: FVec3, angle: f32) {
102 unsafe { citro3d_sys::Mtx_Rotate(self.as_raw_mut(), axis.0, angle, false) }
103 }
104
105 #[doc(alias = "Mtx_RotateX")]
107 pub fn rotate_x(&mut self, angle: f32) {
108 unsafe { citro3d_sys::Mtx_RotateX(self.as_raw_mut(), angle, false) }
109 }
110
111 #[doc(alias = "Mtx_RotateY")]
113 pub fn rotate_y(&mut self, angle: f32) {
114 unsafe { citro3d_sys::Mtx_RotateY(self.as_raw_mut(), angle, false) }
115 }
116
117 #[doc(alias = "Mtx_RotateZ")]
119 pub fn rotate_z(&mut self, angle: f32) {
120 unsafe { citro3d_sys::Mtx_RotateZ(self.as_raw_mut(), angle, false) }
121 }
122
123 #[doc(alias = "Mtx_Inverse")]
129 pub fn inverse(mut self) -> Result<Self, Self> {
130 let determinant = unsafe { citro3d_sys::Mtx_Inverse(self.as_raw_mut()) };
131 if determinant == 0.0 {
132 Err(self)
133 } else {
134 Ok(self)
135 }
136 }
137
138 #[doc(alias = "Mtx_Identity")]
140 pub fn identity() -> Self {
141 let mut out = MaybeUninit::uninit();
142 unsafe {
143 citro3d_sys::Mtx_Identity(out.as_mut_ptr());
144 Self::from_raw(out.assume_init())
145 }
146 }
147
148 #[doc(alias = "Mtx_Diagonal")]
150 pub fn diagonal(x: f32, y: f32, z: f32, w: f32) -> Self {
151 let mut out = MaybeUninit::uninit();
152 unsafe {
153 citro3d_sys::Mtx_Diagonal(out.as_mut_ptr(), x, y, z, w);
154 Self::from_raw(out.assume_init())
155 }
156 }
157
158 #[doc(alias = "Mtx_LookAt")]
161 pub fn looking_at(
162 camera_position: FVec3,
163 camera_target: FVec3,
164 camera_up: FVec3,
165 coordinates: CoordinateOrientation,
166 ) -> Self {
167 let mut out = MaybeUninit::uninit();
168 unsafe {
169 citro3d_sys::Mtx_LookAt(
170 out.as_mut_ptr(),
171 camera_position.0,
172 camera_target.0,
173 camera_up.0,
174 coordinates.is_left_handed(),
175 );
176 Self::from_raw(out.assume_init())
177 }
178 }
179}
180
181impl core::fmt::Debug for Matrix4 {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 f.debug_tuple("Matrix4").field(&self.rows_wzyx()).finish()
184 }
185}
186
187#[cfg(feature = "glam")]
188impl From<glam::Mat4> for Matrix4 {
189 fn from(mat: glam::Mat4) -> Self {
190 Matrix4::from_rows(core::array::from_fn(|i| mat.row(i).into()))
191 }
192}
193
194#[cfg(feature = "glam")]
195impl From<Matrix4> for glam::Mat4 {
196 fn from(mat: Matrix4) -> Self {
197 glam::Mat4::from_cols_array_2d(&mat.rows_xyzw()).transpose()
198 }
199}