|
| 1 | +///////////////////////////////////////////////////////////////////////////////////////////////// |
| 2 | +// |
| 3 | +// Tencent is pleased to support the open source community by making tgfx available. |
| 4 | +// |
| 5 | +// Copyright (C) 2025 Tencent. All rights reserved. |
| 6 | +// |
| 7 | +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except |
| 8 | +// in compliance with the License. You may obtain a copy of the License at |
| 9 | +// |
| 10 | +// https://opensource.org/licenses/BSD-3-Clause |
| 11 | +// |
| 12 | +// unless required by applicable law or agreed to in writing, software distributed under the |
| 13 | +// license is distributed on an "as is" basis, without warranties or conditions of any kind, |
| 14 | +// either express or implied. see the license for the specific language governing permissions |
| 15 | +// and limitations under the license. |
| 16 | +// |
| 17 | +///////////////////////////////////////////////////////////////////////////////////////////////// |
| 18 | + |
| 19 | +#pragma once |
| 20 | + |
| 21 | +#include <cstring> |
| 22 | +#include "Vec.h" |
| 23 | +#include "tgfx/core/Rect.h" |
| 24 | + |
| 25 | +namespace tgfx { |
| 26 | + |
| 27 | +/** |
| 28 | + * Matrix3D holds a 4x4 matrix for transforming coordinates in 3D space. This allows mapping points |
| 29 | + * and vectors with translation, scaling, skewing, rotation, and perspective. These types of |
| 30 | + * transformations are collectively known as projective transformations. Projective transformations |
| 31 | + * preserve the straightness of lines but do not preserve parallelism, so parallel lines may not |
| 32 | + * remain parallel after transformation. |
| 33 | + * The elements of Matrix3D are in column-major order. |
| 34 | + * Matrix3D does not have a default constructor, so it must be explicitly initialized. |
| 35 | + */ |
| 36 | +class Matrix3D { |
| 37 | + public: |
| 38 | + /** |
| 39 | + * Creates a Matrix3D set to the identity matrix. The created Matrix3D is: |
| 40 | + * |
| 41 | + * | 1 0 0 0 | |
| 42 | + * | 0 1 0 0 | |
| 43 | + * | 0 0 1 0 | |
| 44 | + * | 0 0 0 1 | |
| 45 | + */ |
| 46 | + constexpr Matrix3D() : Matrix3D(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) { |
| 47 | + } |
| 48 | + |
| 49 | + /** |
| 50 | + * Returns the matrix value at the given row and column. |
| 51 | + * @param r Row index, valid range 0..3. |
| 52 | + * @param c Column index, valid range 0..3. |
| 53 | + */ |
| 54 | + float getRowColumn(int r, int c) const { |
| 55 | + return values[c * 4 + r]; |
| 56 | + } |
| 57 | + |
| 58 | + /** |
| 59 | + * Sets the matrix value at the given row and column. |
| 60 | + * @param r Row index, valid range 0..3. |
| 61 | + * @param c Column index, valid range 0..3. |
| 62 | + */ |
| 63 | + void setRowColumn(int r, int c, float value) { |
| 64 | + values[c * 4 + r] = value; |
| 65 | + } |
| 66 | + |
| 67 | + /** |
| 68 | + * Returns a reference to a constant identity Matrix3D. The returned Matrix3D is: |
| 69 | + * |
| 70 | + * | 1 0 0 0 | |
| 71 | + * | 0 1 0 0 | |
| 72 | + * | 0 0 1 0 | |
| 73 | + * | 0 0 0 1 | |
| 74 | + */ |
| 75 | + static const Matrix3D& I(); |
| 76 | + |
| 77 | + /** |
| 78 | + * Creates a Matrix3D that scales by (sx, sy, sz). The returned matrix is: |
| 79 | + * |
| 80 | + * | sx 0 0 0 | |
| 81 | + * | 0 sy 0 0 | |
| 82 | + * | 0 0 sz 0 | |
| 83 | + * | 0 0 0 1 | |
| 84 | + */ |
| 85 | + static Matrix3D MakeScale(float sx, float sy, float sz) { |
| 86 | + return {sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1}; |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Creates a Matrix3D that rotates by the given angle (in degrees) around the specified axis. |
| 91 | + * The returned matrix is: |
| 92 | + * |
| 93 | + * | t*x*x + c t*x*y + s*z t*x*z - s*y 0 | |
| 94 | + * | t*x*y - s*z t*y*y + c t*y*z + s*x 0 | |
| 95 | + * | t*x*z + s*y t*y*z - s*x t*z*z + c 0 | |
| 96 | + * | 0 0 0 1 | |
| 97 | + * |
| 98 | + * where: |
| 99 | + * x, y, z = normalized components of axis |
| 100 | + * c = cos(degrees) |
| 101 | + * s = sin(degrees) |
| 102 | + * t = 1 - c |
| 103 | + * @param axis The axis to rotate about. |
| 104 | + * @param degrees The angle of rotation in degrees. |
| 105 | + */ |
| 106 | + static Matrix3D MakeRotate(const Vec3& axis, float degrees) { |
| 107 | + Matrix3D m; |
| 108 | + m.setRotate(axis, degrees); |
| 109 | + return m; |
| 110 | + } |
| 111 | + |
| 112 | + /** |
| 113 | + * Creates a Matrix3D that translates by (tx, ty, tz). The returned matrix is: |
| 114 | + * |
| 115 | + * | 1 0 0 tx | |
| 116 | + * | 0 1 0 ty | |
| 117 | + * | 0 0 1 tz | |
| 118 | + * | 0 0 0 1 | |
| 119 | + */ |
| 120 | + static Matrix3D MakeTranslate(float tx, float ty, float tz) { |
| 121 | + return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1}; |
| 122 | + } |
| 123 | + |
| 124 | + /** |
| 125 | + * Creates a view matrix for a camera. This is commonly used to transform world coordinates to |
| 126 | + * camera (view) coordinates in 3D graphics. |
| 127 | + * @param eye The position of the camera. |
| 128 | + * @param center The point the camera is looking at. |
| 129 | + * @param up The up direction for the camera. |
| 130 | + */ |
| 131 | + static Matrix3D LookAt(const Vec3& eye, const Vec3& center, const Vec3& up); |
| 132 | + |
| 133 | + /** |
| 134 | + * Creates a standard perspective projection matrix. This matrix maps 3D coordinates into |
| 135 | + * clip coordinates for perspective rendering. |
| 136 | + * The standard projection model is established by defining the camera position, orientation, |
| 137 | + * field of view, and near/far planes. Points inside the view frustum are projected onto the near |
| 138 | + * plane. |
| 139 | + * @param fovyDegrees Field of view angle in degrees (vertical). |
| 140 | + * @param aspect Aspect ratio (width / height). |
| 141 | + * @param nearZ Distance to the near clipping plane. |
| 142 | + * @param farZ Distance to the far clipping plane. |
| 143 | + */ |
| 144 | + static Matrix3D Perspective(float fovyDegrees, float aspect, float nearZ, float farZ); |
| 145 | + |
| 146 | + /** |
| 147 | + * Maps a rectangle using this matrix. |
| 148 | + * If the matrix contains a perspective transformation, each corner of the rectangle is mapped as a |
| 149 | + * 4D point (x, y, 0, 1), and the resulting rectangle is computed from the projected points |
| 150 | + * (after perspective division). |
| 151 | + */ |
| 152 | + Rect mapRect(const Rect& src) const; |
| 153 | + |
| 154 | + friend Matrix3D operator*(const Matrix3D& a, const Matrix3D& b) { |
| 155 | + Matrix3D result; |
| 156 | + result.setConcat(a, b); |
| 157 | + return result; |
| 158 | + } |
| 159 | + |
| 160 | + private: |
| 161 | + constexpr Matrix3D(float m00, float m01, float m02, float m03, float m10, float m11, float m12, |
| 162 | + float m13, float m20, float m21, float m22, float m23, float m30, float m31, |
| 163 | + float m32, float m33) |
| 164 | + : values{m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33} { |
| 165 | + } |
| 166 | + |
| 167 | + /** |
| 168 | + * Copies the matrix values into a 16-element array in column-major order. |
| 169 | + */ |
| 170 | + void getColMajor(float buffer[16]) const { |
| 171 | + memcpy(buffer, values, sizeof(values)); |
| 172 | + } |
| 173 | + |
| 174 | + /** |
| 175 | + * Copies the matrix values into a 16-element array in row-major order. |
| 176 | + */ |
| 177 | + void getRowMajor(float buffer[16]) const; |
| 178 | + |
| 179 | + /** |
| 180 | + * Concatenates two matrices and stores the result in this matrix. M' = a * b. |
| 181 | + */ |
| 182 | + void setConcat(const Matrix3D& a, const Matrix3D& b); |
| 183 | + |
| 184 | + /** |
| 185 | + * Concatenates the given matrix with this matrix, and stores the result in this matrix. M' = m * M. |
| 186 | + */ |
| 187 | + void preConcat(const Matrix3D& m); |
| 188 | + |
| 189 | + /** |
| 190 | + * Concatenates this matrix with the given matrix, and stores the result in this matrix. M' = M * m. |
| 191 | + */ |
| 192 | + void postConcat(const Matrix3D& m); |
| 193 | + |
| 194 | + /** |
| 195 | + * Pre-concatenates a scale to this matrix. M' = S * M. |
| 196 | + */ |
| 197 | + void preScale(float sx, float sy, float sz); |
| 198 | + |
| 199 | + /** |
| 200 | + * Post-concatenates a scale to this matrix. M' = M * S. |
| 201 | + */ |
| 202 | + void postScale(float sx, float sy, float sz); |
| 203 | + |
| 204 | + /** |
| 205 | + * Pre-concatenates a translation to this matrix. M' = T * M. |
| 206 | + */ |
| 207 | + void preTranslate(float tx, float ty, float tz); |
| 208 | + |
| 209 | + /** |
| 210 | + * Post-concatenates a translation to this matrix. M' = M * T. |
| 211 | + */ |
| 212 | + void postTranslate(float tx, float ty, float tz); |
| 213 | + |
| 214 | + /** |
| 215 | + * Pre-concatenates a rotation to this matrix. M' = R * M. |
| 216 | + */ |
| 217 | + void preRotate(const Vec3& axis, float degrees); |
| 218 | + |
| 219 | + /** |
| 220 | + * Post-concatenates a rotation to this matrix. M' = M * R. |
| 221 | + */ |
| 222 | + void postRotate(const Vec3& axis, float degrees); |
| 223 | + |
| 224 | + /** |
| 225 | + * Calculates the inverse of the current matrix and stores the result in the Matrix3D object |
| 226 | + * pointed to by inverse. |
| 227 | + * @param inverse Pointer to the Matrix3D object used to store the inverse matrix. Must not be |
| 228 | + * nullptr. If the current matrix is not invertible, inverse will not be modified. |
| 229 | + * @return Returns true if the inverse matrix exists; otherwise, returns false. |
| 230 | + */ |
| 231 | + bool invert(Matrix3D* inverse) const; |
| 232 | + |
| 233 | + /** |
| 234 | + * Returns the transpose of the current matrix. |
| 235 | + */ |
| 236 | + Matrix3D transpose() const; |
| 237 | + |
| 238 | + /** |
| 239 | + * Maps a 4D point (x, y, z, w) using this matrix. |
| 240 | + * If the current matrix contains a perspective transformation, the returned Vec4 is not |
| 241 | + * perspective-divided; i.e., the w component of the result may not be 1. |
| 242 | + */ |
| 243 | + Vec4 mapPoint(float x, float y, float z, float w) const; |
| 244 | + |
| 245 | + Vec4 getCol(int i) const { |
| 246 | + Vec4 v; |
| 247 | + memcpy(&v, values + i * 4, sizeof(Vec4)); |
| 248 | + return v; |
| 249 | + } |
| 250 | + |
| 251 | + void setAll(float m00, float m01, float m02, float m03, float m10, float m11, float m12, |
| 252 | + float m13, float m20, float m21, float m22, float m23, float m30, float m31, |
| 253 | + float m32, float m33); |
| 254 | + |
| 255 | + void setRow(int i, const Vec4& v) { |
| 256 | + values[i + 0] = v.x; |
| 257 | + values[i + 4] = v.y; |
| 258 | + values[i + 8] = v.z; |
| 259 | + values[i + 12] = v.w; |
| 260 | + } |
| 261 | + |
| 262 | + void setColumn(int i, const Vec4& v) { |
| 263 | + memcpy(&values[i * 4], v.ptr(), sizeof(v)); |
| 264 | + } |
| 265 | + |
| 266 | + void setIdentity() { |
| 267 | + *this = Matrix3D(); |
| 268 | + } |
| 269 | + |
| 270 | + void setRotate(const Vec3& axis, float degrees); |
| 271 | + |
| 272 | + void setRotateUnit(const Vec3& axis, float degrees); |
| 273 | + |
| 274 | + void setRotateUnitSinCos(const Vec3& axis, float sinV, float cosV); |
| 275 | + |
| 276 | + bool hasPerspective() const { |
| 277 | + return (values[3] != 0 || values[7] != 0 || values[11] != 0 || values[15] != 1); |
| 278 | + } |
| 279 | + |
| 280 | + bool operator==(const Matrix3D& other) const; |
| 281 | + |
| 282 | + bool operator!=(const Matrix3D& other) const { |
| 283 | + return !(other == *this); |
| 284 | + } |
| 285 | + |
| 286 | + Vec4 operator*(const Vec4& v) const { |
| 287 | + return this->mapPoint(v.x, v.y, v.z, v.w); |
| 288 | + } |
| 289 | + |
| 290 | + float values[16] = {.0f}; |
| 291 | +}; |
| 292 | + |
| 293 | +} // namespace tgfx |
0 commit comments