three-vat 4.1.0 → 4.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 +37 -8
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +585 -0
- package/dist/carrier-DmKhB4QL.d.ts +13 -0
- package/dist/{chunk-OEKKEHRQ.js → chunk-4IKOXUSX.js} +85 -26
- package/dist/chunk-7TT7B5WS.js +263 -0
- package/dist/chunk-P6AFXEBT.js +1104 -0
- package/dist/index.d.ts +37 -5
- package/dist/index.js +142 -965
- package/dist/tsl.d.ts +2 -1
- package/dist/tsl.js +18 -15
- package/dist/{carrier-VLSJLgjX.d.ts → types-CDPXKe0_.d.ts} +99 -34
- package/dist/webgl.d.ts +2 -1
- package/dist/webgl.js +25 -14
- package/dist/write.d.ts +53 -0
- package/dist/write.js +3 -0
- package/package.json +88 -81
package/dist/tsl.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Node } from 'three/webgpu';
|
|
2
|
-
import {
|
|
2
|
+
import { V as VATCarrier } from './carrier-DmKhB4QL.js';
|
|
3
|
+
import { V as VATClock, a as VATPlaybackTexture, b as VAT, c as VATInstance, d as VATCrowd } from './types-CDPXKe0_.js';
|
|
3
4
|
import 'three';
|
|
4
5
|
|
|
5
6
|
/**
|
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, vertexWidthOf, RIG_TEXELS_PER_SLOT } from './chunk-
|
|
2
|
+
import { INFINITE_REPETITIONS, LoopMode, EndMode, createVATPlaybackTexture, PACK_TEXELS, RIG_TEXELS, vertexWidthOf, RIG_TEXELS_PER_SLOT } from './chunk-4IKOXUSX.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
|
|
|
@@ -20,6 +20,7 @@ function texturePlayback(texture, instance) {
|
|
|
20
20
|
packTexel(texture, PACK_TEXELS.playback, instance)
|
|
21
21
|
),
|
|
22
22
|
crossfadeDuration: crossfade.x,
|
|
23
|
+
crossfadeStart: crossfade.y,
|
|
23
24
|
outgoing: outgoingBand(
|
|
24
25
|
packTexel(texture, PACK_TEXELS.outgoingClip, instance),
|
|
25
26
|
packTexel(texture, PACK_TEXELS.outgoingPlayback, instance)
|
|
@@ -73,7 +74,7 @@ function hashedPlayback(clip, desync, instance) {
|
|
|
73
74
|
endMode: float(clip.endMode)
|
|
74
75
|
}
|
|
75
76
|
};
|
|
76
|
-
return { live, crossfadeDuration: float(0), outgoing: live };
|
|
77
|
+
return { live, crossfadeDuration: float(0), crossfadeStart: live.playback.startTime, outgoing: live };
|
|
77
78
|
}
|
|
78
79
|
function vatNodes(vat, options = {}) {
|
|
79
80
|
const { time = uniform(0), carrier } = options;
|
|
@@ -98,25 +99,27 @@ function vatNodes(vat, options = {}) {
|
|
|
98
99
|
function resolveBand(clip, playback, time) {
|
|
99
100
|
const frames = clip.frames;
|
|
100
101
|
const last = frames.sub(1);
|
|
101
|
-
const
|
|
102
|
-
const
|
|
102
|
+
const reversed = clip.speed.lessThan(0);
|
|
103
|
+
const local = time.sub(playback.startTime).mul(clip.speed.abs());
|
|
104
|
+
const loops = local.max(0).div(clip.duration);
|
|
103
105
|
const started = local.greaterThanEqual(0);
|
|
104
106
|
const finished = started.and(playback.repetitions.notEqual(INFINITE_REPETITIONS)).and(loops.greaterThanEqual(playback.repetitions));
|
|
105
107
|
const isPingPong = playback.loopMode.equal(LoopMode.PingPong);
|
|
106
108
|
const bounce = loops.sub(loops.mul(0.5).floor().mul(2));
|
|
107
109
|
const pingPongPhase = bounce.lessThan(1).select(bounce, float(2).sub(bounce));
|
|
108
110
|
const endPhase = playback.endMode.equal(EndMode.Clamp).select(float(1), float(0));
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
);
|
|
113
|
-
const looping = started.select(
|
|
114
|
-
finished.select(bool(false), isPingPong.select(bool(false), bool(true))),
|
|
115
|
-
bool(false)
|
|
116
|
-
);
|
|
117
|
-
const holds = looping.and(playback.endMode.equal(EndMode.Clamp)).and(playback.repetitions.notEqual(INFINITE_REPETITIONS)).and(loops.floor().add(1).greaterThanEqual(playback.repetitions));
|
|
111
|
+
const cascade = finished.select(endPhase, isPingPong.select(pingPongPhase, loops.fract()));
|
|
112
|
+
const looping = finished.select(bool(false), isPingPong.select(bool(false), bool(true)));
|
|
113
|
+
const forwardHolds = playback.endMode.equal(EndMode.Clamp).and(loops.floor().add(1).greaterThanEqual(playback.repetitions));
|
|
114
|
+
const holds = looping.and(playback.repetitions.notEqual(INFINITE_REPETITIONS)).and(reversed.select(loops.lessThan(1), forwardHolds));
|
|
118
115
|
const wraps = looping.and(holds.not());
|
|
119
|
-
const
|
|
116
|
+
const mirrored = float(1).sub(cascade);
|
|
117
|
+
const phase = reversed.select(
|
|
118
|
+
wraps.and(mirrored.greaterThanEqual(1)).select(float(0), mirrored),
|
|
119
|
+
cascade
|
|
120
|
+
);
|
|
121
|
+
const spread = phase.mul(looping.select(frames, last));
|
|
122
|
+
const f = holds.select(spread.min(last), spread);
|
|
120
123
|
const f0 = f.floor().min(last);
|
|
121
124
|
const next = f0.add(1);
|
|
122
125
|
const f1 = wraps.select(next.greaterThanEqual(frames).select(float(0), next), next.min(last));
|
|
@@ -135,7 +138,7 @@ function vatDecode(vat, options = {}) {
|
|
|
135
138
|
const instance = instanceIdOf(carrier);
|
|
136
139
|
const playback = playbackTexture ? texturePlayback(playbackTexture.texture, instance) : hashedPlayback(clipAt(vat, clipIndex), desync, instance);
|
|
137
140
|
const live = resolveBand(playback.live.clip, playback.live.playback, time);
|
|
138
|
-
const elapsed = time.sub(playback.
|
|
141
|
+
const elapsed = time.sub(playback.crossfadeStart);
|
|
139
142
|
const duration = playback.crossfadeDuration;
|
|
140
143
|
const weight = duration.greaterThan(0).select(
|
|
141
144
|
float(1).sub(elapsed.div(duration).clamp(0, 1)),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataTexture, BufferGeometry, Material, Box3, InstancedMesh
|
|
1
|
+
import { DataTexture, BufferGeometry, Material, Box3, InstancedMesh } from 'three';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* How an instance repeats its clip — the numbers `THREE.LoopRepeat`,
|
|
@@ -70,10 +70,14 @@ interface VATPlaybackState {
|
|
|
70
70
|
*/
|
|
71
71
|
startTime: number;
|
|
72
72
|
/**
|
|
73
|
-
* Playback rate multiplier
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
73
|
+
* Playback rate multiplier. Defaults to the clip's speed, then to `1`.
|
|
74
|
+
*
|
|
75
|
+
* A negative rate plays the band backwards at its magnitude, with nothing
|
|
76
|
+
* baked for it (ADR-0033): the pose at every moment is the one forward
|
|
77
|
+
* playback shows at the mirrored point of the clip, so a reversed one-shot
|
|
78
|
+
* starts on its last frame, and the end modes read in the direction of play
|
|
79
|
+
* — `Clamp` holds the pose it stopped on, `Rewind` returns to the one it
|
|
80
|
+
* started from. `0` holds the clip's first row, and is not reversed.
|
|
77
81
|
*/
|
|
78
82
|
speed?: number;
|
|
79
83
|
/** How the clip repeats. Defaults to the clip's, then {@link LoopMode.Repeat}. */
|
|
@@ -114,7 +118,7 @@ interface VATInstance extends VATPlaybackState {
|
|
|
114
118
|
from?: VATPlaybackState;
|
|
115
119
|
/**
|
|
116
120
|
* Seconds to blend {@link from} away over. Uncapped, and wall-clock seconds
|
|
117
|
-
* from {@link
|
|
121
|
+
* from {@link fadeStart}: the incoming clip's `speed` does not stretch a
|
|
118
122
|
* transition.
|
|
119
123
|
*
|
|
120
124
|
* Zero, or absent, is a cut: no outgoing band is written. A negative or
|
|
@@ -124,6 +128,18 @@ interface VATInstance extends VATPlaybackState {
|
|
|
124
128
|
* nothing to blend away from, so this is `setVATInstance`'s field in practice.
|
|
125
129
|
*/
|
|
126
130
|
fadeDuration?: number;
|
|
131
|
+
/**
|
|
132
|
+
* The clock time, in seconds, at which the blend out of {@link from} began.
|
|
133
|
+
* Defaults to {@link startTime}, which is when every transition you write
|
|
134
|
+
* yourself begins, so you normally leave it out: it is normally written by a
|
|
135
|
+
* turn, whose live band's start time is fixed by pose continuity and cannot
|
|
136
|
+
* also place the blend (ADR-0036).
|
|
137
|
+
*
|
|
138
|
+
* Moves the weight and nothing else: both bands resolve exactly as they
|
|
139
|
+
* would without it. A non-finite start is refused when the instance is
|
|
140
|
+
* written.
|
|
141
|
+
*/
|
|
142
|
+
fadeStart?: number;
|
|
127
143
|
}
|
|
128
144
|
/**
|
|
129
145
|
* Where in its VAT an instance is at a given moment: the two frame rows to
|
|
@@ -139,10 +155,11 @@ interface VATFrame {
|
|
|
139
155
|
mix: number;
|
|
140
156
|
/**
|
|
141
157
|
* Whether {@link rowNext} crossed the clip's last row back into its first.
|
|
142
|
-
* True only while a clip is genuinely looping: a ping-pong bounces rather
|
|
158
|
+
* True only while a clip is genuinely looping, or scheduled to start doing so: a ping-pong bounces rather
|
|
143
159
|
* than wraps, and a finished one-shot must not wrap at all or the corpse
|
|
144
160
|
* stands back up for a frame — nor, across its final repetition, may any
|
|
145
|
-
* clip that ends by clamping (#88)
|
|
161
|
+
* clip that ends by clamping (#88), nor a reversed finite play across its
|
|
162
|
+
* first interval (ADR-0033).
|
|
146
163
|
*/
|
|
147
164
|
wraps: boolean;
|
|
148
165
|
/** Whether the repetitions have run out and the instance is holding an end pose. */
|
|
@@ -171,8 +188,9 @@ interface VATOutgoingFrame extends VATFrame {
|
|
|
171
188
|
/**
|
|
172
189
|
* How much of this band is still showing: `1` at the moment of the write,
|
|
173
190
|
* falling to `0` across `fadeDuration`, and `0` once the transition is over.
|
|
174
|
-
* Wall clock — `1 - clamp((time -
|
|
175
|
-
*
|
|
191
|
+
* Wall clock — `1 - clamp((time - fadeStart) / fadeDuration, 0, 1)`, where
|
|
192
|
+
* `fadeStart` defaults to the live `startTime` — so a half-speed incoming
|
|
193
|
+
* clip does not stretch the transition.
|
|
176
194
|
*/
|
|
177
195
|
weight: number;
|
|
178
196
|
}
|
|
@@ -261,14 +279,16 @@ interface VATPlaybackTextureOptions {
|
|
|
261
279
|
* | ------------------------- | -------------- | ----------- | ----------- | -------- |
|
|
262
280
|
* | `x = 0` clip | clip start row | clip frames | clip fps | speed |
|
|
263
281
|
* | `x = 1` playback | start time | loop mode | repetitions | end mode |
|
|
264
|
-
* | `x = 2` crossfade | fade duration |
|
|
282
|
+
* | `x = 2` crossfade | fade duration | fade start | 0 | 0 |
|
|
265
283
|
* | `x = 3` outgoing clip | clip start row | clip frames | clip fps | speed |
|
|
266
284
|
* | `x = 4` outgoing playback | start time | loop mode | repetitions | end mode |
|
|
267
285
|
*
|
|
268
286
|
* The outgoing pair is a full playback state — the same two texels, in the same
|
|
269
287
|
* order, with the same meaning — because that is the whole difference between a
|
|
270
|
-
* freeze and a crossfade (ADR-0025). The crossfade texel's
|
|
271
|
-
*
|
|
288
|
+
* freeze and a crossfade (ADR-0025). The crossfade texel's `g` is the blend
|
|
289
|
+
* start, the instance's `fadeStart` or else its start time, so every write
|
|
290
|
+
* fills it (ADR-0036); its two spare components are written as zero and read
|
|
291
|
+
* by nothing.
|
|
272
292
|
*
|
|
273
293
|
* **A texture, not three instanced attributes.** An attribute with divisor 1 is
|
|
274
294
|
* indexed by the *drawn slot*, and the drawn slot stops being the instance the
|
|
@@ -290,9 +310,10 @@ interface VATPlaybackTextureOptions {
|
|
|
290
310
|
* The policy fields, and the clip texel's speed, come from the instance where
|
|
291
311
|
* it names them and from the clip's baked defaults where it does not — resolved
|
|
292
312
|
* in the one place those tiers are spelled — and both decode paths read them as
|
|
293
|
-
* {@link resolveVATFrame} defines them. The crossfade
|
|
313
|
+
* {@link resolveVATFrame} defines them. The crossfade duration and the outgoing
|
|
294
314
|
* pair are written as zeroes, which is what "not transitioning" is: a crowd
|
|
295
|
-
* being created has no animation to blend away from.
|
|
315
|
+
* being created has no animation to blend away from. The blend start beside
|
|
316
|
+
* the duration is filled anyway, as on every write. Transitions belong to
|
|
296
317
|
* {@link setVATInstance}, where an instance's animation changes and there is
|
|
297
318
|
* something to blend out of.
|
|
298
319
|
*
|
|
@@ -332,8 +353,9 @@ declare function createVATPlaybackTexture(instances: VATInstance[], options?: VA
|
|
|
332
353
|
* in which anything changed (docs/usage.md says what that costs).
|
|
333
354
|
*
|
|
334
355
|
* Ask for a `fadeDuration` and the animation the instance was playing **keeps
|
|
335
|
-
* playing**, blended away over that many wall-clock seconds from `startTime
|
|
336
|
-
*
|
|
356
|
+
* playing**, blended away over that many wall-clock seconds from `startTime`
|
|
357
|
+
* (or from a `fadeStart`, which a turn writes), so the change is a transition
|
|
358
|
+
* rather than a pop (ADR-0025, ADR-0036). Uncapped: a tenth
|
|
337
359
|
* of a second for a death, half a second for a walk into a run, and both clips
|
|
338
360
|
* move throughout. Zero, or none at all, is a cut.
|
|
339
361
|
*
|
|
@@ -341,8 +363,9 @@ declare function createVATPlaybackTexture(instances: VATInstance[], options?: VA
|
|
|
341
363
|
* replaces the outgoing band with the one it was switching to and drops the
|
|
342
364
|
* older band at whatever weight it still had — a pop proportional to how early
|
|
343
365
|
* the interruption came, and the one visible discontinuity a caller can
|
|
344
|
-
* produce. `startTime + fadeDuration` is when the transition ends
|
|
345
|
-
*
|
|
366
|
+
* produce. `startTime + fadeDuration` is when the transition ends — or
|
|
367
|
+
* `fadeStart + fadeDuration`, where one is written — for a caller who would
|
|
368
|
+
* rather wait it out.
|
|
346
369
|
*
|
|
347
370
|
* A written instance is a pure function of the clock from here on, so what
|
|
348
371
|
* happens *after* it is a matter of scheduling one more of these writes —
|
|
@@ -355,10 +378,62 @@ declare function createVATPlaybackTexture(instances: VATInstance[], options?: VA
|
|
|
355
378
|
* to — and the playback texture is the object such a caller holds (ADR-0016).
|
|
356
379
|
*/
|
|
357
380
|
declare function setVATInstance(playback: VATPlaybackTexture, index: number, instance: VATInstance): void;
|
|
381
|
+
/**
|
|
382
|
+
* Turn one instance round at the pose it is showing (ADR-0036): from `time`
|
|
383
|
+
* on, it retraces its path, showing at `time + x` the pose it showed at
|
|
384
|
+
* `time − x`. A walker backs up; a door halfway open closes from halfway.
|
|
385
|
+
*
|
|
386
|
+
* ```ts
|
|
387
|
+
* // the moment the player lets go — and nothing per frame afterwards
|
|
388
|
+
* const back = turnVATInstance(playback, doorId, time.value)
|
|
389
|
+
* const shut = endsAt(back) // when it is closed again, or null if endless
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* The row is read back out of the playback texture, so there is no CPU copy
|
|
393
|
+
* of the instance to keep. The turned instance is written through the same
|
|
394
|
+
* pack writer as {@link setVATInstance}, only its row is flagged for upload,
|
|
395
|
+
* and it is returned so {@link endsAt} can schedule what comes next.
|
|
396
|
+
*
|
|
397
|
+
* - A play with a count runs back to where it began and holds that pose. It
|
|
398
|
+
* is written with `Clamp` whatever its end mode, because a `Rewind` would
|
|
399
|
+
* snap back to the pose it turned at — so a second turn gives back the path
|
|
400
|
+
* but not a `Rewind`, which the pack does not remember.
|
|
401
|
+
* - An endless play has no beginning, only a desync start time, and retraces
|
|
402
|
+
* endlessly.
|
|
403
|
+
* - A turn retraces motion, never waiting. A finished play turns from the
|
|
404
|
+
* moment it finished, not from the end of its hold — and from the pose its
|
|
405
|
+
* path finished on, which is not the one `Clamp` holds for a fractional
|
|
406
|
+
* count or an even ping-pong, so those jump back onto their path. A play finished on the
|
|
407
|
+
* pose it started from (`Rewind`), or still waiting for its start time, has
|
|
408
|
+
* nothing to retrace and holds where it is, for good.
|
|
409
|
+
* - Direction is the way the pose is moving. For a ping-pong the turn chooses
|
|
410
|
+
* the sign, the count and the start time that reproduce the retraced path,
|
|
411
|
+
* an even count included. The speed's magnitude is kept.
|
|
412
|
+
*
|
|
413
|
+
* Distinct from reverse playback, a negative `speed` written with
|
|
414
|
+
* {@link setVATInstance}, which plays a clip backwards from its start rather
|
|
415
|
+
* than from where the instance is.
|
|
416
|
+
*
|
|
417
|
+
* A turn does not ease: the pose is continuous and the velocity reverses at
|
|
418
|
+
* once, as three's `timeScale = -timeScale` does.
|
|
419
|
+
*
|
|
420
|
+
* An instance mid-crossfade retraces the blend too (#120). The weight flows
|
|
421
|
+
* back toward the clip it was leaving: that clip, retraced, is the live band
|
|
422
|
+
* of what the turn writes, the clip it was entering, retraced, is its `from`,
|
|
423
|
+
* and a `fadeStart` places the blend to end when the retrace passes the
|
|
424
|
+
* moment the original began. From then on it plays the clip it was leaving,
|
|
425
|
+
* retraced, alone, and `endsAt` answers for that. Within the blend both bands
|
|
426
|
+
* are mirrored whole, so a band that had finished on `Clamp` holds as long
|
|
427
|
+
* again before it retraces. Not a band finished on `Rewind`, which holds its
|
|
428
|
+
* start pose for good, nor one clamped off its path, which jumps back onto
|
|
429
|
+
* it, both as they would alone. A transition already over is dropped.
|
|
430
|
+
*/
|
|
431
|
+
declare function turnVATInstance(playback: VATPlaybackTexture, index: number, time: number): VATInstance;
|
|
358
432
|
/**
|
|
359
433
|
* The exact clock time this instance stops animating — when
|
|
360
434
|
* {@link resolveVATFrame} first reports `finished` — or `null` for an animation
|
|
361
|
-
* that never gets there: an endless loop, or a speed of zero.
|
|
435
|
+
* that never gets there: an endless loop, or a speed of zero. The same moment
|
|
436
|
+
* for `speed: -1` as for `speed: 1`.
|
|
362
437
|
*
|
|
363
438
|
* This is what makes chaining one clip to the next a single scheduled write
|
|
364
439
|
* rather than a per-frame poll:
|
|
@@ -412,9 +487,9 @@ interface VATClipDefaults {
|
|
|
412
487
|
*/
|
|
413
488
|
endMode: EndMode;
|
|
414
489
|
/**
|
|
415
|
-
* Playback rate, from the action's `timeScale`.
|
|
416
|
-
*
|
|
417
|
-
*
|
|
490
|
+
* Playback rate, from the action's `timeScale`. A negative rate plays the
|
|
491
|
+
* band backwards, as it does on an instance (ADR-0033). `0` is a held first
|
|
492
|
+
* row, on purpose.
|
|
418
493
|
*/
|
|
419
494
|
speed: number;
|
|
420
495
|
}
|
|
@@ -605,14 +680,4 @@ interface VATCrowd {
|
|
|
605
680
|
playback: VATPlaybackTexture;
|
|
606
681
|
}
|
|
607
682
|
|
|
608
|
-
|
|
609
|
-
* A mesh a VAT crowd can ride.
|
|
610
|
-
*
|
|
611
|
-
* `InstancedMesh` is what `createVATMesh` builds on either path.
|
|
612
|
-
* `BatchedMesh` is reached through the primitives, and buys per-instance
|
|
613
|
-
* frustum culling and depth sorting from three.js itself — see
|
|
614
|
-
* docs/usage.md, which also says what it does *not* buy.
|
|
615
|
-
*/
|
|
616
|
-
type VATCarrier = InstancedMesh | BatchedMesh;
|
|
617
|
-
|
|
618
|
-
export { type DeltaVAT as D, EndMode as E, INFINITE_REPETITIONS as I, LoopMode as L, type RigVAT as R, type VAT as V, type VATBase as a, type VATCarrier as b, type VATClip as c, type VATClipDefaults as d, type VATClock as e, type VATCrowd as f, type VATFrame as g, type VATInstance as h, type VATOutgoingFrame as i, type VATPlaybackState as j, type VATPlaybackTexture as k, type VATPlaybackTextureOptions as l, createVATPlaybackTexture as m, endsAt as n, resolveVATFrame as r, setVATInstance as s };
|
|
683
|
+
export { type DeltaVAT as D, EndMode as E, INFINITE_REPETITIONS as I, LoopMode as L, type RigVAT as R, type VATClock as V, type VATPlaybackTexture as a, type VAT as b, type VATInstance as c, type VATCrowd as d, type VATBase as e, type VATClip as f, type VATClipDefaults as g, type VATFrame as h, type VATOutgoingFrame as i, type VATPlaybackState as j, type VATPlaybackTextureOptions as k, createVATPlaybackTexture as l, endsAt as m, resolveVATFrame as r, setVATInstance as s, turnVATInstance as t };
|
package/dist/webgl.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IUniform, MeshDepthMaterial, WebGLRenderer, Material } from 'three';
|
|
2
|
-
import {
|
|
2
|
+
import { V as VATCarrier } from './carrier-DmKhB4QL.js';
|
|
3
|
+
import { b as VAT, a as VATPlaybackTexture, c as VATInstance, d as VATCrowd } from './types-CDPXKe0_.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 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, vertexWidthOf } from './chunk-
|
|
2
|
+
import { PACK_TEXELS, EndMode, LoopMode, RIG_TEXELS_PER_SLOT, RIG_TEXELS, INFINITE_REPETITIONS, createVATPlaybackTexture, vertexWidthOf } from './chunk-4IKOXUSX.js';
|
|
3
3
|
import { MeshDepthMaterial, RGBADepthPacking, InstancedMesh, MeshDistanceMaterial, Material } from 'three';
|
|
4
4
|
|
|
5
5
|
function getMaxTextureSize(renderer) {
|
|
@@ -40,7 +40,7 @@ var ROW_PRELUDE = (
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
// resolveVATFrame for one (clip texel, playback texel) pair, branch for
|
|
43
|
-
// branch:
|
|
43
|
+
// branch: finished, ping-pong, repeat \u2014 the resolver's own
|
|
44
44
|
// order, each case falling out into the shared phase-to-row arithmetic below
|
|
45
45
|
// rather than returning early, so all of them land on the same two rows.
|
|
46
46
|
//
|
|
@@ -53,9 +53,12 @@ var ROW_PRELUDE = (
|
|
|
53
53
|
float duration = frames / vatClip.z;
|
|
54
54
|
// Local time: how far into its own animation this instance is. A start time
|
|
55
55
|
// in the past is what desyncs a crowd; a start time in the future has not
|
|
56
|
-
// begun, which is not the same thing as having finished.
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
// begun, which is not the same thing as having finished. The speed's sign is
|
|
57
|
+
// the direction and its magnitude the rate (ADR-0033).
|
|
58
|
+
bool reversed = vatClip.w < 0.0;
|
|
59
|
+
float local = ( uVatTime - vatPlayback.x ) * abs( vatClip.w );
|
|
60
|
+
// Not yet started sits on the pose its start time will show.
|
|
61
|
+
float loops = max( local, 0.0 ) / duration;
|
|
59
62
|
float repetitions = vatPlayback.z;
|
|
60
63
|
|
|
61
64
|
bool started = local >= 0.0;
|
|
@@ -63,10 +66,7 @@ var ROW_PRELUDE = (
|
|
|
63
66
|
|
|
64
67
|
float phase;
|
|
65
68
|
bool looping;
|
|
66
|
-
if (
|
|
67
|
-
phase = 0.0;
|
|
68
|
-
looping = false;
|
|
69
|
-
} else if ( finished ) {
|
|
69
|
+
if ( finished ) {
|
|
70
70
|
// Held at an end pose, and in neither case sampling past it.
|
|
71
71
|
phase = vatPlayback.w == ${glslFloat(EndMode.Clamp)} ? 1.0 : 0.0;
|
|
72
72
|
looping = false;
|
|
@@ -81,11 +81,20 @@ var ROW_PRELUDE = (
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Except across the final repetition of a clip that clamps, which holds its
|
|
84
|
-
// last row rather than blending back toward its first (#88)
|
|
85
|
-
|
|
84
|
+
// last row rather than blending back toward its first (#88) \u2014 and, reversed,
|
|
85
|
+
// across the first interval of a play that runs out.
|
|
86
|
+
bool holds = looping && repetitions != ${glslFloat(INFINITE_REPETITIONS)} && ( reversed ? loops < 1.0 : vatPlayback.w == ${glslFloat(EndMode.Clamp)} && floor( loops ) + 1.0 >= repetitions );
|
|
86
87
|
bool wraps = looping && !holds;
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
// The mirror, as a select on the phase and never a branch: every instance
|
|
90
|
+
// pays for it, reversed or not (#72). A mirrored 1 across the seam is the
|
|
91
|
+
// seam, the first row again.
|
|
92
|
+
float mirrored = 1.0 - phase;
|
|
93
|
+
phase = reversed ? ( wraps && mirrored >= 1.0 ? 0.0 : mirrored ) : phase;
|
|
94
|
+
|
|
95
|
+
// A held interval sits on the last row, with nothing to blend toward.
|
|
96
|
+
float spread = phase * ( looping ? frames : last );
|
|
97
|
+
float f = holds ? min( spread, last ) : spread;
|
|
89
98
|
float f0 = min( floor( f ), last );
|
|
90
99
|
// A compare, not a mod: a mod divides, and at the last row a quotient a hair
|
|
91
100
|
// under 1 leaves f1 at frames, one row past the band (#79).
|
|
@@ -137,10 +146,12 @@ var ROW_PRELUDE = (
|
|
|
137
146
|
// The crossfade's weight, transcribed from the resolver: wall clock, not
|
|
138
147
|
// clip time \u2014 the incoming clip's speed does not stretch a transition \u2014 and
|
|
139
148
|
// a duration of zero is what a cut is, which is what the pack writes when
|
|
140
|
-
// there is no band to blend away.
|
|
149
|
+
// there is no band to blend away. Measured from the blend start the same
|
|
150
|
+
// texel carries in g, which is the live start time unless a turn placed it
|
|
151
|
+
// (ADR-0036): one component swapped for another, so no fetch and no branch.
|
|
141
152
|
rows.weight = 0.0;
|
|
142
153
|
if ( vatCrossfade.x > 0.0 ) {
|
|
143
|
-
rows.weight = 1.0 - clamp( ( uVatTime -
|
|
154
|
+
rows.weight = 1.0 - clamp( ( uVatTime - vatCrossfade.y ) / vatCrossfade.x, 0.0, 1.0 );
|
|
144
155
|
}
|
|
145
156
|
|
|
146
157
|
return rows;
|
package/dist/write.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { b as VAT } from './types-CDPXKe0_.js';
|
|
2
|
+
import { Texture } from 'three';
|
|
3
|
+
import { GLTFParser } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
4
|
+
|
|
5
|
+
/** One image of the source file, as the file held it. */
|
|
6
|
+
interface SourceImage {
|
|
7
|
+
bytes: Uint8Array;
|
|
8
|
+
mimeType: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* How a source texture reaches its image: through its own `source`, through
|
|
13
|
+
* one of the {@link IMAGE_EXTENSIONS}, or both, one the fallback of the other. Copied through
|
|
14
|
+
* in the same shape, so a KTX2 texture stays KTX2.
|
|
15
|
+
*/
|
|
16
|
+
interface SourceTexture {
|
|
17
|
+
source?: SourceImage;
|
|
18
|
+
extensions: Record<string, SourceImage>;
|
|
19
|
+
/** The image-format extensions the source file required. */
|
|
20
|
+
required: string[];
|
|
21
|
+
}
|
|
22
|
+
/** Each source texture's images, keyed by the image a loaded texture holds, which its clones share. */
|
|
23
|
+
type SourceImages = Map<Texture['source'], SourceTexture>;
|
|
24
|
+
/**
|
|
25
|
+
* How a `.gltf`'s image file beside it is read: its bytes, now or later, for
|
|
26
|
+
* the URI exactly as the `.gltf` writes it.
|
|
27
|
+
*/
|
|
28
|
+
type ReadImageFile = (uri: string) => ArrayBuffer | Promise<ArrayBuffer>;
|
|
29
|
+
/**
|
|
30
|
+
* Every texture the loaded materials hold, keyed by its image, with that
|
|
31
|
+
* image's bytes as the file holds them, for {@link writeBakedFile} to copy
|
|
32
|
+
* into the baked file as they are (ADR-0034). `parser` is the loaded glTF's
|
|
33
|
+
* own (`gltf.parser`): an image in the binary chunk is read through its
|
|
34
|
+
* `getDependency('bufferView', i)`, and a `data:` URI is decoded where it
|
|
35
|
+
* stands. An image in a file beside a `.gltf` is read through `readFile`,
|
|
36
|
+
* which by default fetches it from where the loader found the `.gltf`.
|
|
37
|
+
*/
|
|
38
|
+
declare function readSourceImages(parser: GLTFParser, readFile?: ReadImageFile): Promise<SourceImages>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Write `vat` as a baked file. Resolves to the `.glb`'s bytes. In Node, a
|
|
42
|
+
* `FileReader` has to be installed first (src/file-reader.ts): the exporter
|
|
43
|
+
* reads its own output back through one.
|
|
44
|
+
*
|
|
45
|
+
* A textured material's images are copied from `images`, the source file's
|
|
46
|
+
* own bytes (src/write-materials.ts). A rig-encoded VAT is written with a
|
|
47
|
+
* preview skin, and only as `bakeVAT` returned it ({@link previewSkin}).
|
|
48
|
+
*/
|
|
49
|
+
declare function writeBakedFile(vat: VAT, { images }?: {
|
|
50
|
+
images?: SourceImages;
|
|
51
|
+
}): Promise<Uint8Array>;
|
|
52
|
+
|
|
53
|
+
export { type ReadImageFile, type SourceImage, type SourceImages, type SourceTexture, readSourceImages, writeBakedFile };
|
package/dist/write.js
ADDED
package/package.json
CHANGED
|
@@ -1,81 +1,88 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "three-vat",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Bake glTF animation clips into vertex animation textures for zero-CPU instanced crowds in three.js.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "Mike Fernandez <mike.fernandez.sec@gmail.com>",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/MikeFernandez-Pro/three-vat.git"
|
|
11
|
-
},
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/MikeFernandez-Pro/three-vat/issues"
|
|
14
|
-
},
|
|
15
|
-
"homepage": "https://github.com/MikeFernandez-Pro/three-vat#readme",
|
|
16
|
-
"publishConfig": {
|
|
17
|
-
"access": "public"
|
|
18
|
-
},
|
|
19
|
-
"packageManager": "pnpm@10.19.0",
|
|
20
|
-
"sideEffects": false,
|
|
21
|
-
"
|
|
22
|
-
"dist"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"three": "
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
".",
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "three-vat",
|
|
3
|
+
"version": "4.2.0",
|
|
4
|
+
"description": "Bake glTF animation clips into vertex animation textures for zero-CPU instanced crowds in three.js.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Mike Fernandez <mike.fernandez.sec@gmail.com>",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/MikeFernandez-Pro/three-vat.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/MikeFernandez-Pro/three-vat/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/MikeFernandez-Pro/three-vat#readme",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"packageManager": "pnpm@10.19.0",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"bin": {
|
|
22
|
+
"three-vat": "./dist/bin.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./webgl": {
|
|
33
|
+
"types": "./dist/webgl.d.ts",
|
|
34
|
+
"import": "./dist/webgl.js"
|
|
35
|
+
},
|
|
36
|
+
"./tsl": {
|
|
37
|
+
"types": "./dist/tsl.d.ts",
|
|
38
|
+
"import": "./dist/tsl.js"
|
|
39
|
+
},
|
|
40
|
+
"./write": {
|
|
41
|
+
"types": "./dist/write.d.ts",
|
|
42
|
+
"import": "./dist/write.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "pnpm --filter three-vat-example dev",
|
|
47
|
+
"test": "vitest run && pnpm --filter three-vat-example test",
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p release/tsconfig.json && pnpm --filter three-vat-example typecheck",
|
|
50
|
+
"build:watch": "tsup --watch",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"three",
|
|
56
|
+
"threejs",
|
|
57
|
+
"vat",
|
|
58
|
+
"vertex-animation-texture",
|
|
59
|
+
"instancing",
|
|
60
|
+
"instancedmesh",
|
|
61
|
+
"crowd",
|
|
62
|
+
"animation",
|
|
63
|
+
"skinning",
|
|
64
|
+
"webgpu",
|
|
65
|
+
"tsl"
|
|
66
|
+
],
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"three": ">=0.186.0"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/three": "^0.186.0",
|
|
72
|
+
"gifenc": "1.0.3",
|
|
73
|
+
"playwright-core": "1.63.0",
|
|
74
|
+
"pngjs": "7.0.0",
|
|
75
|
+
"three": "^0.186.0",
|
|
76
|
+
"tsup": "^8.3.0",
|
|
77
|
+
"typescript": "^5.6.0",
|
|
78
|
+
"vite": "^7.1.0",
|
|
79
|
+
"vitest": "^2.1.0"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=20"
|
|
83
|
+
},
|
|
84
|
+
"workspaces": [
|
|
85
|
+
".",
|
|
86
|
+
"examples"
|
|
87
|
+
]
|
|
88
|
+
}
|