three-vat 4.0.0 → 4.1.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 +2 -2
- package/dist/{carrier-D4pkftSk.d.ts → carrier-VLSJLgjX.d.ts} +20 -2
- package/dist/{chunk-BSAJL5PG.js → chunk-OEKKEHRQ.js} +15 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +253 -306
- package/dist/tsl.d.ts +1 -1
- package/dist/tsl.js +23 -6
- package/dist/webgl.d.ts +1 -1
- package/dist/webgl.js +19 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,8 +84,8 @@ Two things worth knowing the first time:
|
|
|
84
84
|
|
|
85
85
|
**A skinned character?** The bake picks the rig encoding for it by itself: the
|
|
86
86
|
posed rig instead of the posed vertices, for two orders of magnitude less
|
|
87
|
-
texture, a bake in milliseconds, and
|
|
88
|
-
|
|
87
|
+
texture, a bake in milliseconds, and a texture whose width is the rig's, not
|
|
88
|
+
the mesh's. Assets a rig cannot express fall back to vertices, and `vat.fallback`
|
|
89
89
|
says why. [The rig encoding](./docs/usage.md#the-rig-encoding-encoding-rig).
|
|
90
90
|
|
|
91
91
|
<details>
|
|
@@ -478,9 +478,17 @@ interface VATBase {
|
|
|
478
478
|
clips: VATClip[];
|
|
479
479
|
/** Union of every baked frame's bounds; use as the geometry bounding box. */
|
|
480
480
|
bounds: Box3;
|
|
481
|
-
/**
|
|
481
|
+
/**
|
|
482
|
+
* Vertices in the merged geometry — the vertex encoding's texture width
|
|
483
|
+
* wherever they fit one row, which is every bake below the ceiling
|
|
484
|
+
* ({@link DeltaVAT.rowsPerFrame}).
|
|
485
|
+
*/
|
|
482
486
|
vertexCount: number;
|
|
483
|
-
/**
|
|
487
|
+
/**
|
|
488
|
+
* Total frames across all clips — the texture's height in rows, times
|
|
489
|
+
* {@link DeltaVAT.rowsPerFrame} under the vertex encoding. The unit every
|
|
490
|
+
* `startFrame` and `frames` in the clip table counts in.
|
|
491
|
+
*/
|
|
484
492
|
totalFrames: number;
|
|
485
493
|
}
|
|
486
494
|
/**
|
|
@@ -515,6 +523,16 @@ interface DeltaVAT extends VATBase {
|
|
|
515
523
|
* a VAT is refused rather than lit by its rest pose.
|
|
516
524
|
*/
|
|
517
525
|
normalTexture: DataTexture | null;
|
|
526
|
+
/**
|
|
527
|
+
* Texture rows one frame takes (ADR-0030). `1` wherever the vertex count
|
|
528
|
+
* fits the bake's `maxTextureSize`, and then both layers are `vertexCount`
|
|
529
|
+
* wide and `totalFrames` tall, as every bake before this field was. Past it,
|
|
530
|
+
* a frame's vertices continue onto the next row: the fewest rows that hold
|
|
531
|
+
* them, `ceil(vertexCount / rowsPerFrame)` texels wide, and vertex `v` of
|
|
532
|
+
* frame `f` at column `v mod width`, row `f × rowsPerFrame + floor(v /
|
|
533
|
+
* width)`. Both decode paths read it; neither adds a line where it is `1`.
|
|
534
|
+
*/
|
|
535
|
+
rowsPerFrame: number;
|
|
518
536
|
/**
|
|
519
537
|
* Why the default encoding fell back to this one (ADR-0029): the message of
|
|
520
538
|
* the rig encoding's refusal, naming what the rig could not store and where
|
|
@@ -3,6 +3,20 @@ import { FloatType, DataTexture, RGBAFormat, RGFormat, UnsignedByteType, HalfFlo
|
|
|
3
3
|
// src/vat-texture.ts
|
|
4
4
|
var MAX_TEXTURE_SIZE = 16384;
|
|
5
5
|
var HALF_FLOAT_MAX = 65504;
|
|
6
|
+
function vertexLayoutFor(vertexCount, totalFrames, maxTextureSize) {
|
|
7
|
+
const rowsPerFrame = Math.max(1, Math.ceil(vertexCount / maxTextureSize));
|
|
8
|
+
const width = vertexWidthOf({ vertexCount, rowsPerFrame });
|
|
9
|
+
const height = totalFrames * rowsPerFrame;
|
|
10
|
+
if (height > maxTextureSize) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`three-vat: totalFrames ${totalFrames} at ${rowsPerFrame} rows a frame is ${height} rows, which exceeds maxTextureSize ${maxTextureSize} \u2014 the vertex encoding spans each frame's ${vertexCount} vertices across ${rowsPerFrame} rows of ${width}; lower fps or bake fewer clips`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return { rowsPerFrame, width, height, frameStride: rowsPerFrame * width };
|
|
16
|
+
}
|
|
17
|
+
function vertexWidthOf({ vertexCount, rowsPerFrame }) {
|
|
18
|
+
return Math.ceil(vertexCount / rowsPerFrame);
|
|
19
|
+
}
|
|
6
20
|
function sampledExactly(tex) {
|
|
7
21
|
tex.minFilter = NearestFilter;
|
|
8
22
|
tex.magFilter = NearestFilter;
|
|
@@ -246,4 +260,4 @@ var RIG_TEXELS = {
|
|
|
246
260
|
};
|
|
247
261
|
var RIG_TEXELS_PER_SLOT = 2;
|
|
248
262
|
|
|
249
|
-
export { EndMode, FORWARD_ONLY_REASON, HALF_FLOAT_MAX, INFINITE_REPETITIONS, LIBRARY_PLAYBACK_DEFAULTS, LoopMode, MAX_TEXTURE_SIZE, PACK_TEXELS, RIG_TEXELS, RIG_TEXELS_PER_SLOT, createVATPlaybackTexture, endsAt, makeVATNormalTexture, makeVATTexture, resolveVATFrame, setVATInstance };
|
|
263
|
+
export { EndMode, FORWARD_ONLY_REASON, HALF_FLOAT_MAX, INFINITE_REPETITIONS, LIBRARY_PLAYBACK_DEFAULTS, LoopMode, MAX_TEXTURE_SIZE, PACK_TEXELS, RIG_TEXELS, RIG_TEXELS_PER_SLOT, createVATPlaybackTexture, endsAt, makeVATNormalTexture, makeVATTexture, resolveVATFrame, setVATInstance, vertexLayoutFor, vertexWidthOf };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AnimationClip, AnimationAction, Object3D, DataTexture, FloatType, HalfFloatType } from 'three';
|
|
2
|
-
import { D as DeltaVAT, R as RigVAT, V as VAT } from './carrier-
|
|
3
|
-
export { E as EndMode, I as INFINITE_REPETITIONS, L as LoopMode, a as VATBase, b as VATCarrier, c as VATClip, d as VATClipDefaults, e as VATClock, f as VATCrowd, g as VATFrame, h as VATInstance, i as VATOutgoingFrame, j as VATPlaybackState, k as VATPlaybackTexture, l as VATPlaybackTextureOptions, m as createVATPlaybackTexture, n as endsAt, r as resolveVATFrame, s as setVATInstance } from './carrier-
|
|
2
|
+
import { D as DeltaVAT, R as RigVAT, V as VAT } from './carrier-VLSJLgjX.js';
|
|
3
|
+
export { E as EndMode, I as INFINITE_REPETITIONS, L as LoopMode, a as VATBase, b as VATCarrier, c as VATClip, d as VATClipDefaults, e as VATClock, f as VATCrowd, g as VATFrame, h as VATInstance, i as VATOutgoingFrame, j as VATPlaybackState, k as VATPlaybackTexture, l as VATPlaybackTextureOptions, m as createVATPlaybackTexture, n as endsAt, r as resolveVATFrame, s as setVATInstance } from './carrier-VLSJLgjX.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* What {@link bakeVAT} takes for each animation: the clip itself, or an
|
|
@@ -15,10 +15,14 @@ interface BakeOptions {
|
|
|
15
15
|
/** Sample rate in frames per second. Default `30`. */
|
|
16
16
|
fps?: number;
|
|
17
17
|
/**
|
|
18
|
-
* Largest texture dimension the target GPU accepts.
|
|
19
|
-
* against it:
|
|
18
|
+
* Largest texture dimension the target GPU accepts. Under the rig encoding
|
|
19
|
+
* both axes are checked against it: the rig's width and `totalFrames`. Under
|
|
20
|
+
* the vertex encoding a frame whose vertices outnumber it spans several rows
|
|
21
|
+
* instead of refusing (ADR-0030), so the height alone is checked: `totalFrames
|
|
22
|
+
* × rowsPerFrame`. Defaults to
|
|
20
23
|
* {@link MAX_TEXTURE_SIZE}; pass the renderer's real limit to avoid baking a
|
|
21
|
-
* VAT that allocates on your desktop and fails on a phone
|
|
24
|
+
* VAT that allocates on your desktop and fails on a phone, and so that a
|
|
25
|
+
* frame spans rows only where this GPU needs it to.
|
|
22
26
|
*/
|
|
23
27
|
maxTextureSize?: number;
|
|
24
28
|
/**
|
|
@@ -220,7 +224,8 @@ interface Vec3Out {
|
|
|
220
224
|
}
|
|
221
225
|
/**
|
|
222
226
|
* Write a normal into two bytes of a VAT normal texture's buffer, at `offset`
|
|
223
|
-
* and `offset + 1` — `(row * vertexCount + vertex) * 2`
|
|
227
|
+
* and `offset + 1` — `(row * vertexCount + vertex) * 2` in a one-row bake,
|
|
228
|
+
* `deltaTexel` in `src/test-utils.ts` in any, for the texel of one
|
|
224
229
|
* vertex at one frame.
|
|
225
230
|
*
|
|
226
231
|
* Takes a direction, not a unit vector: the first step is a division by the L1
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { LoopMode, MAX_TEXTURE_SIZE,
|
|
2
|
-
export { EndMode, INFINITE_REPETITIONS, LoopMode, MAX_TEXTURE_SIZE, createVATPlaybackTexture, endsAt, makeVATNormalTexture, makeVATTexture, resolveVATFrame, setVATInstance } from './chunk-
|
|
3
|
-
import { LoopRepeat, LoopOnce, LoopPingPong, DataUtils, Quaternion, Matrix4,
|
|
1
|
+
import { LoopMode, MAX_TEXTURE_SIZE, vertexLayoutFor, makeVATNormalTexture, makeVATTexture, RIG_TEXELS_PER_SLOT, LIBRARY_PLAYBACK_DEFAULTS, FORWARD_ONLY_REASON, INFINITE_REPETITIONS, HALF_FLOAT_MAX, RIG_TEXELS } from './chunk-OEKKEHRQ.js';
|
|
2
|
+
export { EndMode, INFINITE_REPETITIONS, LoopMode, MAX_TEXTURE_SIZE, createVATPlaybackTexture, endsAt, makeVATNormalTexture, makeVATTexture, resolveVATFrame, setVATInstance } from './chunk-OEKKEHRQ.js';
|
|
3
|
+
import { LoopRepeat, LoopOnce, LoopPingPong, DataUtils, Quaternion, Matrix4, Box3, Vector4, Vector3, Sphere, HalfFloatType, BufferGeometry, BufferAttribute, AdditiveAnimationBlendMode, AnimationMixer, PropertyBinding, FloatType, Material, InterleavedBuffer, SkinnedMesh, Mesh, Bone, Object3D, Skeleton, AnimationClip, Color, InterleavedBufferAttribute, VectorKeyframeTrack, StringKeyframeTrack, QuaternionKeyframeTrack, NumberKeyframeTrack, ColorKeyframeTrack, BooleanKeyframeTrack, Float16BufferAttribute, Interpolant } from 'three';
|
|
4
4
|
|
|
5
5
|
var IGNORED = ["uuid", "name", "color", "userData", "metadata"];
|
|
6
6
|
function flatFacts(material) {
|
|
@@ -125,6 +125,32 @@ function morphCountOf({ mesh, morphPos, morphNrm }) {
|
|
|
125
125
|
const influences = mesh.morphTargetInfluences;
|
|
126
126
|
return influences ? Math.min(influences.length, Math.max(morphPos?.length ?? 0, morphNrm?.length ?? 0)) : 0;
|
|
127
127
|
}
|
|
128
|
+
var _mt = /* @__PURE__ */ new Vector3();
|
|
129
|
+
var _mb = /* @__PURE__ */ new Vector3();
|
|
130
|
+
var _mbn = /* @__PURE__ */ new Vector3();
|
|
131
|
+
function morphVertex(part, v, weights, count, p, n) {
|
|
132
|
+
const { basePos, baseNrm, morphPos, morphNrm, morphRelative } = part;
|
|
133
|
+
if (!morphRelative) {
|
|
134
|
+
_mb.fromBufferAttribute(basePos, v);
|
|
135
|
+
if (n) _mbn.fromBufferAttribute(baseNrm, v);
|
|
136
|
+
}
|
|
137
|
+
for (let t = 0; t < count; t++) {
|
|
138
|
+
const w = weights[t];
|
|
139
|
+
if (w === 0) continue;
|
|
140
|
+
const targetPos = morphPos?.[t];
|
|
141
|
+
if (targetPos) {
|
|
142
|
+
_mt.fromBufferAttribute(targetPos, v);
|
|
143
|
+
if (!morphRelative) _mt.sub(_mb);
|
|
144
|
+
p.addScaledVector(_mt, w);
|
|
145
|
+
}
|
|
146
|
+
const targetNrm = n ? morphNrm?.[t] : void 0;
|
|
147
|
+
if (n && targetNrm) {
|
|
148
|
+
_mt.fromBufferAttribute(targetNrm, v);
|
|
149
|
+
if (!morphRelative) _mt.sub(_mbn);
|
|
150
|
+
n.addScaledVector(_mt, w);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
128
154
|
function asAttribute(value, mesh, name) {
|
|
129
155
|
if (!(value instanceof BufferAttribute)) {
|
|
130
156
|
throw new Error(
|
|
@@ -201,39 +227,48 @@ function collectParts(root, hooks) {
|
|
|
201
227
|
function mergeGeometry(parts, restMatrices, total) {
|
|
202
228
|
const position = new Float32Array(total * 3);
|
|
203
229
|
const normal = new Float32Array(total * 3);
|
|
204
|
-
const
|
|
230
|
+
const _v = new Vector3();
|
|
231
|
+
const _n = new Vector3();
|
|
232
|
+
parts.forEach((part, pi) => {
|
|
233
|
+
const m = restMatrices[pi];
|
|
234
|
+
for (let v = 0; v < part.vertexCount; v++) {
|
|
235
|
+
_v.fromBufferAttribute(part.basePos, v).applyMatrix4(m);
|
|
236
|
+
_n.fromBufferAttribute(part.baseNrm, v).transformDirection(m);
|
|
237
|
+
_v.toArray(position, (part.vertexStart + v) * 3);
|
|
238
|
+
_n.toArray(normal, (part.vertexStart + v) * 3);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const merged = new BufferGeometry();
|
|
242
|
+
merged.setAttribute("position", new BufferAttribute(position, 3));
|
|
243
|
+
merged.setAttribute("normal", new BufferAttribute(normal, 3));
|
|
244
|
+
mergeSurface(merged, parts, total, restMatrices);
|
|
245
|
+
return merged;
|
|
246
|
+
}
|
|
247
|
+
function mergeSurface(merged, parts, total, restMatrices) {
|
|
248
|
+
const merging = parts.some((p) => p.tint);
|
|
249
|
+
const want = optionalAttributes(parts, merging);
|
|
205
250
|
const uv = want.uv ? new Float32Array(total * 2) : null;
|
|
206
251
|
const color = want.color ? new Float32Array(total * 3) : null;
|
|
207
|
-
const merging = parts.some((p) => p.tint);
|
|
208
252
|
const tangent = want.tangent ? new Float32Array(total * 4) : null;
|
|
209
|
-
const _v = new Vector3();
|
|
210
|
-
const _n = new Vector3();
|
|
211
253
|
const _t = new Vector3();
|
|
212
254
|
parts.forEach((part, pi) => {
|
|
213
|
-
const m = restMatrices[pi];
|
|
214
255
|
const geometry = part.mesh.geometry;
|
|
215
256
|
const start = part.vertexStart;
|
|
216
257
|
const srcUV = uv ? asAttribute(geometry.attributes.uv, part.mesh, "uv") : null;
|
|
217
|
-
const srcColor = colorSource(part,
|
|
258
|
+
const srcColor = color ? colorSource(part, merging) : null;
|
|
218
259
|
const srcTangent = tangent ? geometry.attributes.tangent : null;
|
|
260
|
+
const m = restMatrices?.[pi];
|
|
219
261
|
for (let v = 0; v < part.vertexCount; v++) {
|
|
220
|
-
|
|
221
|
-
_n.fromBufferAttribute(part.baseNrm, v).transformDirection(m);
|
|
222
|
-
const o3 = (start + v) * 3;
|
|
223
|
-
position[o3] = _v.x;
|
|
224
|
-
position[o3 + 1] = _v.y;
|
|
225
|
-
position[o3 + 2] = _v.z;
|
|
226
|
-
normal[o3] = _n.x;
|
|
227
|
-
normal[o3 + 1] = _n.y;
|
|
228
|
-
normal[o3 + 2] = _n.z;
|
|
262
|
+
const vi = start + v;
|
|
229
263
|
if (uv && srcUV) {
|
|
230
|
-
uv[
|
|
231
|
-
uv[
|
|
264
|
+
uv[vi * 2] = srcUV.getX(v);
|
|
265
|
+
uv[vi * 2 + 1] = srcUV.getY(v);
|
|
232
266
|
}
|
|
233
|
-
if (color) writeColor(color,
|
|
267
|
+
if (color) writeColor(color, vi * 3, part, srcColor, v);
|
|
234
268
|
if (tangent && srcTangent) {
|
|
235
|
-
_t.fromBufferAttribute(srcTangent, v)
|
|
236
|
-
|
|
269
|
+
_t.fromBufferAttribute(srcTangent, v);
|
|
270
|
+
if (m) _t.transformDirection(m);
|
|
271
|
+
const o4 = vi * 4;
|
|
237
272
|
tangent[o4] = _t.x;
|
|
238
273
|
tangent[o4 + 1] = _t.y;
|
|
239
274
|
tangent[o4 + 2] = _t.z;
|
|
@@ -241,18 +276,14 @@ function mergeGeometry(parts, restMatrices, total) {
|
|
|
241
276
|
}
|
|
242
277
|
}
|
|
243
278
|
});
|
|
244
|
-
const merged = new BufferGeometry();
|
|
245
|
-
merged.setAttribute("position", new BufferAttribute(position, 3));
|
|
246
|
-
merged.setAttribute("normal", new BufferAttribute(normal, 3));
|
|
247
279
|
if (uv) merged.setAttribute("uv", new BufferAttribute(uv, 2));
|
|
248
280
|
if (color) merged.setAttribute("color", new BufferAttribute(color, 3));
|
|
249
281
|
if (tangent) merged.setAttribute("tangent", new BufferAttribute(tangent, 4));
|
|
250
282
|
indexParts(merged, parts);
|
|
251
|
-
return merged;
|
|
252
283
|
}
|
|
253
|
-
function colorSource(part,
|
|
284
|
+
function colorSource(part, merging) {
|
|
254
285
|
const source = part.mesh.geometry.attributes.color;
|
|
255
|
-
if (
|
|
286
|
+
if (part.tint || !source) return null;
|
|
256
287
|
if (merging && source instanceof InterleavedBufferAttribute) return source;
|
|
257
288
|
return asAttribute(source, part.mesh, "color");
|
|
258
289
|
}
|
|
@@ -269,13 +300,13 @@ function writeColor(out, o3, part, source, v) {
|
|
|
269
300
|
out[o3] = out[o3 + 1] = out[o3 + 2] = 1;
|
|
270
301
|
}
|
|
271
302
|
}
|
|
272
|
-
function optionalAttributes(parts) {
|
|
303
|
+
function optionalAttributes(parts, merging) {
|
|
273
304
|
return {
|
|
274
305
|
uv: parts.every((p) => !!p.mesh.geometry.attributes.uv),
|
|
275
306
|
// A merge writes a colour for every part it touched, so the attribute is
|
|
276
307
|
// there whenever one happened; a part it did not touch keeps its own, or
|
|
277
308
|
// white, which leaves an untinted material's shading as it was.
|
|
278
|
-
color:
|
|
309
|
+
color: merging || parts.every((p) => !!p.mesh.geometry.attributes.color),
|
|
279
310
|
tangent: parts.every((p) => {
|
|
280
311
|
const t = p.mesh.geometry.attributes.tangent;
|
|
281
312
|
return t instanceof BufferAttribute && t.itemSize === 4;
|
|
@@ -356,44 +387,13 @@ function bakeVATWith(root, animations, options, hooks) {
|
|
|
356
387
|
function unprefixed(error) {
|
|
357
388
|
return error.message.replace(/^three-vat: /, "");
|
|
358
389
|
}
|
|
359
|
-
function
|
|
360
|
-
const clips = resolved.map((a) => a.clip);
|
|
361
|
-
const vertexCount = parts.reduce((n, p) => n + p.vertexCount, 0);
|
|
362
|
-
if (vertexCount > maxTextureSize) {
|
|
363
|
-
throw new Error(
|
|
364
|
-
`three-vat: vertexCount ${vertexCount} exceeds maxTextureSize ${maxTextureSize}; row wrapping is not implemented`
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
const { frameCounts, totalFrames } = frameCountsFor(clips, fps, maxTextureSize);
|
|
368
|
-
const rootInverse = root.matrixWorld.clone().invert();
|
|
369
|
-
const restMatrices = parts.map(
|
|
370
|
-
(p) => new Matrix4().multiplyMatrices(rootInverse, p.mesh.matrixWorld)
|
|
371
|
-
);
|
|
372
|
-
const geometry = mergeGeometry(parts, restMatrices, vertexCount);
|
|
373
|
-
const mergedBase = geometry.attributes.position;
|
|
374
|
-
const posData = new Uint16Array(vertexCount * totalFrames * 4);
|
|
375
|
-
const nrmData = bakeNormals ? new Uint8Array(vertexCount * totalFrames * 2) : null;
|
|
376
|
-
const bakeNormal = nrmData !== null;
|
|
390
|
+
function sampleClips(root, resolved, frameCounts, poses, writeFrame) {
|
|
377
391
|
const mixer = new AnimationMixer(root);
|
|
378
|
-
const bounds = new Box3();
|
|
379
392
|
const clipTable = [];
|
|
380
|
-
const _si = new Vector4();
|
|
381
|
-
const _sw = new Vector4();
|
|
382
|
-
const _acc = new Matrix4();
|
|
383
|
-
const _skin = new Matrix4();
|
|
384
|
-
const _partMatrix = new Matrix4();
|
|
385
|
-
const _p = new Vector3();
|
|
386
|
-
const _bp = new Vector3();
|
|
387
|
-
const _n = new Vector3();
|
|
388
|
-
const _mt = new Vector3();
|
|
389
|
-
const _mb = new Vector3();
|
|
390
|
-
const _mbn = new Vector3();
|
|
391
|
-
const poses = attachPoseBuffers(parts);
|
|
392
|
-
const influencers = bakeNormals ? parts.filter((p) => p.pose).map(influencedBones) : [];
|
|
393
|
-
let warnedNonUniformScale = false;
|
|
394
393
|
let rowOffset = 0;
|
|
395
394
|
try {
|
|
396
|
-
|
|
395
|
+
for (let ci = 0; ci < resolved.length; ci++) {
|
|
396
|
+
const { clip, defaults } = resolved[ci];
|
|
397
397
|
const frames = frameCounts[ci];
|
|
398
398
|
const action = mixer.clipAction(clip);
|
|
399
399
|
action.play();
|
|
@@ -401,81 +401,8 @@ function bakeVertices(root, parts, resolved, { fps, maxTextureSize, bakeNormals
|
|
|
401
401
|
for (let f = 0; f < frames; f++) {
|
|
402
402
|
mixer.setTime(f / frames * clip.duration);
|
|
403
403
|
root.updateMatrixWorld(true);
|
|
404
|
-
const row = rowOffset + f;
|
|
405
404
|
poseSkeletons(poses);
|
|
406
|
-
|
|
407
|
-
warnedNonUniformScale = warnOnNonUniformBoneScale(influencers);
|
|
408
|
-
}
|
|
409
|
-
for (const part of parts) {
|
|
410
|
-
_partMatrix.multiplyMatrices(rootInverse, part.mesh.matrixWorld);
|
|
411
|
-
const influences = part.mesh.morphTargetInfluences;
|
|
412
|
-
const { morphPos, morphNrm, morphRelative, isSkinned, skinIndex, skinWeight } = part;
|
|
413
|
-
const boneMatrices = part.pose?.matrices;
|
|
414
|
-
const morphCount = morphCountOf(part);
|
|
415
|
-
for (let v = 0; v < part.vertexCount; v++) {
|
|
416
|
-
const vi = part.vertexStart + v;
|
|
417
|
-
_p.fromBufferAttribute(part.basePos, v);
|
|
418
|
-
if (bakeNormal) _n.fromBufferAttribute(part.baseNrm, v);
|
|
419
|
-
if (influences && morphCount > 0) {
|
|
420
|
-
if (!morphRelative) {
|
|
421
|
-
_mb.fromBufferAttribute(part.basePos, v);
|
|
422
|
-
if (bakeNormal) _mbn.fromBufferAttribute(part.baseNrm, v);
|
|
423
|
-
}
|
|
424
|
-
for (let t = 0; t < morphCount; t++) {
|
|
425
|
-
const w = influences[t];
|
|
426
|
-
if (w === 0) continue;
|
|
427
|
-
const targetPos = morphPos?.[t];
|
|
428
|
-
if (targetPos) {
|
|
429
|
-
_mt.fromBufferAttribute(targetPos, v);
|
|
430
|
-
if (!morphRelative) _mt.sub(_mb);
|
|
431
|
-
_p.addScaledVector(_mt, w);
|
|
432
|
-
}
|
|
433
|
-
const targetNrm = bakeNormal ? morphNrm?.[t] : void 0;
|
|
434
|
-
if (targetNrm) {
|
|
435
|
-
_mt.fromBufferAttribute(targetNrm, v);
|
|
436
|
-
if (!morphRelative) _mt.sub(_mbn);
|
|
437
|
-
_n.addScaledVector(_mt, w);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
if (isSkinned && boneMatrices) {
|
|
442
|
-
const skinned = part.mesh;
|
|
443
|
-
_si.fromBufferAttribute(skinIndex, v);
|
|
444
|
-
_sw.fromBufferAttribute(skinWeight, v);
|
|
445
|
-
const ae = _acc.elements;
|
|
446
|
-
ae.fill(0);
|
|
447
|
-
for (let i = 0; i < 4; i++) {
|
|
448
|
-
const w = _sw.getComponent(i);
|
|
449
|
-
if (w === 0) continue;
|
|
450
|
-
const b = _si.getComponent(i) * BONE_STRIDE;
|
|
451
|
-
for (let e = 0; e < 16; e++) ae[e] += boneMatrices[b + e] * w;
|
|
452
|
-
}
|
|
453
|
-
_skin.multiplyMatrices(_acc, skinned.bindMatrix).premultiply(skinned.bindMatrixInverse);
|
|
454
|
-
_p.applyMatrix4(_skin);
|
|
455
|
-
if (bakeNormal) _n.transformDirection(_skin);
|
|
456
|
-
}
|
|
457
|
-
_p.applyMatrix4(_partMatrix);
|
|
458
|
-
if (bakeNormal) _n.transformDirection(_partMatrix);
|
|
459
|
-
bounds.expandByPoint(_p);
|
|
460
|
-
_bp.fromBufferAttribute(mergedBase, vi);
|
|
461
|
-
const o = (row * vertexCount + vi) * 4;
|
|
462
|
-
const dx = _p.x - _bp.x;
|
|
463
|
-
const dy = _p.y - _bp.y;
|
|
464
|
-
const dz = _p.z - _bp.z;
|
|
465
|
-
maxDeltaSq = Math.max(maxDeltaSq, dx * dx + dy * dy + dz * dz);
|
|
466
|
-
const largest = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz));
|
|
467
|
-
if (largest > HALF_FLOAT_MAX) {
|
|
468
|
-
throw new Error(
|
|
469
|
-
`three-vat: position delta component ${largest} (clip "${clip.name}", frame ${f}, vertex ${vi}) exceeds the half-float limit of ${HALF_FLOAT_MAX} the position texture stores; a unit smaller than the metre is the usual cause`
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
posData[o] = toHalfFloat(dx);
|
|
473
|
-
posData[o + 1] = toHalfFloat(dy);
|
|
474
|
-
posData[o + 2] = toHalfFloat(dz);
|
|
475
|
-
posData[o + 3] = HALF_ONE;
|
|
476
|
-
if (bakeNormal) encodeOctahedral(_n.x, _n.y, _n.z, nrmData, (row * vertexCount + vi) * 2);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
405
|
+
maxDeltaSq = Math.max(maxDeltaSq, writeFrame(clip, f, rowOffset + f));
|
|
479
406
|
}
|
|
480
407
|
action.stop();
|
|
481
408
|
clipTable.push({
|
|
@@ -487,26 +414,113 @@ function bakeVertices(root, parts, resolved, { fps, maxTextureSize, bakeNormals
|
|
|
487
414
|
maxDelta: Math.sqrt(maxDeltaSq),
|
|
488
415
|
// Declared once, here, rather than repeated at every instance that plays
|
|
489
416
|
// this band. An instance overrides any of them, field by field.
|
|
490
|
-
...
|
|
417
|
+
...defaults
|
|
491
418
|
});
|
|
492
419
|
rowOffset += frames;
|
|
493
|
-
}
|
|
420
|
+
}
|
|
494
421
|
} finally {
|
|
495
422
|
mixer.stopAllAction();
|
|
496
423
|
mixer.setTime(0);
|
|
497
424
|
root.updateMatrixWorld(true);
|
|
498
425
|
}
|
|
426
|
+
return clipTable;
|
|
427
|
+
}
|
|
428
|
+
function bakeVertices(root, parts, resolved, { fps, maxTextureSize, bakeNormals }) {
|
|
429
|
+
const clips = resolved.map((a) => a.clip);
|
|
430
|
+
const vertexCount = parts.reduce((n, p) => n + p.vertexCount, 0);
|
|
431
|
+
const { frameCounts, totalFrames } = frameCountsFor(clips, fps, maxTextureSize);
|
|
432
|
+
const { rowsPerFrame, width, height, frameStride } = vertexLayoutFor(vertexCount, totalFrames, maxTextureSize);
|
|
433
|
+
const rootInverse = root.matrixWorld.clone().invert();
|
|
434
|
+
const restMatrices = parts.map(
|
|
435
|
+
(p) => new Matrix4().multiplyMatrices(rootInverse, p.mesh.matrixWorld)
|
|
436
|
+
);
|
|
437
|
+
const geometry = mergeGeometry(parts, restMatrices, vertexCount);
|
|
438
|
+
const mergedBase = geometry.attributes.position;
|
|
439
|
+
const posData = new Uint16Array(frameStride * totalFrames * 4);
|
|
440
|
+
const nrmData = bakeNormals ? new Uint8Array(frameStride * totalFrames * 2) : null;
|
|
441
|
+
const bakeNormal = nrmData !== null;
|
|
442
|
+
const bounds = new Box3();
|
|
443
|
+
const _si = new Vector4();
|
|
444
|
+
const _sw = new Vector4();
|
|
445
|
+
const _acc = new Matrix4();
|
|
446
|
+
const _skin = new Matrix4();
|
|
447
|
+
const _partMatrix = new Matrix4();
|
|
448
|
+
const _p = new Vector3();
|
|
449
|
+
const _bp = new Vector3();
|
|
450
|
+
const _n = new Vector3();
|
|
451
|
+
const poses = attachPoseBuffers(parts);
|
|
452
|
+
const influencers = bakeNormals ? parts.filter((p) => p.pose).map(influencedBones) : [];
|
|
453
|
+
let warnedNonUniformScale = false;
|
|
454
|
+
const writeFrame = (clip, f, row) => {
|
|
455
|
+
let maxDeltaSq = 0;
|
|
456
|
+
if (!warnedNonUniformScale) {
|
|
457
|
+
warnedNonUniformScale = warnOnNonUniformBoneScale(influencers);
|
|
458
|
+
}
|
|
459
|
+
for (const part of parts) {
|
|
460
|
+
_partMatrix.multiplyMatrices(rootInverse, part.mesh.matrixWorld);
|
|
461
|
+
const influences = part.mesh.morphTargetInfluences;
|
|
462
|
+
const { isSkinned, skinIndex, skinWeight } = part;
|
|
463
|
+
const boneMatrices = part.pose?.matrices;
|
|
464
|
+
const morphCount = morphCountOf(part);
|
|
465
|
+
for (let v = 0; v < part.vertexCount; v++) {
|
|
466
|
+
const vi = part.vertexStart + v;
|
|
467
|
+
_p.fromBufferAttribute(part.basePos, v);
|
|
468
|
+
if (bakeNormal) _n.fromBufferAttribute(part.baseNrm, v);
|
|
469
|
+
if (influences && morphCount > 0) morphVertex(part, v, influences, morphCount, _p, bakeNormal ? _n : null);
|
|
470
|
+
if (isSkinned && boneMatrices) {
|
|
471
|
+
const skinned = part.mesh;
|
|
472
|
+
_si.fromBufferAttribute(skinIndex, v);
|
|
473
|
+
_sw.fromBufferAttribute(skinWeight, v);
|
|
474
|
+
const ae = _acc.elements;
|
|
475
|
+
for (let e = 0; e < 16; e++) ae[e] = 0;
|
|
476
|
+
for (let i = 0; i < 4; i++) {
|
|
477
|
+
const w = _sw.getComponent(i);
|
|
478
|
+
if (w === 0) continue;
|
|
479
|
+
const b = _si.getComponent(i) * BONE_STRIDE;
|
|
480
|
+
for (let e = 0; e < 16; e++) ae[e] += boneMatrices[b + e] * w;
|
|
481
|
+
}
|
|
482
|
+
_skin.multiplyMatrices(_acc, skinned.bindMatrix).premultiply(skinned.bindMatrixInverse);
|
|
483
|
+
_p.applyMatrix4(_skin);
|
|
484
|
+
if (bakeNormal) _n.transformDirection(_skin);
|
|
485
|
+
}
|
|
486
|
+
_p.applyMatrix4(_partMatrix);
|
|
487
|
+
if (bakeNormal) _n.transformDirection(_partMatrix);
|
|
488
|
+
bounds.expandByPoint(_p);
|
|
489
|
+
_bp.fromBufferAttribute(mergedBase, vi);
|
|
490
|
+
const texel = row * frameStride + vi;
|
|
491
|
+
const o = texel * 4;
|
|
492
|
+
const dx = _p.x - _bp.x;
|
|
493
|
+
const dy = _p.y - _bp.y;
|
|
494
|
+
const dz = _p.z - _bp.z;
|
|
495
|
+
maxDeltaSq = Math.max(maxDeltaSq, dx * dx + dy * dy + dz * dz);
|
|
496
|
+
const largest = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz));
|
|
497
|
+
if (largest > HALF_FLOAT_MAX) {
|
|
498
|
+
throw new Error(
|
|
499
|
+
`three-vat: position delta component ${largest} (clip "${clip.name}", frame ${f}, vertex ${vi}) exceeds the half-float limit of ${HALF_FLOAT_MAX} the position texture stores; a unit smaller than the metre is the usual cause`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
posData[o] = toHalfFloat(dx);
|
|
503
|
+
posData[o + 1] = toHalfFloat(dy);
|
|
504
|
+
posData[o + 2] = toHalfFloat(dz);
|
|
505
|
+
posData[o + 3] = HALF_ONE;
|
|
506
|
+
if (bakeNormal) encodeOctahedral(_n.x, _n.y, _n.z, nrmData, texel * 2);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return maxDeltaSq;
|
|
510
|
+
};
|
|
511
|
+
const clipTable = sampleClips(root, resolved, frameCounts, poses, writeFrame);
|
|
499
512
|
geometry.boundingBox = bounds.clone();
|
|
500
513
|
geometry.boundingSphere = bounds.getBoundingSphere(new Sphere());
|
|
501
514
|
const materials = [];
|
|
502
515
|
for (const part of parts) materials[part.materialIndex] = part.material;
|
|
503
516
|
return {
|
|
504
|
-
positionTexture: makeVATTexture(posData,
|
|
505
|
-
normalTexture: nrmData ? makeVATNormalTexture(nrmData,
|
|
517
|
+
positionTexture: makeVATTexture(posData, width, height, HalfFloatType),
|
|
518
|
+
normalTexture: nrmData ? makeVATNormalTexture(nrmData, width, height) : null,
|
|
506
519
|
clips: clipTable,
|
|
507
520
|
bounds,
|
|
508
521
|
vertexCount,
|
|
509
522
|
totalFrames,
|
|
523
|
+
rowsPerFrame,
|
|
510
524
|
encoding: "delta",
|
|
511
525
|
geometry,
|
|
512
526
|
materials
|
|
@@ -713,34 +727,17 @@ ${clips.join("\n")}`;
|
|
|
713
727
|
);
|
|
714
728
|
}
|
|
715
729
|
function foldMorphs(part, statics) {
|
|
716
|
-
const { vertexCount, basePos, baseNrm
|
|
730
|
+
const { vertexCount, basePos, baseNrm } = part;
|
|
717
731
|
const position = new Float32Array(vertexCount * 3);
|
|
718
732
|
const normal = new Float32Array(vertexCount * 3);
|
|
719
733
|
const active = statics.some((w) => w !== 0);
|
|
720
734
|
const _p = new Vector3();
|
|
721
735
|
const _n = new Vector3();
|
|
722
|
-
const _mt = new Vector3();
|
|
723
|
-
const _mb = new Vector3();
|
|
724
736
|
for (let v = 0; v < vertexCount; v++) {
|
|
725
737
|
_p.fromBufferAttribute(basePos, v);
|
|
726
738
|
_n.fromBufferAttribute(baseNrm, v);
|
|
727
739
|
if (active) {
|
|
728
|
-
|
|
729
|
-
const w = statics[t];
|
|
730
|
-
if (w === 0) continue;
|
|
731
|
-
const targetPos = morphPos?.[t];
|
|
732
|
-
if (targetPos) {
|
|
733
|
-
_mt.fromBufferAttribute(targetPos, v);
|
|
734
|
-
if (!morphRelative) _mt.sub(_mb.fromBufferAttribute(basePos, v));
|
|
735
|
-
_p.addScaledVector(_mt, w);
|
|
736
|
-
}
|
|
737
|
-
const targetNrm = morphNrm?.[t];
|
|
738
|
-
if (targetNrm) {
|
|
739
|
-
_mt.fromBufferAttribute(targetNrm, v);
|
|
740
|
-
if (!morphRelative) _mt.sub(_mb.fromBufferAttribute(baseNrm, v));
|
|
741
|
-
_n.addScaledVector(_mt, w);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
740
|
+
morphVertex(part, v, statics, statics.length, _p, _n);
|
|
744
741
|
_n.normalize();
|
|
745
742
|
}
|
|
746
743
|
_p.toArray(position, v * 3);
|
|
@@ -838,128 +835,101 @@ function bakeRig(root, parts, resolved, { fps, maxTextureSize }) {
|
|
|
838
835
|
const data = new Float32Array(width * totalFrames * 4);
|
|
839
836
|
const slotMatrices = new Float64Array(slotCount * BONE_STRIDE);
|
|
840
837
|
const previous = new Float64Array(slotCount * 4);
|
|
841
|
-
const mixer = new AnimationMixer(root);
|
|
842
838
|
const bounds = new Box3();
|
|
843
|
-
const clipTable = [];
|
|
844
839
|
let minX = Infinity;
|
|
845
840
|
let minY = Infinity;
|
|
846
841
|
let minZ = Infinity;
|
|
847
842
|
let maxX = -Infinity;
|
|
848
843
|
let maxY = -Infinity;
|
|
849
844
|
let maxZ = -Infinity;
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
for (let
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
for (let slot = 0; slot < slotCount; slot++) {
|
|
864
|
-
const { rig, parts: readers } = slots[slot];
|
|
865
|
-
const lead = readers[0];
|
|
866
|
-
const placement = placements.get(lead);
|
|
867
|
-
for (let pi = 1; pi < readers.length; pi++) {
|
|
868
|
-
const other = readers[pi];
|
|
869
|
-
if (!matricesClose(placement, placements.get(other))) {
|
|
870
|
-
throw new RigRefusal(
|
|
871
|
-
`three-vat: parts ${partName(lead)} and ${partName(other)} read the same slots but move apart in clip "${clip.name}"; the rig encoding gives them one set of slots, which cannot place them differently, and ${VERTEX_ENCODING_STORES_IT}`
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
if (rig) {
|
|
876
|
-
_slot.fromArray(rig.pose.matrices, rig.bone * BONE_STRIDE).multiply(rig.bindMatrix).premultiply(placement);
|
|
877
|
-
} else {
|
|
878
|
-
_slot.copy(placement);
|
|
879
|
-
}
|
|
880
|
-
slotMatrices.set(_slot.elements, slot * BONE_STRIDE);
|
|
881
|
-
if (influenced[slot] && (hasNonUniformScale(slotMatrices, slot) || hasShear(slotMatrices, slot))) {
|
|
882
|
-
throw nonUniformScale(slotLabel(slots[slot]), clip);
|
|
883
|
-
}
|
|
884
|
-
const p4 = slot * 4;
|
|
885
|
-
const det = _slot.determinant();
|
|
886
|
-
let scale;
|
|
887
|
-
if (det === 0) {
|
|
888
|
-
_t.setFromMatrixPosition(_slot);
|
|
889
|
-
_q2.fromArray(previous, p4);
|
|
890
|
-
if (_q2.lengthSq() === 0) _q2.identity();
|
|
891
|
-
scale = 0;
|
|
892
|
-
} else {
|
|
893
|
-
if (det < 0) for (const e of MATRIX_BASIS) _slot.elements[e] = -_slot.elements[e];
|
|
894
|
-
_slot.decompose(_t, _q2, _s);
|
|
895
|
-
scale = det < 0 ? -_s.x : _s.x;
|
|
896
|
-
}
|
|
897
|
-
if (previous[p4] * _q2.x + previous[p4 + 1] * _q2.y + previous[p4 + 2] * _q2.z + previous[p4 + 3] * _q2.w < 0) {
|
|
898
|
-
_q2.set(-_q2.x, -_q2.y, -_q2.z, -_q2.w);
|
|
899
|
-
}
|
|
900
|
-
previous[p4] = _q2.x;
|
|
901
|
-
previous[p4 + 1] = _q2.y;
|
|
902
|
-
previous[p4 + 2] = _q2.z;
|
|
903
|
-
previous[p4 + 3] = _q2.w;
|
|
904
|
-
const o = (row * width + slot * RIG_TEXELS_PER_SLOT) * 4;
|
|
905
|
-
const rotation = o + RIG_TEXELS.rotation * 4;
|
|
906
|
-
data[rotation] = _q2.x;
|
|
907
|
-
data[rotation + 1] = _q2.y;
|
|
908
|
-
data[rotation + 2] = _q2.z;
|
|
909
|
-
data[rotation + 3] = _q2.w;
|
|
910
|
-
const placementTexel = o + RIG_TEXELS.placement * 4;
|
|
911
|
-
data[placementTexel] = _t.x;
|
|
912
|
-
data[placementTexel + 1] = _t.y;
|
|
913
|
-
data[placementTexel + 2] = _t.z;
|
|
914
|
-
data[placementTexel + 3] = scale;
|
|
915
|
-
}
|
|
916
|
-
for (let v = 0; v < vertexCount; v++) {
|
|
917
|
-
const o3 = v * 3;
|
|
918
|
-
const o4 = v * 4;
|
|
919
|
-
const x = localPosition[o3];
|
|
920
|
-
const y = localPosition[o3 + 1];
|
|
921
|
-
const z = localPosition[o3 + 2];
|
|
922
|
-
let px = 0;
|
|
923
|
-
let py = 0;
|
|
924
|
-
let pz = 0;
|
|
925
|
-
for (let i = 0; i < 4; i++) {
|
|
926
|
-
const w = skinWeight[o4 + i];
|
|
927
|
-
if (w === 0) continue;
|
|
928
|
-
const m = skinIndex[o4 + i] * BONE_STRIDE;
|
|
929
|
-
px += w * (slotMatrices[m] * x + slotMatrices[m + 4] * y + slotMatrices[m + 8] * z + slotMatrices[m + 12]);
|
|
930
|
-
py += w * (slotMatrices[m + 1] * x + slotMatrices[m + 5] * y + slotMatrices[m + 9] * z + slotMatrices[m + 13]);
|
|
931
|
-
pz += w * (slotMatrices[m + 2] * x + slotMatrices[m + 6] * y + slotMatrices[m + 10] * z + slotMatrices[m + 14]);
|
|
932
|
-
}
|
|
933
|
-
if (px < minX) minX = px;
|
|
934
|
-
if (px > maxX) maxX = px;
|
|
935
|
-
if (py < minY) minY = py;
|
|
936
|
-
if (py > maxY) maxY = py;
|
|
937
|
-
if (pz < minZ) minZ = pz;
|
|
938
|
-
if (pz > maxZ) maxZ = pz;
|
|
939
|
-
const dx = px - restRoot[o3];
|
|
940
|
-
const dy = py - restRoot[o3 + 1];
|
|
941
|
-
const dz = pz - restRoot[o3 + 2];
|
|
942
|
-
const deltaSq = dx * dx + dy * dy + dz * dz;
|
|
943
|
-
if (deltaSq > maxDeltaSq) maxDeltaSq = deltaSq;
|
|
845
|
+
const writeFrame = (clip, _f, row) => {
|
|
846
|
+
let maxDeltaSq = 0;
|
|
847
|
+
for (const [part, placement] of placements) placementOf(part, rootInverse, placement);
|
|
848
|
+
for (let slot = 0; slot < slotCount; slot++) {
|
|
849
|
+
const { rig, parts: readers } = slots[slot];
|
|
850
|
+
const lead = readers[0];
|
|
851
|
+
const placement = placements.get(lead);
|
|
852
|
+
for (let pi = 1; pi < readers.length; pi++) {
|
|
853
|
+
const other = readers[pi];
|
|
854
|
+
if (!matricesClose(placement, placements.get(other))) {
|
|
855
|
+
throw new RigRefusal(
|
|
856
|
+
`three-vat: parts ${partName(lead)} and ${partName(other)} read the same slots but move apart in clip "${clip.name}"; the rig encoding gives them one set of slots, which cannot place them differently, and ${VERTEX_ENCODING_STORES_IT}`
|
|
857
|
+
);
|
|
944
858
|
}
|
|
945
859
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
860
|
+
if (rig) {
|
|
861
|
+
_slot.fromArray(rig.pose.matrices, rig.bone * BONE_STRIDE).multiply(rig.bindMatrix).premultiply(placement);
|
|
862
|
+
} else {
|
|
863
|
+
_slot.copy(placement);
|
|
864
|
+
}
|
|
865
|
+
slotMatrices.set(_slot.elements, slot * BONE_STRIDE);
|
|
866
|
+
if (influenced[slot] && (hasNonUniformScale(slotMatrices, slot) || hasShear(slotMatrices, slot))) {
|
|
867
|
+
throw nonUniformScale(slotLabel(slots[slot]), clip);
|
|
868
|
+
}
|
|
869
|
+
const p4 = slot * 4;
|
|
870
|
+
const det = _slot.determinant();
|
|
871
|
+
let scale;
|
|
872
|
+
if (det === 0) {
|
|
873
|
+
_t.setFromMatrixPosition(_slot);
|
|
874
|
+
_q2.fromArray(previous, p4);
|
|
875
|
+
if (_q2.lengthSq() === 0) _q2.identity();
|
|
876
|
+
scale = 0;
|
|
877
|
+
} else {
|
|
878
|
+
if (det < 0) for (const e of MATRIX_BASIS) _slot.elements[e] = -_slot.elements[e];
|
|
879
|
+
_slot.decompose(_t, _q2, _s);
|
|
880
|
+
scale = det < 0 ? -_s.x : _s.x;
|
|
881
|
+
}
|
|
882
|
+
if (previous[p4] * _q2.x + previous[p4 + 1] * _q2.y + previous[p4 + 2] * _q2.z + previous[p4 + 3] * _q2.w < 0) {
|
|
883
|
+
_q2.set(-_q2.x, -_q2.y, -_q2.z, -_q2.w);
|
|
884
|
+
}
|
|
885
|
+
previous[p4] = _q2.x;
|
|
886
|
+
previous[p4 + 1] = _q2.y;
|
|
887
|
+
previous[p4 + 2] = _q2.z;
|
|
888
|
+
previous[p4 + 3] = _q2.w;
|
|
889
|
+
const o = (row * width + slot * RIG_TEXELS_PER_SLOT) * 4;
|
|
890
|
+
const rotation = o + RIG_TEXELS.rotation * 4;
|
|
891
|
+
data[rotation] = _q2.x;
|
|
892
|
+
data[rotation + 1] = _q2.y;
|
|
893
|
+
data[rotation + 2] = _q2.z;
|
|
894
|
+
data[rotation + 3] = _q2.w;
|
|
895
|
+
const placementTexel = o + RIG_TEXELS.placement * 4;
|
|
896
|
+
data[placementTexel] = _t.x;
|
|
897
|
+
data[placementTexel + 1] = _t.y;
|
|
898
|
+
data[placementTexel + 2] = _t.z;
|
|
899
|
+
data[placementTexel + 3] = scale;
|
|
900
|
+
}
|
|
901
|
+
for (let v = 0; v < vertexCount; v++) {
|
|
902
|
+
const o3 = v * 3;
|
|
903
|
+
const o4 = v * 4;
|
|
904
|
+
const x = localPosition[o3];
|
|
905
|
+
const y = localPosition[o3 + 1];
|
|
906
|
+
const z = localPosition[o3 + 2];
|
|
907
|
+
let px = 0;
|
|
908
|
+
let py = 0;
|
|
909
|
+
let pz = 0;
|
|
910
|
+
for (let i = 0; i < 4; i++) {
|
|
911
|
+
const w = skinWeight[o4 + i];
|
|
912
|
+
if (w === 0) continue;
|
|
913
|
+
const m = skinIndex[o4 + i] * BONE_STRIDE;
|
|
914
|
+
px += w * (slotMatrices[m] * x + slotMatrices[m + 4] * y + slotMatrices[m + 8] * z + slotMatrices[m + 12]);
|
|
915
|
+
py += w * (slotMatrices[m + 1] * x + slotMatrices[m + 5] * y + slotMatrices[m + 9] * z + slotMatrices[m + 13]);
|
|
916
|
+
pz += w * (slotMatrices[m + 2] * x + slotMatrices[m + 6] * y + slotMatrices[m + 10] * z + slotMatrices[m + 14]);
|
|
917
|
+
}
|
|
918
|
+
if (px < minX) minX = px;
|
|
919
|
+
if (px > maxX) maxX = px;
|
|
920
|
+
if (py < minY) minY = py;
|
|
921
|
+
if (py > maxY) maxY = py;
|
|
922
|
+
if (pz < minZ) minZ = pz;
|
|
923
|
+
if (pz > maxZ) maxZ = pz;
|
|
924
|
+
const dx = px - restRoot[o3];
|
|
925
|
+
const dy = py - restRoot[o3 + 1];
|
|
926
|
+
const dz = pz - restRoot[o3 + 2];
|
|
927
|
+
const deltaSq = dx * dx + dy * dy + dz * dz;
|
|
928
|
+
if (deltaSq > maxDeltaSq) maxDeltaSq = deltaSq;
|
|
929
|
+
}
|
|
930
|
+
return maxDeltaSq;
|
|
931
|
+
};
|
|
932
|
+
const clipTable = sampleClips(root, resolved, frameCounts, poses, writeFrame);
|
|
963
933
|
bounds.min.set(minX, minY, minZ);
|
|
964
934
|
bounds.max.set(maxX, maxY, maxZ);
|
|
965
935
|
geometry.boundingBox = bounds.clone();
|
|
@@ -979,30 +949,18 @@ function bakeRig(root, parts, resolved, { fps, maxTextureSize }) {
|
|
|
979
949
|
};
|
|
980
950
|
}
|
|
981
951
|
function mergeRigGeometry(rigParts, total) {
|
|
982
|
-
const parts = rigParts.map((r) => r.part);
|
|
983
952
|
const position = new Float32Array(total * 3);
|
|
984
953
|
const normal = new Float32Array(total * 3);
|
|
985
954
|
const slotIndex = new Uint16Array(total * 4);
|
|
986
955
|
const slotWeight = new Float32Array(total * 4);
|
|
987
|
-
const want = optionalAttributes(parts);
|
|
988
|
-
const uv = want.uv ? new Float32Array(total * 2) : null;
|
|
989
|
-
const color = want.color ? new Float32Array(total * 3) : null;
|
|
990
|
-
const merging = parts.some((p) => p.tint);
|
|
991
|
-
const tangent = want.tangent ? new Float32Array(total * 4) : null;
|
|
992
956
|
for (const { part, slotOf, position: restPos, normal: restNrm } of rigParts) {
|
|
993
|
-
const geometry = part.mesh.geometry;
|
|
994
957
|
const start = part.vertexStart;
|
|
995
958
|
const srcIndex = part.pose ? part.skinIndex : null;
|
|
996
959
|
const srcWeight = part.pose ? part.skinWeight : null;
|
|
997
|
-
const srcUV = uv ? asAttribute(geometry.attributes.uv, part.mesh, "uv") : null;
|
|
998
|
-
const srcColor = colorSource(part, !!color, merging);
|
|
999
|
-
const srcTangent = tangent ? geometry.attributes.tangent : null;
|
|
1000
960
|
position.set(restPos, start * 3);
|
|
1001
961
|
normal.set(restNrm, start * 3);
|
|
1002
962
|
for (let v = 0; v < part.vertexCount; v++) {
|
|
1003
|
-
const
|
|
1004
|
-
const o3 = vi * 3;
|
|
1005
|
-
const o4 = vi * 4;
|
|
963
|
+
const o4 = (start + v) * 4;
|
|
1006
964
|
if (srcIndex && srcWeight) {
|
|
1007
965
|
for (let i = 0; i < 4; i++) {
|
|
1008
966
|
slotIndex[o4 + i] = slotOf[srcIndex.getComponent(v, i)];
|
|
@@ -1012,17 +970,6 @@ function mergeRigGeometry(rigParts, total) {
|
|
|
1012
970
|
slotIndex[o4] = slotOf[0];
|
|
1013
971
|
slotWeight[o4] = 1;
|
|
1014
972
|
}
|
|
1015
|
-
if (uv && srcUV) {
|
|
1016
|
-
uv[vi * 2] = srcUV.getX(v);
|
|
1017
|
-
uv[vi * 2 + 1] = srcUV.getY(v);
|
|
1018
|
-
}
|
|
1019
|
-
if (color) writeColor(color, o3, part, srcColor, v);
|
|
1020
|
-
if (tangent && srcTangent) {
|
|
1021
|
-
tangent[o4] = srcTangent.getX(v);
|
|
1022
|
-
tangent[o4 + 1] = srcTangent.getY(v);
|
|
1023
|
-
tangent[o4 + 2] = srcTangent.getZ(v);
|
|
1024
|
-
tangent[o4 + 3] = srcTangent.getW(v);
|
|
1025
|
-
}
|
|
1026
973
|
}
|
|
1027
974
|
}
|
|
1028
975
|
const merged = new BufferGeometry();
|
|
@@ -1030,10 +977,7 @@ function mergeRigGeometry(rigParts, total) {
|
|
|
1030
977
|
merged.setAttribute("normal", new BufferAttribute(normal, 3));
|
|
1031
978
|
merged.setAttribute("skinIndex", new BufferAttribute(slotIndex, 4));
|
|
1032
979
|
merged.setAttribute("skinWeight", new BufferAttribute(slotWeight, 4));
|
|
1033
|
-
|
|
1034
|
-
if (color) merged.setAttribute("color", new BufferAttribute(color, 3));
|
|
1035
|
-
if (tangent) merged.setAttribute("tangent", new BufferAttribute(tangent, 4));
|
|
1036
|
-
indexParts(merged, parts);
|
|
980
|
+
mergeSurface(merged, rigParts.map((r) => r.part), total, null);
|
|
1037
981
|
return merged;
|
|
1038
982
|
}
|
|
1039
983
|
var REQUEST = "three-vat:bake";
|
|
@@ -1396,9 +1340,11 @@ function recordVAT(vat, stand) {
|
|
|
1396
1340
|
encoding: vat.encoding,
|
|
1397
1341
|
texels: transferable(texture.image.data, true, transfer),
|
|
1398
1342
|
width: texture.image.width,
|
|
1343
|
+
height: texture.image.height,
|
|
1399
1344
|
normals: normals ? transferable(normals, true, transfer) : null,
|
|
1400
1345
|
slotCount: vat.encoding === "rig" ? vat.slotCount : 0,
|
|
1401
1346
|
fallback: vat.encoding === "delta" ? vat.fallback : null,
|
|
1347
|
+
rowsPerFrame: vat.encoding === "delta" ? vat.rowsPerFrame : 1,
|
|
1402
1348
|
geometry: recordGeometry(vat.geometry, true, transfer, /* @__PURE__ */ new Map(), []),
|
|
1403
1349
|
materials: vat.materials.map((m) => stand.get(m)),
|
|
1404
1350
|
clips: vat.clips,
|
|
@@ -1426,17 +1372,18 @@ function rebuildVAT(record, materials) {
|
|
|
1426
1372
|
if (record.encoding === "rig") {
|
|
1427
1373
|
const vat2 = {
|
|
1428
1374
|
encoding: "rig",
|
|
1429
|
-
rigTexture: makeVATTexture(record.texels, record.width, record.
|
|
1375
|
+
rigTexture: makeVATTexture(record.texels, record.width, record.height, FloatType),
|
|
1430
1376
|
slotCount: record.slotCount,
|
|
1431
1377
|
...base
|
|
1432
1378
|
};
|
|
1433
1379
|
return vat2;
|
|
1434
1380
|
}
|
|
1435
1381
|
const vat = {
|
|
1436
|
-
positionTexture: makeVATTexture(record.texels, record.width, record.
|
|
1437
|
-
normalTexture: record.normals ? makeVATNormalTexture(record.normals, record.width, record.
|
|
1382
|
+
positionTexture: makeVATTexture(record.texels, record.width, record.height, HalfFloatType),
|
|
1383
|
+
normalTexture: record.normals ? makeVATNormalTexture(record.normals, record.width, record.height) : null,
|
|
1438
1384
|
encoding: "delta",
|
|
1439
1385
|
fallback: record.fallback,
|
|
1386
|
+
rowsPerFrame: record.rowsPerFrame,
|
|
1440
1387
|
...base
|
|
1441
1388
|
};
|
|
1442
1389
|
return vat;
|
package/dist/tsl.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Node } from 'three/webgpu';
|
|
2
|
-
import { e as VATClock, k as VATPlaybackTexture, b as VATCarrier, V as VAT, h as VATInstance, f as VATCrowd } from './carrier-
|
|
2
|
+
import { e as VATClock, k as VATPlaybackTexture, b as VATCarrier, V as VAT, h as VATInstance, f as VATCrowd } from './carrier-VLSJLgjX.js';
|
|
3
3
|
import 'three';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/tsl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isBatchedCarrier, assertVATCarrier, assertBakedNormal } from './chunk-I4STYOD5.js';
|
|
2
|
-
import { INFINITE_REPETITIONS, LoopMode, EndMode, createVATPlaybackTexture, PACK_TEXELS, RIG_TEXELS, RIG_TEXELS_PER_SLOT } from './chunk-
|
|
2
|
+
import { INFINITE_REPETITIONS, LoopMode, EndMode, createVATPlaybackTexture, PACK_TEXELS, RIG_TEXELS, vertexWidthOf, RIG_TEXELS_PER_SLOT } from './chunk-OEKKEHRQ.js';
|
|
3
3
|
import { InstancedMesh } from 'three';
|
|
4
4
|
import { uniform, Fn, positionLocal, normalLocal, tangentLocal, positionGeometry, batch, instancedMesh, float, bool, int, vertexIndex, attribute, mat3, tangentGeometry, normalGeometry, vec4, batchIndirectIndex, instanceIndex, hash, mix, textureLoad, ivec2, dot, mat4, vec3 } from 'three/tsl';
|
|
5
5
|
|
|
@@ -176,17 +176,34 @@ function octDecode(stored) {
|
|
|
176
176
|
const back = (component) => component.sub(component.greaterThanEqual(0).select(overshoot, overshoot.negate()));
|
|
177
177
|
return vec3(back(ex), back(ey), z).normalize();
|
|
178
178
|
}
|
|
179
|
-
function
|
|
179
|
+
function texelOf(vat, vertexRow) {
|
|
180
|
+
const { rowsPerFrame } = vat;
|
|
181
|
+
if (rowsPerFrame === 1) return { column: vertexRow, rowOf: (row) => row };
|
|
182
|
+
const width = int(vertexWidthOf(vat));
|
|
183
|
+
const line = vertexRow.div(width);
|
|
184
|
+
const spanned = /* @__PURE__ */ new Map();
|
|
185
|
+
return {
|
|
186
|
+
column: vertexRow.mod(width),
|
|
187
|
+
rowOf: (row) => {
|
|
188
|
+
let at = spanned.get(row);
|
|
189
|
+
if (!at) spanned.set(row, at = row.mul(int(rowsPerFrame)).add(line));
|
|
190
|
+
return at;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function vertexDecode(vat, rows) {
|
|
195
|
+
const { positionTexture, normalTexture } = vat;
|
|
180
196
|
const vertexRow = int(vertexIndex);
|
|
197
|
+
const { column, rowOf } = texelOf(vat, vertexRow);
|
|
181
198
|
const band = (tex, of) => {
|
|
182
|
-
const s0 = textureLoad(tex, ivec2(
|
|
183
|
-
const s1 = textureLoad(tex, ivec2(
|
|
199
|
+
const s0 = textureLoad(tex, ivec2(column, rowOf(of.row0))).xyz;
|
|
200
|
+
const s1 = textureLoad(tex, ivec2(column, rowOf(of.row1))).xyz;
|
|
184
201
|
return mix(s0, s1, of.blend);
|
|
185
202
|
};
|
|
186
203
|
const sample = (tex) => mix(band(tex, rows.live), band(tex, rows.outgoing), rows.weight);
|
|
187
204
|
const bandNormal = (tex, of) => {
|
|
188
|
-
const s0 = octDecode(textureLoad(tex, ivec2(
|
|
189
|
-
const s1 = octDecode(textureLoad(tex, ivec2(
|
|
205
|
+
const s0 = octDecode(textureLoad(tex, ivec2(column, rowOf(of.row0))).xy);
|
|
206
|
+
const s1 = octDecode(textureLoad(tex, ivec2(column, rowOf(of.row1))).xy);
|
|
190
207
|
return mix(s0, s1, of.blend);
|
|
191
208
|
};
|
|
192
209
|
const sampleNormal = (tex) => mix(bandNormal(tex, rows.live), bandNormal(tex, rows.outgoing), rows.weight);
|
package/dist/webgl.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IUniform, MeshDepthMaterial, WebGLRenderer, Material } from 'three';
|
|
2
|
-
import { b as VATCarrier, V as VAT, k as VATPlaybackTexture, h as VATInstance, f as VATCrowd } from './carrier-
|
|
2
|
+
import { b as VATCarrier, V as VAT, k as VATPlaybackTexture, h as VATInstance, f as VATCrowd } from './carrier-VLSJLgjX.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The real maximum texture dimension this GPU accepts, for
|
package/dist/webgl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertBakedNormal, assertVATCarrier, isBatchedCarrier } from './chunk-I4STYOD5.js';
|
|
2
|
-
import { PACK_TEXELS, EndMode, LoopMode, RIG_TEXELS_PER_SLOT, RIG_TEXELS, INFINITE_REPETITIONS, createVATPlaybackTexture } from './chunk-
|
|
2
|
+
import { PACK_TEXELS, EndMode, LoopMode, RIG_TEXELS_PER_SLOT, RIG_TEXELS, INFINITE_REPETITIONS, createVATPlaybackTexture, vertexWidthOf } from './chunk-OEKKEHRQ.js';
|
|
3
3
|
import { MeshDepthMaterial, RGBADepthPacking, InstancedMesh, MeshDistanceMaterial, Material } from 'three';
|
|
4
4
|
|
|
5
5
|
function getMaxTextureSize(renderer) {
|
|
@@ -175,7 +175,13 @@ var ROW_PRELUDE = (
|
|
|
175
175
|
}
|
|
176
176
|
`
|
|
177
177
|
);
|
|
178
|
-
|
|
178
|
+
function texelOf(vat) {
|
|
179
|
+
const { rowsPerFrame } = vat;
|
|
180
|
+
if (rowsPerFrame === 1) return (row) => `ivec2( gl_VertexID, ${row} )`;
|
|
181
|
+
const width = vertexWidthOf(vat);
|
|
182
|
+
return (row) => `ivec2( gl_VertexID % ${width}, ${row} * ${rowsPerFrame} + gl_VertexID / ${width} )`;
|
|
183
|
+
}
|
|
184
|
+
var vertexPrelude = (texel) => (
|
|
179
185
|
/* glsl */
|
|
180
186
|
`
|
|
181
187
|
uniform highp sampler2D uVatPosTex;
|
|
@@ -184,8 +190,8 @@ var VERTEX_PRELUDE = (
|
|
|
184
190
|
// mixed. The same function for the live band and the outgoing one, as
|
|
185
191
|
// vatBand is the same function for both pairs.
|
|
186
192
|
vec3 vatBandSample( const in sampler2D tex, const in VatBand band ) {
|
|
187
|
-
vec3 s0 = texelFetch( tex,
|
|
188
|
-
vec3 s1 = texelFetch( tex,
|
|
193
|
+
vec3 s0 = texelFetch( tex, ${texel("band.row0")}, 0 ).xyz;
|
|
194
|
+
vec3 s1 = texelFetch( tex, ${texel("band.row1")}, 0 ).xyz;
|
|
189
195
|
return mix( s0, s1, band.blend );
|
|
190
196
|
}
|
|
191
197
|
|
|
@@ -208,7 +214,7 @@ var VERTEX_PRELUDE = (
|
|
|
208
214
|
}
|
|
209
215
|
`
|
|
210
216
|
);
|
|
211
|
-
var
|
|
217
|
+
var normalPrelude = (texel) => (
|
|
212
218
|
/* glsl */
|
|
213
219
|
`
|
|
214
220
|
uniform highp sampler2D uVatNrmTex;
|
|
@@ -226,8 +232,8 @@ var NORMAL_PRELUDE = (
|
|
|
226
232
|
|
|
227
233
|
// One band of the normal layer \u2014 vatBandSample, over decoded normals.
|
|
228
234
|
vec3 vatBandSampleNormal( const in VatBand band ) {
|
|
229
|
-
vec3 s0 = vatOctDecode( texelFetch( uVatNrmTex,
|
|
230
|
-
vec3 s1 = vatOctDecode( texelFetch( uVatNrmTex,
|
|
235
|
+
vec3 s0 = vatOctDecode( texelFetch( uVatNrmTex, ${texel("band.row0")}, 0 ).xy );
|
|
236
|
+
vec3 s1 = vatOctDecode( texelFetch( uVatNrmTex, ${texel("band.row1")}, 0 ).xy );
|
|
231
237
|
return mix( s0, s1, band.blend );
|
|
232
238
|
}
|
|
233
239
|
|
|
@@ -379,9 +385,12 @@ var rigNormal = (id) => (
|
|
|
379
385
|
#endif
|
|
380
386
|
`
|
|
381
387
|
);
|
|
382
|
-
function vertexDecode(
|
|
388
|
+
function vertexDecode(vat, id) {
|
|
389
|
+
const { positionTexture, normalTexture, rowsPerFrame } = vat;
|
|
390
|
+
const texel = texelOf(vat);
|
|
391
|
+
const span = rowsPerFrame === 1 ? "" : `:rows${rowsPerFrame}x${vertexWidthOf(vat)}`;
|
|
383
392
|
return {
|
|
384
|
-
prelude: (normalTexture ?
|
|
393
|
+
prelude: (normalTexture ? normalPrelude(texel) : "") + vertexPrelude(texel),
|
|
385
394
|
bind(uniforms) {
|
|
386
395
|
uniforms.uVatPosTex = { value: positionTexture };
|
|
387
396
|
if (normalTexture) uniforms.uVatNrmTex = { value: normalTexture };
|
|
@@ -394,7 +403,7 @@ function vertexDecode({ positionTexture, normalTexture }, id) {
|
|
|
394
403
|
normal: normalTexture ? vertexNormal(id) : null,
|
|
395
404
|
// A normal-less VAT injects a different vertex shader off the same material
|
|
396
405
|
// parameters, so the variant is in the key (see `patchVATMaterial`).
|
|
397
|
-
key: `three-vat:${id}${normalTexture ? "" : ":no-normal"}`,
|
|
406
|
+
key: `three-vat:${id}${normalTexture ? "" : ":no-normal"}${span}`,
|
|
398
407
|
instanceIndex: INSTANCE_ID[id]
|
|
399
408
|
};
|
|
400
409
|
}
|