@gltf-transform/core
Version:
glTF 2.0 SDK for JavaScript and TypeScript, on Web and Node.js.
175 lines (149 loc) • 4.86 kB
text/typescript
import { determinant, getRotation } from 'gl-matrix/mat4';
import { length } from 'gl-matrix/vec3';
import type { mat4, vec3, vec4 } from '../constants.js';
import type { GLTF } from '../types/gltf.js';
/** @hidden */
export class MathUtils {
public static identity(v: number): number {
return v;
}
public static eq(a: number[], b: number[], tolerance = 10e-6): boolean {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (Math.abs(a[i] - b[i]) > tolerance) return false;
}
return true;
}
public static clamp(value: number, min: number, max: number): number {
if (value < min) return min;
if (value > max) return max;
return value;
}
// TODO(perf): Compare performance if we replace the switch with individual functions.
public static decodeNormalizedInt(i: number, componentType: GLTF.AccessorComponentType): number {
// Hardcode enums from accessor.ts to avoid a circular dependency.
switch (componentType) {
case 5126: // FLOAT
return i;
case 5123: // UNSIGNED_SHORT
return i / 65535.0;
case 5121: // UNSIGNED_BYTE
return i / 255.0;
case 5122: // SHORT
return Math.max(i / 32767.0, -1.0);
case 5120: // BYTE
return Math.max(i / 127.0, -1.0);
default:
throw new Error('Invalid component type.');
}
}
// TODO(perf): Compare performance if we replace the switch with individual functions.
public static encodeNormalizedInt(f: number, componentType: GLTF.AccessorComponentType): number {
// Hardcode enums from accessor.ts to avoid a circular dependency.
switch (componentType) {
case 5126: // FLOAT
return f;
case 5123: // UNSIGNED_SHORT
return Math.round(MathUtils.clamp(f, 0, 1) * 65535.0);
case 5121: // UNSIGNED_BYTE
return Math.round(MathUtils.clamp(f, 0, 1) * 255.0);
case 5122: // SHORT
return Math.round(MathUtils.clamp(f, -1, 1) * 32767.0);
case 5120: // BYTE
return Math.round(MathUtils.clamp(f, -1, 1) * 127.0);
default:
throw new Error('Invalid component type.');
}
}
/**
* Decompose a mat4 to TRS properties.
*
* Equivalent to the Matrix4 decompose() method in three.js, and intentionally not using the
* gl-matrix version. See: https://github.com/toji/gl-matrix/issues/408
*
* @param srcMat Matrix element, to be decomposed to TRS properties.
* @param dstTranslation Translation element, to be overwritten.
* @param dstRotation Rotation element, to be overwritten.
* @param dstScale Scale element, to be overwritten.
*/
public static decompose(srcMat: mat4, dstTranslation: vec3, dstRotation: vec4, dstScale: vec3): void {
let sx = length([srcMat[0], srcMat[1], srcMat[2]]);
const sy = length([srcMat[4], srcMat[5], srcMat[6]]);
const sz = length([srcMat[8], srcMat[9], srcMat[10]]);
// if determine is negative, we need to invert one scale
const det = determinant(srcMat);
if (det < 0) sx = -sx;
dstTranslation[0] = srcMat[12];
dstTranslation[1] = srcMat[13];
dstTranslation[2] = srcMat[14];
// scale the rotation part
const _m1 = srcMat.slice();
const invSX = 1 / sx;
const invSY = 1 / sy;
const invSZ = 1 / sz;
_m1[0] *= invSX;
_m1[1] *= invSX;
_m1[2] *= invSX;
_m1[4] *= invSY;
_m1[5] *= invSY;
_m1[6] *= invSY;
_m1[8] *= invSZ;
_m1[9] *= invSZ;
_m1[10] *= invSZ;
getRotation(dstRotation, _m1 as mat4);
dstScale[0] = sx;
dstScale[1] = sy;
dstScale[2] = sz;
}
/**
* Compose TRS properties to a mat4.
*
* Equivalent to the Matrix4 compose() method in three.js, and intentionally not using the
* gl-matrix version. See: https://github.com/toji/gl-matrix/issues/408
*
* @param srcTranslation Translation element of matrix.
* @param srcRotation Rotation element of matrix.
* @param srcScale Scale element of matrix.
* @param dstMat Matrix element, to be modified and returned.
* @returns dstMat, overwritten to mat4 equivalent of given TRS properties.
*/
public static compose(srcTranslation: vec3, srcRotation: vec4, srcScale: vec3, dstMat: mat4): mat4 {
const te = dstMat;
const x = srcRotation[0],
y = srcRotation[1],
z = srcRotation[2],
w = srcRotation[3];
const x2 = x + x,
y2 = y + y,
z2 = z + z;
const xx = x * x2,
xy = x * y2,
xz = x * z2;
const yy = y * y2,
yz = y * z2,
zz = z * z2;
const wx = w * x2,
wy = w * y2,
wz = w * z2;
const sx = srcScale[0],
sy = srcScale[1],
sz = srcScale[2];
te[0] = (1 - (yy + zz)) * sx;
te[1] = (xy + wz) * sx;
te[2] = (xz - wy) * sx;
te[3] = 0;
te[4] = (xy - wz) * sy;
te[5] = (1 - (xx + zz)) * sy;
te[6] = (yz + wx) * sy;
te[7] = 0;
te[8] = (xz + wy) * sz;
te[9] = (yz - wx) * sz;
te[10] = (1 - (xx + yy)) * sz;
te[11] = 0;
te[12] = srcTranslation[0];
te[13] = srcTranslation[1];
te[14] = srcTranslation[2];
te[15] = 1;
return te;
}
}