Line data Source code
1 : /**
2 : Copyright 2013 BlackBerry Inc.
3 : Copyright (c) 2014-2015 Chukong Technologies
4 : Copyright (c) 2017-2019 Roman Katuntsev <sbkarr@stappler.org>
5 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
6 :
7 : Licensed under the Apache License, Version 2.0 (the "License");
8 : you may not use this file except in compliance with the License.
9 : You may obtain a copy of the License at
10 :
11 : http://www.apache.org/licenses/LICENSE-2.0
12 :
13 : Unless required by applicable law or agreed to in writing, software
14 : distributed under the License is distributed on an "AS IS" BASIS,
15 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : See the License for the specific language governing permissions and
17 : limitations under the License.
18 :
19 : Original file from GamePlay3D: http://gameplay3d.org
20 :
21 : This file was modified to fit the cocos2d-x project
22 : This file was modified for stappler project
23 : */
24 :
25 : #ifndef CORE_GEOM_SPMAT4_H_
26 : #define CORE_GEOM_SPMAT4_H_
27 :
28 : #include "SPVec2.h"
29 : #include "SPVec3.h"
30 : #include "SPVec4.h"
31 :
32 : namespace STAPPLER_VERSIONIZED stappler::geom {
33 :
34 : /**
35 : * Defines a 4 x 4 floating point matrix representing a 3D transformation.
36 : *
37 : * Vectors are treated as columns, resulting in a matrix that is represented as follows,
38 : * where x, y and z are the translation components of the matrix:
39 : *
40 : * 1 0 0 x
41 : * 0 1 0 y
42 : * 0 0 1 z
43 : * 0 0 0 1
44 : *
45 : * This matrix class is directly compatible with OpenGL since its elements are
46 : * laid out in memory exactly as they are expected by OpenGL.
47 : * The matrix uses column-major format such that array indices increase down column first.
48 : * Since matrix multiplication is not commutative, multiplication must be done in the
49 : * correct order when combining transformations. Suppose we have a translation
50 : * matrix T and a rotation matrix R. To first rotate an object around the origin
51 : * and then translate it, you would multiply the two matrices as TR.
52 : *
53 : * Likewise, to first translate the object and then rotate it, you would do RT.
54 : * So generally, matrices must be multiplied in the reverse order in which you
55 : * want the transformations to take place (this also applies to
56 : * the scale, rotate, and translate methods below; these methods are convenience
57 : * methods for post-multiplying by a matrix representing a scale, rotation, or translation).
58 : *
59 : * In the case of repeated local transformations (i.e. rotate around the Z-axis by 0.76 radians,
60 : * then translate by 2.1 along the X-axis, then ...), it is better to use the Transform class
61 : * (which is optimized for that kind of usage).
62 : *
63 : * @see Transform
64 : */
65 : class alignas(16) Mat4 {
66 : public:
67 : static void createLookAt(const Vec3 &eyePosition, const Vec3 &targetPosition, const Vec3 &up, Mat4 *dst);
68 :
69 : static void createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ,
70 : float targetCenterX, float targetCenterY, float targetCenterZ,
71 : float upX, float upY, float upZ, Mat4 *dst);
72 :
73 : /** Builds a perspective projection matrix based on a field of view and returns by value.
74 : *
75 : * Projection space refers to the space after applying projection transformation from view space.
76 : * After the projection transformation, visible content has x- and y-coordinates ranging from -1 to 1,
77 : * and a z-coordinate ranging from 0 to 1. To obtain the viewable area (in world space) of a scene,
78 : * create a BoundingFrustum and pass the combined view and projection matrix to the constructor. */
79 : static void createPerspective(float fieldOfView, float aspectRatio, float zNearPlane, float zFarPlane, Mat4 *dst);
80 :
81 : /** Creates an orthographic projection matrix. */
82 : static void createOrthographic(float width, float height, float zNearPlane, float zFarPlane, Mat4 *dst);
83 :
84 : /** Creates an orthographic projection matrix.
85 : *
86 : * Projection space refers to the space after applying
87 : * projection transformation from view space. After the
88 : * projection transformation, visible content has
89 : * x and y coordinates ranging from -1 to 1, and z coordinates
90 : * ranging from 0 to 1.
91 : *
92 : * Unlike perspective projection, in orthographic projection
93 : * there is no perspective foreshortening.
94 : *
95 : * The viewable area of this orthographic projection extends
96 : * from left to right on the x-axis, bottom to top on the y-axis,
97 : * and zNearPlane to zFarPlane on the z-axis. These values are
98 : * relative to the position and x, y, and z-axes of the view.
99 : * To obtain the viewable area (in world space) of a scene,
100 : * create a BoundingFrustum and pass the combined view and
101 : * projection matrix to the constructor. */
102 : static void createOrthographicOffCenter(float left, float right, float bottom, float top,
103 : float zNearPlane, float zFarPlane, Mat4 *dst);
104 :
105 : /** Creates a spherical billboard that rotates around a specified object position.
106 : *
107 : * This method computes the facing direction of the billboard from the object position
108 : * and camera position. When the object and camera positions are too close, the matrix
109 : * will not be accurate. To avoid this problem, this method defaults to the identity
110 : * rotation if the positions are too close. (See the other overload of createBillboard
111 : * for an alternative approach). */
112 : static void createBillboard(const Vec3 &objectPosition, const Vec3 &cameraPosition,
113 : const Vec3 &cameraUpVector, Mat4 *dst);
114 :
115 : /** Creates a spherical billboard that rotates around a specified object position with
116 : * provision for a safe default orientation.
117 : *
118 : * This method computes the facing direction of the billboard from the object position
119 : * and camera position. When the object and camera positions are too close, the matrix
120 : * will not be accurate. To avoid this problem, this method uses the specified camera
121 : * forward vector if the positions are too close. (See the other overload of createBillboard
122 : * for an alternative approach). */
123 : static void createBillboard(const Vec3 &objectPosition, const Vec3 &cameraPosition,
124 : const Vec3 &cameraUpVector, const Vec3 &cameraForwardVector, Mat4 *dst);
125 :
126 : static void createScale(const Vec3 &scale, Mat4 *dst);
127 : static void createScale(float xScale, float yScale, float zScale, Mat4 *dst);
128 : static void createRotation(const Quaternion &quat, Mat4 *dst);
129 : static void createRotation(const Vec3 &axis, float angle, Mat4 *dst);
130 : static void createRotationX(float angle, Mat4 *dst);
131 : static void createRotationY(float angle, Mat4 *dst);
132 : static void createRotationZ(float angle, Mat4 *dst);
133 : static void createTranslation(const Vec3 &translation, Mat4 *dst);
134 : static void createTranslation(float xTranslation, float yTranslation, float zTranslation, Mat4 *dst);
135 :
136 : static void add(const Mat4 &m1, const Mat4 &m2, Mat4 *dst) {
137 : simd::addMat4(m1.m, m2.m, dst->m);
138 : }
139 :
140 82100 : static void multiply(const Mat4 &mat, float scalar, Mat4 *dst) {
141 82100 : simd::multiplyMat4Scalar(mat.m, scalar, dst->m);
142 82100 : }
143 :
144 2697115 : static void multiply(const Mat4 &m1, const Mat4 &m2, Mat4 *dst) {
145 2697115 : simd::multiplyMat4(m1.m, m2.m, dst->m);
146 2697348 : }
147 :
148 : static void subtract(const Mat4& m1, const Mat4& m2, Mat4* dst) {
149 : simd::subtractMat4(m1.m, m2.m, dst->m);
150 : }
151 :
152 25680604 : float m[16];
153 :
154 1652592 : constexpr Mat4() { *this = IDENTITY; }
155 :
156 60373 : constexpr Mat4(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
157 : float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
158 60373 : : m { m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44 } { }
159 :
160 125 : constexpr Mat4(float a, float b, float c, float d, float e, float f)
161 125 : : m { a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, e, f, 0, 1 } { }
162 :
163 : constexpr Mat4(const Mat4& copy) = default;
164 :
165 : void add(float scalar) { add(scalar, this); }
166 : void add(float scalar, Mat4 *dst) { simd::addMat4Scalar(m, scalar, dst->m); }
167 : void add(const Mat4 &mat) { add(*this, mat, this); }
168 :
169 : bool decompose(Vec3 *scale, Quaternion *rotation, Vec3 *translation) const;
170 : bool decompose(float *scale, float *rotation, float *translation) const;
171 :
172 : float determinant() const;
173 :
174 : void getScale(Vec3 *scale) const;
175 : bool getRotation(Quaternion *rotation) const;
176 : void getTranslation(Vec3 *translation) const;
177 :
178 : void getScale(float *scale) const;
179 : bool getRotation(float *rotation) const;
180 : void getTranslation(float *translation) const;
181 :
182 : void getUpVector(Vec3 *dst) const;
183 : void getDownVector(Vec3 *dst) const;
184 : void getLeftVector(Vec3 *dst) const;
185 : void getRightVector(Vec3 *dst) const;
186 : void getForwardVector(Vec3 *dst) const;
187 : void getBackVector(Vec3 *dst) const;
188 :
189 : bool inverse();
190 : void negate() { simd::negateMat4(m, m); }
191 : void transpose() { simd::transposeMat4(m, m); }
192 :
193 16479 : Mat4 getInversed() const { Mat4 mat(*this); mat.inverse(); return mat; }
194 : Mat4 getNegated() const { Mat4 mat(*this); mat.negate(); return mat; }
195 : Mat4 getTransposed() const { Mat4 mat(*this); mat.transpose(); return mat; }
196 :
197 1931664 : bool isIdentity() const { return *this == IDENTITY; }
198 :
199 : void multiply(float scalar) { multiply(scalar, this); }
200 : void multiply(float scalar, Mat4 *dst) const { multiply(*this, scalar, dst); }
201 1235471 : void multiply(const Mat4 &mat) { multiply(*this, mat, this); }
202 :
203 : void rotate(const Quaternion &q);
204 : void rotate(const Quaternion &q, Mat4 *dst) const;
205 : void rotate(const Vec3 &axis, float angle);
206 : void rotate(const Vec3 &axis, float angle, Mat4 *dst) const;
207 : void rotateX(float angle);
208 : void rotateX(float angle, Mat4 *dst) const;
209 : void rotateY(float angle);
210 : void rotateY(float angle, Mat4 *dst) const;
211 : void rotateZ(float angle);
212 : void rotateZ(float angle, Mat4 *dst) const;
213 :
214 : void scale(float value);
215 : void scale(float value, Mat4 *dst) const;
216 : void scale(float xScale, float yScale, float zScale);
217 : void scale(float xScale, float yScale, float zScale, Mat4 *dst) const;
218 : void scale(const Vec3 &s);
219 : void scale(const Vec3 &s, Mat4 *dst) const;
220 : void subtract(const Mat4 &mat) { subtract(*this, mat, this); }
221 :
222 6229 : Vec2 transformPoint(const Vec2 &point) const { // TODO: implement simd version of 2D-transform
223 6229 : Vec4 ret;
224 6229 : transformVector(point.x, point.y, 1.0f, 1.0f, &ret);
225 6229 : return Vec2(ret.x, ret.y);
226 : }
227 100 : void transformPoint(Vec2 *point) const { // TODO: implement simd version of 2D-transform
228 100 : Vec4 ret;
229 100 : transformVector(point->x, point->y, 1.0f, 1.0f, &ret);
230 100 : *point = Vec2(ret.x, ret.y);
231 100 : }
232 : void transformVector(Vec4 *vector) const {
233 : simd::transformVec4(m, &vector->x, &vector->x);
234 : }
235 6269 : void transformVector(float x, float y, float z, float w, Vec4 *dst) const {
236 6269 : simd::transformVec4Components(m, x, y, z, w, &dst->x);
237 6269 : }
238 740 : void transformVector(float x, float y, float z, float w, float *dst) const {
239 740 : simd::transformVec4Components(m, x, y, z, w, dst);
240 740 : }
241 : void transformVector(const Vec4 &vector, Vec4 *dst) const {
242 : simd::transformVec4(m, &vector.x, &dst->x);
243 : }
244 : void transformVector(const Vec4 &vector, Vec3 *dst) const {
245 : simd::transformVec4(m, &vector.x, &dst->x);
246 : }
247 :
248 : void translate(float x, float y, float z);
249 : void translate(float x, float y, float z, Mat4 *dst) const;
250 : void translate(const Vec3 &t);
251 :
252 : void translate(const Vec3 &t, Mat4 *dst) const;
253 :
254 : const Mat4 operator+(const Mat4 &mat) const {
255 : Mat4 result(*this);
256 : result.add(mat);
257 : return result;
258 : }
259 :
260 : Mat4& operator+=(const Mat4 &mat) {
261 : add(mat);
262 : return *this;
263 : }
264 :
265 : const Mat4 operator-(const Mat4 &mat) const {
266 : Mat4 result(*this);
267 : result.subtract(mat);
268 : return result;
269 : }
270 :
271 : Mat4& operator-=(const Mat4 &mat) {
272 : subtract(mat);
273 : return *this;
274 : }
275 :
276 : const Mat4 operator-() const {
277 : Mat4 mat(*this);
278 : mat.negate();
279 : return mat;
280 : }
281 :
282 751974 : const Mat4 operator*(const Mat4 &mat) const {
283 751974 : Mat4 result(*this);
284 751974 : result.multiply(mat);
285 751983 : return result;
286 : }
287 :
288 584163 : Mat4& operator*=(const Mat4 &mat) {
289 584163 : multiply(mat);
290 584112 : return *this;
291 : }
292 :
293 27088905 : inline bool operator==(const Mat4&) const = default;
294 : inline bool operator!=(const Mat4&) const = default;
295 :
296 : static const Mat4 ZERO;
297 : static const Mat4 IDENTITY;
298 : static const Mat4 INVALID;
299 :
300 : static const Mat4 ROTATION_Z_90;
301 : static const Mat4 ROTATION_Z_180;
302 : static const Mat4 ROTATION_Z_270;
303 : };
304 :
305 : #ifndef __LCC__
306 :
307 : constexpr const Mat4 Mat4::IDENTITY = Mat4(
308 : 1.0f, 0.0f, 0.0f, 0.0f,
309 : 0.0f, 1.0f, 0.0f, 0.0f,
310 : 0.0f, 0.0f, 1.0f, 0.0f,
311 : 0.0f, 0.0f, 0.0f, 1.0f);
312 :
313 : constexpr const Mat4 Mat4::ZERO = Mat4(
314 : 0.0f, 0.0f, 0.0f, 0.0f,
315 : 0.0f, 0.0f, 0.0f, 0.0f,
316 : 0.0f, 0.0f, 0.0f, 0.0f,
317 : 0.0f, 0.0f, 0.0f, 0.0f );
318 :
319 : constexpr const Mat4 Mat4::INVALID = Mat4(
320 : stappler::nan(), stappler::nan(), stappler::nan(), stappler::nan(),
321 : stappler::nan(), stappler::nan(), stappler::nan(), stappler::nan(),
322 : stappler::nan(), stappler::nan(), stappler::nan(), stappler::nan(),
323 : stappler::nan(), stappler::nan(), stappler::nan(), stappler::nan() );
324 :
325 : constexpr const Mat4 Mat4::ROTATION_Z_90 = Mat4(
326 : 0.0f, -1.0f, 0.0f, 0.0f,
327 : 1.0f, 0.0f, 0.0f, 0.0f,
328 : 0.0f, 0.0f, 1.0f, 0.0f,
329 : 0.0f, 0.0f, 0.0f, 1.0f );
330 :
331 : constexpr const Mat4 Mat4::ROTATION_Z_180 = Mat4(
332 : -1.0f, 0.0f, 0.0f, 0.0f,
333 : 0.0f, -1.0f, 0.0f, 0.0f,
334 : 0.0f, 0.0f, 1.0f, 0.0f,
335 : 0.0f, 0.0f, 0.0f, 1.0f );
336 :
337 : constexpr const Mat4 Mat4::ROTATION_Z_270 = Mat4(
338 : 0.0f, 1.0f, 0.0f, 0.0f,
339 : -1.0f, 0.0f, 0.0f, 0.0f,
340 : 0.0f, 0.0f, 1.0f, 0.0f,
341 : 0.0f, 0.0f, 0.0f, 1.0f );
342 :
343 : #endif
344 :
345 : inline Vec4& operator*=(Vec4& v, const Mat4& m) {
346 : m.transformVector(&v);
347 : return v;
348 : }
349 :
350 : inline const Vec4 operator*(const Mat4& m, const Vec4& v) {
351 : Vec4 x;
352 : m.transformVector(v, &x);
353 : return x;
354 : }
355 :
356 : inline const Vec3 operator*(const Mat4& m, const Vec3& v) {
357 : struct alignas(16) {
358 : Vec3 vec;
359 : float w;
360 : } s;
361 : m.transformVector(v.x, v.y, v.z, 1.0f, &s.vec.x);
362 : return s.vec;
363 : }
364 :
365 740 : inline const Vec2 operator*(const Mat4& m, const Vec2& v) {
366 : struct alignas(16) {
367 : Vec2 vec;
368 : float z;
369 : float w;
370 740 : } s;
371 740 : m.transformVector(v.x, v.y, 1.0f, 1.0f, &s.vec.x);
372 740 : return s.vec;
373 : }
374 :
375 : inline std::basic_ostream<char> &
376 : operator << (std::basic_ostream<char> & os, const Mat4 & m) {
377 : os << "{\n\t( " << m.m[0] << ", " << m.m[4] << ", " << m.m[8] << ", " << m.m[12] << ")\n"
378 : << "\t( " << m.m[1] << ", " << m.m[5] << ", " << m.m[9] << ", " << m.m[13] << ")\n"
379 : << "\t( " << m.m[2] << ", " << m.m[6] << ", " << m.m[10] << ", " << m.m[14] << ")\n"
380 : << "\t( " << m.m[3] << ", " << m.m[7] << ", " << m.m[11] << ", " << m.m[15] << ")\n"
381 : << "}";
382 : return os;
383 : }
384 :
385 : }
386 :
387 : #endif /* CORE_GEOM_SPMAT4_H_ */
|