| 1 | /* |
| 2 | * Copyright 2020 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/core/SkM44.h" |
| 9 | #include "include/core/SkMatrix.h" |
| 10 | #include "include/private/SkVx.h" |
| 11 | |
| 12 | typedef skvx::Vec<4, float> sk4f; |
| 13 | |
| 14 | bool SkM44::operator==(const SkM44& other) const { |
| 15 | if (this == &other) { |
| 16 | return true; |
| 17 | } |
| 18 | |
| 19 | sk4f a0 = sk4f::Load(fMat + 0); |
| 20 | sk4f a1 = sk4f::Load(fMat + 4); |
| 21 | sk4f a2 = sk4f::Load(fMat + 8); |
| 22 | sk4f a3 = sk4f::Load(fMat + 12); |
| 23 | |
| 24 | sk4f b0 = sk4f::Load(other.fMat + 0); |
| 25 | sk4f b1 = sk4f::Load(other.fMat + 4); |
| 26 | sk4f b2 = sk4f::Load(other.fMat + 8); |
| 27 | sk4f b3 = sk4f::Load(other.fMat + 12); |
| 28 | |
| 29 | auto eq = (a0 == b0) & (a1 == b1) & (a2 == b2) & (a3 == b3); |
| 30 | return (eq[0] & eq[1] & eq[2] & eq[3]) == ~0; |
| 31 | } |
| 32 | |
| 33 | static void transpose_arrays(SkScalar dst[], const SkScalar src[]) { |
| 34 | dst[0] = src[0]; dst[1] = src[4]; dst[2] = src[8]; dst[3] = src[12]; |
| 35 | dst[4] = src[1]; dst[5] = src[5]; dst[6] = src[9]; dst[7] = src[13]; |
| 36 | dst[8] = src[2]; dst[9] = src[6]; dst[10] = src[10]; dst[11] = src[14]; |
| 37 | dst[12] = src[3]; dst[13] = src[7]; dst[14] = src[11]; dst[15] = src[15]; |
| 38 | } |
| 39 | |
| 40 | void SkM44::getRowMajor(SkScalar v[]) const { |
| 41 | transpose_arrays(v, fMat); |
| 42 | } |
| 43 | |
| 44 | SkM44& SkM44::setConcat(const SkM44& a, const SkM44& b) { |
| 45 | sk4f c0 = sk4f::Load(a.fMat + 0); |
| 46 | sk4f c1 = sk4f::Load(a.fMat + 4); |
| 47 | sk4f c2 = sk4f::Load(a.fMat + 8); |
| 48 | sk4f c3 = sk4f::Load(a.fMat + 12); |
| 49 | |
| 50 | auto compute = [&](sk4f r) { |
| 51 | return skvx::mad(c0, r[0], skvx::mad(c1, r[1], skvx::mad(c2, r[2], c3 * r[3]))); |
| 52 | }; |
| 53 | |
| 54 | sk4f m0 = compute(sk4f::Load(b.fMat + 0)); |
| 55 | sk4f m1 = compute(sk4f::Load(b.fMat + 4)); |
| 56 | sk4f m2 = compute(sk4f::Load(b.fMat + 8)); |
| 57 | sk4f m3 = compute(sk4f::Load(b.fMat + 12)); |
| 58 | |
| 59 | m0.store(fMat + 0); |
| 60 | m1.store(fMat + 4); |
| 61 | m2.store(fMat + 8); |
| 62 | m3.store(fMat + 12); |
| 63 | return *this; |
| 64 | } |
| 65 | |
| 66 | SkM44& SkM44::preConcat(const SkMatrix& b) { |
| 67 | sk4f c0 = sk4f::Load(fMat + 0); |
| 68 | sk4f c1 = sk4f::Load(fMat + 4); |
| 69 | sk4f c3 = sk4f::Load(fMat + 12); |
| 70 | |
| 71 | auto compute = [&](float r0, float r1, float r3) { |
| 72 | return skvx::mad(c0, r0, skvx::mad(c1, r1, c3 * r3)); |
| 73 | }; |
| 74 | |
| 75 | sk4f m0 = compute(b[0], b[3], b[6]); |
| 76 | sk4f m1 = compute(b[1], b[4], b[7]); |
| 77 | sk4f m3 = compute(b[2], b[5], b[8]); |
| 78 | |
| 79 | m0.store(fMat + 0); |
| 80 | m1.store(fMat + 4); |
| 81 | m3.store(fMat + 12); |
| 82 | return *this; |
| 83 | } |
| 84 | |
| 85 | SkM44& SkM44::preTranslate(SkScalar x, SkScalar y) { |
| 86 | sk4f c0 = sk4f::Load(fMat + 0); |
| 87 | sk4f c1 = sk4f::Load(fMat + 4); |
| 88 | sk4f c3 = sk4f::Load(fMat + 12); |
| 89 | |
| 90 | // only need to update the last column |
| 91 | skvx::mad(c0, x, skvx::mad(c1, y, c3)).store(fMat + 12); |
| 92 | return *this; |
| 93 | } |
| 94 | |
| 95 | SkM44& SkM44::preScale(SkScalar x, SkScalar y) { |
| 96 | sk4f c0 = sk4f::Load(fMat + 0); |
| 97 | sk4f c1 = sk4f::Load(fMat + 4); |
| 98 | |
| 99 | (c0 * x).store(fMat + 0); |
| 100 | (c1 * y).store(fMat + 4); |
| 101 | return *this; |
| 102 | } |
| 103 | |
| 104 | SkV4 SkM44::map(float x, float y, float z, float w) const { |
| 105 | sk4f c0 = sk4f::Load(fMat + 0); |
| 106 | sk4f c1 = sk4f::Load(fMat + 4); |
| 107 | sk4f c2 = sk4f::Load(fMat + 8); |
| 108 | sk4f c3 = sk4f::Load(fMat + 12); |
| 109 | |
| 110 | SkV4 v; |
| 111 | skvx::mad(c0, x, skvx::mad(c1, y, skvx::mad(c2, z, c3 * w))).store(&v.x); |
| 112 | return v; |
| 113 | } |
| 114 | |
| 115 | /////////////////////////////////////////////////////////////////////////////// |
| 116 | |
| 117 | /** We always perform the calculation in doubles, to avoid prematurely losing |
| 118 | precision along the way. This relies on the compiler automatically |
| 119 | promoting our SkScalar values to double (if needed). |
| 120 | */ |
| 121 | double SkM44::determinant() const { |
| 122 | double a00 = fMat[0]; |
| 123 | double a01 = fMat[1]; |
| 124 | double a02 = fMat[2]; |
| 125 | double a03 = fMat[3]; |
| 126 | double a10 = fMat[4]; |
| 127 | double a11 = fMat[5]; |
| 128 | double a12 = fMat[6]; |
| 129 | double a13 = fMat[7]; |
| 130 | double a20 = fMat[8]; |
| 131 | double a21 = fMat[9]; |
| 132 | double a22 = fMat[10]; |
| 133 | double a23 = fMat[11]; |
| 134 | double a30 = fMat[12]; |
| 135 | double a31 = fMat[13]; |
| 136 | double a32 = fMat[14]; |
| 137 | double a33 = fMat[15]; |
| 138 | |
| 139 | double b00 = a00 * a11 - a01 * a10; |
| 140 | double b01 = a00 * a12 - a02 * a10; |
| 141 | double b02 = a00 * a13 - a03 * a10; |
| 142 | double b03 = a01 * a12 - a02 * a11; |
| 143 | double b04 = a01 * a13 - a03 * a11; |
| 144 | double b05 = a02 * a13 - a03 * a12; |
| 145 | double b06 = a20 * a31 - a21 * a30; |
| 146 | double b07 = a20 * a32 - a22 * a30; |
| 147 | double b08 = a20 * a33 - a23 * a30; |
| 148 | double b09 = a21 * a32 - a22 * a31; |
| 149 | double b10 = a21 * a33 - a23 * a31; |
| 150 | double b11 = a22 * a33 - a23 * a32; |
| 151 | |
| 152 | // Calculate the determinant |
| 153 | return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; |
| 154 | } |
| 155 | |
| 156 | /////////////////////////////////////////////////////////////////////////////// |
| 157 | |
| 158 | bool SkM44::invert(SkM44* inverse) const { |
| 159 | double a00 = fMat[0]; |
| 160 | double a01 = fMat[1]; |
| 161 | double a02 = fMat[2]; |
| 162 | double a03 = fMat[3]; |
| 163 | double a10 = fMat[4]; |
| 164 | double a11 = fMat[5]; |
| 165 | double a12 = fMat[6]; |
| 166 | double a13 = fMat[7]; |
| 167 | double a20 = fMat[8]; |
| 168 | double a21 = fMat[9]; |
| 169 | double a22 = fMat[10]; |
| 170 | double a23 = fMat[11]; |
| 171 | double a30 = fMat[12]; |
| 172 | double a31 = fMat[13]; |
| 173 | double a32 = fMat[14]; |
| 174 | double a33 = fMat[15]; |
| 175 | |
| 176 | double b00 = a00 * a11 - a01 * a10; |
| 177 | double b01 = a00 * a12 - a02 * a10; |
| 178 | double b02 = a00 * a13 - a03 * a10; |
| 179 | double b03 = a01 * a12 - a02 * a11; |
| 180 | double b04 = a01 * a13 - a03 * a11; |
| 181 | double b05 = a02 * a13 - a03 * a12; |
| 182 | double b06 = a20 * a31 - a21 * a30; |
| 183 | double b07 = a20 * a32 - a22 * a30; |
| 184 | double b08 = a20 * a33 - a23 * a30; |
| 185 | double b09 = a21 * a32 - a22 * a31; |
| 186 | double b10 = a21 * a33 - a23 * a31; |
| 187 | double b11 = a22 * a33 - a23 * a32; |
| 188 | |
| 189 | // Calculate the determinant |
| 190 | double det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; |
| 191 | |
| 192 | double invdet = sk_ieee_double_divide(1.0, det); |
| 193 | // If det is zero, we want to return false. However, we also want to return false |
| 194 | // if 1/det overflows to infinity (i.e. det is denormalized). Both of these are |
| 195 | // handled by checking that 1/det is finite. |
| 196 | if (!SkScalarIsFinite(SkScalar(invdet))) { |
| 197 | return false; |
| 198 | } |
| 199 | |
| 200 | b00 *= invdet; |
| 201 | b01 *= invdet; |
| 202 | b02 *= invdet; |
| 203 | b03 *= invdet; |
| 204 | b04 *= invdet; |
| 205 | b05 *= invdet; |
| 206 | b06 *= invdet; |
| 207 | b07 *= invdet; |
| 208 | b08 *= invdet; |
| 209 | b09 *= invdet; |
| 210 | b10 *= invdet; |
| 211 | b11 *= invdet; |
| 212 | |
| 213 | SkScalar tmp[16] = { |
| 214 | SkDoubleToScalar(a11 * b11 - a12 * b10 + a13 * b09), |
| 215 | SkDoubleToScalar(a02 * b10 - a01 * b11 - a03 * b09), |
| 216 | SkDoubleToScalar(a31 * b05 - a32 * b04 + a33 * b03), |
| 217 | SkDoubleToScalar(a22 * b04 - a21 * b05 - a23 * b03), |
| 218 | SkDoubleToScalar(a12 * b08 - a10 * b11 - a13 * b07), |
| 219 | SkDoubleToScalar(a00 * b11 - a02 * b08 + a03 * b07), |
| 220 | SkDoubleToScalar(a32 * b02 - a30 * b05 - a33 * b01), |
| 221 | SkDoubleToScalar(a20 * b05 - a22 * b02 + a23 * b01), |
| 222 | SkDoubleToScalar(a10 * b10 - a11 * b08 + a13 * b06), |
| 223 | SkDoubleToScalar(a01 * b08 - a00 * b10 - a03 * b06), |
| 224 | SkDoubleToScalar(a30 * b04 - a31 * b02 + a33 * b00), |
| 225 | SkDoubleToScalar(a21 * b02 - a20 * b04 - a23 * b00), |
| 226 | SkDoubleToScalar(a11 * b07 - a10 * b09 - a12 * b06), |
| 227 | SkDoubleToScalar(a00 * b09 - a01 * b07 + a02 * b06), |
| 228 | SkDoubleToScalar(a31 * b01 - a30 * b03 - a32 * b00), |
| 229 | SkDoubleToScalar(a20 * b03 - a21 * b01 + a22 * b00), |
| 230 | }; |
| 231 | if (!SkScalarsAreFinite(tmp, 16)) { |
| 232 | return false; |
| 233 | } |
| 234 | memcpy(inverse->fMat, tmp, sizeof(tmp)); |
| 235 | return true; |
| 236 | } |
| 237 | |
| 238 | SkM44 SkM44::transpose() const { |
| 239 | SkM44 trans(SkM44::kUninitialized_Constructor); |
| 240 | transpose_arrays(trans.fMat, fMat); |
| 241 | return trans; |
| 242 | } |
| 243 | |
| 244 | SkM44& SkM44::setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle) { |
| 245 | // Taken from "Essential Mathematics for Games and Interactive Applications" |
| 246 | // James M. Van Verth and Lars M. Bishop -- third edition |
| 247 | SkScalar x = axis.x; |
| 248 | SkScalar y = axis.y; |
| 249 | SkScalar z = axis.z; |
| 250 | SkScalar c = cosAngle; |
| 251 | SkScalar s = sinAngle; |
| 252 | SkScalar t = 1 - c; |
| 253 | |
| 254 | *this = { t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0, |
| 255 | t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0, |
| 256 | t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0, |
| 257 | 0, 0, 0, 1 }; |
| 258 | return *this; |
| 259 | } |
| 260 | |
| 261 | SkM44& SkM44::setRotate(SkV3 axis, SkScalar radians) { |
| 262 | SkScalar len = axis.length(); |
| 263 | if (len > 0 && SkScalarIsFinite(len)) { |
| 264 | this->setRotateUnit(axis * (SK_Scalar1 / len), radians); |
| 265 | } else { |
| 266 | this->setIdentity(); |
| 267 | } |
| 268 | return *this; |
| 269 | } |
| 270 | |
| 271 | /////////////////////////////////////////////////////////////////////////////// |
| 272 | |
| 273 | void SkM44::dump() const { |
| 274 | static const char* format = "|%g %g %g %g|\n" |
| 275 | "|%g %g %g %g|\n" |
| 276 | "|%g %g %g %g|\n" |
| 277 | "|%g %g %g %g|\n" ; |
| 278 | SkDebugf(format, |
| 279 | fMat[0], fMat[4], fMat[8], fMat[12], |
| 280 | fMat[1], fMat[5], fMat[9], fMat[13], |
| 281 | fMat[2], fMat[6], fMat[10], fMat[14], |
| 282 | fMat[3], fMat[7], fMat[11], fMat[15]); |
| 283 | } |
| 284 | |
| 285 | static SkV3 normalize(SkV3 v) { return v * (1.0f / v.length()); } |
| 286 | |
| 287 | static SkV4 v4(SkV3 v, SkScalar w) { return {v.x, v.y, v.z, w}; } |
| 288 | |
| 289 | SkM44 Sk3LookAt(const SkV3& eye, const SkV3& center, const SkV3& up) { |
| 290 | SkV3 f = normalize(center - eye); |
| 291 | SkV3 u = normalize(up); |
| 292 | SkV3 s = normalize(f.cross(u)); |
| 293 | |
| 294 | SkM44 m(SkM44::kUninitialized_Constructor); |
| 295 | if (!SkM44::Cols(v4(s, 0), v4(s.cross(f), 0), v4(-f, 0), v4(eye, 1)).invert(&m)) { |
| 296 | m.setIdentity(); |
| 297 | } |
| 298 | return m; |
| 299 | } |
| 300 | |
| 301 | SkM44 Sk3Perspective(float near, float far, float angle) { |
| 302 | SkASSERT(far > near); |
| 303 | |
| 304 | float denomInv = sk_ieee_float_divide(1, far - near); |
| 305 | float halfAngle = angle * 0.5f; |
| 306 | float cot = sk_float_cos(halfAngle) / sk_float_sin(halfAngle); |
| 307 | |
| 308 | SkM44 m; |
| 309 | m.setRC(0, 0, cot); |
| 310 | m.setRC(1, 1, cot); |
| 311 | m.setRC(2, 2, (far + near) * denomInv); |
| 312 | m.setRC(2, 3, 2 * far * near * denomInv); |
| 313 | m.setRC(3, 2, -1); |
| 314 | return m; |
| 315 | } |
| 316 | |