ts-ritofile
Version:
TypeScript library for reading and writing League of Legends game file formats
227 lines (201 loc) • 9.54 kB
text/typescript
import { JsonSerializable } from '../core/json-encoder';
import { Vector } from './vector';
import { Quaternion } from './quaternion';
export class Matrix4 implements JsonSerializable {
public a: number; public b: number; public c: number; public d: number;
public e: number; public f: number; public g: number; public h: number;
public i: number; public j: number; public k: number; public l: number;
public m: number; public n: number; public o: number; public p: number;
constructor(...values: number[]) {
if (values.length === 16) {
[this.a, this.b, this.c, this.d,
this.e, this.f, this.g, this.h,
this.i, this.j, this.k, this.l,
this.m, this.n, this.o, this.p] = values;
} else {
// Identity matrix
this.a = this.f = this.k = this.p = 1.0;
this.b = this.c = this.d = this.e = this.g = this.h = this.i = this.j = this.l = this.m = this.n = this.o = 0.0;
}
}
get(index: number): number {
return [
this.a, this.b, this.c, this.d,
this.e, this.f, this.g, this.h,
this.i, this.j, this.k, this.l,
this.m, this.n, this.o, this.p
][index];
}
set(index: number, value: number): void {
const values = this.toArray();
values[index] = value;
[this.a, this.b, this.c, this.d,
this.e, this.f, this.g, this.h,
this.i, this.j, this.k, this.l,
this.m, this.n, this.o, this.p] = values;
}
multiply(other: Matrix4): Matrix4 {
return new Matrix4(
this.a * other.a + this.b * other.e + this.c * other.i + this.d * other.m,
this.a * other.b + this.b * other.f + this.c * other.j + this.d * other.n,
this.a * other.c + this.b * other.g + this.c * other.k + this.d * other.o,
this.a * other.d + this.b * other.h + this.c * other.l + this.d * other.p,
this.e * other.a + this.f * other.e + this.g * other.i + this.h * other.m,
this.e * other.b + this.f * other.f + this.g * other.j + this.h * other.n,
this.e * other.c + this.f * other.g + this.g * other.k + this.h * other.o,
this.e * other.d + this.f * other.h + this.g * other.l + this.h * other.p,
this.i * other.a + this.j * other.e + this.k * other.i + this.l * other.m,
this.i * other.b + this.j * other.f + this.k * other.j + this.l * other.n,
this.i * other.c + this.j * other.g + this.k * other.k + this.l * other.o,
this.i * other.d + this.j * other.h + this.k * other.l + this.l * other.p,
this.m * other.a + this.n * other.e + this.o * other.i + this.p * other.m,
this.m * other.b + this.n * other.f + this.o * other.j + this.p * other.n,
this.m * other.c + this.n * other.g + this.o * other.k + this.p * other.o,
this.m * other.d + this.n * other.h + this.o * other.l + this.p * other.p
);
}
inverse(): Matrix4 {
const d = (
(this.a * this.f - this.e * this.b)
* (this.k * this.p - this.o * this.l)
- (this.a * this.j - this.i * this.b)
* (this.g * this.p - this.o * this.h)
+ (this.a * this.n - this.m * this.b)
* (this.g * this.l - this.k * this.h)
+ (this.e * this.j - this.i * this.f)
* (this.c * this.p - this.o * this.d)
- (this.e * this.n - this.m * this.f)
* (this.c * this.l - this.k * this.d)
+ (this.i * this.n - this.m * this.j)
* (this.c * this.h - this.g * this.d)
);
const inv = new Matrix4();
if (Math.abs(d) >= 0.001) {
const invD = 1.0 / d;
inv.a = invD * (this.f * (this.k * this.p - this.o * this.l) + this.j * (
this.o * this.h - this.g * this.p) + this.n * (this.g * this.l - this.k * this.h));
inv.e = invD * (this.g * (this.i * this.p - this.m * this.l) + this.k * (
this.m * this.h - this.e * this.p) + this.o * (this.e * this.l - this.i * this.h));
inv.i = invD * (this.h * (this.i * this.n - this.m * this.j) + this.l * (
this.m * this.f - this.e * this.n) + this.p * (this.e * this.j - this.i * this.f));
inv.m = invD * (this.e * (this.n * this.k - this.j * this.o) + this.i * (
this.f * this.o - this.n * this.g) + this.m * (this.j * this.g - this.f * this.k));
inv.b = invD * (this.j * (this.c * this.p - this.o * this.d) + this.n * (
this.k * this.d - this.c * this.l) + this.b * (this.o * this.l - this.k * this.p));
inv.f = invD * (this.k * (this.a * this.p - this.m * this.d) + this.o * (
this.i * this.d - this.a * this.l) + this.c * (this.m * this.l - this.i * this.p));
inv.j = invD * (this.l * (this.a * this.n - this.m * this.b) + this.p * (
this.i * this.b - this.a * this.j) + this.d * (this.m * this.j - this.i * this.n));
inv.n = invD * (this.i * (this.n * this.c - this.b * this.o) + this.m * (
this.b * this.k - this.j * this.c) + this.a * (this.j * this.o - this.n * this.k));
inv.c = invD * (this.n * (this.c * this.h - this.g * this.d) + this.b * (
this.g * this.p - this.o * this.h) + this.f * (this.o * this.d - this.c * this.p));
inv.g = invD * (this.o * (this.a * this.h - this.e * this.d) + this.c * (
this.e * this.p - this.m * this.h) + this.g * (this.m * this.d - this.a * this.p));
inv.k = invD * (this.p * (this.a * this.f - this.e * this.b) + this.d * (
this.e * this.n - this.m * this.f) + this.h * (this.m * this.b - this.a * this.n));
inv.o = invD * (this.m * (this.f * this.c - this.b * this.g) + this.a * (
this.n * this.g - this.f * this.o) + this.e * (this.b * this.o - this.n * this.c));
inv.d = invD * (this.b * (this.k * this.h - this.g * this.l) + this.f * (
this.c * this.l - this.k * this.d) + this.j * (this.g * this.d - this.c * this.h));
inv.h = invD * (this.c * (this.i * this.h - this.e * this.l) + this.g * (
this.a * this.l - this.i * this.d) + this.k * (this.e * this.d - this.a * this.h));
inv.l = invD * (this.d * (this.i * this.f - this.e * this.j) + this.h * (
this.a * this.j - this.i * this.b) + this.l * (this.e * this.b - this.a * this.f));
inv.p = invD * (this.a * (this.f * this.k - this.j * this.g) + this.e * (
this.j * this.c - this.b * this.k) + this.i * (this.b * this.g - this.f * this.c));
}
// else: return identity anyway
return inv;
}
decompose(): { translate: Vector; rotate: Quaternion; scale: Vector } {
// this only support scale (1.0, 1.0, 1.0)
// but we only use for update old skl, so its enough
const translate = new Vector(this.m, this.n, this.o);
const scale = new Vector(
this.p * Math.sqrt(this.a**2 + this.b**2 + this.c**2),
this.p * Math.sqrt(this.e**2 + this.f**2 + this.g**2),
this.p * Math.sqrt(this.i**2 + this.j**2 + this.k**2)
);
const rMat = new Matrix4(
this.a/scale.x!, this.b/scale.y!, this.c/scale.z!, 0,
this.e/scale.x!, this.f/scale.y!, this.g/scale.z!, 0,
this.i/scale.x!, this.j/scale.y!, this.k/scale.z!, 0,
0, 0, 0, 1
);
const dott = (rMat.b * rMat.g - rMat.c * rMat.f) * rMat.i
+ (rMat.c * rMat.e - rMat.a * rMat.g) * rMat.j
+ (rMat.a * rMat.f - rMat.b * rMat.e) * rMat.k;
if (dott < 0) {
scale.x! *= -1;
rMat.a *= -1;
rMat.b *= -1;
rMat.c *= -1;
}
const trace = rMat.a + rMat.f + rMat.k;
let rotate: Quaternion;
if (trace > 0.00000001) {
const s = Math.sqrt(trace + 1.0);
const iS = 0.5 / s;
rotate = new Quaternion(
(rMat.g - rMat.j) * iS,
(rMat.i - rMat.c) * iS,
(rMat.b - rMat.e) * iS,
s * 0.5
);
} else {
if (rMat.a >= rMat.f && rMat.a >= rMat.k) {
const s = Math.sqrt(1.0 + rMat.a - rMat.f - rMat.k);
const iS = 0.5 / s;
rotate = new Quaternion(
0.5 * s,
(rMat.b + rMat.e) * iS,
(rMat.i + rMat.c) * iS,
(rMat.g - rMat.j) * iS,
);
} else if (rMat.f > rMat.k) {
const s = Math.sqrt(1.0 + rMat.f - rMat.a - rMat.k);
const iS = 0.5 / s;
rotate = new Quaternion(
(rMat.e + rMat.b) * iS,
0.5 * s,
(rMat.j + rMat.g) * iS,
(rMat.c - rMat.i) * iS
);
} else {
const s = Math.sqrt(1.0 + rMat.k - rMat.a - rMat.f);
const iS = 0.5 / s;
rotate = new Quaternion(
(rMat.c + rMat.i) * iS,
(rMat.j + rMat.g) * iS,
0.5 * s,
(rMat.b - rMat.e) * iS
);
}
}
return { translate, rotate, scale };
}
toString(): string {
return [
`${this.a.toFixed(4)} ${this.b.toFixed(4)} ${this.c.toFixed(4)} ${this.d.toFixed(4)}`,
`${this.e.toFixed(4)} ${this.f.toFixed(4)} ${this.g.toFixed(4)} ${this.h.toFixed(4)}`,
`${this.i.toFixed(4)} ${this.j.toFixed(4)} ${this.k.toFixed(4)} ${this.l.toFixed(4)}`,
`${this.m.toFixed(4)} ${this.n.toFixed(4)} ${this.o.toFixed(4)} ${this.p.toFixed(4)}`
].join('\n');
}
toArray(): number[] {
return [
this.a, this.b, this.c, this.d,
this.e, this.f, this.g, this.h,
this.i, this.j, this.k, this.l,
this.m, this.n, this.o, this.p
];
}
__json__(): number[] {
return this.toArray();
}
// Keep toJSON for standard JavaScript compatibility
toJSON(): number[] {
return this.__json__();
}
}