1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Prerequisites/BsPrerequisitesUtil.h"
6
7#include "Math/BsVector3.h"
8#include "Math/BsMatrix3.h"
9#include "Math/BsVector4.h"
10#include "Math/BsPlane.h"
11
12namespace bs
13{
14 /** @addtogroup Math
15 * @{
16 */
17
18 /** Class representing a 4x4 matrix, in row major format. */
19 class BS_UTILITY_EXPORT Matrix4
20 {
21 public:
22 Matrix4() = default;
23 constexpr Matrix4(const Matrix4&) = default;
24 constexpr Matrix4& operator=(const Matrix4&) = default;
25
26 constexpr Matrix4(BS_ZERO)
27 :m{ {0.0f, 0.0f, 0.0f, 0.0f},
28 {0.0f, 0.0f, 0.0f, 0.0f},
29 {0.0f, 0.0f, 0.0f, 0.0f},
30 {0.0f, 0.0f, 0.0f, 0.0f} }
31 { }
32
33 constexpr Matrix4(BS_IDENTITY)
34 :m{ {1.0f, 0.0f, 0.0f, 0.0f},
35 {0.0f, 1.0f, 0.0f, 0.0f},
36 {0.0f, 0.0f, 1.0f, 0.0f},
37 {0.0f, 0.0f, 0.0f, 1.0f} }
38 { }
39
40 constexpr Matrix4(
41 float m00, float m01, float m02, float m03,
42 float m10, float m11, float m12, float m13,
43 float m20, float m21, float m22, float m23,
44 float m30, float m31, float m32, float m33)
45 :m{ {m00, m01, m02, m03},
46 {m10, m11, m12, m13},
47 {m20, m21, m22, m23},
48 {m30, m31, m32, m33} }
49 { }
50
51 /** Creates a 4x4 transformation matrix with a zero translation part from a rotation/scaling 3x3 matrix. */
52 constexpr explicit Matrix4(const Matrix3& mat3)
53 :m{ {mat3.m[0][0], mat3.m[0][1], mat3.m[0][2], 0.0f},
54 {mat3.m[1][0], mat3.m[1][1], mat3.m[1][2], 0.0f},
55 {mat3.m[2][0], mat3.m[2][1], mat3.m[2][2], 0.0f},
56 {0.0f, 0.0f, 0.0f, 1.0f} }
57 { }
58
59 /** Swaps the contents of this matrix with another. */
60 void swap(Matrix4& other)
61 {
62 std::swap(m[0][0], other.m[0][0]);
63 std::swap(m[0][1], other.m[0][1]);
64 std::swap(m[0][2], other.m[0][2]);
65 std::swap(m[0][3], other.m[0][3]);
66 std::swap(m[1][0], other.m[1][0]);
67 std::swap(m[1][1], other.m[1][1]);
68 std::swap(m[1][2], other.m[1][2]);
69 std::swap(m[1][3], other.m[1][3]);
70 std::swap(m[2][0], other.m[2][0]);
71 std::swap(m[2][1], other.m[2][1]);
72 std::swap(m[2][2], other.m[2][2]);
73 std::swap(m[2][3], other.m[2][3]);
74 std::swap(m[3][0], other.m[3][0]);
75 std::swap(m[3][1], other.m[3][1]);
76 std::swap(m[3][2], other.m[3][2]);
77 std::swap(m[3][3], other.m[3][3]);
78 }
79
80 /** Returns a row of the matrix. */
81 Vector4& operator[] (UINT32 row)
82 {
83 assert(row < 4);
84
85 return *(Vector4*)m[row];
86 }
87
88 /** Returns a row of the matrix. */
89 const Vector4& operator[] (UINT32 row) const
90 {
91 assert(row < 4);
92
93 return *(Vector4*)m[row];
94 }
95
96 Matrix4 operator* (const Matrix4 &rhs) const
97 {
98 Matrix4 r;
99
100 r.m[0][0] = m[0][0] * rhs.m[0][0] + m[0][1] * rhs.m[1][0] + m[0][2] * rhs.m[2][0] + m[0][3] * rhs.m[3][0];
101 r.m[0][1] = m[0][0] * rhs.m[0][1] + m[0][1] * rhs.m[1][1] + m[0][2] * rhs.m[2][1] + m[0][3] * rhs.m[3][1];
102 r.m[0][2] = m[0][0] * rhs.m[0][2] + m[0][1] * rhs.m[1][2] + m[0][2] * rhs.m[2][2] + m[0][3] * rhs.m[3][2];
103 r.m[0][3] = m[0][0] * rhs.m[0][3] + m[0][1] * rhs.m[1][3] + m[0][2] * rhs.m[2][3] + m[0][3] * rhs.m[3][3];
104
105 r.m[1][0] = m[1][0] * rhs.m[0][0] + m[1][1] * rhs.m[1][0] + m[1][2] * rhs.m[2][0] + m[1][3] * rhs.m[3][0];
106 r.m[1][1] = m[1][0] * rhs.m[0][1] + m[1][1] * rhs.m[1][1] + m[1][2] * rhs.m[2][1] + m[1][3] * rhs.m[3][1];
107 r.m[1][2] = m[1][0] * rhs.m[0][2] + m[1][1] * rhs.m[1][2] + m[1][2] * rhs.m[2][2] + m[1][3] * rhs.m[3][2];
108 r.m[1][3] = m[1][0] * rhs.m[0][3] + m[1][1] * rhs.m[1][3] + m[1][2] * rhs.m[2][3] + m[1][3] * rhs.m[3][3];
109
110 r.m[2][0] = m[2][0] * rhs.m[0][0] + m[2][1] * rhs.m[1][0] + m[2][2] * rhs.m[2][0] + m[2][3] * rhs.m[3][0];
111 r.m[2][1] = m[2][0] * rhs.m[0][1] + m[2][1] * rhs.m[1][1] + m[2][2] * rhs.m[2][1] + m[2][3] * rhs.m[3][1];
112 r.m[2][2] = m[2][0] * rhs.m[0][2] + m[2][1] * rhs.m[1][2] + m[2][2] * rhs.m[2][2] + m[2][3] * rhs.m[3][2];
113 r.m[2][3] = m[2][0] * rhs.m[0][3] + m[2][1] * rhs.m[1][3] + m[2][2] * rhs.m[2][3] + m[2][3] * rhs.m[3][3];
114
115 r.m[3][0] = m[3][0] * rhs.m[0][0] + m[3][1] * rhs.m[1][0] + m[3][2] * rhs.m[2][0] + m[3][3] * rhs.m[3][0];
116 r.m[3][1] = m[3][0] * rhs.m[0][1] + m[3][1] * rhs.m[1][1] + m[3][2] * rhs.m[2][1] + m[3][3] * rhs.m[3][1];
117 r.m[3][2] = m[3][0] * rhs.m[0][2] + m[3][1] * rhs.m[1][2] + m[3][2] * rhs.m[2][2] + m[3][3] * rhs.m[3][2];
118 r.m[3][3] = m[3][0] * rhs.m[0][3] + m[3][1] * rhs.m[1][3] + m[3][2] * rhs.m[2][3] + m[3][3] * rhs.m[3][3];
119
120 return r;
121 }
122
123 Matrix4 operator+ (const Matrix4 &rhs) const
124 {
125 Matrix4 r;
126
127 r.m[0][0] = m[0][0] + rhs.m[0][0];
128 r.m[0][1] = m[0][1] + rhs.m[0][1];
129 r.m[0][2] = m[0][2] + rhs.m[0][2];
130 r.m[0][3] = m[0][3] + rhs.m[0][3];
131
132 r.m[1][0] = m[1][0] + rhs.m[1][0];
133 r.m[1][1] = m[1][1] + rhs.m[1][1];
134 r.m[1][2] = m[1][2] + rhs.m[1][2];
135 r.m[1][3] = m[1][3] + rhs.m[1][3];
136
137 r.m[2][0] = m[2][0] + rhs.m[2][0];
138 r.m[2][1] = m[2][1] + rhs.m[2][1];
139 r.m[2][2] = m[2][2] + rhs.m[2][2];
140 r.m[2][3] = m[2][3] + rhs.m[2][3];
141
142 r.m[3][0] = m[3][0] + rhs.m[3][0];
143 r.m[3][1] = m[3][1] + rhs.m[3][1];
144 r.m[3][2] = m[3][2] + rhs.m[3][2];
145 r.m[3][3] = m[3][3] + rhs.m[3][3];
146
147 return r;
148 }
149
150 Matrix4 operator- (const Matrix4 &rhs) const
151 {
152 Matrix4 r;
153 r.m[0][0] = m[0][0] - rhs.m[0][0];
154 r.m[0][1] = m[0][1] - rhs.m[0][1];
155 r.m[0][2] = m[0][2] - rhs.m[0][2];
156 r.m[0][3] = m[0][3] - rhs.m[0][3];
157
158 r.m[1][0] = m[1][0] - rhs.m[1][0];
159 r.m[1][1] = m[1][1] - rhs.m[1][1];
160 r.m[1][2] = m[1][2] - rhs.m[1][2];
161 r.m[1][3] = m[1][3] - rhs.m[1][3];
162
163 r.m[2][0] = m[2][0] - rhs.m[2][0];
164 r.m[2][1] = m[2][1] - rhs.m[2][1];
165 r.m[2][2] = m[2][2] - rhs.m[2][2];
166 r.m[2][3] = m[2][3] - rhs.m[2][3];
167
168 r.m[3][0] = m[3][0] - rhs.m[3][0];
169 r.m[3][1] = m[3][1] - rhs.m[3][1];
170 r.m[3][2] = m[3][2] - rhs.m[3][2];
171 r.m[3][3] = m[3][3] - rhs.m[3][3];
172
173 return r;
174 }
175
176 inline bool operator== (const Matrix4& rhs) const
177 {
178 if (m[0][0] != rhs.m[0][0] || m[0][1] != rhs.m[0][1] || m[0][2] != rhs.m[0][2] || m[0][3] != rhs.m[0][3] ||
179 m[1][0] != rhs.m[1][0] || m[1][1] != rhs.m[1][1] || m[1][2] != rhs.m[1][2] || m[1][3] != rhs.m[1][3] ||
180 m[2][0] != rhs.m[2][0] || m[2][1] != rhs.m[2][1] || m[2][2] != rhs.m[2][2] || m[2][3] != rhs.m[2][3] ||
181 m[3][0] != rhs.m[3][0] || m[3][1] != rhs.m[3][1] || m[3][2] != rhs.m[3][2] || m[3][3] != rhs.m[3][3])
182 {
183 return false;
184 }
185
186 return true;
187 }
188
189 inline bool operator!= (const Matrix4& rhs) const
190 {
191 return !operator==(rhs);
192 }
193
194 Matrix4 operator*(float rhs) const
195 {
196 return Matrix4(rhs*m[0][0], rhs*m[0][1], rhs*m[0][2], rhs*m[0][3],
197 rhs*m[1][0], rhs*m[1][1], rhs*m[1][2], rhs*m[1][3],
198 rhs*m[2][0], rhs*m[2][1], rhs*m[2][2], rhs*m[2][3],
199 rhs*m[3][0], rhs*m[3][1], rhs*m[3][2], rhs*m[3][3]);
200 }
201
202 /** Returns the specified column of the matrix, ignoring the last row. */
203 Vector3 getColumn(UINT32 col) const
204 {
205 assert(col < 4);
206
207 return Vector3(m[0][col], m[1][col], m[2][col]);
208 }
209
210 /** Returns the specified column of the matrix. */
211 Vector4 getColumn4D(UINT32 col) const
212 {
213 assert(col < 4);
214
215 return Vector4(m[0][col], m[1][col], m[2][col], m[3][col]);
216 }
217
218 /** Returns a transpose of the matrix (switched columns and rows). */
219 Matrix4 transpose() const
220 {
221 return Matrix4(m[0][0], m[1][0], m[2][0], m[3][0],
222 m[0][1], m[1][1], m[2][1], m[3][1],
223 m[0][2], m[1][2], m[2][2], m[3][2],
224 m[0][3], m[1][3], m[2][3], m[3][3]);
225 }
226
227 /** Assigns the vector to a column of the matrix. */
228 void setColumn(UINT32 idx, const Vector4& column)
229 {
230 m[0][idx] = column.x;
231 m[1][idx] = column.y;
232 m[2][idx] = column.z;
233 m[3][idx] = column.w;
234 }
235
236 /** Assigns the vector to a row of the matrix. */
237 void setRow(UINT32 idx, const Vector4& column)
238 {
239 m[idx][0] = column.x;
240 m[idx][1] = column.y;
241 m[idx][2] = column.z;
242 m[idx][3] = column.w;
243 }
244
245 /** Returns the rotation/scaling part of the matrix as a 3x3 matrix. */
246 Matrix3 get3x3() const
247 {
248 Matrix3 m3x3;
249 m3x3.m[0][0] = m[0][0];
250 m3x3.m[0][1] = m[0][1];
251 m3x3.m[0][2] = m[0][2];
252 m3x3.m[1][0] = m[1][0];
253 m3x3.m[1][1] = m[1][1];
254 m3x3.m[1][2] = m[1][2];
255 m3x3.m[2][0] = m[2][0];
256 m3x3.m[2][1] = m[2][1];
257 m3x3.m[2][2] = m[2][2];
258
259 return m3x3;
260 }
261
262 /** Calculates the adjoint of the matrix. */
263 Matrix4 adjoint() const;
264
265 /** Calculates the determinant of the matrix. */
266 float determinant() const;
267
268 /** Calculates the determinant of the 3x3 sub-matrix. */
269 float determinant3x3() const;
270
271 /** Calculates the inverse of the matrix. */
272 Matrix4 inverse() const;
273
274 /**
275 * Creates a matrix from translation, rotation and scale.
276 *
277 * @note The transformation are applied in scale->rotation->translation order.
278 */
279 void setTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale);
280
281 /**
282 * Creates a matrix from inverse translation, rotation and scale.
283 *
284 * @note This is cheaper than setTRS() and then performing inverse().
285 */
286 void setInverseTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale);
287
288 /**
289 * Decompose a Matrix4 to translation, rotation and scale.
290 *
291 * @note
292 * This method is unable to decompose all types of matrices, in particular these are the limitations:
293 * - Only translation, rotation and scale transforms are supported
294 * - Plain TRS matrices (that aren't composed with other matrices) can always be decomposed
295 * - Composed TRS matrices can be decomposed ONLY if the scaling factor is uniform
296 */
297 void decomposition(Vector3& position, Quaternion& rotation, Vector3& scale) const;
298
299 /** Extracts the translation (position) part of the matrix. */
300 Vector3 getTranslation() const { return Vector3(m[0][3], m[1][3], m[2][3]); }
301
302 /**
303 * Check whether or not the matrix is affine matrix.
304 *
305 * @note An affine matrix is a 4x4 matrix with row 3 equal to (0, 0, 0, 1), meaning no projective coefficients.
306 */
307 bool isAffine() const
308 {
309 return m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0 && m[3][3] == 1;
310 }
311
312 /**
313 * Returns the inverse of the affine matrix.
314 *
315 * @note Matrix must be affine.
316 */
317 Matrix4 inverseAffine() const;
318
319 /**
320 * Concatenate two affine matrices.
321 *
322 * @note Both matrices must be affine.
323 */
324 Matrix4 concatenateAffine(const Matrix4 &other) const
325 {
326 return Matrix4(
327 m[0][0] * other.m[0][0] + m[0][1] * other.m[1][0] + m[0][2] * other.m[2][0],
328 m[0][0] * other.m[0][1] + m[0][1] * other.m[1][1] + m[0][2] * other.m[2][1],
329 m[0][0] * other.m[0][2] + m[0][1] * other.m[1][2] + m[0][2] * other.m[2][2],
330 m[0][0] * other.m[0][3] + m[0][1] * other.m[1][3] + m[0][2] * other.m[2][3] + m[0][3],
331
332 m[1][0] * other.m[0][0] + m[1][1] * other.m[1][0] + m[1][2] * other.m[2][0],
333 m[1][0] * other.m[0][1] + m[1][1] * other.m[1][1] + m[1][2] * other.m[2][1],
334 m[1][0] * other.m[0][2] + m[1][1] * other.m[1][2] + m[1][2] * other.m[2][2],
335 m[1][0] * other.m[0][3] + m[1][1] * other.m[1][3] + m[1][2] * other.m[2][3] + m[1][3],
336
337 m[2][0] * other.m[0][0] + m[2][1] * other.m[1][0] + m[2][2] * other.m[2][0],
338 m[2][0] * other.m[0][1] + m[2][1] * other.m[1][1] + m[2][2] * other.m[2][1],
339 m[2][0] * other.m[0][2] + m[2][1] * other.m[1][2] + m[2][2] * other.m[2][2],
340 m[2][0] * other.m[0][3] + m[2][1] * other.m[1][3] + m[2][2] * other.m[2][3] + m[2][3],
341
342 0, 0, 0, 1);
343 }
344
345 /**
346 * Transform a plane by this matrix.
347 *
348 * @note Matrix must be affine.
349 */
350 Plane multiplyAffine(const Plane& p) const
351 {
352 Vector4 localNormal(p.normal.x, p.normal.y, p.normal.z, 0.0f);
353 Vector4 localPoint = localNormal * p.d;
354 localPoint.w = 1.0f;
355
356 Matrix4 itMat = inverse().transpose();
357 Vector4 worldNormal = itMat.multiplyAffine(localNormal);
358 Vector4 worldPoint = multiplyAffine(localPoint);
359
360 float d = worldNormal.dot(worldPoint);
361
362 return Plane(worldNormal.x, worldNormal.y, worldNormal.z, d);
363 }
364
365 /**
366 * Transform a 3D point by this matrix.
367 *
368 * @note Matrix must be affine, if it is not use multiply() method.
369 */
370 Vector3 multiplyAffine(const Vector3& v) const
371 {
372 return Vector3(
373 m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3],
374 m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3],
375 m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3]);
376 }
377
378 /**
379 * Transform a 4D vector by this matrix.
380 *
381 * @note Matrix must be affine, if it is not use multiply() method.
382 */
383 Vector4 multiplyAffine(const Vector4& v) const
384 {
385 return Vector4(
386 m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] * v.w,
387 m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] * v.w,
388 m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] * v.w,
389 v.w);
390 }
391
392 /** Transform a 3D direction by this matrix. */
393 Vector3 multiplyDirection(const Vector3& v) const
394 {
395 return Vector3(
396 m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z,
397 m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z,
398 m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z);
399 }
400
401 /**
402 * Transform a 3D point by this matrix.
403 *
404 * @note
405 * w component of the vector is assumed to be 1. After transformation all components
406 * are projected back so that w remains 1.
407 * @note
408 * If your matrix doesn't contain projection components use multiplyAffine() method as it is faster.
409 */
410 Vector3 multiply(const Vector3& v) const
411 {
412 Vector3 r(BsZero);
413
414 float fInvW = 1.0f / (m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3]);
415
416 r.x = (m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3]) * fInvW;
417 r.y = (m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3]) * fInvW;
418 r.z = (m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3]) * fInvW;
419
420 return r;
421 }
422
423 /**
424 * Transform a 4D vector by this matrix.
425 *
426 * @note If your matrix doesn't contain projection components use multiplyAffine() method as it is faster.
427 */
428 Vector4 multiply(const Vector4& v) const
429 {
430 return Vector4(
431 m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] * v.w,
432 m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] * v.w,
433 m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] * v.w,
434 m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3] * v.w
435 );
436 }
437
438 /** Creates a view matrix and applies optional reflection. */
439 void makeView(const Vector3& position, const Quaternion& orientation);
440
441 /**
442 * Creates an ortographic projection matrix that scales the part of the view bounded by @p left, @p right,
443 * @p top and @p bottom into [-1, 1] range. If @p far is non-zero the matrix will also transform the depth into
444 * [-1, 1] range, otherwise it will leave it as-is.
445 */
446 void makeProjectionOrtho(float left, float right, float top, float bottom, float near, float far);
447
448 /** Creates a 4x4 transformation matrix that performs translation. */
449 static Matrix4 translation(const Vector3& translation);
450
451 /** Creates a 4x4 transformation matrix that performs scaling. */
452 static Matrix4 scaling(const Vector3& scale);
453
454 /** Creates a 4x4 transformation matrix that performs uniform scaling. */
455 static Matrix4 scaling(float scale);
456
457 /** Creates a 4x4 transformation matrix that performs rotation. */
458 static Matrix4 rotation(const Quaternion& rotation);
459
460 /**
461 * Creates a 4x4 perspective projection matrix.
462 *
463 * @param[in] horzFOV Horizontal field of view.
464 * @param[in] aspect Aspect ratio. Determines the vertical field of view.
465 * @param[in] near Distance to the near plane.
466 * @param[in] far Distance to the far plane.
467 * @param[in] positiveZ If true the matrix will project geometry as if its looking along the positive Z axis.
468 * Otherwise it projects along the negative Z axis (default).
469 */
470 static Matrix4 projectionPerspective(const Degree& horzFOV, float aspect, float near, float far,
471 bool positiveZ = false);
472
473 /** @copydoc makeProjectionOrtho() */
474 static Matrix4 projectionOrthographic(float left, float right, float top, float bottom, float near, float far);
475
476 /** Creates a view matrix. */
477 static Matrix4 view(const Vector3& position, const Quaternion& orientation);
478
479 /**
480 * Creates a matrix from translation, rotation and scale.
481 *
482 * @note The transformation are applied in scale->rotation->translation order.
483 */
484 static Matrix4 TRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale);
485
486 /**
487 * Creates a matrix from inverse translation, rotation and scale.
488 *
489 * @note This is cheaper than setTRS() and then performing inverse().
490 */
491 static Matrix4 inverseTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale);
492
493 static const Matrix4 ZERO;
494 static const Matrix4 IDENTITY;
495
496 private:
497 float m[4][4];
498 };
499
500 /** @} */
501
502 /** @cond SPECIALIZATIONS */
503 BS_ALLOW_MEMCPY_SERIALIZATION(Matrix4);
504 /** @endcond */
505}
506