three-vat 2.1.0 → 4.0.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.
@@ -1,211 +0,0 @@
1
- import { FloatType, DataTexture, RGBAFormat, NearestFilter } from 'three';
2
-
3
- // src/vat-texture.ts
4
- var MAX_TEXTURE_SIZE = 16384;
5
- function makeVATTexture(data, width, height, type = FloatType) {
6
- const tex = new DataTexture(data, width, height, RGBAFormat, type);
7
- tex.minFilter = NearestFilter;
8
- tex.magFilter = NearestFilter;
9
- tex.generateMipmaps = false;
10
- tex.needsUpdate = true;
11
- return tex;
12
- }
13
-
14
- // src/instance-playback.ts
15
- var PACK_TEXELS = {
16
- clip: 0,
17
- playback: 1,
18
- fade: 2
19
- };
20
- var PACK_WIDTH = 3;
21
- var PACK_STRIDE = PACK_WIDTH * 4;
22
- var LoopMode = {
23
- /** Play the clip end to end, forever (or `repetitions` times). */
24
- Repeat: 0,
25
- /** Play the clip through once. */
26
- Once: 1,
27
- /** Play forward, then backward, without baking the reversed frames. */
28
- PingPong: 2
29
- };
30
- var EndMode = {
31
- /** Hold the last frame. */
32
- Clamp: 0,
33
- /** Return to the first frame. */
34
- Rewind: 1
35
- };
36
- var INFINITE_REPETITIONS = -1;
37
- var MAX_FADE_DURATION = 0.25;
38
- function defaultRepetitions(loopMode) {
39
- return loopMode === LoopMode.Repeat ? INFINITE_REPETITIONS : 1;
40
- }
41
- var LIBRARY_PLAYBACK_DEFAULTS = {
42
- loopMode: LoopMode.Repeat,
43
- repetitions: defaultRepetitions(LoopMode.Repeat),
44
- endMode: EndMode.Clamp,
45
- speed: 1
46
- };
47
- function resolvedPlaybackOf(instance) {
48
- const { clip } = instance;
49
- const clipLoopMode = clip.loopMode ?? LIBRARY_PLAYBACK_DEFAULTS.loopMode;
50
- const loopMode = instance.loopMode ?? clipLoopMode;
51
- const clipRepetitions = loopMode === clipLoopMode ? clip.repetitions : void 0;
52
- return {
53
- loopMode,
54
- repetitions: instance.repetitions ?? clipRepetitions ?? defaultRepetitions(loopMode),
55
- endMode: instance.endMode ?? clip.endMode ?? LIBRARY_PLAYBACK_DEFAULTS.endMode,
56
- speed: instance.speed ?? clip.speed ?? LIBRARY_PLAYBACK_DEFAULTS.speed
57
- };
58
- }
59
- function fadeOf(instance) {
60
- const duration = Math.min(instance.fadeDuration ?? 0, MAX_FADE_DURATION);
61
- const from = instance.from;
62
- if (!from || duration <= 0 || from.frames <= 0) return null;
63
- return { from, duration };
64
- }
65
- function fadeRowOf(from) {
66
- return from.startFrame + Math.max(Math.min(Math.floor(from.phase * from.frames), from.frames - 1), 0);
67
- }
68
- function resolveVATFrame(instance, time) {
69
- const { clip } = instance;
70
- const frames = clip.frames;
71
- const last = frames - 1;
72
- const duration = frames / clip.fps;
73
- const { loopMode, repetitions, endMode, speed } = resolvedPlaybackOf(instance);
74
- const local = (time - instance.startTime) * speed;
75
- const loops = local / duration;
76
- const started = local >= 0;
77
- const finished = started && repetitions !== INFINITE_REPETITIONS && loops >= repetitions;
78
- let phase;
79
- let wraps;
80
- if (!started) {
81
- phase = 0;
82
- wraps = false;
83
- } else if (finished) {
84
- phase = endMode === EndMode.Clamp ? 1 : 0;
85
- wraps = false;
86
- } else if (loopMode === LoopMode.PingPong) {
87
- const m = loops % 2;
88
- phase = m < 1 ? m : 2 - m;
89
- wraps = false;
90
- } else {
91
- phase = loops % 1;
92
- wraps = true;
93
- }
94
- const f = phase * (wraps ? frames : last);
95
- const f0 = Math.min(Math.floor(f), last);
96
- const f1 = wraps ? (f0 + 1) % frames : Math.min(f0 + 1, last);
97
- const row = clip.startFrame + f0;
98
- const fading = fadeOf(instance);
99
- const elapsed = fading ? (time - instance.startTime) / fading.duration : 0;
100
- return {
101
- row,
102
- rowNext: clip.startFrame + f1,
103
- mix: f - f0,
104
- wraps,
105
- finished,
106
- phase,
107
- fadeRow: fading ? fadeRowOf(fading.from) : row,
108
- fadeWeight: fading ? 1 - Math.min(Math.max(elapsed, 0), 1) : 0
109
- };
110
- }
111
- function createVATPlaybackTexture(instances) {
112
- const count = instances.length;
113
- if (count < 1) {
114
- throw new Error(
115
- "three-vat: a crowd needs at least one instance \u2014 a playback texture is one row per instance, and there is no zero-row texture to carry none"
116
- );
117
- }
118
- if (count > MAX_TEXTURE_SIZE) {
119
- throw new Error(
120
- `three-vat: a crowd of ${count} instances needs ${count} rows of playback texture, past the ${MAX_TEXTURE_SIZE}-row ceiling \u2014 the playback texture holds one row per instance, so MAX_TEXTURE_SIZE is the instance ceiling.`
121
- );
122
- }
123
- const data = new Float32Array(count * PACK_STRIDE);
124
- for (let i = 0; i < count; i++) writePack(data, i, instances[i]);
125
- return { texture: makeVATTexture(data, PACK_WIDTH, count), count };
126
- }
127
- var FORWARD_ONLY_REASON = "a baked band plays forward from its own first row, so a negative speed would freeze it on that row rather than run it backwards \u2014 bake a reversed clip instead. A speed of 0 is a held first row, and is fine";
128
- var rowStart = (index) => index * PACK_STRIDE;
129
- var texelStart = (index, field) => rowStart(index) + field * 4;
130
- function writePack(data, index, instance) {
131
- const clip = texelStart(index, PACK_TEXELS.clip);
132
- const playback = texelStart(index, PACK_TEXELS.playback);
133
- const fade = texelStart(index, PACK_TEXELS.fade);
134
- const policy = resolvedPlaybackOf(instance);
135
- if (policy.speed < 0) {
136
- throw new Error(`three-vat: instance ${index} has speed ${policy.speed}; ${FORWARD_ONLY_REASON}.`);
137
- }
138
- data[clip] = instance.clip.startFrame;
139
- data[clip + 1] = instance.clip.frames;
140
- data[clip + 2] = instance.clip.fps;
141
- data[clip + 3] = policy.speed;
142
- data[playback] = instance.startTime;
143
- data[playback + 1] = policy.loopMode;
144
- data[playback + 2] = policy.repetitions;
145
- data[playback + 3] = policy.endMode;
146
- const fading = fadeOf(instance);
147
- data[fade] = fading ? fading.from.startFrame : 0;
148
- data[fade + 1] = fading ? fading.from.frames : 0;
149
- data[fade + 2] = fading ? fading.from.phase : 0;
150
- data[fade + 3] = fading ? fading.duration : 0;
151
- }
152
- function readPack(data, index) {
153
- const clip = texelStart(index, PACK_TEXELS.clip);
154
- const playback = texelStart(index, PACK_TEXELS.playback);
155
- return {
156
- clip: { startFrame: data[clip], frames: data[clip + 1], fps: data[clip + 2] },
157
- startTime: data[playback],
158
- speed: data[clip + 3],
159
- loopMode: data[playback + 1],
160
- repetitions: data[playback + 2],
161
- endMode: data[playback + 3]
162
- };
163
- }
164
- function assertInstance(playback, index) {
165
- if (!Number.isInteger(index) || index < 0 || index >= playback.count) {
166
- throw new Error(`three-vat: instance ${index} is outside this crowd of ${playback.count}`);
167
- }
168
- }
169
- function setVATInstance(playback, index, instance) {
170
- assertInstance(playback, index);
171
- const data = playback.texture.image.data;
172
- const fading = instance.from === void 0 && (instance.fadeDuration ?? 0) > 0 ? { ...instance, from: freezeOf(data, index, instance.startTime) } : instance;
173
- writePack(data, index, fading);
174
- playback.texture.addUpdateRange(rowStart(index), PACK_STRIDE);
175
- playback.texture.needsUpdate = true;
176
- }
177
- function freezeOf(data, index, time) {
178
- const outgoing = readPack(data, index);
179
- const { row } = resolveVATFrame(outgoing, time);
180
- return {
181
- startFrame: outgoing.clip.startFrame,
182
- frames: outgoing.clip.frames,
183
- // The row the instance is actually displaying at that moment — so a fade
184
- // out of a finished one-shot freezes the end pose it was holding, not the
185
- // first row of a clip it stopped playing seconds ago.
186
- //
187
- // Named as the phase at the *centre* of that row rather than the playback
188
- // phase itself, because {@link fadeRowOf} is what reads it back and the two
189
- // do not spread a phase the same way: a bouncing ping-pong is up to a row
190
- // apart between them, and rounding in a shader could cost another. Half a
191
- // row of slack costs nothing and lands all three decodes on this row.
192
- phase: (row - outgoing.clip.startFrame + 0.5) / outgoing.clip.frames
193
- };
194
- }
195
- function endsAt(instance) {
196
- const { repetitions, speed } = resolvedPlaybackOf(instance);
197
- if (repetitions === INFINITE_REPETITIONS || speed <= 0) return null;
198
- const duration = instance.clip.frames / instance.clip.fps;
199
- return instance.startTime + duration * repetitions / speed;
200
- }
201
-
202
- // src/rig-texture.ts
203
- var RIG_TEXELS = {
204
- /** `(qx, qy, qz, qw)` — the slot's rotation, on one hemisphere with its neighbouring rows. */
205
- rotation: 0,
206
- /** `(tx, ty, tz, s)` — where the slot puts the origin, and its uniform scale in the spare component. */
207
- placement: 1
208
- };
209
- var RIG_TEXELS_PER_SLOT = 2;
210
-
211
- export { EndMode, FORWARD_ONLY_REASON, INFINITE_REPETITIONS, LIBRARY_PLAYBACK_DEFAULTS, LoopMode, MAX_FADE_DURATION, MAX_TEXTURE_SIZE, PACK_TEXELS, RIG_TEXELS, RIG_TEXELS_PER_SLOT, createVATPlaybackTexture, endsAt, makeVATTexture, resolveVATFrame, setVATInstance };