1/* 2 * Copyright (C) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16export class Matrix4 { 17 elements; 18 19 constructor() { 20 this.elements = new Float32Array(16); 21 this.setIdentity(); 22 } 23 24 setIdentity() { 25 this.elements[0] = 1; 26 this.elements[5] = 1; 27 this.elements[10] = 1; 28 this.elements[15] = 1; 29 return this; 30 } 31 32 set(m) { 33 let elements = m.getElements(); 34 for (let i = 0; i < 16; i++) { 35 this.elements[i] = elements[i]; 36 } 37 return this; 38 } 39 40 setTranslate(x, y, z) { 41 this.elements[12] = x; 42 this.elements[13] = y; 43 this.elements[14] = z; 44 return this; 45 } 46 47 setScale(x, y, z) { 48 this.elements[0] = x; 49 this.elements[5] = y; 50 this.elements[10] = z; 51 return this; 52 } 53 54 rotateX(e, s, c) { 55 e[0] = 1; 56 e[4] = 0; 57 e[8] = 0; 58 e[12] = 0; 59 e[1] = 0; 60 e[5] = c; 61 e[9] = -s; 62 e[13] = 0; 63 e[2] = 0; 64 e[6] = s; 65 e[10] = c; 66 e[14] = 0; 67 e[3] = 0; 68 e[7] = 0; 69 e[11] = 0; 70 e[15] = 1; 71 } 72 73 rotateY(e, s, c) { 74 e[0] = c; 75 e[4] = 0; 76 e[8] = s; 77 e[12] = 0; 78 e[1] = 0; 79 e[5] = 1; 80 e[9] = 0; 81 e[13] = 0; 82 e[2] = -s; 83 e[6] = 0; 84 e[10] = c; 85 e[14] = 0; 86 e[3] = 0; 87 e[7] = 0; 88 e[11] = 0; 89 e[15] = 1; 90 } 91 92 rotateZ(e, s, c) { 93 e[0] = c; 94 e[4] = -s; 95 e[8] = 0; 96 e[12] = 0; 97 e[1] = s; 98 e[5] = c; 99 e[9] = 0; 100 e[13] = 0; 101 e[2] = 0; 102 e[6] = 0; 103 e[10] = 1; 104 e[14] = 0; 105 e[3] = 0; 106 e[7] = 0; 107 e[11] = 0; 108 e[15] = 1; 109 } 110 111 setRotate(angle, x, y, z) { 112 let e, s, c, len, rlen, nc, xy, yz, zx, xs, ys, zs; 113 angle = Math.PI * angle / 180; 114 e = this.elements; 115 s = Math.sin(angle); 116 c = Math.cos(angle); 117 if (0 !== x && 0 === y && 0 === z) { 118 if (x < 0) { 119 s = -s; 120 } 121 this.rotateX(e, s, c) 122 } else if (0 === x && 0 !== y && 0 === z) { 123 if (y < 0) { 124 s = -s; 125 } 126 this.rotateY(e, s, c) 127 } else if (0 === x && 0 === y && 0 !== z) { 128 if (z < 0) { 129 s = -s; 130 } 131 this.rotateZ(e, s, c) 132 } else { 133 len = Math.sqrt(x * x + y * y + z * z); 134 if (len !== 1) { 135 rlen = 1 / len; 136 x *= rlen; 137 y *= rlen; 138 z *= rlen; 139 } 140 nc = 1 - c; 141 xy = x * y; 142 yz = y * z; 143 zx = z * x; 144 xs = x * s; 145 ys = y * s; 146 zs = z * s; 147 e[0] = x * x * nc + c; 148 e[1] = xy * nc + zs; 149 e[2] = zx * nc - ys; 150 e[3] = e[7] = 0; 151 e[4] = xy * nc - zs; 152 e[5] = y * y * nc + c; 153 e[6] = yz * nc + xs; 154 e[8] = zx * nc + ys; 155 e[9] = yz * nc - xs; 156 e[10] = z * z * nc + c; 157 e[11] = e[12] = e[13] = e[14] = 0; 158 e[15] = 1; 159 } 160 return this; 161 } 162 163 multiply(other) { 164 let i, e, a, b, ai0, ai1, ai2, ai3; 165 e = this.elements; 166 a = this.elements; 167 b = other.elements; 168 if (e === b) { 169 b = new Float32Array(16); 170 for (i = 0; i < 16; ++i) { 171 b[i] = e[i]; 172 } 173 } 174 for (i = 0; i < 4; i++) { 175 ai0 = a[i]; 176 ai1 = a[i + 4]; 177 ai2 = a[i + 8]; 178 ai3 = a[i + 12]; 179 e[i] = ai0 * b[0] + ai1 * b[1] + ai2 * b[2] + ai3 * b[3]; 180 e[i + 4] = ai0 * b[4] + ai1 * b[5] + ai2 * b[6] + ai3 * b[7]; 181 e[i + 8] = ai0 * b[8] + ai1 * b[9] + ai2 * b[10] + ai3 * b[11]; 182 e[i + 12] = ai0 * b[12] + ai1 * b[13] + ai2 * b[14] + ai3 * b[15]; 183 } 184 return this; 185 } 186 187 getElements() { 188 return this.elements; 189 } 190 191 getInverse() { 192 let result = new Matrix4(); 193 let a = this.elements; 194 let b = result.elements; 195 let A0 = a[0] * a[5] - a[1] * a[4]; 196 let A1 = a[0] * a[6] - a[2] * a[4]; 197 let A2 = a[0] * a[7] - a[3] * a[4]; 198 let A3 = a[1] * a[6] - a[2] * a[5]; 199 let A4 = a[1] * a[7] - a[3] * a[5]; 200 let A5 = a[2] * a[7] - a[3] * a[6]; 201 let B0 = a[8] * a[13] - a[9] * a[12]; 202 let B1 = a[8] * a[14] - a[10] * a[12]; 203 let B2 = a[8] * a[15] - a[11] * a[12]; 204 let B3 = a[9] * a[14] - a[10] * a[13]; 205 let B4 = a[9] * a[15] - a[11] * a[13]; 206 let B5 = a[10] * a[15] - a[11] * a[14]; 207 let det = A0 * B5 - A1 * B4 + A2 * B3 + A3 * B2 - A4 * B1 + A5 * B0; 208 if (det == 0) { 209 return null; 210 } 211 let invDet = 1 / det; 212 b[0] = (a[5] * B5 - a[6] * B4 + a[7] * B3) * invDet; 213 b[1] = (-a[1] * B5 + a[2] * B4 - a[3] * B3) * invDet; 214 b[2] = (a[13] * A5 - a[14] * A4 + a[15] * A3) * invDet; 215 b[3] = (-a[9] * A5 + a[10] * A4 - a[11] * A3) * invDet; 216 b[4] = (-a[4] * B5 + a[6] * B2 - a[7] * B1) * invDet; 217 b[5] = (a[0] * B5 - a[2] * B2 + a[3] * B1) * invDet; 218 b[6] = (-a[12] * A5 + a[14] * A2 - a[15] * A1) * invDet; 219 b[7] = (a[8] * A5 - a[10] * A2 + a[11] * A1) * invDet; 220 b[8] = (a[4] * B4 - a[5] * B2 + a[7] * B0) * invDet; 221 b[9] = (-a[0] * B4 + a[1] * B2 - a[3] * B0) * invDet; 222 b[10] = (a[12] * A4 - a[13] * A2 + a[15] * A0) * invDet; 223 b[11] = (-a[8] * A4 + a[9] * A2 - a[11] * A0) * invDet; 224 b[12] = (-a[4] * B3 + a[5] * B1 - a[6] * B0) * invDet; 225 b[13] = (a[0] * B3 - a[1] * B1 + a[2] * B0) * invDet; 226 b[14] = (-a[12] * A3 + a[13] * A1 - a[14] * A0) * invDet; 227 b[15] = (a[8] * A3 - a[9] * A1 + a[10] * A0) * invDet; 228 return result; 229 } 230 231 setPerspective(fovy, aspect, near, far) { 232 let e, rd, s, ct; 233 if (near === far || aspect === 0) { 234 throw 'null frustum'; 235 } 236 if (near <= 0) { 237 throw 'near <= 0'; 238 } 239 if (far <= 0) { 240 throw 'far <= 0'; 241 } 242 fovy = Math.PI * fovy / 180 / 2; 243 s = Math.sin(fovy); 244 if (s === 0) { 245 throw 'null frustum'; 246 } 247 rd = 1 / (far - near); 248 ct = Math.cos(fovy) / s; 249 e = this.elements; 250 e[0] = ct / aspect; 251 e[1] = 0; 252 e[2] = 0; 253 e[3] = 0; 254 e[4] = 0; 255 e[5] = ct; 256 e[6] = 0; 257 e[7] = 0; 258 e[8] = 0; 259 e[9] = 0; 260 e[10] = -(far + near) * rd; 261 e[11] = -1; 262 e[12] = 0; 263 e[13] = 0; 264 e[14] = -2 * near * far * rd; 265 e[15] = 0; 266 return this; 267 } 268 269 setOrtho(left, right, bottom, top, near, far) { 270 let e = this.elements; 271 let rw = 1 / (right - left); 272 let rh = 1 / (top - bottom); 273 let rd = 1 / (far - near); 274 e[0] = 2 * rw; 275 e[1] = 0; 276 e[2] = 0; 277 e[3] = 0; 278 e[4] = 0; 279 e[5] = 2 * rh; 280 e[6] = 0; 281 e[7] = 0; 282 e[8] = 0; 283 e[9] = 0; 284 e[10] = -2 * rd; 285 e[11] = 0; 286 e[12] = -(right + left) * rw; 287 e[13] = -(top + bottom) * rh; 288 e[14] = -(far + near) * rd; 289 e[15] = 1; 290 } 291 292 lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { 293 let lookAt = new Matrix4().setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); 294 return this.multiply(lookAt); 295 } 296 297 setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { 298 let e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz; 299 fx = centerX - eyeX; 300 fy = centerY - eyeY; 301 fz = centerZ - eyeZ; 302 rlf = 1 / Math.sqrt(fx * fx + fy * fy + fz * fz); 303 fx *= rlf; 304 fy *= rlf; 305 fz *= rlf; 306 sx = fy * upZ - fz * upY; 307 sy = fz * upX - fx * upZ; 308 sz = fx * upY - fy * upX; 309 rls = 1 / Math.sqrt(sx * sx + sy * sy + sz * sz); 310 sx *= rls; 311 sy *= rls; 312 sz *= rls; 313 ux = sy * fz - sz * fy; 314 uy = sz * fx - sx * fz; 315 uz = sx * fy - sy * fx; 316 e = this.elements; 317 e[0] = sx; 318 e[1] = ux; 319 e[2] = -fx; 320 e[3] = 0; 321 e[4] = sy; 322 e[5] = uy; 323 e[6] = -fy; 324 e[7] = 0; 325 e[8] = sz; 326 e[9] = uz; 327 e[10] = -fz; 328 e[11] = 0; 329 e[12] = 0; 330 e[13] = 0; 331 e[14] = 0; 332 e[15] = 1; 333 return this.translate(-eyeX, -eyeY, -eyeZ); 334 } 335 336 translate(x, y, z) { 337 let e = this.elements; 338 e[12] += e[0] * x + e[4] * y + e[8] * z; 339 e[13] += e[1] * x + e[5] * y + e[9] * z; 340 e[14] += e[2] * x + e[6] * y + e[10] * z; 341 e[15] += e[3] * x + e[7] * y + e[11] * z; 342 return this; 343 } 344 345 scale(x, y, z) { 346 let e = this.elements; 347 e[0] *= x; 348 e[1] *= x; 349 e[2] *= x; 350 e[3] *= x; 351 e[4] *= y; 352 e[5] *= y; 353 e[6] *= y; 354 e[7] *= y; 355 e[8] *= z; 356 e[9] *= z; 357 e[10] *= z; 358 e[11] *= z; 359 } 360 361 rotate(angle, x, y, z) { 362 let e = this.elements; 363 let radian = angle * Math.PI / 180; 364 let cosB = Math.cos(radian); 365 let sinB = Math.sin(radian); 366 let len = Math.sqrt(x * x + y * y + z * z); 367 if (len != 1) { 368 let rlen = 1 / len; 369 x *= rlen; 370 y *= rlen; 371 z *= rlen; 372 } 373 let nc = 1 - cosB; 374 let xy = x * y; 375 let yz = y * z; 376 let zx = z * x; 377 let xs = x * sinB; 378 let ys = y * sinB; 379 let zs = z * sinB; 380 let f00 = x * x * nc + cosB; 381 let f01 = xy * nc + zs; 382 let f02 = zx * nc - ys; 383 let f10 = xy * nc - zs; 384 let f11 = y * y * nc + cosB; 385 let f12 = yz * nc + xs; 386 let f20 = zx * nc + ys; 387 let f21 = yz * nc - xs; 388 let f22 = z * z * nc + cosB; 389 let t00 = e[0] * f00 + e[4] * f01 + e[8] * f02; 390 let t01 = e[1] * f00 + e[5] * f01 + e[9] * f02; 391 let t02 = e[2] * f00 + e[6] * f01 + e[10] * f02; 392 let t03 = e[3] * f00 + e[7] * f01 + e[11] * f02; 393 let t10 = e[0] * f10 + e[4] * f11 + e[8] * f12; 394 let t11 = e[1] * f10 + e[5] * f11 + e[9] * f12; 395 let t12 = e[2] * f10 + e[6] * f11 + e[10] * f12; 396 let t13 = e[3] * f10 + e[7] * f11 + e[11] * f12; 397 e[8] = e[0] * f20 + e[4] * f21 + e[8] * f22; 398 e[9] = e[1] * f20 + e[5] * f21 + e[9] * f22; 399 e[10] = e[2] * f20 + e[6] * f21 + e[10] * f22; 400 e[11] = e[3] * f20 + e[7] * f21 + e[11] * f22; 401 e[0] = t00; 402 e[1] = t01; 403 e[2] = t02; 404 e[3] = t03; 405 e[4] = t10; 406 e[5] = t11; 407 e[6] = t12; 408 e[7] = t13; 409 } 410 411 setInverseOf(other) { 412 let i, s, d, arr, det; 413 s = other.elements; 414 d = this.elements; 415 arr = new Float32Array(16); 416 arr[0] = s[5] * s[10] * s[15] - s[5] * s[11] * s[14] - s[9] * s[6] * s[15] + s[9] * s[7] * s[14] + s[13] * s[6] * s[11] - s[13] * s[7] * s[10]; 417 arr[4] = -s[4] * s[10] * s[15] + s[4] * s[11] * s[14] + s[8] * s[6] * s[15] - s[8] * s[7] * s[14] - s[12] * s[6] * s[11] + s[12] * s[7] * s[10]; 418 arr[8] = s[4] * s[9] * s[15] - s[4] * s[11] * s[13] - s[8] * s[5] * s[15] + s[8] * s[7] * s[13] + s[12] * s[5] * s[11] - s[12] * s[7] * s[9]; 419 arr[12] = -s[4] * s[9] * s[14] + s[4] * s[10] * s[13] + s[8] * s[5] * s[14] - s[8] * s[6] * s[13] - s[12] * s[5] * s[10] + s[12] * s[6] * s[9]; 420 arr[1] = -s[1] * s[10] * s[15] + s[1] * s[11] * s[14] + s[9] * s[2] * s[15] - s[9] * s[3] * s[14] - s[13] * s[2] * s[11] + s[13] * s[3] * s[10]; 421 arr[5] = s[0] * s[10] * s[15] - s[0] * s[11] * s[14] - s[8] * s[2] * s[15] + s[8] * s[3] * s[14] + s[12] * s[2] * s[11] - s[12] * s[3] * s[10]; 422 arr[9] = -s[0] * s[9] * s[15] + s[0] * s[11] * s[13] + s[8] * s[1] * s[15] - s[8] * s[3] * s[13] - s[12] * s[1] * s[11] + s[12] * s[3] * s[9]; 423 arr[13] = s[0] * s[9] * s[14] - s[0] * s[10] * s[13] - s[8] * s[1] * s[14] + s[8] * s[2] * s[13] + s[12] * s[1] * s[10] - s[12] * s[2] * s[9]; 424 arr[2] = s[1] * s[6] * s[15] - s[1] * s[7] * s[14] - s[5] * s[2] * s[15] + s[5] * s[3] * s[14] + s[13] * s[2] * s[7] - s[13] * s[3] * s[6]; 425 arr[6] = -s[0] * s[6] * s[15] + s[0] * s[7] * s[14] + s[4] * s[2] * s[15] - s[4] * s[3] * s[14] - s[12] * s[2] * s[7] + s[12] * s[3] * s[6]; 426 arr[10] = s[0] * s[5] * s[15] - s[0] * s[7] * s[13] - s[4] * s[1] * s[15] + s[4] * s[3] * s[13] + s[12] * s[1] * s[7] - s[12] * s[3] * s[5]; 427 arr[14] = -s[0] * s[5] * s[14] + s[0] * s[6] * s[13] + s[4] * s[1] * s[14] - s[4] * s[2] * s[13] - s[12] * s[1] * s[6] + s[12] * s[2] * s[5]; 428 arr[3] = -s[1] * s[6] * s[11] + s[1] * s[7] * s[10] + s[5] * s[2] * s[11] - s[5] * s[3] * s[10] - s[9] * s[2] * s[7] + s[9] * s[3] * s[6]; 429 arr[7] = s[0] * s[6] * s[11] - s[0] * s[7] * s[10] - s[4] * s[2] * s[11] + s[4] * s[3] * s[10] + s[8] * s[2] * s[7] - s[8] * s[3] * s[6]; 430 arr[11] = -s[0] * s[5] * s[11] + s[0] * s[7] * s[9] + s[4] * s[1] * s[11] - s[4] * s[3] * s[9] - s[8] * s[1] * s[7] + s[8] * s[3] * s[5]; 431 arr[15] = s[0] * s[5] * s[10] - s[0] * s[6] * s[9] - s[4] * s[1] * s[10] + s[4] * s[2] * s[9] + s[8] * s[1] * s[6] - s[8] * s[2] * s[5]; 432 det = s[0] * arr[0] + s[1] * arr[4] + s[2] * arr[8] + s[3] * arr[12]; 433 if (det == 0) { 434 return false; 435 } 436 det = 1 / det; 437 for (i = 0; i < 16; i++) { 438 d[i] = arr[i] * det; 439 } 440 return true; 441 } 442 443 invert() { 444 this.setInverseOf(this); 445 } 446 447 setTranslateRotateScale(tx, ty, tz, rx, ry, rz, sx, sy, sz) { 448 let radian = rx * Math.PI / 180; 449 let cosB = Math.cos(radian); 450 let sinB = Math.sin(radian); 451 let cosA = 1, sinA = 0; 452 if (ry != 0) { 453 cosA = this.elements[5]; 454 sinA = this.elements[6]; 455 } 456 if (rz != 0) { 457 cosA = this.elements[0]; 458 sinA = this.elements[1]; 459 } 460 this.elements[0] = cosA * cosB; 461 this.elements[1] = sinA * cosB; 462 this.elements[2] = -sinB; 463 this.elements[4] = -sinA; 464 this.elements[5] = cosA; 465 this.elements[6] = 0; 466 this.elements[8] = cosA * sinB; 467 this.elements[9] = sinA * sinB; 468 this.elements[10] = cosB; 469 this.elements[12] = tx; 470 this.elements[13] = ty; 471 this.elements[14] = tz; 472 this.elements[3] = 0; 473 this.elements[7] = 0; 474 this.elements[11] = 0; 475 this.elements[15] = 1; 476 this.scale(sx, sy, sz); 477 } 478 479 setTRS(tx, ty, tz, rx, ry, rz, sx, sy, sz) { 480 let radian = rx * Math.PI / 180; 481 let cosB = Math.cos(radian); 482 let sinB = Math.sin(radian); 483 let cosA = 1, sinA = 0; 484 if (ry != 0) { 485 cosA = this.elements[5]; 486 sinA = this.elements[6]; 487 } 488 if (rz != 0) { 489 cosA = this.elements[0]; 490 sinA = this.elements[1]; 491 } 492 this.elements[0] = cosA * cosB; 493 this.elements[1] = sinA * cosB; 494 this.elements[2] = -sinB; 495 this.elements[4] = -sinA; 496 this.elements[5] = cosA; 497 this.elements[6] = 0; 498 this.elements[8] = cosA * sinB; 499 this.elements[9] = sinA * sinB; 500 this.elements[10] = cosB; 501 this.elements[12] = tx; 502 this.elements[13] = ty; 503 this.elements[14] = tz; 504 this.elements[3] = 0; 505 this.elements[7] = 0; 506 this.elements[11] = 0; 507 this.elements[15] = 1; 508 this.scale(sx, sy, sz); 509 } 510 511 transpose() { 512 let e, t; 513 e = this.elements; 514 t = e[1]; 515 e[1] = e[4]; 516 e[4] = t; 517 t = e[2]; 518 e[2] = e[8]; 519 e[8] = t; 520 t = e[3]; 521 e[3] = e[12]; 522 e[12] = t; 523 t = e[6]; 524 e[6] = e[9]; 525 e[9] = t; 526 t = e[7]; 527 e[7] = e[13]; 528 e[13] = t; 529 t = e[11]; 530 e[11] = e[14]; 531 e[14] = t; 532 return this; 533 } 534} 535