@playcanvas/splat-transform 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/dist/index.mjs +2934 -818
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -12
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,2311 @@
|
|
|
1
1
|
import { open } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { exit } from 'node:process';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { Buffer as Buffer$1 } from 'node:buffer';
|
|
6
|
+
import sharp from 'sharp';
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
const math = {
|
|
9
|
+
DEG_TO_RAD: Math.PI / 180,
|
|
10
|
+
RAD_TO_DEG: 180 / Math.PI};
|
|
11
|
+
|
|
12
|
+
class Vec3 {
|
|
13
|
+
constructor(x = 0, y = 0, z = 0){
|
|
14
|
+
if (x.length === 3) {
|
|
15
|
+
this.x = x[0];
|
|
16
|
+
this.y = x[1];
|
|
17
|
+
this.z = x[2];
|
|
18
|
+
} else {
|
|
19
|
+
this.x = x;
|
|
20
|
+
this.y = y;
|
|
21
|
+
this.z = z;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
add(rhs) {
|
|
25
|
+
this.x += rhs.x;
|
|
26
|
+
this.y += rhs.y;
|
|
27
|
+
this.z += rhs.z;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
add2(lhs, rhs) {
|
|
31
|
+
this.x = lhs.x + rhs.x;
|
|
32
|
+
this.y = lhs.y + rhs.y;
|
|
33
|
+
this.z = lhs.z + rhs.z;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
addScalar(scalar) {
|
|
37
|
+
this.x += scalar;
|
|
38
|
+
this.y += scalar;
|
|
39
|
+
this.z += scalar;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
addScaled(rhs, scalar) {
|
|
43
|
+
this.x += rhs.x * scalar;
|
|
44
|
+
this.y += rhs.y * scalar;
|
|
45
|
+
this.z += rhs.z * scalar;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
clone() {
|
|
49
|
+
const cstr = this.constructor;
|
|
50
|
+
return new cstr(this.x, this.y, this.z);
|
|
51
|
+
}
|
|
52
|
+
copy(rhs) {
|
|
53
|
+
this.x = rhs.x;
|
|
54
|
+
this.y = rhs.y;
|
|
55
|
+
this.z = rhs.z;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
cross(lhs, rhs) {
|
|
59
|
+
const lx = lhs.x;
|
|
60
|
+
const ly = lhs.y;
|
|
61
|
+
const lz = lhs.z;
|
|
62
|
+
const rx = rhs.x;
|
|
63
|
+
const ry = rhs.y;
|
|
64
|
+
const rz = rhs.z;
|
|
65
|
+
this.x = ly * rz - ry * lz;
|
|
66
|
+
this.y = lz * rx - rz * lx;
|
|
67
|
+
this.z = lx * ry - rx * ly;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
distance(rhs) {
|
|
71
|
+
const x = this.x - rhs.x;
|
|
72
|
+
const y = this.y - rhs.y;
|
|
73
|
+
const z = this.z - rhs.z;
|
|
74
|
+
return Math.sqrt(x * x + y * y + z * z);
|
|
75
|
+
}
|
|
76
|
+
div(rhs) {
|
|
77
|
+
this.x /= rhs.x;
|
|
78
|
+
this.y /= rhs.y;
|
|
79
|
+
this.z /= rhs.z;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
div2(lhs, rhs) {
|
|
83
|
+
this.x = lhs.x / rhs.x;
|
|
84
|
+
this.y = lhs.y / rhs.y;
|
|
85
|
+
this.z = lhs.z / rhs.z;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
divScalar(scalar) {
|
|
89
|
+
this.x /= scalar;
|
|
90
|
+
this.y /= scalar;
|
|
91
|
+
this.z /= scalar;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
dot(rhs) {
|
|
95
|
+
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
|
|
96
|
+
}
|
|
97
|
+
equals(rhs) {
|
|
98
|
+
return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z;
|
|
99
|
+
}
|
|
100
|
+
equalsApprox(rhs, epsilon = 1e-6) {
|
|
101
|
+
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
|
|
102
|
+
}
|
|
103
|
+
length() {
|
|
104
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
105
|
+
}
|
|
106
|
+
lengthSq() {
|
|
107
|
+
return this.x * this.x + this.y * this.y + this.z * this.z;
|
|
108
|
+
}
|
|
109
|
+
lerp(lhs, rhs, alpha) {
|
|
110
|
+
this.x = lhs.x + alpha * (rhs.x - lhs.x);
|
|
111
|
+
this.y = lhs.y + alpha * (rhs.y - lhs.y);
|
|
112
|
+
this.z = lhs.z + alpha * (rhs.z - lhs.z);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
mul(rhs) {
|
|
116
|
+
this.x *= rhs.x;
|
|
117
|
+
this.y *= rhs.y;
|
|
118
|
+
this.z *= rhs.z;
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
mul2(lhs, rhs) {
|
|
122
|
+
this.x = lhs.x * rhs.x;
|
|
123
|
+
this.y = lhs.y * rhs.y;
|
|
124
|
+
this.z = lhs.z * rhs.z;
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
mulScalar(scalar) {
|
|
128
|
+
this.x *= scalar;
|
|
129
|
+
this.y *= scalar;
|
|
130
|
+
this.z *= scalar;
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
normalize(src = this) {
|
|
134
|
+
const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z;
|
|
135
|
+
if (lengthSq > 0) {
|
|
136
|
+
const invLength = 1 / Math.sqrt(lengthSq);
|
|
137
|
+
this.x = src.x * invLength;
|
|
138
|
+
this.y = src.y * invLength;
|
|
139
|
+
this.z = src.z * invLength;
|
|
140
|
+
}
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
floor(src = this) {
|
|
144
|
+
this.x = Math.floor(src.x);
|
|
145
|
+
this.y = Math.floor(src.y);
|
|
146
|
+
this.z = Math.floor(src.z);
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
ceil(src = this) {
|
|
150
|
+
this.x = Math.ceil(src.x);
|
|
151
|
+
this.y = Math.ceil(src.y);
|
|
152
|
+
this.z = Math.ceil(src.z);
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
round(src = this) {
|
|
156
|
+
this.x = Math.round(src.x);
|
|
157
|
+
this.y = Math.round(src.y);
|
|
158
|
+
this.z = Math.round(src.z);
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
min(rhs) {
|
|
162
|
+
if (rhs.x < this.x) this.x = rhs.x;
|
|
163
|
+
if (rhs.y < this.y) this.y = rhs.y;
|
|
164
|
+
if (rhs.z < this.z) this.z = rhs.z;
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
max(rhs) {
|
|
168
|
+
if (rhs.x > this.x) this.x = rhs.x;
|
|
169
|
+
if (rhs.y > this.y) this.y = rhs.y;
|
|
170
|
+
if (rhs.z > this.z) this.z = rhs.z;
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
project(rhs) {
|
|
174
|
+
const a_dot_b = this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
|
|
175
|
+
const b_dot_b = rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z;
|
|
176
|
+
const s = a_dot_b / b_dot_b;
|
|
177
|
+
this.x = rhs.x * s;
|
|
178
|
+
this.y = rhs.y * s;
|
|
179
|
+
this.z = rhs.z * s;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
set(x, y, z) {
|
|
183
|
+
this.x = x;
|
|
184
|
+
this.y = y;
|
|
185
|
+
this.z = z;
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
sub(rhs) {
|
|
189
|
+
this.x -= rhs.x;
|
|
190
|
+
this.y -= rhs.y;
|
|
191
|
+
this.z -= rhs.z;
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
sub2(lhs, rhs) {
|
|
195
|
+
this.x = lhs.x - rhs.x;
|
|
196
|
+
this.y = lhs.y - rhs.y;
|
|
197
|
+
this.z = lhs.z - rhs.z;
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
subScalar(scalar) {
|
|
201
|
+
this.x -= scalar;
|
|
202
|
+
this.y -= scalar;
|
|
203
|
+
this.z -= scalar;
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
fromArray(arr, offset = 0) {
|
|
207
|
+
this.x = arr[offset] ?? this.x;
|
|
208
|
+
this.y = arr[offset + 1] ?? this.y;
|
|
209
|
+
this.z = arr[offset + 2] ?? this.z;
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
toString() {
|
|
213
|
+
return `[${this.x}, ${this.y}, ${this.z}]`;
|
|
214
|
+
}
|
|
215
|
+
toArray(arr = [], offset = 0) {
|
|
216
|
+
arr[offset] = this.x;
|
|
217
|
+
arr[offset + 1] = this.y;
|
|
218
|
+
arr[offset + 2] = this.z;
|
|
219
|
+
return arr;
|
|
220
|
+
}
|
|
221
|
+
static{
|
|
222
|
+
this.ZERO = Object.freeze(new Vec3(0, 0, 0));
|
|
223
|
+
}
|
|
224
|
+
static{
|
|
225
|
+
this.HALF = Object.freeze(new Vec3(0.5, 0.5, 0.5));
|
|
226
|
+
}
|
|
227
|
+
static{
|
|
228
|
+
this.ONE = Object.freeze(new Vec3(1, 1, 1));
|
|
229
|
+
}
|
|
230
|
+
static{
|
|
231
|
+
this.UP = Object.freeze(new Vec3(0, 1, 0));
|
|
232
|
+
}
|
|
233
|
+
static{
|
|
234
|
+
this.DOWN = Object.freeze(new Vec3(0, -1, 0));
|
|
235
|
+
}
|
|
236
|
+
static{
|
|
237
|
+
this.RIGHT = Object.freeze(new Vec3(1, 0, 0));
|
|
238
|
+
}
|
|
239
|
+
static{
|
|
240
|
+
this.LEFT = Object.freeze(new Vec3(-1, 0, 0));
|
|
241
|
+
}
|
|
242
|
+
static{
|
|
243
|
+
this.FORWARD = Object.freeze(new Vec3(0, 0, -1));
|
|
244
|
+
}
|
|
245
|
+
static{
|
|
246
|
+
this.BACK = Object.freeze(new Vec3(0, 0, 1));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
class Mat3 {
|
|
251
|
+
constructor(){
|
|
252
|
+
this.data = new Float32Array(9);
|
|
253
|
+
this.data[0] = this.data[4] = this.data[8] = 1;
|
|
254
|
+
}
|
|
255
|
+
clone() {
|
|
256
|
+
const cstr = this.constructor;
|
|
257
|
+
return new cstr().copy(this);
|
|
258
|
+
}
|
|
259
|
+
copy(rhs) {
|
|
260
|
+
const src = rhs.data;
|
|
261
|
+
const dst = this.data;
|
|
262
|
+
dst[0] = src[0];
|
|
263
|
+
dst[1] = src[1];
|
|
264
|
+
dst[2] = src[2];
|
|
265
|
+
dst[3] = src[3];
|
|
266
|
+
dst[4] = src[4];
|
|
267
|
+
dst[5] = src[5];
|
|
268
|
+
dst[6] = src[6];
|
|
269
|
+
dst[7] = src[7];
|
|
270
|
+
dst[8] = src[8];
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
set(src) {
|
|
274
|
+
const dst = this.data;
|
|
275
|
+
dst[0] = src[0];
|
|
276
|
+
dst[1] = src[1];
|
|
277
|
+
dst[2] = src[2];
|
|
278
|
+
dst[3] = src[3];
|
|
279
|
+
dst[4] = src[4];
|
|
280
|
+
dst[5] = src[5];
|
|
281
|
+
dst[6] = src[6];
|
|
282
|
+
dst[7] = src[7];
|
|
283
|
+
dst[8] = src[8];
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
getX(x = new Vec3()) {
|
|
287
|
+
return x.set(this.data[0], this.data[1], this.data[2]);
|
|
288
|
+
}
|
|
289
|
+
getY(y = new Vec3()) {
|
|
290
|
+
return y.set(this.data[3], this.data[4], this.data[5]);
|
|
291
|
+
}
|
|
292
|
+
getZ(z = new Vec3()) {
|
|
293
|
+
return z.set(this.data[6], this.data[7], this.data[8]);
|
|
294
|
+
}
|
|
295
|
+
equals(rhs) {
|
|
296
|
+
const l = this.data;
|
|
297
|
+
const r = rhs.data;
|
|
298
|
+
return l[0] === r[0] && l[1] === r[1] && l[2] === r[2] && l[3] === r[3] && l[4] === r[4] && l[5] === r[5] && l[6] === r[6] && l[7] === r[7] && l[8] === r[8];
|
|
299
|
+
}
|
|
300
|
+
isIdentity() {
|
|
301
|
+
const m = this.data;
|
|
302
|
+
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0 && m[4] === 1 && m[5] === 0 && m[6] === 0 && m[7] === 0 && m[8] === 1;
|
|
303
|
+
}
|
|
304
|
+
setIdentity() {
|
|
305
|
+
const m = this.data;
|
|
306
|
+
m[0] = 1;
|
|
307
|
+
m[1] = 0;
|
|
308
|
+
m[2] = 0;
|
|
309
|
+
m[3] = 0;
|
|
310
|
+
m[4] = 1;
|
|
311
|
+
m[5] = 0;
|
|
312
|
+
m[6] = 0;
|
|
313
|
+
m[7] = 0;
|
|
314
|
+
m[8] = 1;
|
|
315
|
+
return this;
|
|
316
|
+
}
|
|
317
|
+
toString() {
|
|
318
|
+
return `[${this.data.join(', ')}]`;
|
|
319
|
+
}
|
|
320
|
+
transpose(src = this) {
|
|
321
|
+
const s = src.data;
|
|
322
|
+
const t = this.data;
|
|
323
|
+
if (s === t) {
|
|
324
|
+
let tmp;
|
|
325
|
+
tmp = s[1];
|
|
326
|
+
t[1] = s[3];
|
|
327
|
+
t[3] = tmp;
|
|
328
|
+
tmp = s[2];
|
|
329
|
+
t[2] = s[6];
|
|
330
|
+
t[6] = tmp;
|
|
331
|
+
tmp = s[5];
|
|
332
|
+
t[5] = s[7];
|
|
333
|
+
t[7] = tmp;
|
|
334
|
+
} else {
|
|
335
|
+
t[0] = s[0];
|
|
336
|
+
t[1] = s[3];
|
|
337
|
+
t[2] = s[6];
|
|
338
|
+
t[3] = s[1];
|
|
339
|
+
t[4] = s[4];
|
|
340
|
+
t[5] = s[7];
|
|
341
|
+
t[6] = s[2];
|
|
342
|
+
t[7] = s[5];
|
|
343
|
+
t[8] = s[8];
|
|
344
|
+
}
|
|
345
|
+
return this;
|
|
346
|
+
}
|
|
347
|
+
setFromMat4(m) {
|
|
348
|
+
const src = m.data;
|
|
349
|
+
const dst = this.data;
|
|
350
|
+
dst[0] = src[0];
|
|
351
|
+
dst[1] = src[1];
|
|
352
|
+
dst[2] = src[2];
|
|
353
|
+
dst[3] = src[4];
|
|
354
|
+
dst[4] = src[5];
|
|
355
|
+
dst[5] = src[6];
|
|
356
|
+
dst[6] = src[8];
|
|
357
|
+
dst[7] = src[9];
|
|
358
|
+
dst[8] = src[10];
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
setFromQuat(r) {
|
|
362
|
+
const qx = r.x;
|
|
363
|
+
const qy = r.y;
|
|
364
|
+
const qz = r.z;
|
|
365
|
+
const qw = r.w;
|
|
366
|
+
const x2 = qx + qx;
|
|
367
|
+
const y2 = qy + qy;
|
|
368
|
+
const z2 = qz + qz;
|
|
369
|
+
const xx = qx * x2;
|
|
370
|
+
const xy = qx * y2;
|
|
371
|
+
const xz = qx * z2;
|
|
372
|
+
const yy = qy * y2;
|
|
373
|
+
const yz = qy * z2;
|
|
374
|
+
const zz = qz * z2;
|
|
375
|
+
const wx = qw * x2;
|
|
376
|
+
const wy = qw * y2;
|
|
377
|
+
const wz = qw * z2;
|
|
378
|
+
const m = this.data;
|
|
379
|
+
m[0] = 1 - (yy + zz);
|
|
380
|
+
m[1] = xy + wz;
|
|
381
|
+
m[2] = xz - wy;
|
|
382
|
+
m[3] = xy - wz;
|
|
383
|
+
m[4] = 1 - (xx + zz);
|
|
384
|
+
m[5] = yz + wx;
|
|
385
|
+
m[6] = xz + wy;
|
|
386
|
+
m[7] = yz - wx;
|
|
387
|
+
m[8] = 1 - (xx + yy);
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
invertMat4(src) {
|
|
391
|
+
const s = src.data;
|
|
392
|
+
const a0 = s[0];
|
|
393
|
+
const a1 = s[1];
|
|
394
|
+
const a2 = s[2];
|
|
395
|
+
const a4 = s[4];
|
|
396
|
+
const a5 = s[5];
|
|
397
|
+
const a6 = s[6];
|
|
398
|
+
const a8 = s[8];
|
|
399
|
+
const a9 = s[9];
|
|
400
|
+
const a10 = s[10];
|
|
401
|
+
const b11 = a10 * a5 - a6 * a9;
|
|
402
|
+
const b21 = -a10 * a1 + a2 * a9;
|
|
403
|
+
const b31 = a6 * a1 - a2 * a5;
|
|
404
|
+
const b12 = -a10 * a4 + a6 * a8;
|
|
405
|
+
const b22 = a10 * a0 - a2 * a8;
|
|
406
|
+
const b32 = -a6 * a0 + a2 * a4;
|
|
407
|
+
const b13 = a9 * a4 - a5 * a8;
|
|
408
|
+
const b23 = -a9 * a0 + a1 * a8;
|
|
409
|
+
const b33 = a5 * a0 - a1 * a4;
|
|
410
|
+
const det = a0 * b11 + a1 * b12 + a2 * b13;
|
|
411
|
+
if (det === 0) {
|
|
412
|
+
this.setIdentity();
|
|
413
|
+
} else {
|
|
414
|
+
const invDet = 1 / det;
|
|
415
|
+
const t = this.data;
|
|
416
|
+
t[0] = b11 * invDet;
|
|
417
|
+
t[1] = b21 * invDet;
|
|
418
|
+
t[2] = b31 * invDet;
|
|
419
|
+
t[3] = b12 * invDet;
|
|
420
|
+
t[4] = b22 * invDet;
|
|
421
|
+
t[5] = b32 * invDet;
|
|
422
|
+
t[6] = b13 * invDet;
|
|
423
|
+
t[7] = b23 * invDet;
|
|
424
|
+
t[8] = b33 * invDet;
|
|
425
|
+
}
|
|
426
|
+
return this;
|
|
427
|
+
}
|
|
428
|
+
transformVector(vec, res = new Vec3()) {
|
|
429
|
+
const m = this.data;
|
|
430
|
+
const { x, y, z } = vec;
|
|
431
|
+
res.x = x * m[0] + y * m[3] + z * m[6];
|
|
432
|
+
res.y = x * m[1] + y * m[4] + z * m[7];
|
|
433
|
+
res.z = x * m[2] + y * m[5] + z * m[8];
|
|
434
|
+
return res;
|
|
435
|
+
}
|
|
436
|
+
static{
|
|
437
|
+
this.IDENTITY = Object.freeze(new Mat3());
|
|
438
|
+
}
|
|
439
|
+
static{
|
|
440
|
+
this.ZERO = Object.freeze(new Mat3().set([
|
|
441
|
+
0,
|
|
442
|
+
0,
|
|
443
|
+
0,
|
|
444
|
+
0,
|
|
445
|
+
0,
|
|
446
|
+
0,
|
|
447
|
+
0,
|
|
448
|
+
0,
|
|
449
|
+
0
|
|
450
|
+
]));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
class Vec2 {
|
|
455
|
+
constructor(x = 0, y = 0){
|
|
456
|
+
if (x.length === 2) {
|
|
457
|
+
this.x = x[0];
|
|
458
|
+
this.y = x[1];
|
|
459
|
+
} else {
|
|
460
|
+
this.x = x;
|
|
461
|
+
this.y = y;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
add(rhs) {
|
|
465
|
+
this.x += rhs.x;
|
|
466
|
+
this.y += rhs.y;
|
|
467
|
+
return this;
|
|
468
|
+
}
|
|
469
|
+
add2(lhs, rhs) {
|
|
470
|
+
this.x = lhs.x + rhs.x;
|
|
471
|
+
this.y = lhs.y + rhs.y;
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
addScalar(scalar) {
|
|
475
|
+
this.x += scalar;
|
|
476
|
+
this.y += scalar;
|
|
477
|
+
return this;
|
|
478
|
+
}
|
|
479
|
+
addScaled(rhs, scalar) {
|
|
480
|
+
this.x += rhs.x * scalar;
|
|
481
|
+
this.y += rhs.y * scalar;
|
|
482
|
+
return this;
|
|
483
|
+
}
|
|
484
|
+
clone() {
|
|
485
|
+
const cstr = this.constructor;
|
|
486
|
+
return new cstr(this.x, this.y);
|
|
487
|
+
}
|
|
488
|
+
copy(rhs) {
|
|
489
|
+
this.x = rhs.x;
|
|
490
|
+
this.y = rhs.y;
|
|
491
|
+
return this;
|
|
492
|
+
}
|
|
493
|
+
cross(rhs) {
|
|
494
|
+
return this.x * rhs.y - this.y * rhs.x;
|
|
495
|
+
}
|
|
496
|
+
distance(rhs) {
|
|
497
|
+
const x = this.x - rhs.x;
|
|
498
|
+
const y = this.y - rhs.y;
|
|
499
|
+
return Math.sqrt(x * x + y * y);
|
|
500
|
+
}
|
|
501
|
+
div(rhs) {
|
|
502
|
+
this.x /= rhs.x;
|
|
503
|
+
this.y /= rhs.y;
|
|
504
|
+
return this;
|
|
505
|
+
}
|
|
506
|
+
div2(lhs, rhs) {
|
|
507
|
+
this.x = lhs.x / rhs.x;
|
|
508
|
+
this.y = lhs.y / rhs.y;
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
divScalar(scalar) {
|
|
512
|
+
this.x /= scalar;
|
|
513
|
+
this.y /= scalar;
|
|
514
|
+
return this;
|
|
515
|
+
}
|
|
516
|
+
dot(rhs) {
|
|
517
|
+
return this.x * rhs.x + this.y * rhs.y;
|
|
518
|
+
}
|
|
519
|
+
equals(rhs) {
|
|
520
|
+
return this.x === rhs.x && this.y === rhs.y;
|
|
521
|
+
}
|
|
522
|
+
equalsApprox(rhs, epsilon = 1e-6) {
|
|
523
|
+
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon;
|
|
524
|
+
}
|
|
525
|
+
length() {
|
|
526
|
+
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
527
|
+
}
|
|
528
|
+
lengthSq() {
|
|
529
|
+
return this.x * this.x + this.y * this.y;
|
|
530
|
+
}
|
|
531
|
+
lerp(lhs, rhs, alpha) {
|
|
532
|
+
this.x = lhs.x + alpha * (rhs.x - lhs.x);
|
|
533
|
+
this.y = lhs.y + alpha * (rhs.y - lhs.y);
|
|
534
|
+
return this;
|
|
535
|
+
}
|
|
536
|
+
mul(rhs) {
|
|
537
|
+
this.x *= rhs.x;
|
|
538
|
+
this.y *= rhs.y;
|
|
539
|
+
return this;
|
|
540
|
+
}
|
|
541
|
+
mul2(lhs, rhs) {
|
|
542
|
+
this.x = lhs.x * rhs.x;
|
|
543
|
+
this.y = lhs.y * rhs.y;
|
|
544
|
+
return this;
|
|
545
|
+
}
|
|
546
|
+
mulScalar(scalar) {
|
|
547
|
+
this.x *= scalar;
|
|
548
|
+
this.y *= scalar;
|
|
549
|
+
return this;
|
|
550
|
+
}
|
|
551
|
+
normalize(src = this) {
|
|
552
|
+
const lengthSq = src.x * src.x + src.y * src.y;
|
|
553
|
+
if (lengthSq > 0) {
|
|
554
|
+
const invLength = 1 / Math.sqrt(lengthSq);
|
|
555
|
+
this.x = src.x * invLength;
|
|
556
|
+
this.y = src.y * invLength;
|
|
557
|
+
}
|
|
558
|
+
return this;
|
|
559
|
+
}
|
|
560
|
+
rotate(degrees) {
|
|
561
|
+
const angle = Math.atan2(this.x, this.y) + degrees * math.DEG_TO_RAD;
|
|
562
|
+
const len = Math.sqrt(this.x * this.x + this.y * this.y);
|
|
563
|
+
this.x = Math.sin(angle) * len;
|
|
564
|
+
this.y = Math.cos(angle) * len;
|
|
565
|
+
return this;
|
|
566
|
+
}
|
|
567
|
+
angle() {
|
|
568
|
+
return Math.atan2(this.x, this.y) * math.RAD_TO_DEG;
|
|
569
|
+
}
|
|
570
|
+
angleTo(rhs) {
|
|
571
|
+
return Math.atan2(this.x * rhs.y + this.y * rhs.x, this.x * rhs.x + this.y * rhs.y) * math.RAD_TO_DEG;
|
|
572
|
+
}
|
|
573
|
+
floor(src = this) {
|
|
574
|
+
this.x = Math.floor(src.x);
|
|
575
|
+
this.y = Math.floor(src.y);
|
|
576
|
+
return this;
|
|
577
|
+
}
|
|
578
|
+
ceil(src = this) {
|
|
579
|
+
this.x = Math.ceil(src.x);
|
|
580
|
+
this.y = Math.ceil(src.y);
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
round(src = this) {
|
|
584
|
+
this.x = Math.round(src.x);
|
|
585
|
+
this.y = Math.round(src.y);
|
|
586
|
+
return this;
|
|
587
|
+
}
|
|
588
|
+
min(rhs) {
|
|
589
|
+
if (rhs.x < this.x) this.x = rhs.x;
|
|
590
|
+
if (rhs.y < this.y) this.y = rhs.y;
|
|
591
|
+
return this;
|
|
592
|
+
}
|
|
593
|
+
max(rhs) {
|
|
594
|
+
if (rhs.x > this.x) this.x = rhs.x;
|
|
595
|
+
if (rhs.y > this.y) this.y = rhs.y;
|
|
596
|
+
return this;
|
|
597
|
+
}
|
|
598
|
+
set(x, y) {
|
|
599
|
+
this.x = x;
|
|
600
|
+
this.y = y;
|
|
601
|
+
return this;
|
|
602
|
+
}
|
|
603
|
+
sub(rhs) {
|
|
604
|
+
this.x -= rhs.x;
|
|
605
|
+
this.y -= rhs.y;
|
|
606
|
+
return this;
|
|
607
|
+
}
|
|
608
|
+
sub2(lhs, rhs) {
|
|
609
|
+
this.x = lhs.x - rhs.x;
|
|
610
|
+
this.y = lhs.y - rhs.y;
|
|
611
|
+
return this;
|
|
612
|
+
}
|
|
613
|
+
subScalar(scalar) {
|
|
614
|
+
this.x -= scalar;
|
|
615
|
+
this.y -= scalar;
|
|
616
|
+
return this;
|
|
617
|
+
}
|
|
618
|
+
fromArray(arr, offset = 0) {
|
|
619
|
+
this.x = arr[offset] ?? this.x;
|
|
620
|
+
this.y = arr[offset + 1] ?? this.y;
|
|
621
|
+
return this;
|
|
622
|
+
}
|
|
623
|
+
toString() {
|
|
624
|
+
return `[${this.x}, ${this.y}]`;
|
|
625
|
+
}
|
|
626
|
+
toArray(arr = [], offset = 0) {
|
|
627
|
+
arr[offset] = this.x;
|
|
628
|
+
arr[offset + 1] = this.y;
|
|
629
|
+
return arr;
|
|
630
|
+
}
|
|
631
|
+
static angleRad(lhs, rhs) {
|
|
632
|
+
return Math.atan2(lhs.x * rhs.y - lhs.y * rhs.x, lhs.x * rhs.x + lhs.y * rhs.y);
|
|
633
|
+
}
|
|
634
|
+
static{
|
|
635
|
+
this.ZERO = Object.freeze(new Vec2(0, 0));
|
|
636
|
+
}
|
|
637
|
+
static{
|
|
638
|
+
this.HALF = Object.freeze(new Vec2(0.5, 0.5));
|
|
639
|
+
}
|
|
640
|
+
static{
|
|
641
|
+
this.ONE = Object.freeze(new Vec2(1, 1));
|
|
642
|
+
}
|
|
643
|
+
static{
|
|
644
|
+
this.UP = Object.freeze(new Vec2(0, 1));
|
|
645
|
+
}
|
|
646
|
+
static{
|
|
647
|
+
this.DOWN = Object.freeze(new Vec2(0, -1));
|
|
648
|
+
}
|
|
649
|
+
static{
|
|
650
|
+
this.RIGHT = Object.freeze(new Vec2(1, 0));
|
|
651
|
+
}
|
|
652
|
+
static{
|
|
653
|
+
this.LEFT = Object.freeze(new Vec2(-1, 0));
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
class Vec4 {
|
|
658
|
+
constructor(x = 0, y = 0, z = 0, w = 0){
|
|
659
|
+
if (x.length === 4) {
|
|
660
|
+
this.x = x[0];
|
|
661
|
+
this.y = x[1];
|
|
662
|
+
this.z = x[2];
|
|
663
|
+
this.w = x[3];
|
|
664
|
+
} else {
|
|
665
|
+
this.x = x;
|
|
666
|
+
this.y = y;
|
|
667
|
+
this.z = z;
|
|
668
|
+
this.w = w;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
add(rhs) {
|
|
672
|
+
this.x += rhs.x;
|
|
673
|
+
this.y += rhs.y;
|
|
674
|
+
this.z += rhs.z;
|
|
675
|
+
this.w += rhs.w;
|
|
676
|
+
return this;
|
|
677
|
+
}
|
|
678
|
+
add2(lhs, rhs) {
|
|
679
|
+
this.x = lhs.x + rhs.x;
|
|
680
|
+
this.y = lhs.y + rhs.y;
|
|
681
|
+
this.z = lhs.z + rhs.z;
|
|
682
|
+
this.w = lhs.w + rhs.w;
|
|
683
|
+
return this;
|
|
684
|
+
}
|
|
685
|
+
addScalar(scalar) {
|
|
686
|
+
this.x += scalar;
|
|
687
|
+
this.y += scalar;
|
|
688
|
+
this.z += scalar;
|
|
689
|
+
this.w += scalar;
|
|
690
|
+
return this;
|
|
691
|
+
}
|
|
692
|
+
addScaled(rhs, scalar) {
|
|
693
|
+
this.x += rhs.x * scalar;
|
|
694
|
+
this.y += rhs.y * scalar;
|
|
695
|
+
this.z += rhs.z * scalar;
|
|
696
|
+
this.w += rhs.w * scalar;
|
|
697
|
+
return this;
|
|
698
|
+
}
|
|
699
|
+
clone() {
|
|
700
|
+
const cstr = this.constructor;
|
|
701
|
+
return new cstr(this.x, this.y, this.z, this.w);
|
|
702
|
+
}
|
|
703
|
+
copy(rhs) {
|
|
704
|
+
this.x = rhs.x;
|
|
705
|
+
this.y = rhs.y;
|
|
706
|
+
this.z = rhs.z;
|
|
707
|
+
this.w = rhs.w;
|
|
708
|
+
return this;
|
|
709
|
+
}
|
|
710
|
+
div(rhs) {
|
|
711
|
+
this.x /= rhs.x;
|
|
712
|
+
this.y /= rhs.y;
|
|
713
|
+
this.z /= rhs.z;
|
|
714
|
+
this.w /= rhs.w;
|
|
715
|
+
return this;
|
|
716
|
+
}
|
|
717
|
+
div2(lhs, rhs) {
|
|
718
|
+
this.x = lhs.x / rhs.x;
|
|
719
|
+
this.y = lhs.y / rhs.y;
|
|
720
|
+
this.z = lhs.z / rhs.z;
|
|
721
|
+
this.w = lhs.w / rhs.w;
|
|
722
|
+
return this;
|
|
723
|
+
}
|
|
724
|
+
divScalar(scalar) {
|
|
725
|
+
this.x /= scalar;
|
|
726
|
+
this.y /= scalar;
|
|
727
|
+
this.z /= scalar;
|
|
728
|
+
this.w /= scalar;
|
|
729
|
+
return this;
|
|
730
|
+
}
|
|
731
|
+
dot(rhs) {
|
|
732
|
+
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z + this.w * rhs.w;
|
|
733
|
+
}
|
|
734
|
+
equals(rhs) {
|
|
735
|
+
return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z && this.w === rhs.w;
|
|
736
|
+
}
|
|
737
|
+
equalsApprox(rhs, epsilon = 1e-6) {
|
|
738
|
+
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon && Math.abs(this.w - rhs.w) < epsilon;
|
|
739
|
+
}
|
|
740
|
+
length() {
|
|
741
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
742
|
+
}
|
|
743
|
+
lengthSq() {
|
|
744
|
+
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
|
|
745
|
+
}
|
|
746
|
+
lerp(lhs, rhs, alpha) {
|
|
747
|
+
this.x = lhs.x + alpha * (rhs.x - lhs.x);
|
|
748
|
+
this.y = lhs.y + alpha * (rhs.y - lhs.y);
|
|
749
|
+
this.z = lhs.z + alpha * (rhs.z - lhs.z);
|
|
750
|
+
this.w = lhs.w + alpha * (rhs.w - lhs.w);
|
|
751
|
+
return this;
|
|
752
|
+
}
|
|
753
|
+
mul(rhs) {
|
|
754
|
+
this.x *= rhs.x;
|
|
755
|
+
this.y *= rhs.y;
|
|
756
|
+
this.z *= rhs.z;
|
|
757
|
+
this.w *= rhs.w;
|
|
758
|
+
return this;
|
|
759
|
+
}
|
|
760
|
+
mul2(lhs, rhs) {
|
|
761
|
+
this.x = lhs.x * rhs.x;
|
|
762
|
+
this.y = lhs.y * rhs.y;
|
|
763
|
+
this.z = lhs.z * rhs.z;
|
|
764
|
+
this.w = lhs.w * rhs.w;
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
mulScalar(scalar) {
|
|
768
|
+
this.x *= scalar;
|
|
769
|
+
this.y *= scalar;
|
|
770
|
+
this.z *= scalar;
|
|
771
|
+
this.w *= scalar;
|
|
772
|
+
return this;
|
|
773
|
+
}
|
|
774
|
+
normalize(src = this) {
|
|
775
|
+
const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z + src.w * src.w;
|
|
776
|
+
if (lengthSq > 0) {
|
|
777
|
+
const invLength = 1 / Math.sqrt(lengthSq);
|
|
778
|
+
this.x = src.x * invLength;
|
|
779
|
+
this.y = src.y * invLength;
|
|
780
|
+
this.z = src.z * invLength;
|
|
781
|
+
this.w = src.w * invLength;
|
|
782
|
+
}
|
|
783
|
+
return this;
|
|
784
|
+
}
|
|
785
|
+
floor(src = this) {
|
|
786
|
+
this.x = Math.floor(src.x);
|
|
787
|
+
this.y = Math.floor(src.y);
|
|
788
|
+
this.z = Math.floor(src.z);
|
|
789
|
+
this.w = Math.floor(src.w);
|
|
790
|
+
return this;
|
|
791
|
+
}
|
|
792
|
+
ceil(src = this) {
|
|
793
|
+
this.x = Math.ceil(src.x);
|
|
794
|
+
this.y = Math.ceil(src.y);
|
|
795
|
+
this.z = Math.ceil(src.z);
|
|
796
|
+
this.w = Math.ceil(src.w);
|
|
797
|
+
return this;
|
|
798
|
+
}
|
|
799
|
+
round(src = this) {
|
|
800
|
+
this.x = Math.round(src.x);
|
|
801
|
+
this.y = Math.round(src.y);
|
|
802
|
+
this.z = Math.round(src.z);
|
|
803
|
+
this.w = Math.round(src.w);
|
|
804
|
+
return this;
|
|
805
|
+
}
|
|
806
|
+
min(rhs) {
|
|
807
|
+
if (rhs.x < this.x) this.x = rhs.x;
|
|
808
|
+
if (rhs.y < this.y) this.y = rhs.y;
|
|
809
|
+
if (rhs.z < this.z) this.z = rhs.z;
|
|
810
|
+
if (rhs.w < this.w) this.w = rhs.w;
|
|
811
|
+
return this;
|
|
812
|
+
}
|
|
813
|
+
max(rhs) {
|
|
814
|
+
if (rhs.x > this.x) this.x = rhs.x;
|
|
815
|
+
if (rhs.y > this.y) this.y = rhs.y;
|
|
816
|
+
if (rhs.z > this.z) this.z = rhs.z;
|
|
817
|
+
if (rhs.w > this.w) this.w = rhs.w;
|
|
818
|
+
return this;
|
|
819
|
+
}
|
|
820
|
+
set(x, y, z, w) {
|
|
821
|
+
this.x = x;
|
|
822
|
+
this.y = y;
|
|
823
|
+
this.z = z;
|
|
824
|
+
this.w = w;
|
|
825
|
+
return this;
|
|
826
|
+
}
|
|
827
|
+
sub(rhs) {
|
|
828
|
+
this.x -= rhs.x;
|
|
829
|
+
this.y -= rhs.y;
|
|
830
|
+
this.z -= rhs.z;
|
|
831
|
+
this.w -= rhs.w;
|
|
832
|
+
return this;
|
|
833
|
+
}
|
|
834
|
+
sub2(lhs, rhs) {
|
|
835
|
+
this.x = lhs.x - rhs.x;
|
|
836
|
+
this.y = lhs.y - rhs.y;
|
|
837
|
+
this.z = lhs.z - rhs.z;
|
|
838
|
+
this.w = lhs.w - rhs.w;
|
|
839
|
+
return this;
|
|
840
|
+
}
|
|
841
|
+
subScalar(scalar) {
|
|
842
|
+
this.x -= scalar;
|
|
843
|
+
this.y -= scalar;
|
|
844
|
+
this.z -= scalar;
|
|
845
|
+
this.w -= scalar;
|
|
846
|
+
return this;
|
|
847
|
+
}
|
|
848
|
+
fromArray(arr, offset = 0) {
|
|
849
|
+
this.x = arr[offset] ?? this.x;
|
|
850
|
+
this.y = arr[offset + 1] ?? this.y;
|
|
851
|
+
this.z = arr[offset + 2] ?? this.z;
|
|
852
|
+
this.w = arr[offset + 3] ?? this.w;
|
|
853
|
+
return this;
|
|
854
|
+
}
|
|
855
|
+
toString() {
|
|
856
|
+
return `[${this.x}, ${this.y}, ${this.z}, ${this.w}]`;
|
|
857
|
+
}
|
|
858
|
+
toArray(arr = [], offset = 0) {
|
|
859
|
+
arr[offset] = this.x;
|
|
860
|
+
arr[offset + 1] = this.y;
|
|
861
|
+
arr[offset + 2] = this.z;
|
|
862
|
+
arr[offset + 3] = this.w;
|
|
863
|
+
return arr;
|
|
864
|
+
}
|
|
865
|
+
static{
|
|
866
|
+
this.ZERO = Object.freeze(new Vec4(0, 0, 0, 0));
|
|
867
|
+
}
|
|
868
|
+
static{
|
|
869
|
+
this.HALF = Object.freeze(new Vec4(0.5, 0.5, 0.5, 0.5));
|
|
870
|
+
}
|
|
871
|
+
static{
|
|
872
|
+
this.ONE = Object.freeze(new Vec4(1, 1, 1, 1));
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
const _halfSize = new Vec2();
|
|
877
|
+
const x = new Vec3();
|
|
878
|
+
const y = new Vec3();
|
|
879
|
+
const z = new Vec3();
|
|
880
|
+
const scale = new Vec3();
|
|
881
|
+
class Mat4 {
|
|
882
|
+
constructor(){
|
|
883
|
+
this.data = new Float32Array(16);
|
|
884
|
+
this.data[0] = this.data[5] = this.data[10] = this.data[15] = 1;
|
|
885
|
+
}
|
|
886
|
+
static _getPerspectiveHalfSize(halfSize, fov, aspect, znear, fovIsHorizontal) {
|
|
887
|
+
if (fovIsHorizontal) {
|
|
888
|
+
halfSize.x = znear * Math.tan(fov * Math.PI / 360);
|
|
889
|
+
halfSize.y = halfSize.x / aspect;
|
|
890
|
+
} else {
|
|
891
|
+
halfSize.y = znear * Math.tan(fov * Math.PI / 360);
|
|
892
|
+
halfSize.x = halfSize.y * aspect;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
add2(lhs, rhs) {
|
|
896
|
+
const a = lhs.data, b = rhs.data, r = this.data;
|
|
897
|
+
r[0] = a[0] + b[0];
|
|
898
|
+
r[1] = a[1] + b[1];
|
|
899
|
+
r[2] = a[2] + b[2];
|
|
900
|
+
r[3] = a[3] + b[3];
|
|
901
|
+
r[4] = a[4] + b[4];
|
|
902
|
+
r[5] = a[5] + b[5];
|
|
903
|
+
r[6] = a[6] + b[6];
|
|
904
|
+
r[7] = a[7] + b[7];
|
|
905
|
+
r[8] = a[8] + b[8];
|
|
906
|
+
r[9] = a[9] + b[9];
|
|
907
|
+
r[10] = a[10] + b[10];
|
|
908
|
+
r[11] = a[11] + b[11];
|
|
909
|
+
r[12] = a[12] + b[12];
|
|
910
|
+
r[13] = a[13] + b[13];
|
|
911
|
+
r[14] = a[14] + b[14];
|
|
912
|
+
r[15] = a[15] + b[15];
|
|
913
|
+
return this;
|
|
914
|
+
}
|
|
915
|
+
add(rhs) {
|
|
916
|
+
return this.add2(this, rhs);
|
|
917
|
+
}
|
|
918
|
+
clone() {
|
|
919
|
+
const cstr = this.constructor;
|
|
920
|
+
return new cstr().copy(this);
|
|
921
|
+
}
|
|
922
|
+
copy(rhs) {
|
|
923
|
+
const src = rhs.data, dst = this.data;
|
|
924
|
+
dst[0] = src[0];
|
|
925
|
+
dst[1] = src[1];
|
|
926
|
+
dst[2] = src[2];
|
|
927
|
+
dst[3] = src[3];
|
|
928
|
+
dst[4] = src[4];
|
|
929
|
+
dst[5] = src[5];
|
|
930
|
+
dst[6] = src[6];
|
|
931
|
+
dst[7] = src[7];
|
|
932
|
+
dst[8] = src[8];
|
|
933
|
+
dst[9] = src[9];
|
|
934
|
+
dst[10] = src[10];
|
|
935
|
+
dst[11] = src[11];
|
|
936
|
+
dst[12] = src[12];
|
|
937
|
+
dst[13] = src[13];
|
|
938
|
+
dst[14] = src[14];
|
|
939
|
+
dst[15] = src[15];
|
|
940
|
+
return this;
|
|
941
|
+
}
|
|
942
|
+
equals(rhs) {
|
|
943
|
+
const l = this.data, r = rhs.data;
|
|
944
|
+
return l[0] === r[0] && l[1] === r[1] && l[2] === r[2] && l[3] === r[3] && l[4] === r[4] && l[5] === r[5] && l[6] === r[6] && l[7] === r[7] && l[8] === r[8] && l[9] === r[9] && l[10] === r[10] && l[11] === r[11] && l[12] === r[12] && l[13] === r[13] && l[14] === r[14] && l[15] === r[15];
|
|
945
|
+
}
|
|
946
|
+
isIdentity() {
|
|
947
|
+
const m = this.data;
|
|
948
|
+
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0 && m[4] === 0 && m[5] === 1 && m[6] === 0 && m[7] === 0 && m[8] === 0 && m[9] === 0 && m[10] === 1 && m[11] === 0 && m[12] === 0 && m[13] === 0 && m[14] === 0 && m[15] === 1;
|
|
949
|
+
}
|
|
950
|
+
mul2(lhs, rhs) {
|
|
951
|
+
const a = lhs.data;
|
|
952
|
+
const b = rhs.data;
|
|
953
|
+
const r = this.data;
|
|
954
|
+
const a00 = a[0];
|
|
955
|
+
const a01 = a[1];
|
|
956
|
+
const a02 = a[2];
|
|
957
|
+
const a03 = a[3];
|
|
958
|
+
const a10 = a[4];
|
|
959
|
+
const a11 = a[5];
|
|
960
|
+
const a12 = a[6];
|
|
961
|
+
const a13 = a[7];
|
|
962
|
+
const a20 = a[8];
|
|
963
|
+
const a21 = a[9];
|
|
964
|
+
const a22 = a[10];
|
|
965
|
+
const a23 = a[11];
|
|
966
|
+
const a30 = a[12];
|
|
967
|
+
const a31 = a[13];
|
|
968
|
+
const a32 = a[14];
|
|
969
|
+
const a33 = a[15];
|
|
970
|
+
let b0, b1, b2, b3;
|
|
971
|
+
b0 = b[0];
|
|
972
|
+
b1 = b[1];
|
|
973
|
+
b2 = b[2];
|
|
974
|
+
b3 = b[3];
|
|
975
|
+
r[0] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
|
|
976
|
+
r[1] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
|
|
977
|
+
r[2] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
|
|
978
|
+
r[3] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
|
|
979
|
+
b0 = b[4];
|
|
980
|
+
b1 = b[5];
|
|
981
|
+
b2 = b[6];
|
|
982
|
+
b3 = b[7];
|
|
983
|
+
r[4] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
|
|
984
|
+
r[5] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
|
|
985
|
+
r[6] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
|
|
986
|
+
r[7] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
|
|
987
|
+
b0 = b[8];
|
|
988
|
+
b1 = b[9];
|
|
989
|
+
b2 = b[10];
|
|
990
|
+
b3 = b[11];
|
|
991
|
+
r[8] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
|
|
992
|
+
r[9] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
|
|
993
|
+
r[10] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
|
|
994
|
+
r[11] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
|
|
995
|
+
b0 = b[12];
|
|
996
|
+
b1 = b[13];
|
|
997
|
+
b2 = b[14];
|
|
998
|
+
b3 = b[15];
|
|
999
|
+
r[12] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
|
|
1000
|
+
r[13] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
|
|
1001
|
+
r[14] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
|
|
1002
|
+
r[15] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
|
|
1003
|
+
return this;
|
|
1004
|
+
}
|
|
1005
|
+
mulAffine2(lhs, rhs) {
|
|
1006
|
+
const a = lhs.data;
|
|
1007
|
+
const b = rhs.data;
|
|
1008
|
+
const r = this.data;
|
|
1009
|
+
const a00 = a[0];
|
|
1010
|
+
const a01 = a[1];
|
|
1011
|
+
const a02 = a[2];
|
|
1012
|
+
const a10 = a[4];
|
|
1013
|
+
const a11 = a[5];
|
|
1014
|
+
const a12 = a[6];
|
|
1015
|
+
const a20 = a[8];
|
|
1016
|
+
const a21 = a[9];
|
|
1017
|
+
const a22 = a[10];
|
|
1018
|
+
const a30 = a[12];
|
|
1019
|
+
const a31 = a[13];
|
|
1020
|
+
const a32 = a[14];
|
|
1021
|
+
let b0, b1, b2;
|
|
1022
|
+
b0 = b[0];
|
|
1023
|
+
b1 = b[1];
|
|
1024
|
+
b2 = b[2];
|
|
1025
|
+
r[0] = a00 * b0 + a10 * b1 + a20 * b2;
|
|
1026
|
+
r[1] = a01 * b0 + a11 * b1 + a21 * b2;
|
|
1027
|
+
r[2] = a02 * b0 + a12 * b1 + a22 * b2;
|
|
1028
|
+
r[3] = 0;
|
|
1029
|
+
b0 = b[4];
|
|
1030
|
+
b1 = b[5];
|
|
1031
|
+
b2 = b[6];
|
|
1032
|
+
r[4] = a00 * b0 + a10 * b1 + a20 * b2;
|
|
1033
|
+
r[5] = a01 * b0 + a11 * b1 + a21 * b2;
|
|
1034
|
+
r[6] = a02 * b0 + a12 * b1 + a22 * b2;
|
|
1035
|
+
r[7] = 0;
|
|
1036
|
+
b0 = b[8];
|
|
1037
|
+
b1 = b[9];
|
|
1038
|
+
b2 = b[10];
|
|
1039
|
+
r[8] = a00 * b0 + a10 * b1 + a20 * b2;
|
|
1040
|
+
r[9] = a01 * b0 + a11 * b1 + a21 * b2;
|
|
1041
|
+
r[10] = a02 * b0 + a12 * b1 + a22 * b2;
|
|
1042
|
+
r[11] = 0;
|
|
1043
|
+
b0 = b[12];
|
|
1044
|
+
b1 = b[13];
|
|
1045
|
+
b2 = b[14];
|
|
1046
|
+
r[12] = a00 * b0 + a10 * b1 + a20 * b2 + a30;
|
|
1047
|
+
r[13] = a01 * b0 + a11 * b1 + a21 * b2 + a31;
|
|
1048
|
+
r[14] = a02 * b0 + a12 * b1 + a22 * b2 + a32;
|
|
1049
|
+
r[15] = 1;
|
|
1050
|
+
return this;
|
|
1051
|
+
}
|
|
1052
|
+
mul(rhs) {
|
|
1053
|
+
return this.mul2(this, rhs);
|
|
1054
|
+
}
|
|
1055
|
+
transformPoint(vec, res = new Vec3()) {
|
|
1056
|
+
const m = this.data;
|
|
1057
|
+
const { x, y, z } = vec;
|
|
1058
|
+
res.x = x * m[0] + y * m[4] + z * m[8] + m[12];
|
|
1059
|
+
res.y = x * m[1] + y * m[5] + z * m[9] + m[13];
|
|
1060
|
+
res.z = x * m[2] + y * m[6] + z * m[10] + m[14];
|
|
1061
|
+
return res;
|
|
1062
|
+
}
|
|
1063
|
+
transformVector(vec, res = new Vec3()) {
|
|
1064
|
+
const m = this.data;
|
|
1065
|
+
const { x, y, z } = vec;
|
|
1066
|
+
res.x = x * m[0] + y * m[4] + z * m[8];
|
|
1067
|
+
res.y = x * m[1] + y * m[5] + z * m[9];
|
|
1068
|
+
res.z = x * m[2] + y * m[6] + z * m[10];
|
|
1069
|
+
return res;
|
|
1070
|
+
}
|
|
1071
|
+
transformVec4(vec, res = new Vec4()) {
|
|
1072
|
+
const m = this.data;
|
|
1073
|
+
const { x, y, z, w } = vec;
|
|
1074
|
+
res.x = x * m[0] + y * m[4] + z * m[8] + w * m[12];
|
|
1075
|
+
res.y = x * m[1] + y * m[5] + z * m[9] + w * m[13];
|
|
1076
|
+
res.z = x * m[2] + y * m[6] + z * m[10] + w * m[14];
|
|
1077
|
+
res.w = x * m[3] + y * m[7] + z * m[11] + w * m[15];
|
|
1078
|
+
return res;
|
|
1079
|
+
}
|
|
1080
|
+
setLookAt(position, target, up) {
|
|
1081
|
+
z.sub2(position, target).normalize();
|
|
1082
|
+
y.copy(up).normalize();
|
|
1083
|
+
x.cross(y, z).normalize();
|
|
1084
|
+
y.cross(z, x);
|
|
1085
|
+
const r = this.data;
|
|
1086
|
+
r[0] = x.x;
|
|
1087
|
+
r[1] = x.y;
|
|
1088
|
+
r[2] = x.z;
|
|
1089
|
+
r[3] = 0;
|
|
1090
|
+
r[4] = y.x;
|
|
1091
|
+
r[5] = y.y;
|
|
1092
|
+
r[6] = y.z;
|
|
1093
|
+
r[7] = 0;
|
|
1094
|
+
r[8] = z.x;
|
|
1095
|
+
r[9] = z.y;
|
|
1096
|
+
r[10] = z.z;
|
|
1097
|
+
r[11] = 0;
|
|
1098
|
+
r[12] = position.x;
|
|
1099
|
+
r[13] = position.y;
|
|
1100
|
+
r[14] = position.z;
|
|
1101
|
+
r[15] = 1;
|
|
1102
|
+
return this;
|
|
1103
|
+
}
|
|
1104
|
+
setFrustum(left, right, bottom, top, znear, zfar) {
|
|
1105
|
+
const temp1 = 2 * znear;
|
|
1106
|
+
const temp2 = right - left;
|
|
1107
|
+
const temp3 = top - bottom;
|
|
1108
|
+
const temp4 = zfar - znear;
|
|
1109
|
+
const r = this.data;
|
|
1110
|
+
r[0] = temp1 / temp2;
|
|
1111
|
+
r[1] = 0;
|
|
1112
|
+
r[2] = 0;
|
|
1113
|
+
r[3] = 0;
|
|
1114
|
+
r[4] = 0;
|
|
1115
|
+
r[5] = temp1 / temp3;
|
|
1116
|
+
r[6] = 0;
|
|
1117
|
+
r[7] = 0;
|
|
1118
|
+
r[8] = (right + left) / temp2;
|
|
1119
|
+
r[9] = (top + bottom) / temp3;
|
|
1120
|
+
r[10] = (-zfar - znear) / temp4;
|
|
1121
|
+
r[11] = -1;
|
|
1122
|
+
r[12] = 0;
|
|
1123
|
+
r[13] = 0;
|
|
1124
|
+
r[14] = -temp1 * zfar / temp4;
|
|
1125
|
+
r[15] = 0;
|
|
1126
|
+
return this;
|
|
1127
|
+
}
|
|
1128
|
+
setPerspective(fov, aspect, znear, zfar, fovIsHorizontal) {
|
|
1129
|
+
Mat4._getPerspectiveHalfSize(_halfSize, fov, aspect, znear, fovIsHorizontal);
|
|
1130
|
+
return this.setFrustum(-_halfSize.x, _halfSize.x, -_halfSize.y, _halfSize.y, znear, zfar);
|
|
1131
|
+
}
|
|
1132
|
+
setOrtho(left, right, bottom, top, near, far) {
|
|
1133
|
+
const r = this.data;
|
|
1134
|
+
r[0] = 2 / (right - left);
|
|
1135
|
+
r[1] = 0;
|
|
1136
|
+
r[2] = 0;
|
|
1137
|
+
r[3] = 0;
|
|
1138
|
+
r[4] = 0;
|
|
1139
|
+
r[5] = 2 / (top - bottom);
|
|
1140
|
+
r[6] = 0;
|
|
1141
|
+
r[7] = 0;
|
|
1142
|
+
r[8] = 0;
|
|
1143
|
+
r[9] = 0;
|
|
1144
|
+
r[10] = -2 / (far - near);
|
|
1145
|
+
r[11] = 0;
|
|
1146
|
+
r[12] = -(right + left) / (right - left);
|
|
1147
|
+
r[13] = -(top + bottom) / (top - bottom);
|
|
1148
|
+
r[14] = -(far + near) / (far - near);
|
|
1149
|
+
r[15] = 1;
|
|
1150
|
+
return this;
|
|
1151
|
+
}
|
|
1152
|
+
setFromAxisAngle(axis, angle) {
|
|
1153
|
+
angle *= math.DEG_TO_RAD;
|
|
1154
|
+
const { x, y, z } = axis;
|
|
1155
|
+
const c = Math.cos(angle);
|
|
1156
|
+
const s = Math.sin(angle);
|
|
1157
|
+
const t = 1 - c;
|
|
1158
|
+
const tx = t * x;
|
|
1159
|
+
const ty = t * y;
|
|
1160
|
+
const m = this.data;
|
|
1161
|
+
m[0] = tx * x + c;
|
|
1162
|
+
m[1] = tx * y + s * z;
|
|
1163
|
+
m[2] = tx * z - s * y;
|
|
1164
|
+
m[3] = 0;
|
|
1165
|
+
m[4] = tx * y - s * z;
|
|
1166
|
+
m[5] = ty * y + c;
|
|
1167
|
+
m[6] = ty * z + s * x;
|
|
1168
|
+
m[7] = 0;
|
|
1169
|
+
m[8] = tx * z + s * y;
|
|
1170
|
+
m[9] = ty * z - x * s;
|
|
1171
|
+
m[10] = t * z * z + c;
|
|
1172
|
+
m[11] = 0;
|
|
1173
|
+
m[12] = 0;
|
|
1174
|
+
m[13] = 0;
|
|
1175
|
+
m[14] = 0;
|
|
1176
|
+
m[15] = 1;
|
|
1177
|
+
return this;
|
|
1178
|
+
}
|
|
1179
|
+
setTranslate(x, y, z) {
|
|
1180
|
+
const m = this.data;
|
|
1181
|
+
m[0] = 1;
|
|
1182
|
+
m[1] = 0;
|
|
1183
|
+
m[2] = 0;
|
|
1184
|
+
m[3] = 0;
|
|
1185
|
+
m[4] = 0;
|
|
1186
|
+
m[5] = 1;
|
|
1187
|
+
m[6] = 0;
|
|
1188
|
+
m[7] = 0;
|
|
1189
|
+
m[8] = 0;
|
|
1190
|
+
m[9] = 0;
|
|
1191
|
+
m[10] = 1;
|
|
1192
|
+
m[11] = 0;
|
|
1193
|
+
m[12] = x;
|
|
1194
|
+
m[13] = y;
|
|
1195
|
+
m[14] = z;
|
|
1196
|
+
m[15] = 1;
|
|
1197
|
+
return this;
|
|
1198
|
+
}
|
|
1199
|
+
setScale(x, y, z) {
|
|
1200
|
+
const m = this.data;
|
|
1201
|
+
m[0] = x;
|
|
1202
|
+
m[1] = 0;
|
|
1203
|
+
m[2] = 0;
|
|
1204
|
+
m[3] = 0;
|
|
1205
|
+
m[4] = 0;
|
|
1206
|
+
m[5] = y;
|
|
1207
|
+
m[6] = 0;
|
|
1208
|
+
m[7] = 0;
|
|
1209
|
+
m[8] = 0;
|
|
1210
|
+
m[9] = 0;
|
|
1211
|
+
m[10] = z;
|
|
1212
|
+
m[11] = 0;
|
|
1213
|
+
m[12] = 0;
|
|
1214
|
+
m[13] = 0;
|
|
1215
|
+
m[14] = 0;
|
|
1216
|
+
m[15] = 1;
|
|
1217
|
+
return this;
|
|
1218
|
+
}
|
|
1219
|
+
setViewport(x, y, width, height) {
|
|
1220
|
+
const m = this.data;
|
|
1221
|
+
m[0] = width * 0.5;
|
|
1222
|
+
m[1] = 0;
|
|
1223
|
+
m[2] = 0;
|
|
1224
|
+
m[3] = 0;
|
|
1225
|
+
m[4] = 0;
|
|
1226
|
+
m[5] = height * 0.5;
|
|
1227
|
+
m[6] = 0;
|
|
1228
|
+
m[7] = 0;
|
|
1229
|
+
m[8] = 0;
|
|
1230
|
+
m[9] = 0;
|
|
1231
|
+
m[10] = 0.5;
|
|
1232
|
+
m[11] = 0;
|
|
1233
|
+
m[12] = x + width * 0.5;
|
|
1234
|
+
m[13] = y + height * 0.5;
|
|
1235
|
+
m[14] = 0.5;
|
|
1236
|
+
m[15] = 1;
|
|
1237
|
+
return this;
|
|
1238
|
+
}
|
|
1239
|
+
setReflection(normal, distance) {
|
|
1240
|
+
const a = normal.x;
|
|
1241
|
+
const b = normal.y;
|
|
1242
|
+
const c = normal.z;
|
|
1243
|
+
const data = this.data;
|
|
1244
|
+
data[0] = 1.0 - 2 * a * a;
|
|
1245
|
+
data[1] = -2 * a * b;
|
|
1246
|
+
data[2] = -2 * a * c;
|
|
1247
|
+
data[3] = 0;
|
|
1248
|
+
data[4] = -2 * a * b;
|
|
1249
|
+
data[5] = 1.0 - 2 * b * b;
|
|
1250
|
+
data[6] = -2 * b * c;
|
|
1251
|
+
data[7] = 0;
|
|
1252
|
+
data[8] = -2 * a * c;
|
|
1253
|
+
data[9] = -2 * b * c;
|
|
1254
|
+
data[10] = 1.0 - 2 * c * c;
|
|
1255
|
+
data[11] = 0;
|
|
1256
|
+
data[12] = -2 * a * distance;
|
|
1257
|
+
data[13] = -2 * b * distance;
|
|
1258
|
+
data[14] = -2 * c * distance;
|
|
1259
|
+
data[15] = 1;
|
|
1260
|
+
return this;
|
|
1261
|
+
}
|
|
1262
|
+
invert(src = this) {
|
|
1263
|
+
const s = src.data;
|
|
1264
|
+
const a00 = s[0];
|
|
1265
|
+
const a01 = s[1];
|
|
1266
|
+
const a02 = s[2];
|
|
1267
|
+
const a03 = s[3];
|
|
1268
|
+
const a10 = s[4];
|
|
1269
|
+
const a11 = s[5];
|
|
1270
|
+
const a12 = s[6];
|
|
1271
|
+
const a13 = s[7];
|
|
1272
|
+
const a20 = s[8];
|
|
1273
|
+
const a21 = s[9];
|
|
1274
|
+
const a22 = s[10];
|
|
1275
|
+
const a23 = s[11];
|
|
1276
|
+
const a30 = s[12];
|
|
1277
|
+
const a31 = s[13];
|
|
1278
|
+
const a32 = s[14];
|
|
1279
|
+
const a33 = s[15];
|
|
1280
|
+
const b00 = a00 * a11 - a01 * a10;
|
|
1281
|
+
const b01 = a00 * a12 - a02 * a10;
|
|
1282
|
+
const b02 = a00 * a13 - a03 * a10;
|
|
1283
|
+
const b03 = a01 * a12 - a02 * a11;
|
|
1284
|
+
const b04 = a01 * a13 - a03 * a11;
|
|
1285
|
+
const b05 = a02 * a13 - a03 * a12;
|
|
1286
|
+
const b06 = a20 * a31 - a21 * a30;
|
|
1287
|
+
const b07 = a20 * a32 - a22 * a30;
|
|
1288
|
+
const b08 = a20 * a33 - a23 * a30;
|
|
1289
|
+
const b09 = a21 * a32 - a22 * a31;
|
|
1290
|
+
const b10 = a21 * a33 - a23 * a31;
|
|
1291
|
+
const b11 = a22 * a33 - a23 * a32;
|
|
1292
|
+
const det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
|
|
1293
|
+
if (det === 0) {
|
|
1294
|
+
this.setIdentity();
|
|
1295
|
+
} else {
|
|
1296
|
+
const invDet = 1 / det;
|
|
1297
|
+
const t = this.data;
|
|
1298
|
+
t[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;
|
|
1299
|
+
t[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet;
|
|
1300
|
+
t[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;
|
|
1301
|
+
t[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet;
|
|
1302
|
+
t[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet;
|
|
1303
|
+
t[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;
|
|
1304
|
+
t[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet;
|
|
1305
|
+
t[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;
|
|
1306
|
+
t[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;
|
|
1307
|
+
t[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet;
|
|
1308
|
+
t[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;
|
|
1309
|
+
t[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet;
|
|
1310
|
+
t[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet;
|
|
1311
|
+
t[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;
|
|
1312
|
+
t[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet;
|
|
1313
|
+
t[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;
|
|
1314
|
+
}
|
|
1315
|
+
return this;
|
|
1316
|
+
}
|
|
1317
|
+
set(src) {
|
|
1318
|
+
const dst = this.data;
|
|
1319
|
+
dst[0] = src[0];
|
|
1320
|
+
dst[1] = src[1];
|
|
1321
|
+
dst[2] = src[2];
|
|
1322
|
+
dst[3] = src[3];
|
|
1323
|
+
dst[4] = src[4];
|
|
1324
|
+
dst[5] = src[5];
|
|
1325
|
+
dst[6] = src[6];
|
|
1326
|
+
dst[7] = src[7];
|
|
1327
|
+
dst[8] = src[8];
|
|
1328
|
+
dst[9] = src[9];
|
|
1329
|
+
dst[10] = src[10];
|
|
1330
|
+
dst[11] = src[11];
|
|
1331
|
+
dst[12] = src[12];
|
|
1332
|
+
dst[13] = src[13];
|
|
1333
|
+
dst[14] = src[14];
|
|
1334
|
+
dst[15] = src[15];
|
|
1335
|
+
return this;
|
|
1336
|
+
}
|
|
1337
|
+
setIdentity() {
|
|
1338
|
+
const m = this.data;
|
|
1339
|
+
m[0] = 1;
|
|
1340
|
+
m[1] = 0;
|
|
1341
|
+
m[2] = 0;
|
|
1342
|
+
m[3] = 0;
|
|
1343
|
+
m[4] = 0;
|
|
1344
|
+
m[5] = 1;
|
|
1345
|
+
m[6] = 0;
|
|
1346
|
+
m[7] = 0;
|
|
1347
|
+
m[8] = 0;
|
|
1348
|
+
m[9] = 0;
|
|
1349
|
+
m[10] = 1;
|
|
1350
|
+
m[11] = 0;
|
|
1351
|
+
m[12] = 0;
|
|
1352
|
+
m[13] = 0;
|
|
1353
|
+
m[14] = 0;
|
|
1354
|
+
m[15] = 1;
|
|
1355
|
+
return this;
|
|
1356
|
+
}
|
|
1357
|
+
setTRS(t, r, s) {
|
|
1358
|
+
const qx = r.x;
|
|
1359
|
+
const qy = r.y;
|
|
1360
|
+
const qz = r.z;
|
|
1361
|
+
const qw = r.w;
|
|
1362
|
+
const sx = s.x;
|
|
1363
|
+
const sy = s.y;
|
|
1364
|
+
const sz = s.z;
|
|
1365
|
+
const x2 = qx + qx;
|
|
1366
|
+
const y2 = qy + qy;
|
|
1367
|
+
const z2 = qz + qz;
|
|
1368
|
+
const xx = qx * x2;
|
|
1369
|
+
const xy = qx * y2;
|
|
1370
|
+
const xz = qx * z2;
|
|
1371
|
+
const yy = qy * y2;
|
|
1372
|
+
const yz = qy * z2;
|
|
1373
|
+
const zz = qz * z2;
|
|
1374
|
+
const wx = qw * x2;
|
|
1375
|
+
const wy = qw * y2;
|
|
1376
|
+
const wz = qw * z2;
|
|
1377
|
+
const m = this.data;
|
|
1378
|
+
m[0] = (1 - (yy + zz)) * sx;
|
|
1379
|
+
m[1] = (xy + wz) * sx;
|
|
1380
|
+
m[2] = (xz - wy) * sx;
|
|
1381
|
+
m[3] = 0;
|
|
1382
|
+
m[4] = (xy - wz) * sy;
|
|
1383
|
+
m[5] = (1 - (xx + zz)) * sy;
|
|
1384
|
+
m[6] = (yz + wx) * sy;
|
|
1385
|
+
m[7] = 0;
|
|
1386
|
+
m[8] = (xz + wy) * sz;
|
|
1387
|
+
m[9] = (yz - wx) * sz;
|
|
1388
|
+
m[10] = (1 - (xx + yy)) * sz;
|
|
1389
|
+
m[11] = 0;
|
|
1390
|
+
m[12] = t.x;
|
|
1391
|
+
m[13] = t.y;
|
|
1392
|
+
m[14] = t.z;
|
|
1393
|
+
m[15] = 1;
|
|
1394
|
+
return this;
|
|
1395
|
+
}
|
|
1396
|
+
transpose(src = this) {
|
|
1397
|
+
const s = src.data;
|
|
1398
|
+
const t = this.data;
|
|
1399
|
+
if (s === t) {
|
|
1400
|
+
let tmp;
|
|
1401
|
+
tmp = s[1];
|
|
1402
|
+
t[1] = s[4];
|
|
1403
|
+
t[4] = tmp;
|
|
1404
|
+
tmp = s[2];
|
|
1405
|
+
t[2] = s[8];
|
|
1406
|
+
t[8] = tmp;
|
|
1407
|
+
tmp = s[3];
|
|
1408
|
+
t[3] = s[12];
|
|
1409
|
+
t[12] = tmp;
|
|
1410
|
+
tmp = s[6];
|
|
1411
|
+
t[6] = s[9];
|
|
1412
|
+
t[9] = tmp;
|
|
1413
|
+
tmp = s[7];
|
|
1414
|
+
t[7] = s[13];
|
|
1415
|
+
t[13] = tmp;
|
|
1416
|
+
tmp = s[11];
|
|
1417
|
+
t[11] = s[14];
|
|
1418
|
+
t[14] = tmp;
|
|
1419
|
+
} else {
|
|
1420
|
+
t[0] = s[0];
|
|
1421
|
+
t[1] = s[4];
|
|
1422
|
+
t[2] = s[8];
|
|
1423
|
+
t[3] = s[12];
|
|
1424
|
+
t[4] = s[1];
|
|
1425
|
+
t[5] = s[5];
|
|
1426
|
+
t[6] = s[9];
|
|
1427
|
+
t[7] = s[13];
|
|
1428
|
+
t[8] = s[2];
|
|
1429
|
+
t[9] = s[6];
|
|
1430
|
+
t[10] = s[10];
|
|
1431
|
+
t[11] = s[14];
|
|
1432
|
+
t[12] = s[3];
|
|
1433
|
+
t[13] = s[7];
|
|
1434
|
+
t[14] = s[11];
|
|
1435
|
+
t[15] = s[15];
|
|
1436
|
+
}
|
|
1437
|
+
return this;
|
|
1438
|
+
}
|
|
1439
|
+
getTranslation(t = new Vec3()) {
|
|
1440
|
+
return t.set(this.data[12], this.data[13], this.data[14]);
|
|
1441
|
+
}
|
|
1442
|
+
getX(x = new Vec3()) {
|
|
1443
|
+
return x.set(this.data[0], this.data[1], this.data[2]);
|
|
1444
|
+
}
|
|
1445
|
+
getY(y = new Vec3()) {
|
|
1446
|
+
return y.set(this.data[4], this.data[5], this.data[6]);
|
|
1447
|
+
}
|
|
1448
|
+
getZ(z = new Vec3()) {
|
|
1449
|
+
return z.set(this.data[8], this.data[9], this.data[10]);
|
|
1450
|
+
}
|
|
1451
|
+
getScale(scale = new Vec3()) {
|
|
1452
|
+
this.getX(x);
|
|
1453
|
+
this.getY(y);
|
|
1454
|
+
this.getZ(z);
|
|
1455
|
+
scale.set(x.length(), y.length(), z.length());
|
|
1456
|
+
return scale;
|
|
1457
|
+
}
|
|
1458
|
+
get scaleSign() {
|
|
1459
|
+
this.getX(x);
|
|
1460
|
+
this.getY(y);
|
|
1461
|
+
this.getZ(z);
|
|
1462
|
+
x.cross(x, y);
|
|
1463
|
+
return x.dot(z) < 0 ? -1 : 1;
|
|
1464
|
+
}
|
|
1465
|
+
setFromEulerAngles(ex, ey, ez) {
|
|
1466
|
+
ex *= math.DEG_TO_RAD;
|
|
1467
|
+
ey *= math.DEG_TO_RAD;
|
|
1468
|
+
ez *= math.DEG_TO_RAD;
|
|
1469
|
+
const s1 = Math.sin(-ex);
|
|
1470
|
+
const c1 = Math.cos(-ex);
|
|
1471
|
+
const s2 = Math.sin(-ey);
|
|
1472
|
+
const c2 = Math.cos(-ey);
|
|
1473
|
+
const s3 = Math.sin(-ez);
|
|
1474
|
+
const c3 = Math.cos(-ez);
|
|
1475
|
+
const m = this.data;
|
|
1476
|
+
m[0] = c2 * c3;
|
|
1477
|
+
m[1] = -c2 * s3;
|
|
1478
|
+
m[2] = s2;
|
|
1479
|
+
m[3] = 0;
|
|
1480
|
+
m[4] = c1 * s3 + c3 * s1 * s2;
|
|
1481
|
+
m[5] = c1 * c3 - s1 * s2 * s3;
|
|
1482
|
+
m[6] = -c2 * s1;
|
|
1483
|
+
m[7] = 0;
|
|
1484
|
+
m[8] = s1 * s3 - c1 * c3 * s2;
|
|
1485
|
+
m[9] = c3 * s1 + c1 * s2 * s3;
|
|
1486
|
+
m[10] = c1 * c2;
|
|
1487
|
+
m[11] = 0;
|
|
1488
|
+
m[12] = 0;
|
|
1489
|
+
m[13] = 0;
|
|
1490
|
+
m[14] = 0;
|
|
1491
|
+
m[15] = 1;
|
|
1492
|
+
return this;
|
|
1493
|
+
}
|
|
1494
|
+
getEulerAngles(eulers = new Vec3()) {
|
|
1495
|
+
this.getScale(scale);
|
|
1496
|
+
const sx = scale.x;
|
|
1497
|
+
const sy = scale.y;
|
|
1498
|
+
const sz = scale.z;
|
|
1499
|
+
if (sx === 0 || sy === 0 || sz === 0) {
|
|
1500
|
+
return eulers.set(0, 0, 0);
|
|
1501
|
+
}
|
|
1502
|
+
const m = this.data;
|
|
1503
|
+
const y = Math.asin(-m[2] / sx);
|
|
1504
|
+
const halfPi = Math.PI * 0.5;
|
|
1505
|
+
let x, z;
|
|
1506
|
+
if (y < halfPi) {
|
|
1507
|
+
if (y > -halfPi) {
|
|
1508
|
+
x = Math.atan2(m[6] / sy, m[10] / sz);
|
|
1509
|
+
z = Math.atan2(m[1] / sx, m[0] / sx);
|
|
1510
|
+
} else {
|
|
1511
|
+
z = 0;
|
|
1512
|
+
x = -Math.atan2(m[4] / sy, m[5] / sy);
|
|
1513
|
+
}
|
|
1514
|
+
} else {
|
|
1515
|
+
z = 0;
|
|
1516
|
+
x = Math.atan2(m[4] / sy, m[5] / sy);
|
|
1517
|
+
}
|
|
1518
|
+
return eulers.set(x, y, z).mulScalar(math.RAD_TO_DEG);
|
|
1519
|
+
}
|
|
1520
|
+
toString() {
|
|
1521
|
+
return `[${this.data.join(', ')}]`;
|
|
1522
|
+
}
|
|
1523
|
+
static{
|
|
1524
|
+
this.IDENTITY = Object.freeze(new Mat4());
|
|
1525
|
+
}
|
|
1526
|
+
static{
|
|
1527
|
+
this.ZERO = Object.freeze(new Mat4().set([
|
|
1528
|
+
0,
|
|
1529
|
+
0,
|
|
1530
|
+
0,
|
|
1531
|
+
0,
|
|
1532
|
+
0,
|
|
1533
|
+
0,
|
|
1534
|
+
0,
|
|
1535
|
+
0,
|
|
1536
|
+
0,
|
|
1537
|
+
0,
|
|
1538
|
+
0,
|
|
1539
|
+
0,
|
|
1540
|
+
0,
|
|
1541
|
+
0,
|
|
1542
|
+
0,
|
|
1543
|
+
0
|
|
1544
|
+
]));
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
class Quat {
|
|
1549
|
+
constructor(x = 0, y = 0, z = 0, w = 1){
|
|
1550
|
+
if (x.length === 4) {
|
|
1551
|
+
this.x = x[0];
|
|
1552
|
+
this.y = x[1];
|
|
1553
|
+
this.z = x[2];
|
|
1554
|
+
this.w = x[3];
|
|
1555
|
+
} else {
|
|
1556
|
+
this.x = x;
|
|
1557
|
+
this.y = y;
|
|
1558
|
+
this.z = z;
|
|
1559
|
+
this.w = w;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
clone() {
|
|
1563
|
+
const cstr = this.constructor;
|
|
1564
|
+
return new cstr(this.x, this.y, this.z, this.w);
|
|
1565
|
+
}
|
|
1566
|
+
conjugate(src = this) {
|
|
1567
|
+
this.x = src.x * -1;
|
|
1568
|
+
this.y = src.y * -1;
|
|
1569
|
+
this.z = src.z * -1;
|
|
1570
|
+
this.w = src.w;
|
|
1571
|
+
return this;
|
|
1572
|
+
}
|
|
1573
|
+
copy(rhs) {
|
|
1574
|
+
this.x = rhs.x;
|
|
1575
|
+
this.y = rhs.y;
|
|
1576
|
+
this.z = rhs.z;
|
|
1577
|
+
this.w = rhs.w;
|
|
1578
|
+
return this;
|
|
1579
|
+
}
|
|
1580
|
+
dot(other) {
|
|
1581
|
+
return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w;
|
|
1582
|
+
}
|
|
1583
|
+
equals(rhs) {
|
|
1584
|
+
return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z && this.w === rhs.w;
|
|
1585
|
+
}
|
|
1586
|
+
equalsApprox(rhs, epsilon = 1e-6) {
|
|
1587
|
+
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon && Math.abs(this.w - rhs.w) < epsilon;
|
|
1588
|
+
}
|
|
1589
|
+
getAxisAngle(axis) {
|
|
1590
|
+
let rad = Math.acos(this.w) * 2;
|
|
1591
|
+
const s = Math.sin(rad / 2);
|
|
1592
|
+
if (s !== 0) {
|
|
1593
|
+
axis.x = this.x / s;
|
|
1594
|
+
axis.y = this.y / s;
|
|
1595
|
+
axis.z = this.z / s;
|
|
1596
|
+
if (axis.x < 0 || axis.y < 0 || axis.z < 0) {
|
|
1597
|
+
axis.x *= -1;
|
|
1598
|
+
axis.y *= -1;
|
|
1599
|
+
axis.z *= -1;
|
|
1600
|
+
rad *= -1;
|
|
1601
|
+
}
|
|
1602
|
+
} else {
|
|
1603
|
+
axis.x = 1;
|
|
1604
|
+
axis.y = 0;
|
|
1605
|
+
axis.z = 0;
|
|
1606
|
+
}
|
|
1607
|
+
return rad * math.RAD_TO_DEG;
|
|
1608
|
+
}
|
|
1609
|
+
getEulerAngles(eulers = new Vec3()) {
|
|
1610
|
+
let x, y, z;
|
|
1611
|
+
const qx = this.x;
|
|
1612
|
+
const qy = this.y;
|
|
1613
|
+
const qz = this.z;
|
|
1614
|
+
const qw = this.w;
|
|
1615
|
+
const a2 = 2 * (qw * qy - qx * qz);
|
|
1616
|
+
if (a2 <= -0.99999) {
|
|
1617
|
+
x = 2 * Math.atan2(qx, qw);
|
|
1618
|
+
y = -Math.PI / 2;
|
|
1619
|
+
z = 0;
|
|
1620
|
+
} else if (a2 >= 0.99999) {
|
|
1621
|
+
x = 2 * Math.atan2(qx, qw);
|
|
1622
|
+
y = Math.PI / 2;
|
|
1623
|
+
z = 0;
|
|
1624
|
+
} else {
|
|
1625
|
+
x = Math.atan2(2 * (qw * qx + qy * qz), 1 - 2 * (qx * qx + qy * qy));
|
|
1626
|
+
y = Math.asin(a2);
|
|
1627
|
+
z = Math.atan2(2 * (qw * qz + qx * qy), 1 - 2 * (qy * qy + qz * qz));
|
|
1628
|
+
}
|
|
1629
|
+
return eulers.set(x, y, z).mulScalar(math.RAD_TO_DEG);
|
|
1630
|
+
}
|
|
1631
|
+
invert(src = this) {
|
|
1632
|
+
return this.conjugate(src).normalize();
|
|
1633
|
+
}
|
|
1634
|
+
length() {
|
|
1635
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
1636
|
+
}
|
|
1637
|
+
lengthSq() {
|
|
1638
|
+
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
|
|
1639
|
+
}
|
|
1640
|
+
lerp(lhs, rhs, alpha) {
|
|
1641
|
+
const omt = (1 - alpha) * (lhs.dot(rhs) < 0 ? -1 : 1);
|
|
1642
|
+
this.x = lhs.x * omt + rhs.x * alpha;
|
|
1643
|
+
this.y = lhs.y * omt + rhs.y * alpha;
|
|
1644
|
+
this.z = lhs.z * omt + rhs.z * alpha;
|
|
1645
|
+
this.w = lhs.w * omt + rhs.w * alpha;
|
|
1646
|
+
return this.normalize();
|
|
1647
|
+
}
|
|
1648
|
+
mul(rhs) {
|
|
1649
|
+
const q1x = this.x;
|
|
1650
|
+
const q1y = this.y;
|
|
1651
|
+
const q1z = this.z;
|
|
1652
|
+
const q1w = this.w;
|
|
1653
|
+
const q2x = rhs.x;
|
|
1654
|
+
const q2y = rhs.y;
|
|
1655
|
+
const q2z = rhs.z;
|
|
1656
|
+
const q2w = rhs.w;
|
|
1657
|
+
this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
|
|
1658
|
+
this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
|
|
1659
|
+
this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
|
|
1660
|
+
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
|
|
1661
|
+
return this;
|
|
1662
|
+
}
|
|
1663
|
+
mulScalar(scalar, src = this) {
|
|
1664
|
+
this.x = src.x * scalar;
|
|
1665
|
+
this.y = src.y * scalar;
|
|
1666
|
+
this.z = src.z * scalar;
|
|
1667
|
+
this.w = src.w * scalar;
|
|
1668
|
+
return this;
|
|
1669
|
+
}
|
|
1670
|
+
mul2(lhs, rhs) {
|
|
1671
|
+
const q1x = lhs.x;
|
|
1672
|
+
const q1y = lhs.y;
|
|
1673
|
+
const q1z = lhs.z;
|
|
1674
|
+
const q1w = lhs.w;
|
|
1675
|
+
const q2x = rhs.x;
|
|
1676
|
+
const q2y = rhs.y;
|
|
1677
|
+
const q2z = rhs.z;
|
|
1678
|
+
const q2w = rhs.w;
|
|
1679
|
+
this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
|
|
1680
|
+
this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
|
|
1681
|
+
this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
|
|
1682
|
+
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
|
|
1683
|
+
return this;
|
|
1684
|
+
}
|
|
1685
|
+
normalize(src = this) {
|
|
1686
|
+
let len = src.length();
|
|
1687
|
+
if (len === 0) {
|
|
1688
|
+
this.x = this.y = this.z = 0;
|
|
1689
|
+
this.w = 1;
|
|
1690
|
+
} else {
|
|
1691
|
+
len = 1 / len;
|
|
1692
|
+
this.x = src.x * len;
|
|
1693
|
+
this.y = src.y * len;
|
|
1694
|
+
this.z = src.z * len;
|
|
1695
|
+
this.w = src.w * len;
|
|
1696
|
+
}
|
|
1697
|
+
return this;
|
|
1698
|
+
}
|
|
1699
|
+
set(x, y, z, w) {
|
|
1700
|
+
this.x = x;
|
|
1701
|
+
this.y = y;
|
|
1702
|
+
this.z = z;
|
|
1703
|
+
this.w = w;
|
|
1704
|
+
return this;
|
|
1705
|
+
}
|
|
1706
|
+
setFromAxisAngle(axis, angle) {
|
|
1707
|
+
angle *= 0.5 * math.DEG_TO_RAD;
|
|
1708
|
+
const sa = Math.sin(angle);
|
|
1709
|
+
const ca = Math.cos(angle);
|
|
1710
|
+
this.x = sa * axis.x;
|
|
1711
|
+
this.y = sa * axis.y;
|
|
1712
|
+
this.z = sa * axis.z;
|
|
1713
|
+
this.w = ca;
|
|
1714
|
+
return this;
|
|
1715
|
+
}
|
|
1716
|
+
setFromEulerAngles(ex, ey, ez) {
|
|
1717
|
+
if (ex instanceof Vec3) {
|
|
1718
|
+
const vec = ex;
|
|
1719
|
+
ex = vec.x;
|
|
1720
|
+
ey = vec.y;
|
|
1721
|
+
ez = vec.z;
|
|
1722
|
+
}
|
|
1723
|
+
const halfToRad = 0.5 * math.DEG_TO_RAD;
|
|
1724
|
+
ex *= halfToRad;
|
|
1725
|
+
ey *= halfToRad;
|
|
1726
|
+
ez *= halfToRad;
|
|
1727
|
+
const sx = Math.sin(ex);
|
|
1728
|
+
const cx = Math.cos(ex);
|
|
1729
|
+
const sy = Math.sin(ey);
|
|
1730
|
+
const cy = Math.cos(ey);
|
|
1731
|
+
const sz = Math.sin(ez);
|
|
1732
|
+
const cz = Math.cos(ez);
|
|
1733
|
+
this.x = sx * cy * cz - cx * sy * sz;
|
|
1734
|
+
this.y = cx * sy * cz + sx * cy * sz;
|
|
1735
|
+
this.z = cx * cy * sz - sx * sy * cz;
|
|
1736
|
+
this.w = cx * cy * cz + sx * sy * sz;
|
|
1737
|
+
return this;
|
|
1738
|
+
}
|
|
1739
|
+
setFromMat4(m) {
|
|
1740
|
+
const d = m.data;
|
|
1741
|
+
let m00 = d[0];
|
|
1742
|
+
let m01 = d[1];
|
|
1743
|
+
let m02 = d[2];
|
|
1744
|
+
let m10 = d[4];
|
|
1745
|
+
let m11 = d[5];
|
|
1746
|
+
let m12 = d[6];
|
|
1747
|
+
let m20 = d[8];
|
|
1748
|
+
let m21 = d[9];
|
|
1749
|
+
let m22 = d[10];
|
|
1750
|
+
let l;
|
|
1751
|
+
l = m00 * m00 + m01 * m01 + m02 * m02;
|
|
1752
|
+
if (l === 0) return this.set(0, 0, 0, 1);
|
|
1753
|
+
l = 1 / Math.sqrt(l);
|
|
1754
|
+
m00 *= l;
|
|
1755
|
+
m01 *= l;
|
|
1756
|
+
m02 *= l;
|
|
1757
|
+
l = m10 * m10 + m11 * m11 + m12 * m12;
|
|
1758
|
+
if (l === 0) return this.set(0, 0, 0, 1);
|
|
1759
|
+
l = 1 / Math.sqrt(l);
|
|
1760
|
+
m10 *= l;
|
|
1761
|
+
m11 *= l;
|
|
1762
|
+
m12 *= l;
|
|
1763
|
+
l = m20 * m20 + m21 * m21 + m22 * m22;
|
|
1764
|
+
if (l === 0) return this.set(0, 0, 0, 1);
|
|
1765
|
+
l = 1 / Math.sqrt(l);
|
|
1766
|
+
m20 *= l;
|
|
1767
|
+
m21 *= l;
|
|
1768
|
+
m22 *= l;
|
|
1769
|
+
if (m22 < 0) {
|
|
1770
|
+
if (m00 > m11) {
|
|
1771
|
+
this.set(1 + m00 - m11 - m22, m01 + m10, m20 + m02, m12 - m21);
|
|
1772
|
+
} else {
|
|
1773
|
+
this.set(m01 + m10, 1 - m00 + m11 - m22, m12 + m21, m20 - m02);
|
|
1774
|
+
}
|
|
1775
|
+
} else {
|
|
1776
|
+
if (m00 < -m11) {
|
|
1777
|
+
this.set(m20 + m02, m12 + m21, 1 - m00 - m11 + m22, m01 - m10);
|
|
1778
|
+
} else {
|
|
1779
|
+
this.set(m12 - m21, m20 - m02, m01 - m10, 1 + m00 + m11 + m22);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
return this.mulScalar(1.0 / this.length());
|
|
1783
|
+
}
|
|
1784
|
+
setFromDirections(from, to) {
|
|
1785
|
+
const dotProduct = 1 + from.dot(to);
|
|
1786
|
+
if (dotProduct < Number.EPSILON) {
|
|
1787
|
+
if (Math.abs(from.x) > Math.abs(from.y)) {
|
|
1788
|
+
this.x = -from.z;
|
|
1789
|
+
this.y = 0;
|
|
1790
|
+
this.z = from.x;
|
|
1791
|
+
this.w = 0;
|
|
1792
|
+
} else {
|
|
1793
|
+
this.x = 0;
|
|
1794
|
+
this.y = -from.z;
|
|
1795
|
+
this.z = from.y;
|
|
1796
|
+
this.w = 0;
|
|
1797
|
+
}
|
|
1798
|
+
} else {
|
|
1799
|
+
this.x = from.y * to.z - from.z * to.y;
|
|
1800
|
+
this.y = from.z * to.x - from.x * to.z;
|
|
1801
|
+
this.z = from.x * to.y - from.y * to.x;
|
|
1802
|
+
this.w = dotProduct;
|
|
1803
|
+
}
|
|
1804
|
+
return this.normalize();
|
|
1805
|
+
}
|
|
1806
|
+
slerp(lhs, rhs, alpha) {
|
|
1807
|
+
const lx = lhs.x;
|
|
1808
|
+
const ly = lhs.y;
|
|
1809
|
+
const lz = lhs.z;
|
|
1810
|
+
const lw = lhs.w;
|
|
1811
|
+
let rx = rhs.x;
|
|
1812
|
+
let ry = rhs.y;
|
|
1813
|
+
let rz = rhs.z;
|
|
1814
|
+
let rw = rhs.w;
|
|
1815
|
+
let cosHalfTheta = lw * rw + lx * rx + ly * ry + lz * rz;
|
|
1816
|
+
if (cosHalfTheta < 0) {
|
|
1817
|
+
rw = -rw;
|
|
1818
|
+
rx = -rx;
|
|
1819
|
+
ry = -ry;
|
|
1820
|
+
rz = -rz;
|
|
1821
|
+
cosHalfTheta = -cosHalfTheta;
|
|
1822
|
+
}
|
|
1823
|
+
if (Math.abs(cosHalfTheta) >= 1) {
|
|
1824
|
+
this.w = lw;
|
|
1825
|
+
this.x = lx;
|
|
1826
|
+
this.y = ly;
|
|
1827
|
+
this.z = lz;
|
|
1828
|
+
return this;
|
|
1829
|
+
}
|
|
1830
|
+
const halfTheta = Math.acos(cosHalfTheta);
|
|
1831
|
+
const sinHalfTheta = Math.sqrt(1 - cosHalfTheta * cosHalfTheta);
|
|
1832
|
+
if (Math.abs(sinHalfTheta) < 0.001) {
|
|
1833
|
+
this.w = lw * 0.5 + rw * 0.5;
|
|
1834
|
+
this.x = lx * 0.5 + rx * 0.5;
|
|
1835
|
+
this.y = ly * 0.5 + ry * 0.5;
|
|
1836
|
+
this.z = lz * 0.5 + rz * 0.5;
|
|
1837
|
+
return this;
|
|
1838
|
+
}
|
|
1839
|
+
const ratioA = Math.sin((1 - alpha) * halfTheta) / sinHalfTheta;
|
|
1840
|
+
const ratioB = Math.sin(alpha * halfTheta) / sinHalfTheta;
|
|
1841
|
+
this.w = lw * ratioA + rw * ratioB;
|
|
1842
|
+
this.x = lx * ratioA + rx * ratioB;
|
|
1843
|
+
this.y = ly * ratioA + ry * ratioB;
|
|
1844
|
+
this.z = lz * ratioA + rz * ratioB;
|
|
1845
|
+
return this;
|
|
1846
|
+
}
|
|
1847
|
+
transformVector(vec, res = new Vec3()) {
|
|
1848
|
+
const x = vec.x, y = vec.y, z = vec.z;
|
|
1849
|
+
const qx = this.x, qy = this.y, qz = this.z, qw = this.w;
|
|
1850
|
+
const ix = qw * x + qy * z - qz * y;
|
|
1851
|
+
const iy = qw * y + qz * x - qx * z;
|
|
1852
|
+
const iz = qw * z + qx * y - qy * x;
|
|
1853
|
+
const iw = -qx * x - qy * y - qz * z;
|
|
1854
|
+
res.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
|
|
1855
|
+
res.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
|
|
1856
|
+
res.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
|
|
1857
|
+
return res;
|
|
1858
|
+
}
|
|
1859
|
+
fromArray(arr, offset = 0) {
|
|
1860
|
+
this.x = arr[offset] ?? this.x;
|
|
1861
|
+
this.y = arr[offset + 1] ?? this.y;
|
|
1862
|
+
this.z = arr[offset + 2] ?? this.z;
|
|
1863
|
+
this.w = arr[offset + 3] ?? this.w;
|
|
1864
|
+
return this;
|
|
1865
|
+
}
|
|
1866
|
+
toString() {
|
|
1867
|
+
return `[${this.x}, ${this.y}, ${this.z}, ${this.w}]`;
|
|
1868
|
+
}
|
|
1869
|
+
toArray(arr = [], offset = 0) {
|
|
1870
|
+
arr[offset] = this.x;
|
|
1871
|
+
arr[offset + 1] = this.y;
|
|
1872
|
+
arr[offset + 2] = this.z;
|
|
1873
|
+
arr[offset + 3] = this.w;
|
|
1874
|
+
return arr;
|
|
1875
|
+
}
|
|
1876
|
+
static{
|
|
1877
|
+
this.IDENTITY = Object.freeze(new Quat(0, 0, 0, 1));
|
|
1878
|
+
}
|
|
1879
|
+
static{
|
|
1880
|
+
this.ZERO = Object.freeze(new Quat(0, 0, 0, 0));
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
var version = "0.2.0";
|
|
1885
|
+
|
|
1886
|
+
class Column {
|
|
1887
|
+
name;
|
|
1888
|
+
data;
|
|
1889
|
+
constructor(name, data) {
|
|
1890
|
+
this.name = name;
|
|
1891
|
+
this.data = data;
|
|
1892
|
+
}
|
|
1893
|
+
get dataType() {
|
|
1894
|
+
switch (this.data.constructor) {
|
|
1895
|
+
case Int8Array: return 'int8';
|
|
1896
|
+
case Uint8Array: return 'uint8';
|
|
1897
|
+
case Int16Array: return 'int16';
|
|
1898
|
+
case Uint16Array: return 'uint16';
|
|
1899
|
+
case Int32Array: return 'int32';
|
|
1900
|
+
case Uint32Array: return 'uint32';
|
|
1901
|
+
case Float32Array: return 'float32';
|
|
1902
|
+
case Float64Array: return 'float64';
|
|
1903
|
+
}
|
|
1904
|
+
return null;
|
|
1905
|
+
}
|
|
1906
|
+
clone() {
|
|
1907
|
+
return new Column(this.name, this.data.slice());
|
|
1908
|
+
}
|
|
1909
|
+
filter(length, filter) {
|
|
1910
|
+
const constructor = this.data.constructor;
|
|
1911
|
+
const data = new constructor(length);
|
|
1912
|
+
let j = 0;
|
|
1913
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
1914
|
+
if (filter[i]) {
|
|
1915
|
+
data[j++] = this.data[i];
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
return new Column(this.name, data);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
class DataTable {
|
|
1922
|
+
columns;
|
|
1923
|
+
constructor(columns) {
|
|
1924
|
+
if (columns.length === 0) {
|
|
1925
|
+
throw new Error('DataTable must have at least one column');
|
|
1926
|
+
}
|
|
1927
|
+
// check all columns have the same lengths
|
|
1928
|
+
for (let i = 1; i < columns.length; i++) {
|
|
1929
|
+
if (columns[i].data.length !== columns[0].data.length) {
|
|
1930
|
+
throw new Error(`Column '${columns[i].name}' has inconsistent number of rows: expected ${columns[0].data.length}, got ${columns[i].data.length}`);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
this.columns = columns;
|
|
1934
|
+
}
|
|
1935
|
+
// rows
|
|
1936
|
+
get numRows() {
|
|
1937
|
+
return this.columns[0].data.length;
|
|
1938
|
+
}
|
|
1939
|
+
getRow(index, row = {}, columns = this.columns) {
|
|
1940
|
+
for (const column of columns) {
|
|
1941
|
+
row[column.name] = column.data[index];
|
|
1942
|
+
}
|
|
1943
|
+
return row;
|
|
1944
|
+
}
|
|
1945
|
+
setRow(index, row, columns = this.columns) {
|
|
1946
|
+
for (const column of columns) {
|
|
1947
|
+
if (row.hasOwnProperty(column.name)) {
|
|
1948
|
+
column.data[index] = row[column.name];
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
// columns
|
|
1953
|
+
get numColumns() {
|
|
1954
|
+
return this.columns.length;
|
|
1955
|
+
}
|
|
1956
|
+
get columnNames() {
|
|
1957
|
+
return this.columns.map(column => column.name);
|
|
1958
|
+
}
|
|
1959
|
+
get columnData() {
|
|
1960
|
+
return this.columns.map(column => column.data);
|
|
1961
|
+
}
|
|
1962
|
+
get columnTypes() {
|
|
1963
|
+
return this.columns.map(column => column.dataType);
|
|
1964
|
+
}
|
|
1965
|
+
getColumn(index) {
|
|
1966
|
+
return this.columns[index];
|
|
1967
|
+
}
|
|
1968
|
+
getColumnIndex(name) {
|
|
1969
|
+
return this.columns.findIndex(column => column.name === name);
|
|
1970
|
+
}
|
|
1971
|
+
getColumnByName(name) {
|
|
1972
|
+
return this.columns.find(column => column.name === name);
|
|
1973
|
+
}
|
|
1974
|
+
hasColumn(name) {
|
|
1975
|
+
return this.columns.some(column => column.name === name);
|
|
1976
|
+
}
|
|
1977
|
+
addColumn(column) {
|
|
1978
|
+
if (column.data.length !== this.numRows) {
|
|
1979
|
+
throw new Error(`Column '${column.name}' has inconsistent number of rows: expected ${this.numRows}, got ${column.data.length}`);
|
|
1980
|
+
}
|
|
1981
|
+
this.columns.push(column);
|
|
1982
|
+
}
|
|
1983
|
+
removeColumn(name) {
|
|
1984
|
+
const index = this.columns.findIndex(column => column.name === name);
|
|
1985
|
+
if (index === -1) {
|
|
1986
|
+
return false;
|
|
1987
|
+
}
|
|
1988
|
+
this.columns.splice(index, 1);
|
|
1989
|
+
return true;
|
|
1990
|
+
}
|
|
1991
|
+
// general
|
|
1992
|
+
clone() {
|
|
1993
|
+
return new DataTable(this.columns.map(c => c.clone()));
|
|
1994
|
+
}
|
|
1995
|
+
filter(predicate) {
|
|
1996
|
+
const flags = new Uint8Array(this.numRows);
|
|
1997
|
+
const row = {};
|
|
1998
|
+
let numRows = 0;
|
|
1999
|
+
for (let i = 0; i < this.numRows; i++) {
|
|
2000
|
+
this.getRow(i, row);
|
|
2001
|
+
flags[i] = predicate(i, row) ? 1 : 0;
|
|
2002
|
+
numRows += flags[i];
|
|
2003
|
+
}
|
|
2004
|
+
if (numRows === 0) {
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
if (numRows === this.numRows) {
|
|
2008
|
+
return this;
|
|
2009
|
+
}
|
|
2010
|
+
return new DataTable(this.columns.map(c => c.filter(numRows, flags)));
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
/* eslint-disable indent */
|
|
2015
|
+
const kSqrt03_02 = Math.sqrt(3.0 / 2.0);
|
|
2016
|
+
const kSqrt01_03 = Math.sqrt(1.0 / 3.0);
|
|
2017
|
+
const kSqrt02_03 = Math.sqrt(2.0 / 3.0);
|
|
2018
|
+
const kSqrt04_03 = Math.sqrt(4.0 / 3.0);
|
|
2019
|
+
const kSqrt01_04 = Math.sqrt(1.0 / 4.0);
|
|
2020
|
+
const kSqrt03_04 = Math.sqrt(3.0 / 4.0);
|
|
2021
|
+
const kSqrt01_05 = Math.sqrt(1.0 / 5.0);
|
|
2022
|
+
const kSqrt03_05 = Math.sqrt(3.0 / 5.0);
|
|
2023
|
+
const kSqrt06_05 = Math.sqrt(6.0 / 5.0);
|
|
2024
|
+
const kSqrt08_05 = Math.sqrt(8.0 / 5.0);
|
|
2025
|
+
const kSqrt09_05 = Math.sqrt(9.0 / 5.0);
|
|
2026
|
+
const kSqrt01_06 = Math.sqrt(1.0 / 6.0);
|
|
2027
|
+
const kSqrt05_06 = Math.sqrt(5.0 / 6.0);
|
|
2028
|
+
const kSqrt03_08 = Math.sqrt(3.0 / 8.0);
|
|
2029
|
+
const kSqrt05_08 = Math.sqrt(5.0 / 8.0);
|
|
2030
|
+
const kSqrt09_08 = Math.sqrt(9.0 / 8.0);
|
|
2031
|
+
const kSqrt05_09 = Math.sqrt(5.0 / 9.0);
|
|
2032
|
+
const kSqrt08_09 = Math.sqrt(8.0 / 9.0);
|
|
2033
|
+
const kSqrt01_10 = Math.sqrt(1.0 / 10.0);
|
|
2034
|
+
const kSqrt03_10 = Math.sqrt(3.0 / 10.0);
|
|
2035
|
+
const kSqrt01_12 = Math.sqrt(1.0 / 12.0);
|
|
2036
|
+
const kSqrt04_15 = Math.sqrt(4.0 / 15.0);
|
|
2037
|
+
const kSqrt01_16 = Math.sqrt(1.0 / 16.0);
|
|
2038
|
+
const kSqrt15_16 = Math.sqrt(15.0 / 16.0);
|
|
2039
|
+
const kSqrt01_18 = Math.sqrt(1.0 / 18.0);
|
|
2040
|
+
const kSqrt01_60 = Math.sqrt(1.0 / 60.0);
|
|
2041
|
+
const dp = (n, start, a, b) => {
|
|
2042
|
+
let sum = 0;
|
|
2043
|
+
for (let i = 0; i < n; i++) {
|
|
2044
|
+
sum += a[start + i] * b[i];
|
|
2045
|
+
}
|
|
2046
|
+
return sum;
|
|
2047
|
+
};
|
|
2048
|
+
const coeffsIn = new Float32Array(15);
|
|
2049
|
+
// Rotate spherical harmonics up to band 3 based on https://github.com/andrewwillmott/sh-lib
|
|
2050
|
+
//
|
|
2051
|
+
// This implementation calculates the rotation factors during construction which can then
|
|
2052
|
+
// be used to rotate multiple spherical harmonics cheaply.
|
|
2053
|
+
class RotateSH {
|
|
2054
|
+
apply;
|
|
2055
|
+
constructor(mat) {
|
|
2056
|
+
const rot = mat.data;
|
|
2057
|
+
// band 1
|
|
2058
|
+
const sh1 = [
|
|
2059
|
+
[rot[4], -rot[7], rot[1]],
|
|
2060
|
+
[-rot[5], rot[8], -rot[2]],
|
|
2061
|
+
[rot[3], -rot[6], rot[0]]
|
|
2062
|
+
];
|
|
2063
|
+
// band 2
|
|
2064
|
+
const sh2 = [[
|
|
2065
|
+
kSqrt01_04 * ((sh1[2][2] * sh1[0][0] + sh1[2][0] * sh1[0][2]) + (sh1[0][2] * sh1[2][0] + sh1[0][0] * sh1[2][2])),
|
|
2066
|
+
(sh1[2][1] * sh1[0][0] + sh1[0][1] * sh1[2][0]),
|
|
2067
|
+
kSqrt03_04 * (sh1[2][1] * sh1[0][1] + sh1[0][1] * sh1[2][1]),
|
|
2068
|
+
(sh1[2][1] * sh1[0][2] + sh1[0][1] * sh1[2][2]),
|
|
2069
|
+
kSqrt01_04 * ((sh1[2][2] * sh1[0][2] - sh1[2][0] * sh1[0][0]) + (sh1[0][2] * sh1[2][2] - sh1[0][0] * sh1[2][0]))
|
|
2070
|
+
], [
|
|
2071
|
+
kSqrt01_04 * ((sh1[1][2] * sh1[0][0] + sh1[1][0] * sh1[0][2]) + (sh1[0][2] * sh1[1][0] + sh1[0][0] * sh1[1][2])),
|
|
2072
|
+
sh1[1][1] * sh1[0][0] + sh1[0][1] * sh1[1][0],
|
|
2073
|
+
kSqrt03_04 * (sh1[1][1] * sh1[0][1] + sh1[0][1] * sh1[1][1]),
|
|
2074
|
+
sh1[1][1] * sh1[0][2] + sh1[0][1] * sh1[1][2],
|
|
2075
|
+
kSqrt01_04 * ((sh1[1][2] * sh1[0][2] - sh1[1][0] * sh1[0][0]) + (sh1[0][2] * sh1[1][2] - sh1[0][0] * sh1[1][0]))
|
|
2076
|
+
], [
|
|
2077
|
+
kSqrt01_03 * (sh1[1][2] * sh1[1][0] + sh1[1][0] * sh1[1][2]) - kSqrt01_12 * ((sh1[2][2] * sh1[2][0] + sh1[2][0] * sh1[2][2]) + (sh1[0][2] * sh1[0][0] + sh1[0][0] * sh1[0][2])),
|
|
2078
|
+
kSqrt04_03 * sh1[1][1] * sh1[1][0] - kSqrt01_03 * (sh1[2][1] * sh1[2][0] + sh1[0][1] * sh1[0][0]),
|
|
2079
|
+
sh1[1][1] * sh1[1][1] - kSqrt01_04 * (sh1[2][1] * sh1[2][1] + sh1[0][1] * sh1[0][1]),
|
|
2080
|
+
kSqrt04_03 * sh1[1][1] * sh1[1][2] - kSqrt01_03 * (sh1[2][1] * sh1[2][2] + sh1[0][1] * sh1[0][2]),
|
|
2081
|
+
kSqrt01_03 * (sh1[1][2] * sh1[1][2] - sh1[1][0] * sh1[1][0]) - kSqrt01_12 * ((sh1[2][2] * sh1[2][2] - sh1[2][0] * sh1[2][0]) + (sh1[0][2] * sh1[0][2] - sh1[0][0] * sh1[0][0]))
|
|
2082
|
+
], [
|
|
2083
|
+
kSqrt01_04 * ((sh1[1][2] * sh1[2][0] + sh1[1][0] * sh1[2][2]) + (sh1[2][2] * sh1[1][0] + sh1[2][0] * sh1[1][2])),
|
|
2084
|
+
sh1[1][1] * sh1[2][0] + sh1[2][1] * sh1[1][0],
|
|
2085
|
+
kSqrt03_04 * (sh1[1][1] * sh1[2][1] + sh1[2][1] * sh1[1][1]),
|
|
2086
|
+
sh1[1][1] * sh1[2][2] + sh1[2][1] * sh1[1][2],
|
|
2087
|
+
kSqrt01_04 * ((sh1[1][2] * sh1[2][2] - sh1[1][0] * sh1[2][0]) + (sh1[2][2] * sh1[1][2] - sh1[2][0] * sh1[1][0]))
|
|
2088
|
+
], [
|
|
2089
|
+
kSqrt01_04 * ((sh1[2][2] * sh1[2][0] + sh1[2][0] * sh1[2][2]) - (sh1[0][2] * sh1[0][0] + sh1[0][0] * sh1[0][2])),
|
|
2090
|
+
(sh1[2][1] * sh1[2][0] - sh1[0][1] * sh1[0][0]),
|
|
2091
|
+
kSqrt03_04 * (sh1[2][1] * sh1[2][1] - sh1[0][1] * sh1[0][1]),
|
|
2092
|
+
(sh1[2][1] * sh1[2][2] - sh1[0][1] * sh1[0][2]),
|
|
2093
|
+
kSqrt01_04 * ((sh1[2][2] * sh1[2][2] - sh1[2][0] * sh1[2][0]) - (sh1[0][2] * sh1[0][2] - sh1[0][0] * sh1[0][0]))
|
|
2094
|
+
]];
|
|
2095
|
+
// band 3
|
|
2096
|
+
const sh3 = [[
|
|
2097
|
+
kSqrt01_04 * ((sh1[2][2] * sh2[0][0] + sh1[2][0] * sh2[0][4]) + (sh1[0][2] * sh2[4][0] + sh1[0][0] * sh2[4][4])),
|
|
2098
|
+
kSqrt03_02 * (sh1[2][1] * sh2[0][0] + sh1[0][1] * sh2[4][0]),
|
|
2099
|
+
kSqrt15_16 * (sh1[2][1] * sh2[0][1] + sh1[0][1] * sh2[4][1]),
|
|
2100
|
+
kSqrt05_06 * (sh1[2][1] * sh2[0][2] + sh1[0][1] * sh2[4][2]),
|
|
2101
|
+
kSqrt15_16 * (sh1[2][1] * sh2[0][3] + sh1[0][1] * sh2[4][3]),
|
|
2102
|
+
kSqrt03_02 * (sh1[2][1] * sh2[0][4] + sh1[0][1] * sh2[4][4]),
|
|
2103
|
+
kSqrt01_04 * ((sh1[2][2] * sh2[0][4] - sh1[2][0] * sh2[0][0]) + (sh1[0][2] * sh2[4][4] - sh1[0][0] * sh2[4][0]))
|
|
2104
|
+
], [
|
|
2105
|
+
kSqrt01_06 * (sh1[1][2] * sh2[0][0] + sh1[1][0] * sh2[0][4]) + kSqrt01_06 * ((sh1[2][2] * sh2[1][0] + sh1[2][0] * sh2[1][4]) + (sh1[0][2] * sh2[3][0] + sh1[0][0] * sh2[3][4])),
|
|
2106
|
+
sh1[1][1] * sh2[0][0] + (sh1[2][1] * sh2[1][0] + sh1[0][1] * sh2[3][0]),
|
|
2107
|
+
kSqrt05_08 * sh1[1][1] * sh2[0][1] + kSqrt05_08 * (sh1[2][1] * sh2[1][1] + sh1[0][1] * sh2[3][1]),
|
|
2108
|
+
kSqrt05_09 * sh1[1][1] * sh2[0][2] + kSqrt05_09 * (sh1[2][1] * sh2[1][2] + sh1[0][1] * sh2[3][2]),
|
|
2109
|
+
kSqrt05_08 * sh1[1][1] * sh2[0][3] + kSqrt05_08 * (sh1[2][1] * sh2[1][3] + sh1[0][1] * sh2[3][3]),
|
|
2110
|
+
sh1[1][1] * sh2[0][4] + (sh1[2][1] * sh2[1][4] + sh1[0][1] * sh2[3][4]),
|
|
2111
|
+
kSqrt01_06 * (sh1[1][2] * sh2[0][4] - sh1[1][0] * sh2[0][0]) + kSqrt01_06 * ((sh1[2][2] * sh2[1][4] - sh1[2][0] * sh2[1][0]) + (sh1[0][2] * sh2[3][4] - sh1[0][0] * sh2[3][0]))
|
|
2112
|
+
], [
|
|
2113
|
+
kSqrt04_15 * (sh1[1][2] * sh2[1][0] + sh1[1][0] * sh2[1][4]) + kSqrt01_05 * (sh1[0][2] * sh2[2][0] + sh1[0][0] * sh2[2][4]) - kSqrt01_60 * ((sh1[2][2] * sh2[0][0] + sh1[2][0] * sh2[0][4]) - (sh1[0][2] * sh2[4][0] + sh1[0][0] * sh2[4][4])),
|
|
2114
|
+
kSqrt08_05 * sh1[1][1] * sh2[1][0] + kSqrt06_05 * sh1[0][1] * sh2[2][0] - kSqrt01_10 * (sh1[2][1] * sh2[0][0] - sh1[0][1] * sh2[4][0]),
|
|
2115
|
+
sh1[1][1] * sh2[1][1] + kSqrt03_04 * sh1[0][1] * sh2[2][1] - kSqrt01_16 * (sh1[2][1] * sh2[0][1] - sh1[0][1] * sh2[4][1]),
|
|
2116
|
+
kSqrt08_09 * sh1[1][1] * sh2[1][2] + kSqrt02_03 * sh1[0][1] * sh2[2][2] - kSqrt01_18 * (sh1[2][1] * sh2[0][2] - sh1[0][1] * sh2[4][2]),
|
|
2117
|
+
sh1[1][1] * sh2[1][3] + kSqrt03_04 * sh1[0][1] * sh2[2][3] - kSqrt01_16 * (sh1[2][1] * sh2[0][3] - sh1[0][1] * sh2[4][3]),
|
|
2118
|
+
kSqrt08_05 * sh1[1][1] * sh2[1][4] + kSqrt06_05 * sh1[0][1] * sh2[2][4] - kSqrt01_10 * (sh1[2][1] * sh2[0][4] - sh1[0][1] * sh2[4][4]),
|
|
2119
|
+
kSqrt04_15 * (sh1[1][2] * sh2[1][4] - sh1[1][0] * sh2[1][0]) + kSqrt01_05 * (sh1[0][2] * sh2[2][4] - sh1[0][0] * sh2[2][0]) - kSqrt01_60 * ((sh1[2][2] * sh2[0][4] - sh1[2][0] * sh2[0][0]) - (sh1[0][2] * sh2[4][4] - sh1[0][0] * sh2[4][0]))
|
|
2120
|
+
], [
|
|
2121
|
+
kSqrt03_10 * (sh1[1][2] * sh2[2][0] + sh1[1][0] * sh2[2][4]) - kSqrt01_10 * ((sh1[2][2] * sh2[3][0] + sh1[2][0] * sh2[3][4]) + (sh1[0][2] * sh2[1][0] + sh1[0][0] * sh2[1][4])),
|
|
2122
|
+
kSqrt09_05 * sh1[1][1] * sh2[2][0] - kSqrt03_05 * (sh1[2][1] * sh2[3][0] + sh1[0][1] * sh2[1][0]),
|
|
2123
|
+
kSqrt09_08 * sh1[1][1] * sh2[2][1] - kSqrt03_08 * (sh1[2][1] * sh2[3][1] + sh1[0][1] * sh2[1][1]),
|
|
2124
|
+
sh1[1][1] * sh2[2][2] - kSqrt01_03 * (sh1[2][1] * sh2[3][2] + sh1[0][1] * sh2[1][2]),
|
|
2125
|
+
kSqrt09_08 * sh1[1][1] * sh2[2][3] - kSqrt03_08 * (sh1[2][1] * sh2[3][3] + sh1[0][1] * sh2[1][3]),
|
|
2126
|
+
kSqrt09_05 * sh1[1][1] * sh2[2][4] - kSqrt03_05 * (sh1[2][1] * sh2[3][4] + sh1[0][1] * sh2[1][4]),
|
|
2127
|
+
kSqrt03_10 * (sh1[1][2] * sh2[2][4] - sh1[1][0] * sh2[2][0]) - kSqrt01_10 * ((sh1[2][2] * sh2[3][4] - sh1[2][0] * sh2[3][0]) + (sh1[0][2] * sh2[1][4] - sh1[0][0] * sh2[1][0]))
|
|
2128
|
+
], [
|
|
2129
|
+
kSqrt04_15 * (sh1[1][2] * sh2[3][0] + sh1[1][0] * sh2[3][4]) + kSqrt01_05 * (sh1[2][2] * sh2[2][0] + sh1[2][0] * sh2[2][4]) - kSqrt01_60 * ((sh1[2][2] * sh2[4][0] + sh1[2][0] * sh2[4][4]) + (sh1[0][2] * sh2[0][0] + sh1[0][0] * sh2[0][4])),
|
|
2130
|
+
kSqrt08_05 * sh1[1][1] * sh2[3][0] + kSqrt06_05 * sh1[2][1] * sh2[2][0] - kSqrt01_10 * (sh1[2][1] * sh2[4][0] + sh1[0][1] * sh2[0][0]),
|
|
2131
|
+
sh1[1][1] * sh2[3][1] + kSqrt03_04 * sh1[2][1] * sh2[2][1] - kSqrt01_16 * (sh1[2][1] * sh2[4][1] + sh1[0][1] * sh2[0][1]),
|
|
2132
|
+
kSqrt08_09 * sh1[1][1] * sh2[3][2] + kSqrt02_03 * sh1[2][1] * sh2[2][2] - kSqrt01_18 * (sh1[2][1] * sh2[4][2] + sh1[0][1] * sh2[0][2]),
|
|
2133
|
+
sh1[1][1] * sh2[3][3] + kSqrt03_04 * sh1[2][1] * sh2[2][3] - kSqrt01_16 * (sh1[2][1] * sh2[4][3] + sh1[0][1] * sh2[0][3]),
|
|
2134
|
+
kSqrt08_05 * sh1[1][1] * sh2[3][4] + kSqrt06_05 * sh1[2][1] * sh2[2][4] - kSqrt01_10 * (sh1[2][1] * sh2[4][4] + sh1[0][1] * sh2[0][4]),
|
|
2135
|
+
kSqrt04_15 * (sh1[1][2] * sh2[3][4] - sh1[1][0] * sh2[3][0]) + kSqrt01_05 * (sh1[2][2] * sh2[2][4] - sh1[2][0] * sh2[2][0]) - kSqrt01_60 * ((sh1[2][2] * sh2[4][4] - sh1[2][0] * sh2[4][0]) + (sh1[0][2] * sh2[0][4] - sh1[0][0] * sh2[0][0]))
|
|
2136
|
+
], [
|
|
2137
|
+
kSqrt01_06 * (sh1[1][2] * sh2[4][0] + sh1[1][0] * sh2[4][4]) + kSqrt01_06 * ((sh1[2][2] * sh2[3][0] + sh1[2][0] * sh2[3][4]) - (sh1[0][2] * sh2[1][0] + sh1[0][0] * sh2[1][4])),
|
|
2138
|
+
sh1[1][1] * sh2[4][0] + (sh1[2][1] * sh2[3][0] - sh1[0][1] * sh2[1][0]),
|
|
2139
|
+
kSqrt05_08 * sh1[1][1] * sh2[4][1] + kSqrt05_08 * (sh1[2][1] * sh2[3][1] - sh1[0][1] * sh2[1][1]),
|
|
2140
|
+
kSqrt05_09 * sh1[1][1] * sh2[4][2] + kSqrt05_09 * (sh1[2][1] * sh2[3][2] - sh1[0][1] * sh2[1][2]),
|
|
2141
|
+
kSqrt05_08 * sh1[1][1] * sh2[4][3] + kSqrt05_08 * (sh1[2][1] * sh2[3][3] - sh1[0][1] * sh2[1][3]),
|
|
2142
|
+
sh1[1][1] * sh2[4][4] + (sh1[2][1] * sh2[3][4] - sh1[0][1] * sh2[1][4]),
|
|
2143
|
+
kSqrt01_06 * (sh1[1][2] * sh2[4][4] - sh1[1][0] * sh2[4][0]) + kSqrt01_06 * ((sh1[2][2] * sh2[3][4] - sh1[2][0] * sh2[3][0]) - (sh1[0][2] * sh2[1][4] - sh1[0][0] * sh2[1][0]))
|
|
2144
|
+
], [
|
|
2145
|
+
kSqrt01_04 * ((sh1[2][2] * sh2[4][0] + sh1[2][0] * sh2[4][4]) - (sh1[0][2] * sh2[0][0] + sh1[0][0] * sh2[0][4])),
|
|
2146
|
+
kSqrt03_02 * (sh1[2][1] * sh2[4][0] - sh1[0][1] * sh2[0][0]),
|
|
2147
|
+
kSqrt15_16 * (sh1[2][1] * sh2[4][1] - sh1[0][1] * sh2[0][1]),
|
|
2148
|
+
kSqrt05_06 * (sh1[2][1] * sh2[4][2] - sh1[0][1] * sh2[0][2]),
|
|
2149
|
+
kSqrt15_16 * (sh1[2][1] * sh2[4][3] - sh1[0][1] * sh2[0][3]),
|
|
2150
|
+
kSqrt03_02 * (sh1[2][1] * sh2[4][4] - sh1[0][1] * sh2[0][4]),
|
|
2151
|
+
kSqrt01_04 * ((sh1[2][2] * sh2[4][4] - sh1[2][0] * sh2[4][0]) - (sh1[0][2] * sh2[0][4] - sh1[0][0] * sh2[0][0]))
|
|
2152
|
+
]];
|
|
2153
|
+
// rotate spherical harmonic coefficients, up to band 3
|
|
2154
|
+
this.apply = (result, src) => {
|
|
2155
|
+
if (!src || src === result) {
|
|
2156
|
+
coeffsIn.set(result);
|
|
2157
|
+
src = coeffsIn;
|
|
2158
|
+
}
|
|
2159
|
+
// band 1
|
|
2160
|
+
if (result.length < 3) {
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
result[0] = dp(3, 0, src, sh1[0]);
|
|
2164
|
+
result[1] = dp(3, 0, src, sh1[1]);
|
|
2165
|
+
result[2] = dp(3, 0, src, sh1[2]);
|
|
2166
|
+
// band 2
|
|
2167
|
+
if (result.length < 8) {
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
result[3] = dp(5, 3, src, sh2[0]);
|
|
2171
|
+
result[4] = dp(5, 3, src, sh2[1]);
|
|
2172
|
+
result[5] = dp(5, 3, src, sh2[2]);
|
|
2173
|
+
result[6] = dp(5, 3, src, sh2[3]);
|
|
2174
|
+
result[7] = dp(5, 3, src, sh2[4]);
|
|
2175
|
+
// band 3
|
|
2176
|
+
if (result.length < 15) {
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
result[8] = dp(7, 8, src, sh3[0]);
|
|
2180
|
+
result[9] = dp(7, 8, src, sh3[1]);
|
|
2181
|
+
result[10] = dp(7, 8, src, sh3[2]);
|
|
2182
|
+
result[11] = dp(7, 8, src, sh3[3]);
|
|
2183
|
+
result[12] = dp(7, 8, src, sh3[4]);
|
|
2184
|
+
result[13] = dp(7, 8, src, sh3[5]);
|
|
2185
|
+
result[14] = dp(7, 8, src, sh3[6]);
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
const shNames$2 = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
|
|
2191
|
+
const v = new Vec3();
|
|
2192
|
+
const q$1 = new Quat();
|
|
2193
|
+
// apply translation, rotation and scale to a data table
|
|
2194
|
+
const transform = (dataTable, t, r, s) => {
|
|
2195
|
+
const mat = new Mat4().setTRS(t, r, new Vec3(s, s, s));
|
|
2196
|
+
const mat3 = new Mat3().setFromQuat(r);
|
|
2197
|
+
const rotateSH = new RotateSH(mat3);
|
|
2198
|
+
const hasTranslation = ['x', 'y', 'z'].every(c => dataTable.hasColumn(c));
|
|
2199
|
+
const hasRotation = ['rot_0', 'rot_1', 'rot_2', 'rot_3'].every(c => dataTable.hasColumn(c));
|
|
2200
|
+
const hasScale = ['scale_0', 'scale_1', 'scale_2'].every(c => dataTable.hasColumn(c));
|
|
2201
|
+
const shBands = { '9': 1, '24': 2, '-1': 3 }[shNames$2.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
|
|
2202
|
+
const shCoeffs = new Float32Array([0, 3, 8, 15][shBands]);
|
|
2203
|
+
const row = {};
|
|
2204
|
+
for (let i = 0; i < dataTable.numRows; ++i) {
|
|
2205
|
+
dataTable.getRow(i, row);
|
|
2206
|
+
if (hasTranslation) {
|
|
2207
|
+
v.set(row.x, row.y, row.z);
|
|
2208
|
+
mat.transformPoint(v, v);
|
|
2209
|
+
row.x = v.x;
|
|
2210
|
+
row.y = v.y;
|
|
2211
|
+
row.z = v.z;
|
|
2212
|
+
}
|
|
2213
|
+
if (hasRotation) {
|
|
2214
|
+
q$1.set(row.rot_1, row.rot_2, row.rot_3, row.rot_0).mul2(r, q$1);
|
|
2215
|
+
row.rot_0 = q$1.w;
|
|
2216
|
+
row.rot_1 = q$1.x;
|
|
2217
|
+
row.rot_2 = q$1.y;
|
|
2218
|
+
row.rot_3 = q$1.z;
|
|
2219
|
+
}
|
|
2220
|
+
if (hasScale) {
|
|
2221
|
+
row.scale_0 = Math.log(Math.exp(row.scale_0) * s);
|
|
2222
|
+
row.scale_1 = Math.log(Math.exp(row.scale_1) * s);
|
|
2223
|
+
row.scale_2 = Math.log(Math.exp(row.scale_2) * s);
|
|
2224
|
+
}
|
|
2225
|
+
if (shBands > 0) {
|
|
2226
|
+
for (let j = 0; j < 3; ++j) {
|
|
2227
|
+
for (let k = 0; k < shCoeffs.length; ++k) {
|
|
2228
|
+
shCoeffs[k] = row[shNames$2[k + j * shCoeffs.length]];
|
|
2229
|
+
}
|
|
2230
|
+
rotateSH.apply(shCoeffs);
|
|
2231
|
+
for (let k = 0; k < shCoeffs.length; ++k) {
|
|
2232
|
+
row[shNames$2[k + j * shCoeffs.length]] = shCoeffs[k];
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
dataTable.setRow(i, row);
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
|
|
2240
|
+
const shNames$1 = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
|
|
2241
|
+
// process a data table with standard options
|
|
2242
|
+
const process = (dataTable, processActions) => {
|
|
2243
|
+
let result = dataTable;
|
|
2244
|
+
for (let i = 0; i < processActions.length; i++) {
|
|
2245
|
+
const processAction = processActions[i];
|
|
2246
|
+
switch (processAction.kind) {
|
|
2247
|
+
case 'translate':
|
|
2248
|
+
transform(result, processAction.value, Quat.IDENTITY, 1);
|
|
2249
|
+
break;
|
|
2250
|
+
case 'rotate':
|
|
2251
|
+
transform(result, Vec3.ZERO, new Quat().setFromEulerAngles(processAction.value.x, processAction.value.y, processAction.value.z), 1);
|
|
2252
|
+
break;
|
|
2253
|
+
case 'scale':
|
|
2254
|
+
transform(result, Vec3.ZERO, Quat.IDENTITY, processAction.value);
|
|
2255
|
+
break;
|
|
2256
|
+
case 'filterNaN': {
|
|
2257
|
+
const predicate = (rowIndex, row) => {
|
|
2258
|
+
for (const key in row) {
|
|
2259
|
+
if (!isFinite(row[key])) {
|
|
2260
|
+
return false;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
return true;
|
|
2264
|
+
};
|
|
2265
|
+
result = result.filter(predicate);
|
|
2266
|
+
break;
|
|
2267
|
+
}
|
|
2268
|
+
case 'filterByValue': {
|
|
2269
|
+
const { columnName, comparator, value } = processAction;
|
|
2270
|
+
const Predicates = {
|
|
2271
|
+
'lt': (rowIndex, row) => row[columnName] < value,
|
|
2272
|
+
'lte': (rowIndex, row) => row[columnName] <= value,
|
|
2273
|
+
'gt': (rowIndex, row) => row[columnName] > value,
|
|
2274
|
+
'gte': (rowIndex, row) => row[columnName] >= value,
|
|
2275
|
+
'eq': (rowIndex, row) => row[columnName] === value,
|
|
2276
|
+
'neq': (rowIndex, row) => row[columnName] !== value
|
|
2277
|
+
};
|
|
2278
|
+
const predicate = Predicates[comparator] ?? ((rowIndex, row) => true);
|
|
2279
|
+
result = result.filter(predicate);
|
|
2280
|
+
break;
|
|
2281
|
+
}
|
|
2282
|
+
case 'filterBands': {
|
|
2283
|
+
const inputBands = { '9': 1, '24': 2, '-1': 3 }[shNames$1.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
|
|
2284
|
+
const outputBands = processAction.value;
|
|
2285
|
+
if (outputBands < inputBands) {
|
|
2286
|
+
const inputCoeffs = [0, 3, 8, 15][inputBands];
|
|
2287
|
+
const outputCoeffs = [0, 3, 8, 15][outputBands];
|
|
2288
|
+
const map = {};
|
|
2289
|
+
for (let i = 0; i < inputCoeffs; ++i) {
|
|
2290
|
+
for (let j = 0; j < 3; ++j) {
|
|
2291
|
+
const inputName = `f_rest_${i + j * inputCoeffs}`;
|
|
2292
|
+
map[inputName] = i < outputCoeffs ? `f_rest_${i + j * outputCoeffs}` : null;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
result = new DataTable(result.columns.map((column) => {
|
|
2296
|
+
if (map.hasOwnProperty(column.name)) {
|
|
2297
|
+
const name = map[column.name];
|
|
2298
|
+
return name ? new Column(name, column.data) : null;
|
|
2299
|
+
}
|
|
2300
|
+
return column;
|
|
2301
|
+
}).filter(c => c !== null));
|
|
2302
|
+
}
|
|
2303
|
+
break;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return result;
|
|
2308
|
+
};
|
|
5
2309
|
|
|
6
2310
|
const getDataType = (type) => {
|
|
7
2311
|
switch (type) {
|
|
@@ -16,36 +2320,28 @@ const getDataType = (type) => {
|
|
|
16
2320
|
default: return null;
|
|
17
2321
|
}
|
|
18
2322
|
};
|
|
19
|
-
const calcDataSize = (plyFile) => {
|
|
20
|
-
let result = 0;
|
|
21
|
-
for (const element of plyFile.elements) {
|
|
22
|
-
for (const property of element.properties) {
|
|
23
|
-
result += getDataType(property.type).BYTES_PER_ELEMENT * element.count;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return result;
|
|
27
|
-
};
|
|
28
|
-
const shNames = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
|
|
29
|
-
|
|
30
2323
|
// parse the ply header text and return an array of Element structures and a
|
|
31
2324
|
// string containing the ply format
|
|
32
|
-
const
|
|
2325
|
+
const parseHeader = (data) => {
|
|
33
2326
|
// decode header and split into lines
|
|
34
2327
|
const strings = new TextDecoder('ascii')
|
|
35
2328
|
.decode(data)
|
|
36
2329
|
.split('\n')
|
|
37
2330
|
.filter(line => line);
|
|
38
2331
|
const elements = [];
|
|
2332
|
+
const comments = [];
|
|
39
2333
|
let element;
|
|
40
2334
|
for (let i = 1; i < strings.length; ++i) {
|
|
41
2335
|
const words = strings[i].split(' ');
|
|
42
2336
|
switch (words[0]) {
|
|
43
2337
|
case 'ply':
|
|
44
2338
|
case 'format':
|
|
45
|
-
case 'comment':
|
|
46
2339
|
case 'end_header':
|
|
47
2340
|
// skip
|
|
48
2341
|
break;
|
|
2342
|
+
case 'comment':
|
|
2343
|
+
comments.push(strings[i].substring(8)); // skip 'comment '
|
|
2344
|
+
break;
|
|
49
2345
|
case 'element': {
|
|
50
2346
|
if (words.length !== 3) {
|
|
51
2347
|
throw new Error('invalid ply header');
|
|
@@ -73,7 +2369,7 @@ const parsePlyHeader = (data) => {
|
|
|
73
2369
|
}
|
|
74
2370
|
}
|
|
75
2371
|
}
|
|
76
|
-
return {
|
|
2372
|
+
return { comments, elements };
|
|
77
2373
|
};
|
|
78
2374
|
const cmp = (a, b, aOffset = 0) => {
|
|
79
2375
|
for (let i = 0; i < b.length; ++i) {
|
|
@@ -87,7 +2383,7 @@ const magicBytes = new Uint8Array([112, 108, 121, 10]); // ply\n
|
|
|
87
2383
|
const endHeaderBytes = new Uint8Array([10, 101, 110, 100, 95, 104, 101, 97, 100, 101, 114, 10]); // \nend_header\n
|
|
88
2384
|
const readPly = async (fileHandle) => {
|
|
89
2385
|
// we don't support ply text header larger than 128k
|
|
90
|
-
const headerBuf = Buffer.alloc(128 * 1024);
|
|
2386
|
+
const headerBuf = Buffer$1.alloc(128 * 1024);
|
|
91
2387
|
// smallest possible header size
|
|
92
2388
|
let headerSize = magicBytes.length + endHeaderBytes.length;
|
|
93
2389
|
if ((await fileHandle.read(headerBuf, 0, headerSize)).bytesRead !== headerSize) {
|
|
@@ -108,693 +2404,52 @@ const readPly = async (fileHandle) => {
|
|
|
108
2404
|
}
|
|
109
2405
|
}
|
|
110
2406
|
// parse the header
|
|
111
|
-
const header =
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
type: property.type,
|
|
139
|
-
offset
|
|
140
|
-
};
|
|
2407
|
+
const header = parseHeader(headerBuf.subarray(0, headerSize));
|
|
2408
|
+
// create a data table for each ply element
|
|
2409
|
+
const elements = [];
|
|
2410
|
+
for (let i = 0; i < header.elements.length; ++i) {
|
|
2411
|
+
const element = header.elements[i];
|
|
2412
|
+
const columns = element.properties.map((property) => {
|
|
2413
|
+
return new Column(property.name, new (getDataType(property.type))(element.count));
|
|
2414
|
+
});
|
|
2415
|
+
const buffers = columns.map(column => new Uint8Array(column.data.buffer));
|
|
2416
|
+
const sizes = columns.map(column => column.data.BYTES_PER_ELEMENT);
|
|
2417
|
+
const rowSize = sizes.reduce((total, size) => total + size, 0);
|
|
2418
|
+
// read data in chunks of 1024 rows at a time
|
|
2419
|
+
const chunkSize = 1024;
|
|
2420
|
+
const numChunks = Math.ceil(element.count / chunkSize);
|
|
2421
|
+
const chunkData = Buffer$1.alloc(chunkSize * rowSize);
|
|
2422
|
+
for (let c = 0; c < numChunks; ++c) {
|
|
2423
|
+
const numRows = Math.min(chunkSize, element.count - c * chunkSize);
|
|
2424
|
+
await fileHandle.read(chunkData, 0, rowSize * numRows);
|
|
2425
|
+
let offset = 0;
|
|
2426
|
+
// read data row at a time
|
|
2427
|
+
for (let r = 0; r < numRows; ++r) {
|
|
2428
|
+
const rowOffset = c * chunkSize + r;
|
|
2429
|
+
// copy into column data
|
|
2430
|
+
for (let p = 0; p < columns.length; ++p) {
|
|
2431
|
+
const s = sizes[p];
|
|
2432
|
+
chunkData.copy(buffers[p], rowOffset * s, offset, offset + s);
|
|
2433
|
+
offset += s;
|
|
141
2434
|
}
|
|
142
|
-
offset += getDataType(property.type).BYTES_PER_ELEMENT;
|
|
143
2435
|
}
|
|
144
2436
|
}
|
|
2437
|
+
elements.push({
|
|
2438
|
+
name: element.name,
|
|
2439
|
+
dataTable: new DataTable(columns)
|
|
2440
|
+
});
|
|
145
2441
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
// return the number of spherical harmonic bands present in the data
|
|
151
|
-
get numSHBands() {
|
|
152
|
-
return { '9': 1, '24': 2, '-1': 3 }[shNames.findIndex(v => !this.properties.hasOwnProperty(v))] ?? 0;
|
|
153
|
-
}
|
|
154
|
-
// simple iterator that assumes input data is float32
|
|
155
|
-
createIterator(fields, result) {
|
|
156
|
-
const offsets = fields.map(f => this.properties[f].offset / 4);
|
|
157
|
-
const float32 = new Float32Array(this.plyFile.data.buffer);
|
|
158
|
-
return (index) => {
|
|
159
|
-
const base = index * this.vertex.properties.length;
|
|
160
|
-
for (let i = 0; i < fields.length; ++i) {
|
|
161
|
-
result[i] = float32[base + offsets[i]];
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const math = {
|
|
168
|
-
DEG_TO_RAD: Math.PI / 180,
|
|
169
|
-
RAD_TO_DEG: 180 / Math.PI,
|
|
170
|
-
clamp(value, min, max) {
|
|
171
|
-
if (value >= max) return max;
|
|
172
|
-
if (value <= min) return min;
|
|
173
|
-
return value;
|
|
174
|
-
},
|
|
175
|
-
intToBytes24(i) {
|
|
176
|
-
const r = i >> 16 & 0xff;
|
|
177
|
-
const g = i >> 8 & 0xff;
|
|
178
|
-
const b = i & 0xff;
|
|
179
|
-
return [r, g, b];
|
|
180
|
-
},
|
|
181
|
-
intToBytes32(i) {
|
|
182
|
-
const r = i >> 24 & 0xff;
|
|
183
|
-
const g = i >> 16 & 0xff;
|
|
184
|
-
const b = i >> 8 & 0xff;
|
|
185
|
-
const a = i & 0xff;
|
|
186
|
-
return [r, g, b, a];
|
|
187
|
-
},
|
|
188
|
-
bytesToInt24(r, g, b) {
|
|
189
|
-
if (r.length) {
|
|
190
|
-
b = r[2];
|
|
191
|
-
g = r[1];
|
|
192
|
-
r = r[0];
|
|
193
|
-
}
|
|
194
|
-
return r << 16 | g << 8 | b;
|
|
195
|
-
},
|
|
196
|
-
bytesToInt32(r, g, b, a) {
|
|
197
|
-
if (r.length) {
|
|
198
|
-
a = r[3];
|
|
199
|
-
b = r[2];
|
|
200
|
-
g = r[1];
|
|
201
|
-
r = r[0];
|
|
202
|
-
}
|
|
203
|
-
return (r << 24 | g << 16 | b << 8 | a) >>> 0;
|
|
204
|
-
},
|
|
205
|
-
lerp(a, b, alpha) {
|
|
206
|
-
return a + (b - a) * math.clamp(alpha, 0, 1);
|
|
207
|
-
},
|
|
208
|
-
lerpAngle(a, b, alpha) {
|
|
209
|
-
if (b - a > 180) {
|
|
210
|
-
b -= 360;
|
|
211
|
-
}
|
|
212
|
-
if (b - a < -180) {
|
|
213
|
-
b += 360;
|
|
214
|
-
}
|
|
215
|
-
return math.lerp(a, b, math.clamp(alpha, 0, 1));
|
|
216
|
-
},
|
|
217
|
-
powerOfTwo(x) {
|
|
218
|
-
return x !== 0 && !(x & x - 1);
|
|
219
|
-
},
|
|
220
|
-
nextPowerOfTwo(val) {
|
|
221
|
-
val--;
|
|
222
|
-
val |= val >> 1;
|
|
223
|
-
val |= val >> 2;
|
|
224
|
-
val |= val >> 4;
|
|
225
|
-
val |= val >> 8;
|
|
226
|
-
val |= val >> 16;
|
|
227
|
-
val++;
|
|
228
|
-
return val;
|
|
229
|
-
},
|
|
230
|
-
nearestPowerOfTwo(val) {
|
|
231
|
-
return Math.pow(2, Math.round(Math.log(val) / Math.log(2)));
|
|
232
|
-
},
|
|
233
|
-
random(min, max) {
|
|
234
|
-
const diff = max - min;
|
|
235
|
-
return Math.random() * diff + min;
|
|
236
|
-
},
|
|
237
|
-
smoothstep(min, max, x) {
|
|
238
|
-
if (x <= min) return 0;
|
|
239
|
-
if (x >= max) return 1;
|
|
240
|
-
x = (x - min) / (max - min);
|
|
241
|
-
return x * x * (3 - 2 * x);
|
|
242
|
-
},
|
|
243
|
-
smootherstep(min, max, x) {
|
|
244
|
-
if (x <= min) return 0;
|
|
245
|
-
if (x >= max) return 1;
|
|
246
|
-
x = (x - min) / (max - min);
|
|
247
|
-
return x * x * x * (x * (x * 6 - 15) + 10);
|
|
248
|
-
},
|
|
249
|
-
roundUp(numToRound, multiple) {
|
|
250
|
-
if (multiple === 0) {
|
|
251
|
-
return numToRound;
|
|
252
|
-
}
|
|
253
|
-
return Math.ceil(numToRound / multiple) * multiple;
|
|
254
|
-
},
|
|
255
|
-
between(num, a, b, inclusive) {
|
|
256
|
-
const min = Math.min(a, b);
|
|
257
|
-
const max = Math.max(a, b);
|
|
258
|
-
return inclusive ? num >= min && num <= max : num > min && num < max;
|
|
259
|
-
}
|
|
2442
|
+
return {
|
|
2443
|
+
comments: header.comments,
|
|
2444
|
+
elements
|
|
2445
|
+
};
|
|
260
2446
|
};
|
|
261
2447
|
|
|
262
|
-
|
|
263
|
-
class Vec3 {
|
|
264
|
-
constructor(x = 0, y = 0, z = 0) {
|
|
265
|
-
this.x = void 0;
|
|
266
|
-
this.y = void 0;
|
|
267
|
-
this.z = void 0;
|
|
268
|
-
if (x.length === 3) {
|
|
269
|
-
this.x = x[0];
|
|
270
|
-
this.y = x[1];
|
|
271
|
-
this.z = x[2];
|
|
272
|
-
} else {
|
|
273
|
-
this.x = x;
|
|
274
|
-
this.y = y;
|
|
275
|
-
this.z = z;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
add(rhs) {
|
|
279
|
-
this.x += rhs.x;
|
|
280
|
-
this.y += rhs.y;
|
|
281
|
-
this.z += rhs.z;
|
|
282
|
-
return this;
|
|
283
|
-
}
|
|
284
|
-
add2(lhs, rhs) {
|
|
285
|
-
this.x = lhs.x + rhs.x;
|
|
286
|
-
this.y = lhs.y + rhs.y;
|
|
287
|
-
this.z = lhs.z + rhs.z;
|
|
288
|
-
return this;
|
|
289
|
-
}
|
|
290
|
-
addScalar(scalar) {
|
|
291
|
-
this.x += scalar;
|
|
292
|
-
this.y += scalar;
|
|
293
|
-
this.z += scalar;
|
|
294
|
-
return this;
|
|
295
|
-
}
|
|
296
|
-
addScaled(rhs, scalar) {
|
|
297
|
-
this.x += rhs.x * scalar;
|
|
298
|
-
this.y += rhs.y * scalar;
|
|
299
|
-
this.z += rhs.z * scalar;
|
|
300
|
-
return this;
|
|
301
|
-
}
|
|
302
|
-
clone() {
|
|
303
|
-
const cstr = this.constructor;
|
|
304
|
-
return new cstr(this.x, this.y, this.z);
|
|
305
|
-
}
|
|
306
|
-
copy(rhs) {
|
|
307
|
-
this.x = rhs.x;
|
|
308
|
-
this.y = rhs.y;
|
|
309
|
-
this.z = rhs.z;
|
|
310
|
-
return this;
|
|
311
|
-
}
|
|
312
|
-
cross(lhs, rhs) {
|
|
313
|
-
const lx = lhs.x;
|
|
314
|
-
const ly = lhs.y;
|
|
315
|
-
const lz = lhs.z;
|
|
316
|
-
const rx = rhs.x;
|
|
317
|
-
const ry = rhs.y;
|
|
318
|
-
const rz = rhs.z;
|
|
319
|
-
this.x = ly * rz - ry * lz;
|
|
320
|
-
this.y = lz * rx - rz * lx;
|
|
321
|
-
this.z = lx * ry - rx * ly;
|
|
322
|
-
return this;
|
|
323
|
-
}
|
|
324
|
-
distance(rhs) {
|
|
325
|
-
const x = this.x - rhs.x;
|
|
326
|
-
const y = this.y - rhs.y;
|
|
327
|
-
const z = this.z - rhs.z;
|
|
328
|
-
return Math.sqrt(x * x + y * y + z * z);
|
|
329
|
-
}
|
|
330
|
-
div(rhs) {
|
|
331
|
-
this.x /= rhs.x;
|
|
332
|
-
this.y /= rhs.y;
|
|
333
|
-
this.z /= rhs.z;
|
|
334
|
-
return this;
|
|
335
|
-
}
|
|
336
|
-
div2(lhs, rhs) {
|
|
337
|
-
this.x = lhs.x / rhs.x;
|
|
338
|
-
this.y = lhs.y / rhs.y;
|
|
339
|
-
this.z = lhs.z / rhs.z;
|
|
340
|
-
return this;
|
|
341
|
-
}
|
|
342
|
-
divScalar(scalar) {
|
|
343
|
-
this.x /= scalar;
|
|
344
|
-
this.y /= scalar;
|
|
345
|
-
this.z /= scalar;
|
|
346
|
-
return this;
|
|
347
|
-
}
|
|
348
|
-
dot(rhs) {
|
|
349
|
-
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
|
|
350
|
-
}
|
|
351
|
-
equals(rhs) {
|
|
352
|
-
return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z;
|
|
353
|
-
}
|
|
354
|
-
equalsApprox(rhs, epsilon = 1e-6) {
|
|
355
|
-
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
|
|
356
|
-
}
|
|
357
|
-
length() {
|
|
358
|
-
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
359
|
-
}
|
|
360
|
-
lengthSq() {
|
|
361
|
-
return this.x * this.x + this.y * this.y + this.z * this.z;
|
|
362
|
-
}
|
|
363
|
-
lerp(lhs, rhs, alpha) {
|
|
364
|
-
this.x = lhs.x + alpha * (rhs.x - lhs.x);
|
|
365
|
-
this.y = lhs.y + alpha * (rhs.y - lhs.y);
|
|
366
|
-
this.z = lhs.z + alpha * (rhs.z - lhs.z);
|
|
367
|
-
return this;
|
|
368
|
-
}
|
|
369
|
-
mul(rhs) {
|
|
370
|
-
this.x *= rhs.x;
|
|
371
|
-
this.y *= rhs.y;
|
|
372
|
-
this.z *= rhs.z;
|
|
373
|
-
return this;
|
|
374
|
-
}
|
|
375
|
-
mul2(lhs, rhs) {
|
|
376
|
-
this.x = lhs.x * rhs.x;
|
|
377
|
-
this.y = lhs.y * rhs.y;
|
|
378
|
-
this.z = lhs.z * rhs.z;
|
|
379
|
-
return this;
|
|
380
|
-
}
|
|
381
|
-
mulScalar(scalar) {
|
|
382
|
-
this.x *= scalar;
|
|
383
|
-
this.y *= scalar;
|
|
384
|
-
this.z *= scalar;
|
|
385
|
-
return this;
|
|
386
|
-
}
|
|
387
|
-
normalize(src = this) {
|
|
388
|
-
const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z;
|
|
389
|
-
if (lengthSq > 0) {
|
|
390
|
-
const invLength = 1 / Math.sqrt(lengthSq);
|
|
391
|
-
this.x = src.x * invLength;
|
|
392
|
-
this.y = src.y * invLength;
|
|
393
|
-
this.z = src.z * invLength;
|
|
394
|
-
}
|
|
395
|
-
return this;
|
|
396
|
-
}
|
|
397
|
-
floor(src = this) {
|
|
398
|
-
this.x = Math.floor(src.x);
|
|
399
|
-
this.y = Math.floor(src.y);
|
|
400
|
-
this.z = Math.floor(src.z);
|
|
401
|
-
return this;
|
|
402
|
-
}
|
|
403
|
-
ceil(src = this) {
|
|
404
|
-
this.x = Math.ceil(src.x);
|
|
405
|
-
this.y = Math.ceil(src.y);
|
|
406
|
-
this.z = Math.ceil(src.z);
|
|
407
|
-
return this;
|
|
408
|
-
}
|
|
409
|
-
round(src = this) {
|
|
410
|
-
this.x = Math.round(src.x);
|
|
411
|
-
this.y = Math.round(src.y);
|
|
412
|
-
this.z = Math.round(src.z);
|
|
413
|
-
return this;
|
|
414
|
-
}
|
|
415
|
-
min(rhs) {
|
|
416
|
-
if (rhs.x < this.x) this.x = rhs.x;
|
|
417
|
-
if (rhs.y < this.y) this.y = rhs.y;
|
|
418
|
-
if (rhs.z < this.z) this.z = rhs.z;
|
|
419
|
-
return this;
|
|
420
|
-
}
|
|
421
|
-
max(rhs) {
|
|
422
|
-
if (rhs.x > this.x) this.x = rhs.x;
|
|
423
|
-
if (rhs.y > this.y) this.y = rhs.y;
|
|
424
|
-
if (rhs.z > this.z) this.z = rhs.z;
|
|
425
|
-
return this;
|
|
426
|
-
}
|
|
427
|
-
project(rhs) {
|
|
428
|
-
const a_dot_b = this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
|
|
429
|
-
const b_dot_b = rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z;
|
|
430
|
-
const s = a_dot_b / b_dot_b;
|
|
431
|
-
this.x = rhs.x * s;
|
|
432
|
-
this.y = rhs.y * s;
|
|
433
|
-
this.z = rhs.z * s;
|
|
434
|
-
return this;
|
|
435
|
-
}
|
|
436
|
-
set(x, y, z) {
|
|
437
|
-
this.x = x;
|
|
438
|
-
this.y = y;
|
|
439
|
-
this.z = z;
|
|
440
|
-
return this;
|
|
441
|
-
}
|
|
442
|
-
sub(rhs) {
|
|
443
|
-
this.x -= rhs.x;
|
|
444
|
-
this.y -= rhs.y;
|
|
445
|
-
this.z -= rhs.z;
|
|
446
|
-
return this;
|
|
447
|
-
}
|
|
448
|
-
sub2(lhs, rhs) {
|
|
449
|
-
this.x = lhs.x - rhs.x;
|
|
450
|
-
this.y = lhs.y - rhs.y;
|
|
451
|
-
this.z = lhs.z - rhs.z;
|
|
452
|
-
return this;
|
|
453
|
-
}
|
|
454
|
-
subScalar(scalar) {
|
|
455
|
-
this.x -= scalar;
|
|
456
|
-
this.y -= scalar;
|
|
457
|
-
this.z -= scalar;
|
|
458
|
-
return this;
|
|
459
|
-
}
|
|
460
|
-
toString() {
|
|
461
|
-
return `[${this.x}, ${this.y}, ${this.z}]`;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
_Vec = Vec3;
|
|
465
|
-
Vec3.ZERO = Object.freeze(new _Vec(0, 0, 0));
|
|
466
|
-
Vec3.HALF = Object.freeze(new _Vec(0.5, 0.5, 0.5));
|
|
467
|
-
Vec3.ONE = Object.freeze(new _Vec(1, 1, 1));
|
|
468
|
-
Vec3.UP = Object.freeze(new _Vec(0, 1, 0));
|
|
469
|
-
Vec3.DOWN = Object.freeze(new _Vec(0, -1, 0));
|
|
470
|
-
Vec3.RIGHT = Object.freeze(new _Vec(1, 0, 0));
|
|
471
|
-
Vec3.LEFT = Object.freeze(new _Vec(-1, 0, 0));
|
|
472
|
-
Vec3.FORWARD = Object.freeze(new _Vec(0, 0, -1));
|
|
473
|
-
Vec3.BACK = Object.freeze(new _Vec(0, 0, 1));
|
|
474
|
-
|
|
475
|
-
var _Quat;
|
|
476
|
-
class Quat {
|
|
477
|
-
constructor(x = 0, y = 0, z = 0, w = 1) {
|
|
478
|
-
this.x = void 0;
|
|
479
|
-
this.y = void 0;
|
|
480
|
-
this.z = void 0;
|
|
481
|
-
this.w = void 0;
|
|
482
|
-
if (x.length === 4) {
|
|
483
|
-
this.x = x[0];
|
|
484
|
-
this.y = x[1];
|
|
485
|
-
this.z = x[2];
|
|
486
|
-
this.w = x[3];
|
|
487
|
-
} else {
|
|
488
|
-
this.x = x;
|
|
489
|
-
this.y = y;
|
|
490
|
-
this.z = z;
|
|
491
|
-
this.w = w;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
clone() {
|
|
495
|
-
const cstr = this.constructor;
|
|
496
|
-
return new cstr(this.x, this.y, this.z, this.w);
|
|
497
|
-
}
|
|
498
|
-
conjugate(src = this) {
|
|
499
|
-
this.x = src.x * -1;
|
|
500
|
-
this.y = src.y * -1;
|
|
501
|
-
this.z = src.z * -1;
|
|
502
|
-
this.w = src.w;
|
|
503
|
-
return this;
|
|
504
|
-
}
|
|
505
|
-
copy(rhs) {
|
|
506
|
-
this.x = rhs.x;
|
|
507
|
-
this.y = rhs.y;
|
|
508
|
-
this.z = rhs.z;
|
|
509
|
-
this.w = rhs.w;
|
|
510
|
-
return this;
|
|
511
|
-
}
|
|
512
|
-
equals(rhs) {
|
|
513
|
-
return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z && this.w === rhs.w;
|
|
514
|
-
}
|
|
515
|
-
equalsApprox(rhs, epsilon = 1e-6) {
|
|
516
|
-
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon && Math.abs(this.w - rhs.w) < epsilon;
|
|
517
|
-
}
|
|
518
|
-
getAxisAngle(axis) {
|
|
519
|
-
let rad = Math.acos(this.w) * 2;
|
|
520
|
-
const s = Math.sin(rad / 2);
|
|
521
|
-
if (s !== 0) {
|
|
522
|
-
axis.x = this.x / s;
|
|
523
|
-
axis.y = this.y / s;
|
|
524
|
-
axis.z = this.z / s;
|
|
525
|
-
if (axis.x < 0 || axis.y < 0 || axis.z < 0) {
|
|
526
|
-
axis.x *= -1;
|
|
527
|
-
axis.y *= -1;
|
|
528
|
-
axis.z *= -1;
|
|
529
|
-
rad *= -1;
|
|
530
|
-
}
|
|
531
|
-
} else {
|
|
532
|
-
axis.x = 1;
|
|
533
|
-
axis.y = 0;
|
|
534
|
-
axis.z = 0;
|
|
535
|
-
}
|
|
536
|
-
return rad * math.RAD_TO_DEG;
|
|
537
|
-
}
|
|
538
|
-
getEulerAngles(eulers = new Vec3()) {
|
|
539
|
-
let x, y, z;
|
|
540
|
-
const qx = this.x;
|
|
541
|
-
const qy = this.y;
|
|
542
|
-
const qz = this.z;
|
|
543
|
-
const qw = this.w;
|
|
544
|
-
const a2 = 2 * (qw * qy - qx * qz);
|
|
545
|
-
if (a2 <= -0.99999) {
|
|
546
|
-
x = 2 * Math.atan2(qx, qw);
|
|
547
|
-
y = -Math.PI / 2;
|
|
548
|
-
z = 0;
|
|
549
|
-
} else if (a2 >= 0.99999) {
|
|
550
|
-
x = 2 * Math.atan2(qx, qw);
|
|
551
|
-
y = Math.PI / 2;
|
|
552
|
-
z = 0;
|
|
553
|
-
} else {
|
|
554
|
-
x = Math.atan2(2 * (qw * qx + qy * qz), 1 - 2 * (qx * qx + qy * qy));
|
|
555
|
-
y = Math.asin(a2);
|
|
556
|
-
z = Math.atan2(2 * (qw * qz + qx * qy), 1 - 2 * (qy * qy + qz * qz));
|
|
557
|
-
}
|
|
558
|
-
return eulers.set(x, y, z).mulScalar(math.RAD_TO_DEG);
|
|
559
|
-
}
|
|
560
|
-
invert(src = this) {
|
|
561
|
-
return this.conjugate(src).normalize();
|
|
562
|
-
}
|
|
563
|
-
length() {
|
|
564
|
-
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
565
|
-
}
|
|
566
|
-
lengthSq() {
|
|
567
|
-
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
|
|
568
|
-
}
|
|
569
|
-
mul(rhs) {
|
|
570
|
-
const q1x = this.x;
|
|
571
|
-
const q1y = this.y;
|
|
572
|
-
const q1z = this.z;
|
|
573
|
-
const q1w = this.w;
|
|
574
|
-
const q2x = rhs.x;
|
|
575
|
-
const q2y = rhs.y;
|
|
576
|
-
const q2z = rhs.z;
|
|
577
|
-
const q2w = rhs.w;
|
|
578
|
-
this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
|
|
579
|
-
this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
|
|
580
|
-
this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
|
|
581
|
-
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
|
|
582
|
-
return this;
|
|
583
|
-
}
|
|
584
|
-
mulScalar(scalar, src = this) {
|
|
585
|
-
this.x = src.x * scalar;
|
|
586
|
-
this.y = src.y * scalar;
|
|
587
|
-
this.z = src.z * scalar;
|
|
588
|
-
this.w = src.w * scalar;
|
|
589
|
-
return this;
|
|
590
|
-
}
|
|
591
|
-
mul2(lhs, rhs) {
|
|
592
|
-
const q1x = lhs.x;
|
|
593
|
-
const q1y = lhs.y;
|
|
594
|
-
const q1z = lhs.z;
|
|
595
|
-
const q1w = lhs.w;
|
|
596
|
-
const q2x = rhs.x;
|
|
597
|
-
const q2y = rhs.y;
|
|
598
|
-
const q2z = rhs.z;
|
|
599
|
-
const q2w = rhs.w;
|
|
600
|
-
this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
|
|
601
|
-
this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
|
|
602
|
-
this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
|
|
603
|
-
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
|
|
604
|
-
return this;
|
|
605
|
-
}
|
|
606
|
-
normalize(src = this) {
|
|
607
|
-
let len = src.length();
|
|
608
|
-
if (len === 0) {
|
|
609
|
-
this.x = this.y = this.z = 0;
|
|
610
|
-
this.w = 1;
|
|
611
|
-
} else {
|
|
612
|
-
len = 1 / len;
|
|
613
|
-
this.x = src.x * len;
|
|
614
|
-
this.y = src.y * len;
|
|
615
|
-
this.z = src.z * len;
|
|
616
|
-
this.w = src.w * len;
|
|
617
|
-
}
|
|
618
|
-
return this;
|
|
619
|
-
}
|
|
620
|
-
set(x, y, z, w) {
|
|
621
|
-
this.x = x;
|
|
622
|
-
this.y = y;
|
|
623
|
-
this.z = z;
|
|
624
|
-
this.w = w;
|
|
625
|
-
return this;
|
|
626
|
-
}
|
|
627
|
-
setFromAxisAngle(axis, angle) {
|
|
628
|
-
angle *= 0.5 * math.DEG_TO_RAD;
|
|
629
|
-
const sa = Math.sin(angle);
|
|
630
|
-
const ca = Math.cos(angle);
|
|
631
|
-
this.x = sa * axis.x;
|
|
632
|
-
this.y = sa * axis.y;
|
|
633
|
-
this.z = sa * axis.z;
|
|
634
|
-
this.w = ca;
|
|
635
|
-
return this;
|
|
636
|
-
}
|
|
637
|
-
setFromEulerAngles(ex, ey, ez) {
|
|
638
|
-
if (ex instanceof Vec3) {
|
|
639
|
-
const vec = ex;
|
|
640
|
-
ex = vec.x;
|
|
641
|
-
ey = vec.y;
|
|
642
|
-
ez = vec.z;
|
|
643
|
-
}
|
|
644
|
-
const halfToRad = 0.5 * math.DEG_TO_RAD;
|
|
645
|
-
ex *= halfToRad;
|
|
646
|
-
ey *= halfToRad;
|
|
647
|
-
ez *= halfToRad;
|
|
648
|
-
const sx = Math.sin(ex);
|
|
649
|
-
const cx = Math.cos(ex);
|
|
650
|
-
const sy = Math.sin(ey);
|
|
651
|
-
const cy = Math.cos(ey);
|
|
652
|
-
const sz = Math.sin(ez);
|
|
653
|
-
const cz = Math.cos(ez);
|
|
654
|
-
this.x = sx * cy * cz - cx * sy * sz;
|
|
655
|
-
this.y = cx * sy * cz + sx * cy * sz;
|
|
656
|
-
this.z = cx * cy * sz - sx * sy * cz;
|
|
657
|
-
this.w = cx * cy * cz + sx * sy * sz;
|
|
658
|
-
return this;
|
|
659
|
-
}
|
|
660
|
-
setFromMat4(m) {
|
|
661
|
-
const d = m.data;
|
|
662
|
-
let m00 = d[0];
|
|
663
|
-
let m01 = d[1];
|
|
664
|
-
let m02 = d[2];
|
|
665
|
-
let m10 = d[4];
|
|
666
|
-
let m11 = d[5];
|
|
667
|
-
let m12 = d[6];
|
|
668
|
-
let m20 = d[8];
|
|
669
|
-
let m21 = d[9];
|
|
670
|
-
let m22 = d[10];
|
|
671
|
-
let l;
|
|
672
|
-
l = m00 * m00 + m01 * m01 + m02 * m02;
|
|
673
|
-
if (l === 0) return this.set(0, 0, 0, 1);
|
|
674
|
-
l = 1 / Math.sqrt(l);
|
|
675
|
-
m00 *= l;
|
|
676
|
-
m01 *= l;
|
|
677
|
-
m02 *= l;
|
|
678
|
-
l = m10 * m10 + m11 * m11 + m12 * m12;
|
|
679
|
-
if (l === 0) return this.set(0, 0, 0, 1);
|
|
680
|
-
l = 1 / Math.sqrt(l);
|
|
681
|
-
m10 *= l;
|
|
682
|
-
m11 *= l;
|
|
683
|
-
m12 *= l;
|
|
684
|
-
l = m20 * m20 + m21 * m21 + m22 * m22;
|
|
685
|
-
if (l === 0) return this.set(0, 0, 0, 1);
|
|
686
|
-
l = 1 / Math.sqrt(l);
|
|
687
|
-
m20 *= l;
|
|
688
|
-
m21 *= l;
|
|
689
|
-
m22 *= l;
|
|
690
|
-
if (m22 < 0) {
|
|
691
|
-
if (m00 > m11) {
|
|
692
|
-
this.set(1 + m00 - m11 - m22, m01 + m10, m20 + m02, m12 - m21);
|
|
693
|
-
} else {
|
|
694
|
-
this.set(m01 + m10, 1 - m00 + m11 - m22, m12 + m21, m20 - m02);
|
|
695
|
-
}
|
|
696
|
-
} else {
|
|
697
|
-
if (m00 < -m11) {
|
|
698
|
-
this.set(m20 + m02, m12 + m21, 1 - m00 - m11 + m22, m01 - m10);
|
|
699
|
-
} else {
|
|
700
|
-
this.set(m12 - m21, m20 - m02, m01 - m10, 1 + m00 + m11 + m22);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
return this.mulScalar(1.0 / this.length());
|
|
704
|
-
}
|
|
705
|
-
setFromDirections(from, to) {
|
|
706
|
-
const dotProduct = 1 + from.dot(to);
|
|
707
|
-
if (dotProduct < Number.EPSILON) {
|
|
708
|
-
if (Math.abs(from.x) > Math.abs(from.y)) {
|
|
709
|
-
this.x = -from.z;
|
|
710
|
-
this.y = 0;
|
|
711
|
-
this.z = from.x;
|
|
712
|
-
this.w = 0;
|
|
713
|
-
} else {
|
|
714
|
-
this.x = 0;
|
|
715
|
-
this.y = -from.z;
|
|
716
|
-
this.z = from.y;
|
|
717
|
-
this.w = 0;
|
|
718
|
-
}
|
|
719
|
-
} else {
|
|
720
|
-
this.x = from.y * to.z - from.z * to.y;
|
|
721
|
-
this.y = from.z * to.x - from.x * to.z;
|
|
722
|
-
this.z = from.x * to.y - from.y * to.x;
|
|
723
|
-
this.w = dotProduct;
|
|
724
|
-
}
|
|
725
|
-
return this.normalize();
|
|
726
|
-
}
|
|
727
|
-
slerp(lhs, rhs, alpha) {
|
|
728
|
-
const lx = lhs.x;
|
|
729
|
-
const ly = lhs.y;
|
|
730
|
-
const lz = lhs.z;
|
|
731
|
-
const lw = lhs.w;
|
|
732
|
-
let rx = rhs.x;
|
|
733
|
-
let ry = rhs.y;
|
|
734
|
-
let rz = rhs.z;
|
|
735
|
-
let rw = rhs.w;
|
|
736
|
-
let cosHalfTheta = lw * rw + lx * rx + ly * ry + lz * rz;
|
|
737
|
-
if (cosHalfTheta < 0) {
|
|
738
|
-
rw = -rw;
|
|
739
|
-
rx = -rx;
|
|
740
|
-
ry = -ry;
|
|
741
|
-
rz = -rz;
|
|
742
|
-
cosHalfTheta = -cosHalfTheta;
|
|
743
|
-
}
|
|
744
|
-
if (Math.abs(cosHalfTheta) >= 1) {
|
|
745
|
-
this.w = lw;
|
|
746
|
-
this.x = lx;
|
|
747
|
-
this.y = ly;
|
|
748
|
-
this.z = lz;
|
|
749
|
-
return this;
|
|
750
|
-
}
|
|
751
|
-
const halfTheta = Math.acos(cosHalfTheta);
|
|
752
|
-
const sinHalfTheta = Math.sqrt(1 - cosHalfTheta * cosHalfTheta);
|
|
753
|
-
if (Math.abs(sinHalfTheta) < 0.001) {
|
|
754
|
-
this.w = lw * 0.5 + rw * 0.5;
|
|
755
|
-
this.x = lx * 0.5 + rx * 0.5;
|
|
756
|
-
this.y = ly * 0.5 + ry * 0.5;
|
|
757
|
-
this.z = lz * 0.5 + rz * 0.5;
|
|
758
|
-
return this;
|
|
759
|
-
}
|
|
760
|
-
const ratioA = Math.sin((1 - alpha) * halfTheta) / sinHalfTheta;
|
|
761
|
-
const ratioB = Math.sin(alpha * halfTheta) / sinHalfTheta;
|
|
762
|
-
this.w = lw * ratioA + rw * ratioB;
|
|
763
|
-
this.x = lx * ratioA + rx * ratioB;
|
|
764
|
-
this.y = ly * ratioA + ry * ratioB;
|
|
765
|
-
this.z = lz * ratioA + rz * ratioB;
|
|
766
|
-
return this;
|
|
767
|
-
}
|
|
768
|
-
transformVector(vec, res = new Vec3()) {
|
|
769
|
-
const x = vec.x,
|
|
770
|
-
y = vec.y,
|
|
771
|
-
z = vec.z;
|
|
772
|
-
const qx = this.x,
|
|
773
|
-
qy = this.y,
|
|
774
|
-
qz = this.z,
|
|
775
|
-
qw = this.w;
|
|
776
|
-
const ix = qw * x + qy * z - qz * y;
|
|
777
|
-
const iy = qw * y + qz * x - qx * z;
|
|
778
|
-
const iz = qw * z + qx * y - qy * x;
|
|
779
|
-
const iw = -qx * x - qy * y - qz * z;
|
|
780
|
-
res.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
|
|
781
|
-
res.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
|
|
782
|
-
res.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
|
|
783
|
-
return res;
|
|
784
|
-
}
|
|
785
|
-
toString() {
|
|
786
|
-
return `[${this.x}, ${this.y}, ${this.z}, ${this.w}]`;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
_Quat = Quat;
|
|
790
|
-
Quat.IDENTITY = Object.freeze(new _Quat(0, 0, 0, 1));
|
|
791
|
-
Quat.ZERO = Object.freeze(new _Quat(0, 0, 0, 0));
|
|
2448
|
+
const sigmoid = (v) => 1 / (1 + Math.exp(-v));
|
|
792
2449
|
|
|
793
|
-
const generatedByString = `Generated by splat-transform ${version}`;
|
|
794
|
-
const shBandCoeffs = [0, 3, 8, 15];
|
|
795
2450
|
const q = new Quat();
|
|
796
2451
|
// process and compress a chunk of 256 splats
|
|
797
|
-
class
|
|
2452
|
+
class CompressedChunk {
|
|
798
2453
|
static members = [
|
|
799
2454
|
'x', 'y', 'z',
|
|
800
2455
|
'scale_0', 'scale_1', 'scale_2',
|
|
@@ -811,7 +2466,7 @@ class Chunk {
|
|
|
811
2466
|
color;
|
|
812
2467
|
constructor(size = 256) {
|
|
813
2468
|
this.size = size;
|
|
814
|
-
|
|
2469
|
+
CompressedChunk.members.forEach((m) => {
|
|
815
2470
|
this.data[m] = new Float32Array(size);
|
|
816
2471
|
});
|
|
817
2472
|
this.chunkData = new Float32Array(18);
|
|
@@ -820,10 +2475,10 @@ class Chunk {
|
|
|
820
2475
|
this.scale = new Uint32Array(size);
|
|
821
2476
|
this.color = new Uint32Array(size);
|
|
822
2477
|
}
|
|
823
|
-
set(index,
|
|
824
|
-
|
|
825
|
-
this.data[
|
|
826
|
-
}
|
|
2478
|
+
set(index, data) {
|
|
2479
|
+
CompressedChunk.members.forEach((m) => {
|
|
2480
|
+
this.data[m][index] = data[m];
|
|
2481
|
+
});
|
|
827
2482
|
}
|
|
828
2483
|
pack() {
|
|
829
2484
|
const calcMinMax = (data) => {
|
|
@@ -923,7 +2578,7 @@ class Chunk {
|
|
|
923
2578
|
this.position[i] = pack111011(normalize(x[i], px.min, px.max), normalize(y[i], py.min, py.max), normalize(z[i], pz.min, pz.max));
|
|
924
2579
|
this.rotation[i] = packRot(rot_0[i], rot_1[i], rot_2[i], rot_3[i]);
|
|
925
2580
|
this.scale[i] = pack111011(normalize(scale_0[i], sx.min, sx.max), normalize(scale_1[i], sy.min, sy.max), normalize(scale_2[i], sz.min, sz.max));
|
|
926
|
-
this.color[i] = pack8888(normalize(f_dc_0[i], cr.min, cr.max), normalize(f_dc_1[i], cg.min, cg.max), normalize(f_dc_2[i], cb.min, cb.max),
|
|
2581
|
+
this.color[i] = pack8888(normalize(f_dc_0[i], cr.min, cr.max), normalize(f_dc_1[i], cg.min, cg.max), normalize(f_dc_2[i], cb.min, cb.max), sigmoid(opacity[i]));
|
|
927
2582
|
}
|
|
928
2583
|
this.chunkData.set([
|
|
929
2584
|
px.min, py.min, pz.min, px.max, py.max, pz.max,
|
|
@@ -932,72 +2587,108 @@ class Chunk {
|
|
|
932
2587
|
], 0);
|
|
933
2588
|
}
|
|
934
2589
|
}
|
|
2590
|
+
|
|
935
2591
|
// sort the compressed indices into morton order
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
2592
|
+
const generateOrdering = (dataTable) => {
|
|
2593
|
+
const cx = dataTable.getColumnByName('x').data;
|
|
2594
|
+
const cy = dataTable.getColumnByName('y').data;
|
|
2595
|
+
const cz = dataTable.getColumnByName('z').data;
|
|
2596
|
+
const generate = (indices) => {
|
|
2597
|
+
// https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
|
|
2598
|
+
const encodeMorton3 = (x, y, z) => {
|
|
2599
|
+
const Part1By2 = (x) => {
|
|
2600
|
+
x &= 0x000003ff;
|
|
2601
|
+
x = (x ^ (x << 16)) & 0xff0000ff;
|
|
2602
|
+
x = (x ^ (x << 8)) & 0x0300f00f;
|
|
2603
|
+
x = (x ^ (x << 4)) & 0x030c30c3;
|
|
2604
|
+
x = (x ^ (x << 2)) & 0x09249249;
|
|
2605
|
+
return x;
|
|
2606
|
+
};
|
|
2607
|
+
return (Part1By2(z) << 2) + (Part1By2(y) << 1) + Part1By2(x);
|
|
946
2608
|
};
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
2609
|
+
let mx;
|
|
2610
|
+
let my;
|
|
2611
|
+
let mz;
|
|
2612
|
+
let Mx;
|
|
2613
|
+
let My;
|
|
2614
|
+
let Mz;
|
|
2615
|
+
// calculate scene extents across all splats (using sort centers, because they're in world space)
|
|
2616
|
+
for (let i = 0; i < indices.length; ++i) {
|
|
2617
|
+
const ri = indices[i];
|
|
2618
|
+
const x = cx[ri];
|
|
2619
|
+
const y = cy[ri];
|
|
2620
|
+
const z = cz[ri];
|
|
2621
|
+
if (mx === undefined) {
|
|
2622
|
+
mx = Mx = x;
|
|
2623
|
+
my = My = y;
|
|
2624
|
+
mz = Mz = z;
|
|
2625
|
+
}
|
|
2626
|
+
else {
|
|
2627
|
+
if (x < mx)
|
|
2628
|
+
mx = x;
|
|
2629
|
+
else if (x > Mx)
|
|
2630
|
+
Mx = x;
|
|
2631
|
+
if (y < my)
|
|
2632
|
+
my = y;
|
|
2633
|
+
else if (y > My)
|
|
2634
|
+
My = y;
|
|
2635
|
+
if (z < mz)
|
|
2636
|
+
mz = z;
|
|
2637
|
+
else if (z > Mz)
|
|
2638
|
+
Mz = z;
|
|
2639
|
+
}
|
|
967
2640
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
miny = y;
|
|
975
|
-
else if (y > maxy)
|
|
976
|
-
maxy = y;
|
|
977
|
-
if (z < minz)
|
|
978
|
-
minz = z;
|
|
979
|
-
else if (z > maxz)
|
|
980
|
-
maxz = z;
|
|
2641
|
+
const xlen = Mx - mx;
|
|
2642
|
+
const ylen = My - my;
|
|
2643
|
+
const zlen = Mz - mz;
|
|
2644
|
+
if (!isFinite(xlen) || !isFinite(ylen) || !isFinite(zlen)) {
|
|
2645
|
+
console.log('invalid extents', xlen, ylen, zlen);
|
|
2646
|
+
return;
|
|
981
2647
|
}
|
|
2648
|
+
const xmul = 1024 / xlen;
|
|
2649
|
+
const ymul = 1024 / ylen;
|
|
2650
|
+
const zmul = 1024 / zlen;
|
|
2651
|
+
const morton = new Uint32Array(indices.length);
|
|
2652
|
+
for (let i = 0; i < indices.length; ++i) {
|
|
2653
|
+
const ri = indices[i];
|
|
2654
|
+
const x = cx[ri];
|
|
2655
|
+
const y = cy[ri];
|
|
2656
|
+
const z = cz[ri];
|
|
2657
|
+
const ix = Math.min(1023, (x - mx) * xmul) >>> 0;
|
|
2658
|
+
const iy = Math.min(1023, (y - my) * ymul) >>> 0;
|
|
2659
|
+
const iz = Math.min(1023, (z - mz) * zmul) >>> 0;
|
|
2660
|
+
morton[i] = encodeMorton3(ix, iy, iz);
|
|
2661
|
+
}
|
|
2662
|
+
// sort indices by morton code
|
|
2663
|
+
const order = indices.map((_, i) => i);
|
|
2664
|
+
order.sort((a, b) => morton[a] - morton[b]);
|
|
2665
|
+
const tmpIndices = indices.slice();
|
|
2666
|
+
for (let i = 0; i < indices.length; ++i) {
|
|
2667
|
+
indices[i] = tmpIndices[order[i]];
|
|
2668
|
+
}
|
|
2669
|
+
// sort the largest buckets recursively
|
|
2670
|
+
let start = 0;
|
|
2671
|
+
let end = 1;
|
|
2672
|
+
while (start < indices.length) {
|
|
2673
|
+
while (end < indices.length && morton[order[end]] === morton[order[start]]) {
|
|
2674
|
+
++end;
|
|
2675
|
+
}
|
|
2676
|
+
if (end - start > 256) {
|
|
2677
|
+
// console.log('sorting', end - start);
|
|
2678
|
+
generate(indices.subarray(start, end));
|
|
2679
|
+
}
|
|
2680
|
+
start = end;
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
const indices = new Uint32Array(dataTable.numRows);
|
|
2684
|
+
for (let i = 0; i < indices.length; ++i) {
|
|
2685
|
+
indices[i] = i;
|
|
982
2686
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const zlen = maxz - minz;
|
|
986
|
-
const morton = new Uint32Array(indices.length);
|
|
987
|
-
let idx = 0;
|
|
988
|
-
for (let i = 0; i < splat.numSplats; ++i) {
|
|
989
|
-
it(i);
|
|
990
|
-
const x = vertex[0];
|
|
991
|
-
const y = vertex[1];
|
|
992
|
-
const z = vertex[2];
|
|
993
|
-
const ix = Math.floor(1024 * (x - minx) / xlen);
|
|
994
|
-
const iy = Math.floor(1024 * (y - miny) / ylen);
|
|
995
|
-
const iz = Math.floor(1024 * (z - minz) / zlen);
|
|
996
|
-
morton[idx++] = encodeMorton3(ix, iy, iz);
|
|
997
|
-
}
|
|
998
|
-
// order splats by morton code
|
|
999
|
-
indices.sort((a, b) => morton[a.globalIndex] - morton[b.globalIndex]);
|
|
2687
|
+
generate(indices);
|
|
2688
|
+
return indices;
|
|
1000
2689
|
};
|
|
2690
|
+
|
|
2691
|
+
const generatedByString = `Generated by splat-transform ${version}`;
|
|
1001
2692
|
const chunkProps = [
|
|
1002
2693
|
'min_x', 'min_y', 'min_z',
|
|
1003
2694
|
'max_x', 'max_y', 'max_z',
|
|
@@ -1012,20 +2703,13 @@ const vertexProps = [
|
|
|
1012
2703
|
'packed_scale',
|
|
1013
2704
|
'packed_color'
|
|
1014
2705
|
];
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
}
|
|
1021
|
-
if (indices.length === 0) {
|
|
1022
|
-
throw new Error('no splats to write');
|
|
1023
|
-
}
|
|
1024
|
-
const numSplats = indices.length;
|
|
2706
|
+
const shNames = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
|
|
2707
|
+
const writeCompressedPly = async (fileHandle, dataTable) => {
|
|
2708
|
+
const shBands = { '9': 1, '24': 2, '-1': 3 }[shNames.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
|
|
2709
|
+
const outputSHCoeffs = [0, 3, 8, 15][shBands];
|
|
2710
|
+
const numSplats = dataTable.numRows;
|
|
1025
2711
|
const numChunks = Math.ceil(numSplats / 256);
|
|
1026
|
-
const
|
|
1027
|
-
const outputSHCoeffs = shBandCoeffs[outputSHBands];
|
|
1028
|
-
const shHeader = outputSHBands ? [
|
|
2712
|
+
const shHeader = shBands ? [
|
|
1029
2713
|
`element sh ${numSplats}`,
|
|
1030
2714
|
new Array(outputSHCoeffs * 3).fill('').map((_, i) => `property uchar f_rest_${i}`)
|
|
1031
2715
|
].flat() : [];
|
|
@@ -1042,32 +2726,31 @@ const writeCompressedPly = async (fileHandle, splat) => {
|
|
|
1042
2726
|
].flat().join('\n');
|
|
1043
2727
|
const header = (new TextEncoder()).encode(headerText);
|
|
1044
2728
|
const chunkData = new Float32Array(numChunks * chunkProps.length);
|
|
1045
|
-
const
|
|
2729
|
+
const splatIData = new Uint32Array(numSplats * vertexProps.length);
|
|
1046
2730
|
const shData = new Uint8Array(numSplats * outputSHCoeffs * 3);
|
|
1047
2731
|
// sort splats into some kind of order (morton order rn)
|
|
1048
|
-
|
|
1049
|
-
const
|
|
1050
|
-
const
|
|
1051
|
-
const shMembers = shNames.slice(0, outputSHCoeffs * 3);
|
|
1052
|
-
const singleSH = shMembers.map(_ => 0);
|
|
1053
|
-
const shIt = splat.createIterator(shMembers, singleSH);
|
|
1054
|
-
const chunk = new Chunk();
|
|
2732
|
+
const sortIndices = generateOrdering(dataTable);
|
|
2733
|
+
const row = {};
|
|
2734
|
+
const chunk = new CompressedChunk();
|
|
1055
2735
|
for (let i = 0; i < numChunks; ++i) {
|
|
1056
2736
|
const num = Math.min(numSplats, (i + 1) * 256) - i * 256;
|
|
1057
2737
|
for (let j = 0; j < num; ++j) {
|
|
1058
|
-
const index =
|
|
2738
|
+
const index = sortIndices[i * 256 + j];
|
|
1059
2739
|
// read splat data
|
|
1060
|
-
|
|
2740
|
+
dataTable.getRow(index, row);
|
|
1061
2741
|
// update chunk
|
|
1062
|
-
chunk.set(j,
|
|
1063
|
-
shIt(index.i);
|
|
2742
|
+
chunk.set(j, row);
|
|
1064
2743
|
// quantize and write sh data
|
|
1065
2744
|
let off = (i * 256 + j) * outputSHCoeffs * 3;
|
|
1066
2745
|
for (let k = 0; k < outputSHCoeffs * 3; ++k) {
|
|
1067
|
-
const nvalue =
|
|
2746
|
+
const nvalue = row[shNames[k]] / 8 + 0.5;
|
|
1068
2747
|
shData[off++] = Math.max(0, Math.min(255, Math.trunc(nvalue * 256)));
|
|
1069
2748
|
}
|
|
1070
2749
|
}
|
|
2750
|
+
// repeat the last gaussian to fill the rest of the final chunk
|
|
2751
|
+
for (let j = num; j < 256; ++j) {
|
|
2752
|
+
chunk.set(j, row);
|
|
2753
|
+
}
|
|
1071
2754
|
// pack the chunk
|
|
1072
2755
|
chunk.pack();
|
|
1073
2756
|
// store the float data
|
|
@@ -1075,57 +2758,490 @@ const writeCompressedPly = async (fileHandle, splat) => {
|
|
|
1075
2758
|
// write packed bits
|
|
1076
2759
|
const offset = i * 256 * 4;
|
|
1077
2760
|
for (let j = 0; j < num; ++j) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
2761
|
+
splatIData[offset + j * 4 + 0] = chunk.position[j];
|
|
2762
|
+
splatIData[offset + j * 4 + 1] = chunk.rotation[j];
|
|
2763
|
+
splatIData[offset + j * 4 + 2] = chunk.scale[j];
|
|
2764
|
+
splatIData[offset + j * 4 + 3] = chunk.color[j];
|
|
1082
2765
|
}
|
|
1083
2766
|
}
|
|
1084
2767
|
await fileHandle.write(header);
|
|
1085
2768
|
await fileHandle.write(new Uint8Array(chunkData.buffer));
|
|
1086
|
-
await fileHandle.write(new Uint8Array(
|
|
2769
|
+
await fileHandle.write(new Uint8Array(splatIData.buffer));
|
|
1087
2770
|
await fileHandle.write(shData);
|
|
1088
2771
|
};
|
|
1089
2772
|
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
2773
|
+
const columnTypeToPlyType = (type) => {
|
|
2774
|
+
switch (type) {
|
|
2775
|
+
case 'float32': return 'float';
|
|
2776
|
+
case 'float64': return 'double';
|
|
2777
|
+
case 'int8': return 'char';
|
|
2778
|
+
case 'uint8': return 'uchar';
|
|
2779
|
+
case 'int16': return 'short';
|
|
2780
|
+
case 'uint16': return 'ushort';
|
|
2781
|
+
case 'int32': return 'int';
|
|
2782
|
+
case 'uint32': return 'uint';
|
|
2783
|
+
}
|
|
2784
|
+
};
|
|
2785
|
+
const writePly = async (fileHandle, plyData) => {
|
|
2786
|
+
const header = [
|
|
2787
|
+
'ply',
|
|
2788
|
+
'format binary_little_endian 1.0',
|
|
2789
|
+
plyData.comments.map(c => `comment ${c}`),
|
|
2790
|
+
plyData.elements.map((element) => {
|
|
2791
|
+
return [
|
|
2792
|
+
`element ${element.name} ${element.dataTable.numRows}`,
|
|
2793
|
+
element.dataTable.columns.map((column) => {
|
|
2794
|
+
return `property ${columnTypeToPlyType(column.dataType)} ${column.name}`;
|
|
2795
|
+
})
|
|
2796
|
+
];
|
|
2797
|
+
}),
|
|
2798
|
+
'end_header'
|
|
2799
|
+
];
|
|
2800
|
+
// write the header
|
|
2801
|
+
await fileHandle.write((new TextEncoder()).encode(`${header.flat(3).join('\n')}\n`));
|
|
2802
|
+
for (let i = 0; i < plyData.elements.length; ++i) {
|
|
2803
|
+
const table = plyData.elements[i].dataTable;
|
|
2804
|
+
const columns = table.columns;
|
|
2805
|
+
const buffers = columns.map(c => Buffer.from(c.data.buffer));
|
|
2806
|
+
const sizes = columns.map(c => c.data.BYTES_PER_ELEMENT);
|
|
2807
|
+
const rowSize = sizes.reduce((total, size) => total + size, 0);
|
|
2808
|
+
// write to file in chunks of 1024 rows
|
|
2809
|
+
const chunkSize = 1024;
|
|
2810
|
+
const numChunks = Math.ceil(table.numRows / chunkSize);
|
|
2811
|
+
const chunkData = Buffer.alloc(chunkSize * rowSize);
|
|
2812
|
+
for (let c = 0; c < numChunks; ++c) {
|
|
2813
|
+
const numRows = Math.min(chunkSize, table.numRows - c * chunkSize);
|
|
2814
|
+
let offset = 0;
|
|
2815
|
+
for (let r = 0; r < numRows; ++r) {
|
|
2816
|
+
const rowOffset = c * chunkSize + r;
|
|
2817
|
+
for (let p = 0; p < columns.length; ++p) {
|
|
2818
|
+
const s = sizes[p];
|
|
2819
|
+
buffers[p].copy(chunkData, offset, rowOffset * s, rowOffset * s + s);
|
|
2820
|
+
offset += s;
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
// write the chunk
|
|
2824
|
+
await fileHandle.write(chunkData.subarray(0, offset));
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
};
|
|
2828
|
+
|
|
2829
|
+
new Array(45).fill('').map((_, i) => `f_rest_${i}`);
|
|
2830
|
+
const calcMinMax = (dataTable, columnNames) => {
|
|
2831
|
+
const columns = columnNames.map(name => dataTable.getColumnByName(name));
|
|
2832
|
+
const minMax = columnNames.map(() => [Infinity, -Infinity]);
|
|
2833
|
+
const row = {};
|
|
2834
|
+
for (let i = 0; i < dataTable.numRows; ++i) {
|
|
2835
|
+
const r = dataTable.getRow(i, row, columns);
|
|
2836
|
+
for (let j = 0; j < columnNames.length; ++j) {
|
|
2837
|
+
const value = r[columnNames[j]];
|
|
2838
|
+
if (value < minMax[j][0])
|
|
2839
|
+
minMax[j][0] = value;
|
|
2840
|
+
if (value > minMax[j][1])
|
|
2841
|
+
minMax[j][1] = value;
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
return minMax;
|
|
2845
|
+
};
|
|
2846
|
+
const logTransform = (value) => {
|
|
2847
|
+
return Math.sign(value) * Math.log(Math.abs(value) + 1);
|
|
2848
|
+
};
|
|
2849
|
+
// calculate the output index of the gaussian given its index. chunks of
|
|
2850
|
+
// 256 gaussians are packed into 16x16 tiles on the output texture.
|
|
2851
|
+
const target = (index, width) => {
|
|
2852
|
+
const chunkWidth = width / 16;
|
|
2853
|
+
const chunkIndex = Math.floor(index / 256);
|
|
2854
|
+
const chunkX = chunkIndex % chunkWidth;
|
|
2855
|
+
const chunkY = Math.floor(chunkIndex / chunkWidth);
|
|
2856
|
+
const x = chunkX * 16 + (index % 16);
|
|
2857
|
+
const y = chunkY * 16 + Math.floor((index % 256) / 16);
|
|
2858
|
+
return x + y * width;
|
|
2859
|
+
};
|
|
2860
|
+
const writeSogs = async (outputFilename, dataTable) => {
|
|
2861
|
+
// generate an optimal ordering
|
|
2862
|
+
const sortIndices = generateOrdering(dataTable);
|
|
2863
|
+
const numRows = dataTable.numRows;
|
|
2864
|
+
const width = Math.ceil(Math.sqrt(numRows) / 16) * 16;
|
|
2865
|
+
const height = Math.ceil(numRows / width / 16) * 16;
|
|
2866
|
+
const channels = 4;
|
|
2867
|
+
const write = (filename, data, w = width, h = height) => {
|
|
2868
|
+
const pathname = resolve(dirname(outputFilename), filename);
|
|
2869
|
+
console.log(`writing '${pathname}'...`);
|
|
2870
|
+
return sharp(data, { raw: { width: w, height: h, channels } })
|
|
2871
|
+
.webp({ lossless: true })
|
|
2872
|
+
.toFile(pathname);
|
|
2873
|
+
};
|
|
2874
|
+
const row = {};
|
|
2875
|
+
// convert position/means
|
|
2876
|
+
const meansL = new Uint8Array(width * height * channels);
|
|
2877
|
+
const meansU = new Uint8Array(width * height * channels);
|
|
2878
|
+
const meansNames = ['x', 'y', 'z'];
|
|
2879
|
+
const meansMinMax = calcMinMax(dataTable, meansNames).map(v => v.map(logTransform));
|
|
2880
|
+
const meansColumns = meansNames.map(name => dataTable.getColumnByName(name));
|
|
2881
|
+
for (let i = 0; i < dataTable.numRows; ++i) {
|
|
2882
|
+
dataTable.getRow(sortIndices[i], row, meansColumns);
|
|
2883
|
+
const x = 65535 * (logTransform(row.x) - meansMinMax[0][0]) / (meansMinMax[0][1] - meansMinMax[0][0]);
|
|
2884
|
+
const y = 65535 * (logTransform(row.y) - meansMinMax[1][0]) / (meansMinMax[1][1] - meansMinMax[1][0]);
|
|
2885
|
+
const z = 65535 * (logTransform(row.z) - meansMinMax[2][0]) / (meansMinMax[2][1] - meansMinMax[2][0]);
|
|
2886
|
+
const ti = target(i, width);
|
|
2887
|
+
meansL[ti * 4] = x & 0xff;
|
|
2888
|
+
meansL[ti * 4 + 1] = y & 0xff;
|
|
2889
|
+
meansL[ti * 4 + 2] = z & 0xff;
|
|
2890
|
+
meansL[ti * 4 + 3] = 0xff;
|
|
2891
|
+
meansU[ti * 4] = (x >> 8) & 0xff;
|
|
2892
|
+
meansU[ti * 4 + 1] = (y >> 8) & 0xff;
|
|
2893
|
+
meansU[ti * 4 + 2] = (z >> 8) & 0xff;
|
|
2894
|
+
meansU[ti * 4 + 3] = 0xff;
|
|
2895
|
+
}
|
|
2896
|
+
await write('means_l.webp', meansL);
|
|
2897
|
+
await write('means_u.webp', meansU);
|
|
2898
|
+
// convert quaternions
|
|
2899
|
+
const quats = new Uint8Array(width * height * channels);
|
|
2900
|
+
const quatNames = ['rot_0', 'rot_1', 'rot_2', 'rot_3'];
|
|
2901
|
+
const quatColumns = quatNames.map(name => dataTable.getColumnByName(name));
|
|
2902
|
+
const q = [0, 0, 0, 0];
|
|
2903
|
+
for (let i = 0; i < dataTable.numRows; ++i) {
|
|
2904
|
+
dataTable.getRow(sortIndices[i], row, quatColumns);
|
|
2905
|
+
q[0] = row.rot_0;
|
|
2906
|
+
q[1] = row.rot_1;
|
|
2907
|
+
q[2] = row.rot_2;
|
|
2908
|
+
q[3] = row.rot_3;
|
|
2909
|
+
const l = Math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
|
|
2910
|
+
// normalize
|
|
2911
|
+
q.forEach((v, j) => {
|
|
2912
|
+
q[j] = v / l;
|
|
2913
|
+
});
|
|
2914
|
+
// find max component
|
|
2915
|
+
const maxComp = q.reduce((v, _, i) => (Math.abs(q[i]) > Math.abs(q[v]) ? i : v), 0);
|
|
2916
|
+
// invert if max component is negative
|
|
2917
|
+
if (q[maxComp] < 0) {
|
|
2918
|
+
q.forEach((v, j) => {
|
|
2919
|
+
q[j] *= -1;
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
// scale by sqrt(2) to fit in [-1, 1] range
|
|
2923
|
+
const sqrt2 = Math.sqrt(2);
|
|
2924
|
+
q.forEach((v, j) => {
|
|
2925
|
+
q[j] *= sqrt2;
|
|
2926
|
+
});
|
|
2927
|
+
const idx = [
|
|
2928
|
+
[1, 2, 3],
|
|
2929
|
+
[0, 2, 3],
|
|
2930
|
+
[0, 1, 3],
|
|
2931
|
+
[0, 1, 2]
|
|
2932
|
+
][maxComp];
|
|
2933
|
+
const ti = target(i, width);
|
|
2934
|
+
quats[ti * 4] = 255 * (q[idx[0]] * 0.5 + 0.5);
|
|
2935
|
+
quats[ti * 4 + 1] = 255 * (q[idx[1]] * 0.5 + 0.5);
|
|
2936
|
+
quats[ti * 4 + 2] = 255 * (q[idx[2]] * 0.5 + 0.5);
|
|
2937
|
+
quats[ti * 4 + 3] = 252 + maxComp;
|
|
2938
|
+
}
|
|
2939
|
+
await write('quats.webp', quats);
|
|
2940
|
+
// scales
|
|
2941
|
+
const scales = new Uint8Array(width * height * channels);
|
|
2942
|
+
const scaleNames = ['scale_0', 'scale_1', 'scale_2'];
|
|
2943
|
+
const scaleColumns = scaleNames.map(name => dataTable.getColumnByName(name));
|
|
2944
|
+
const scaleMinMax = calcMinMax(dataTable, scaleNames);
|
|
2945
|
+
for (let i = 0; i < dataTable.numRows; ++i) {
|
|
2946
|
+
dataTable.getRow(sortIndices[i], row, scaleColumns);
|
|
2947
|
+
const ti = target(i, width);
|
|
2948
|
+
scales[ti * 4] = 255 * (row.scale_0 - scaleMinMax[0][0]) / (scaleMinMax[0][1] - scaleMinMax[0][0]);
|
|
2949
|
+
scales[ti * 4 + 1] = 255 * (row.scale_1 - scaleMinMax[1][0]) / (scaleMinMax[1][1] - scaleMinMax[1][0]);
|
|
2950
|
+
scales[ti * 4 + 2] = 255 * (row.scale_2 - scaleMinMax[2][0]) / (scaleMinMax[2][1] - scaleMinMax[2][0]);
|
|
2951
|
+
scales[ti * 4 + 3] = 0xff;
|
|
2952
|
+
}
|
|
2953
|
+
await write('scales.webp', scales);
|
|
2954
|
+
// colors
|
|
2955
|
+
const sh0 = new Uint8Array(width * height * channels);
|
|
2956
|
+
const sh0Names = ['f_dc_0', 'f_dc_1', 'f_dc_2', 'opacity'];
|
|
2957
|
+
const sh0Columns = sh0Names.map(name => dataTable.getColumnByName(name));
|
|
2958
|
+
const sh0MinMax = calcMinMax(dataTable, sh0Names);
|
|
2959
|
+
for (let i = 0; i < dataTable.numRows; ++i) {
|
|
2960
|
+
dataTable.getRow(sortIndices[i], row, sh0Columns);
|
|
2961
|
+
const ti = target(i, width);
|
|
2962
|
+
sh0[ti * 4] = 255 * (row.f_dc_0 - sh0MinMax[0][0]) / (sh0MinMax[0][1] - sh0MinMax[0][0]);
|
|
2963
|
+
sh0[ti * 4 + 1] = 255 * (row.f_dc_1 - sh0MinMax[1][0]) / (sh0MinMax[1][1] - sh0MinMax[1][0]);
|
|
2964
|
+
sh0[ti * 4 + 2] = 255 * (row.f_dc_2 - sh0MinMax[2][0]) / (sh0MinMax[2][1] - sh0MinMax[2][0]);
|
|
2965
|
+
sh0[ti * 4 + 3] = 255 * (row.opacity - sh0MinMax[3][0]) / (sh0MinMax[3][1] - sh0MinMax[3][0]);
|
|
2966
|
+
}
|
|
2967
|
+
await write('sh0.webp', sh0);
|
|
2968
|
+
// write meta.json
|
|
2969
|
+
const meta = {
|
|
2970
|
+
means: {
|
|
2971
|
+
shape: [numRows, 3],
|
|
2972
|
+
dtype: 'float32',
|
|
2973
|
+
mins: meansMinMax.map(v => v[0]),
|
|
2974
|
+
maxs: meansMinMax.map(v => v[1]),
|
|
2975
|
+
files: [
|
|
2976
|
+
'means_l.webp',
|
|
2977
|
+
'means_u.webp'
|
|
2978
|
+
]
|
|
2979
|
+
},
|
|
2980
|
+
scales: {
|
|
2981
|
+
shape: [numRows, 3],
|
|
2982
|
+
dtype: 'float32',
|
|
2983
|
+
mins: scaleMinMax.map(v => v[0]),
|
|
2984
|
+
maxs: scaleMinMax.map(v => v[1]),
|
|
2985
|
+
files: ['scales.webp']
|
|
2986
|
+
},
|
|
2987
|
+
quats: {
|
|
2988
|
+
shape: [numRows, 4],
|
|
2989
|
+
dtype: 'uint8',
|
|
2990
|
+
encoding: 'quaternion_packed',
|
|
2991
|
+
files: ['quats.webp']
|
|
2992
|
+
},
|
|
2993
|
+
sh0: {
|
|
2994
|
+
shape: [numRows, 1, 4],
|
|
2995
|
+
dtype: 'float32',
|
|
2996
|
+
mins: sh0MinMax.map(v => v[0]),
|
|
2997
|
+
maxs: sh0MinMax.map(v => v[1]),
|
|
2998
|
+
files: ['sh0.webp']
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
const outputFile = await open(outputFilename, 'w');
|
|
3002
|
+
await outputFile.write((new TextEncoder()).encode(JSON.stringify(meta, null, 4)));
|
|
3003
|
+
await outputFile.close();
|
|
3004
|
+
};
|
|
3005
|
+
|
|
3006
|
+
const readFile = async (filename) => {
|
|
3007
|
+
console.log(`reading '${filename}'...`);
|
|
1093
3008
|
const inputFile = await open(filename, 'r');
|
|
1094
|
-
|
|
1095
|
-
console.log(`reading contents...`);
|
|
1096
|
-
const plyFile = await readPly(inputFile);
|
|
1097
|
-
// close file
|
|
3009
|
+
const plyData = await readPly(inputFile);
|
|
1098
3010
|
await inputFile.close();
|
|
1099
|
-
return
|
|
3011
|
+
return plyData;
|
|
1100
3012
|
};
|
|
1101
|
-
const
|
|
1102
|
-
|
|
3013
|
+
const writeFile = async (filename, dataTable) => {
|
|
3014
|
+
if (filename.endsWith('.json')) {
|
|
3015
|
+
await writeSogs(filename, dataTable);
|
|
3016
|
+
}
|
|
3017
|
+
else if (filename.endsWith('.compressed.ply')) {
|
|
3018
|
+
console.log(`writing '${filename}'...`);
|
|
3019
|
+
const outputFile = await open(filename, 'w');
|
|
3020
|
+
await writeCompressedPly(outputFile, dataTable);
|
|
3021
|
+
await outputFile.close();
|
|
3022
|
+
}
|
|
3023
|
+
else {
|
|
3024
|
+
console.log(`writing '${filename}'...`);
|
|
3025
|
+
const outputFile = await open(filename, 'w');
|
|
3026
|
+
await writePly(outputFile, {
|
|
3027
|
+
comments: [],
|
|
3028
|
+
elements: [{
|
|
3029
|
+
name: 'vertex',
|
|
3030
|
+
dataTable: dataTable
|
|
3031
|
+
}]
|
|
3032
|
+
});
|
|
3033
|
+
await outputFile.close();
|
|
3034
|
+
}
|
|
1103
3035
|
};
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
3036
|
+
// combine multiple tables into one
|
|
3037
|
+
// columns with matching name and type are combined
|
|
3038
|
+
const combine = (dataTables) => {
|
|
3039
|
+
if (dataTables.length === 1) {
|
|
3040
|
+
// nothing to combine
|
|
3041
|
+
return dataTables[0];
|
|
3042
|
+
}
|
|
3043
|
+
const findMatchingColumn = (columns, column) => {
|
|
3044
|
+
for (let i = 0; i < columns.length; ++i) {
|
|
3045
|
+
if (columns[i].name === column.name &&
|
|
3046
|
+
columns[i].dataType === column.dataType) {
|
|
3047
|
+
return columns[i];
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
return null;
|
|
3051
|
+
};
|
|
3052
|
+
// make unique list of columns where name and type much match
|
|
3053
|
+
const columns = dataTables[0].columns.slice();
|
|
3054
|
+
for (let i = 1; i < dataTables.length; ++i) {
|
|
3055
|
+
const dataTable = dataTables[i];
|
|
3056
|
+
for (let j = 0; j < dataTable.columns.length; ++j) {
|
|
3057
|
+
if (!findMatchingColumn(columns, dataTable.columns[j])) {
|
|
3058
|
+
columns.push(dataTable.columns[j]);
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
// count total number of rows
|
|
3063
|
+
const totalRows = dataTables.reduce((sum, dataTable) => sum + dataTable.numRows, 0);
|
|
3064
|
+
// construct output dataTable
|
|
3065
|
+
const resultColumns = columns.map((column) => {
|
|
3066
|
+
const constructor = column.data.constructor;
|
|
3067
|
+
return new Column(column.name, new constructor(totalRows));
|
|
3068
|
+
});
|
|
3069
|
+
const result = new DataTable(resultColumns);
|
|
3070
|
+
// copy data
|
|
3071
|
+
let rowOffset = 0;
|
|
3072
|
+
for (let i = 0; i < dataTables.length; ++i) {
|
|
3073
|
+
const dataTable = dataTables[i];
|
|
3074
|
+
for (let j = 0; j < dataTable.columns.length; ++j) {
|
|
3075
|
+
const column = dataTable.columns[j];
|
|
3076
|
+
const targetColumn = findMatchingColumn(result.columns, column);
|
|
3077
|
+
targetColumn.data.set(column.data, rowOffset);
|
|
3078
|
+
}
|
|
3079
|
+
rowOffset += dataTable.numRows;
|
|
3080
|
+
}
|
|
3081
|
+
return result;
|
|
3082
|
+
};
|
|
3083
|
+
const isGSDataTable = (dataTable) => {
|
|
3084
|
+
if (![
|
|
3085
|
+
'x', 'y', 'z',
|
|
3086
|
+
'rot_0', 'rot_1', 'rot_2', 'rot_3',
|
|
3087
|
+
'scale_0', 'scale_1', 'scale_2',
|
|
3088
|
+
'f_dc_0', 'f_dc_1', 'f_dc_2',
|
|
3089
|
+
'opacity'
|
|
3090
|
+
].every(c => dataTable.hasColumn(c))) {
|
|
3091
|
+
return false;
|
|
3092
|
+
}
|
|
3093
|
+
return true;
|
|
3094
|
+
};
|
|
3095
|
+
const parseArguments = () => {
|
|
3096
|
+
const { values: v, tokens } = parseArgs({
|
|
3097
|
+
tokens: true,
|
|
3098
|
+
strict: true,
|
|
3099
|
+
allowPositionals: true,
|
|
3100
|
+
options: {
|
|
3101
|
+
translate: { type: 'string', short: 't', multiple: true },
|
|
3102
|
+
rotate: { type: 'string', short: 'r', multiple: true },
|
|
3103
|
+
scale: { type: 'string', short: 's', multiple: true },
|
|
3104
|
+
filterNaN: { type: 'boolean', short: 'n', multiple: true },
|
|
3105
|
+
filterByValue: { type: 'string', short: 'c', multiple: true },
|
|
3106
|
+
filterBands: { type: 'string', short: 'h', multiple: true }
|
|
3107
|
+
}
|
|
3108
|
+
});
|
|
3109
|
+
const parseNumber = (value) => {
|
|
3110
|
+
const result = Number(value);
|
|
3111
|
+
if (isNaN(result)) {
|
|
3112
|
+
throw new Error(`Invalid number value: ${value}`);
|
|
3113
|
+
}
|
|
3114
|
+
return result;
|
|
3115
|
+
};
|
|
3116
|
+
const parseVec3 = (value) => {
|
|
3117
|
+
const parts = value.split(',').map(Number);
|
|
3118
|
+
if (parts.length !== 3 || parts.some(isNaN)) {
|
|
3119
|
+
throw new Error(`Invalid Vec3 value: ${value}`);
|
|
3120
|
+
}
|
|
3121
|
+
return new Vec3(parts[0], parts[1], parts[2]);
|
|
3122
|
+
};
|
|
3123
|
+
const parseComparator = (value) => {
|
|
3124
|
+
switch (value) {
|
|
3125
|
+
case 'lt': return 'lt';
|
|
3126
|
+
case 'lte': return 'lte';
|
|
3127
|
+
case 'gt': return 'gt';
|
|
3128
|
+
case 'gte': return 'gte';
|
|
3129
|
+
case 'eq': return 'eq';
|
|
3130
|
+
case 'neq': return 'neq';
|
|
3131
|
+
default:
|
|
3132
|
+
throw new Error(`Invalid comparator value: ${value}`);
|
|
3133
|
+
}
|
|
3134
|
+
};
|
|
3135
|
+
const files = [];
|
|
3136
|
+
for (const t of tokens) {
|
|
3137
|
+
if (t.kind === 'positional') {
|
|
3138
|
+
files.push({
|
|
3139
|
+
filename: t.value,
|
|
3140
|
+
processActions: []
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
else if (t.kind === 'option' && files.length > 0) {
|
|
3144
|
+
const current = files[files.length - 1];
|
|
3145
|
+
switch (t.name) {
|
|
3146
|
+
case 'translate':
|
|
3147
|
+
current.processActions.push({
|
|
3148
|
+
kind: 'translate',
|
|
3149
|
+
value: parseVec3(t.value)
|
|
3150
|
+
});
|
|
3151
|
+
break;
|
|
3152
|
+
case 'rotate':
|
|
3153
|
+
current.processActions.push({
|
|
3154
|
+
kind: 'rotate',
|
|
3155
|
+
value: parseVec3(t.value)
|
|
3156
|
+
});
|
|
3157
|
+
break;
|
|
3158
|
+
case 'scale':
|
|
3159
|
+
current.processActions.push({
|
|
3160
|
+
kind: 'scale',
|
|
3161
|
+
value: parseNumber(t.value)
|
|
3162
|
+
});
|
|
3163
|
+
break;
|
|
3164
|
+
case 'filterNaN':
|
|
3165
|
+
current.processActions.push({
|
|
3166
|
+
kind: 'filterNaN'
|
|
3167
|
+
});
|
|
3168
|
+
break;
|
|
3169
|
+
case 'filterByValue': {
|
|
3170
|
+
const parts = t.value.split(',').map(p => p.trim());
|
|
3171
|
+
if (parts.length !== 3) {
|
|
3172
|
+
throw new Error(`Invalid filterByValue value: ${t.value}`);
|
|
3173
|
+
}
|
|
3174
|
+
current.processActions.push({
|
|
3175
|
+
kind: 'filterByValue',
|
|
3176
|
+
columnName: parts[0],
|
|
3177
|
+
comparator: parseComparator(parts[1]),
|
|
3178
|
+
value: parseNumber(parts[2])
|
|
3179
|
+
});
|
|
3180
|
+
break;
|
|
3181
|
+
}
|
|
3182
|
+
case 'filterBands': {
|
|
3183
|
+
const shBands = parseNumber(t.value);
|
|
3184
|
+
if (![0, 1, 2, 3].includes(shBands)) {
|
|
3185
|
+
throw new Error(`Invalid filterBands value: ${t.value}. Must be 0, 1, 2, or 3.`);
|
|
3186
|
+
}
|
|
3187
|
+
current.processActions.push({
|
|
3188
|
+
kind: 'filterBands',
|
|
3189
|
+
value: shBands
|
|
3190
|
+
});
|
|
3191
|
+
break;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
return files;
|
|
1108
3197
|
};
|
|
3198
|
+
const usage = `Usage: splat-transform input.ply [actions] input.ply [actions] ... output.ply [actions]
|
|
3199
|
+
actions:
|
|
3200
|
+
-translate -t x,y,z Translate splats by (x, y, z)
|
|
3201
|
+
-rotate -r x,y,z Rotate splats by euler angles (x, y, z) (in degrees)
|
|
3202
|
+
-scale -s x Scale splats by x (uniform scaling)
|
|
3203
|
+
-filterNaN -n Remove gaussians containing any NaN or Inf value
|
|
3204
|
+
-filterByValue -c name,comparator,value Filter gaussians by a value. Specify the value name, comparator (lt, lte, gt, gte, eq, neq) and value
|
|
3205
|
+
-filterBands -h 1 Filter spherical harmonic band data. Value must be 0, 1, 2 or 3.
|
|
3206
|
+
`;
|
|
1109
3207
|
const main = async () => {
|
|
1110
3208
|
console.log(`splat-transform v${version}`);
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
3209
|
+
// read args
|
|
3210
|
+
const files = parseArguments();
|
|
3211
|
+
if (files.length < 2) {
|
|
3212
|
+
console.error(usage);
|
|
3213
|
+
exit(1);
|
|
1114
3214
|
}
|
|
1115
|
-
const
|
|
1116
|
-
const
|
|
3215
|
+
const inputArgs = files.slice(0, -1);
|
|
3216
|
+
const outputArg = files[files.length - 1];
|
|
1117
3217
|
try {
|
|
1118
|
-
//
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
3218
|
+
// read, filter, process input files
|
|
3219
|
+
const inputFiles = await Promise.all(inputArgs.map(async (inputArg) => {
|
|
3220
|
+
const file = await readFile(resolve(inputArg.filename));
|
|
3221
|
+
// filter out non-gs files
|
|
3222
|
+
if (file.elements.length !== 1) {
|
|
3223
|
+
return null;
|
|
3224
|
+
}
|
|
3225
|
+
const element = file.elements[0];
|
|
3226
|
+
if (element.name !== 'vertex') {
|
|
3227
|
+
return null;
|
|
3228
|
+
}
|
|
3229
|
+
const { dataTable } = element;
|
|
3230
|
+
if (dataTable.numRows === 0 || !isGSDataTable(dataTable)) {
|
|
3231
|
+
return null;
|
|
3232
|
+
}
|
|
3233
|
+
file.elements[0].dataTable = process(dataTable, inputArg.processActions);
|
|
3234
|
+
return file;
|
|
3235
|
+
}));
|
|
3236
|
+
// combine inputs into a single output dataTable
|
|
3237
|
+
const dataTable = process(combine(inputFiles.map(file => file.elements[0].dataTable)), outputArg.processActions);
|
|
3238
|
+
// write file
|
|
3239
|
+
await writeFile(resolve(outputArg.filename), dataTable);
|
|
1124
3240
|
}
|
|
1125
3241
|
catch (err) {
|
|
1126
3242
|
// handle errors
|
|
1127
3243
|
console.error(`error: ${err.message}`);
|
|
1128
|
-
|
|
3244
|
+
exit(1);
|
|
1129
3245
|
}
|
|
1130
3246
|
console.log('done');
|
|
1131
3247
|
};
|