three-vat 2.0.0 → 3.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.
- package/README.md +8 -4
- package/dist/{carrier-BFCPmcQK.d.ts → carrier-BxRuW47G.d.ts} +194 -100
- package/dist/{chunk-W2ZAFMPB.js → chunk-I4STYOD5.js} +1 -1
- package/dist/{chunk-2PGZ44TP.js → chunk-J5IEUGSB.js} +80 -56
- package/dist/index.d.ts +39 -4
- package/dist/index.js +499 -35
- package/dist/tsl.d.ts +107 -27
- package/dist/tsl.js +185 -69
- package/dist/webgl.d.ts +89 -7
- package/dist/webgl.js +370 -60
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,11 @@ Two things worth knowing the first time:
|
|
|
81
81
|
- **Materials are never merged.** A 500-robot crowd with 3 materials is 3 draw
|
|
82
82
|
calls — not 1, and not 500. VAT collapses instance count, not material count.
|
|
83
83
|
|
|
84
|
+
**A skinned character?** There is a second encoding: `{ encoding: 'rig' }` bakes
|
|
85
|
+
the posed rig instead of the posed vertices — two orders of magnitude less
|
|
86
|
+
texture, a bake in milliseconds, and faster on a phone, for an asset a rig can
|
|
87
|
+
express. [The rig encoding](./docs/usage.md#the-rig-encoding-encoding-rig).
|
|
88
|
+
|
|
84
89
|
<details>
|
|
85
90
|
<summary><b>Does it work with my model?</b></summary>
|
|
86
91
|
|
|
@@ -143,7 +148,7 @@ is [the CHANGELOG's 2.0.0 entry](./CHANGELOG.md).
|
|
|
143
148
|
```
|
|
144
149
|
|
|
145
150
|
`timeOffset` is gone (`startTime: -timeOffset / speed`), and with it
|
|
146
|
-
`aTimeOffset`. The pack
|
|
151
|
+
`aTimeOffset`. The pack became three `vec4`s — clip, playback, fade — in a
|
|
147
152
|
**playback texture** keyed by the instance's logical index, not instanced
|
|
148
153
|
attributes, which are indexed by the *drawn* slot:
|
|
149
154
|
`addInstancedVATAttributes` is removed, `createVATPlaybackTexture(instances)`
|
|
@@ -179,9 +184,8 @@ worker recipe, the draw-call arithmetic, and the primitives underneath
|
|
|
179
184
|
<details>
|
|
180
185
|
<summary><b>What it does not do</b></summary>
|
|
181
186
|
|
|
182
|
-
No
|
|
183
|
-
|
|
184
|
-
format, no React/drei binding, glTF input only. Each is a decision rather than a
|
|
187
|
+
No LOD, no baking CLI or file format, no React/drei binding, glTF input
|
|
188
|
+
only. Each is a decision rather than a
|
|
185
189
|
gap, and each is written up with its reasoning in
|
|
186
190
|
**[docs/usage.md](./docs/usage.md#what-10-does-not-do)**, alongside the
|
|
187
191
|
trade-offs against `SkinnedMesh` and bone-texture instancing.
|
|
@@ -40,44 +40,20 @@ type EndMode = (typeof EndMode)[keyof typeof EndMode];
|
|
|
40
40
|
*/
|
|
41
41
|
declare const INFINITE_REPETITIONS = -1;
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
43
|
+
* One clip playing: which band, from when, how fast and under what policy.
|
|
44
44
|
*
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* would be a more visible bug, so the number is clamped rather than trusted.
|
|
51
|
-
*
|
|
52
|
-
* Provisional, like the fade itself: a real two-clip crossfade (#30) replaces
|
|
53
|
-
* both, and nothing else should be built on top of them.
|
|
54
|
-
*/
|
|
55
|
-
declare const MAX_FADE_DURATION = 0.25;
|
|
56
|
-
/**
|
|
57
|
-
* The frozen pose a fade blends away from: one phase of the clip an instance
|
|
58
|
-
* was playing when its animation changed, and the band that phase indexes.
|
|
59
|
-
*
|
|
60
|
-
* Not a second playback state — there is no start time and no speed here,
|
|
61
|
-
* because nothing about it moves. That is the whole of the freeze, and the
|
|
62
|
-
* whole of its limit; see {@link MAX_FADE_DURATION}.
|
|
63
|
-
*/
|
|
64
|
-
interface VATFadeFrom {
|
|
65
|
-
/** First texture row of the outgoing clip's band. */
|
|
66
|
-
startFrame: number;
|
|
67
|
-
/** Rows in that band. */
|
|
68
|
-
frames: number;
|
|
69
|
-
/** The phase of that band the instance was at, in `[0, 1]`. */
|
|
70
|
-
phase: number;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Per-instance playback state consumed by both decode paths.
|
|
45
|
+
* The unit the pack carries twice — as the animation an instance is playing,
|
|
46
|
+
* and as the one it is blending out of ({@link VATInstance.from}). Both are
|
|
47
|
+
* resolved by {@link resolveVATFrame} through the very same arithmetic, which
|
|
48
|
+
* is the whole difference between a crossfade and the pose freeze it replaced
|
|
49
|
+
* (ADR-0025): the outgoing half is a clip still *playing*, not a photograph.
|
|
74
50
|
*
|
|
75
51
|
* Every policy field is optional because the clip already answers it: a bake
|
|
76
52
|
* handed a configured `AnimationAction` records the answer in the clip table
|
|
77
|
-
* ({@link VATClipDefaults}), and
|
|
53
|
+
* ({@link VATClipDefaults}), and a state that says nothing inherits it. A
|
|
78
54
|
* crowd of a thousand deaths says "once, clamped" once, at the bake.
|
|
79
55
|
*/
|
|
80
|
-
interface
|
|
56
|
+
interface VATPlaybackState {
|
|
81
57
|
/**
|
|
82
58
|
* The clip band to play, straight out of `vat.clips`. Its playback defaults
|
|
83
59
|
* come along with it; a clip table assembled by hand may carry none, and then
|
|
@@ -119,19 +95,33 @@ interface VATInstance {
|
|
|
119
95
|
* `false`; see {@link EndMode}.
|
|
120
96
|
*/
|
|
121
97
|
endMode?: EndMode;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Per-instance playback state consumed by both decode paths: the clip this
|
|
101
|
+
* instance is playing, and — while it is transitioning — the one it is
|
|
102
|
+
* crossfading out of.
|
|
103
|
+
*/
|
|
104
|
+
interface VATInstance extends VATPlaybackState {
|
|
122
105
|
/**
|
|
123
|
-
* The
|
|
124
|
-
*
|
|
125
|
-
*
|
|
106
|
+
* The outgoing playback state to blend away from: a clip *still playing*, in
|
|
107
|
+
* every respect an instance except that it carries no transition of its own.
|
|
108
|
+
*
|
|
109
|
+
* Normally you do not write this yourself — {@link setVATInstance} reads the
|
|
110
|
+
* instance's current pack back, whole, and fills it in when you ask for a
|
|
111
|
+
* {@link fadeDuration}. Write it by hand when you are assembling a crowd the
|
|
112
|
+
* library does not build for you; what you pass is what is written.
|
|
126
113
|
*/
|
|
127
|
-
from?:
|
|
114
|
+
from?: VATPlaybackState;
|
|
128
115
|
/**
|
|
129
|
-
* Seconds to blend {@link from} away over,
|
|
130
|
-
*
|
|
131
|
-
*
|
|
116
|
+
* Seconds to blend {@link from} away over. Uncapped, and wall-clock seconds
|
|
117
|
+
* from {@link startTime}: the incoming clip's `speed` does not stretch a
|
|
118
|
+
* transition.
|
|
132
119
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
120
|
+
* Zero, or absent, is a cut: no outgoing band is written. A negative or
|
|
121
|
+
* non-finite duration is refused when the instance is written.
|
|
122
|
+
*
|
|
123
|
+
* Ignored without a `from` to blend away from — and at creation there is
|
|
124
|
+
* nothing to blend away from, so this is `setVATInstance`'s field in practice.
|
|
135
125
|
*/
|
|
136
126
|
fadeDuration?: number;
|
|
137
127
|
}
|
|
@@ -159,26 +149,39 @@ interface VATFrame {
|
|
|
159
149
|
/** How far through the clip this is, in `[0, 1]` — what {@link row} is derived from. */
|
|
160
150
|
phase: number;
|
|
161
151
|
/**
|
|
162
|
-
* The
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
152
|
+
* The band this instance is blending out of, resolved at the same moment —
|
|
153
|
+
* or `null` when it is not transitioning, which is almost always. Not a
|
|
154
|
+
* weight of zero, so a reader with no interest in transitions ignores one
|
|
155
|
+
* field rather than testing one.
|
|
166
156
|
*/
|
|
167
|
-
|
|
157
|
+
outgoing: VATOutgoingFrame | null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The outgoing half of a crossfade: the very frame the outgoing clip would be
|
|
161
|
+
* showing if nothing had interrupted it, and how much of it is still showing.
|
|
162
|
+
*
|
|
163
|
+
* It is a {@link VATFrame} because it is one — resolved by
|
|
164
|
+
* {@link resolveVATFrame} from the outgoing playback state, through the same
|
|
165
|
+
* arithmetic, so an outgoing one-shot that runs out mid-transition clamps
|
|
166
|
+
* exactly as it would have. Its own `outgoing` is always `null`: the pack holds
|
|
167
|
+
* two bands, and the recursion is one level deep.
|
|
168
|
+
*/
|
|
169
|
+
interface VATOutgoingFrame extends VATFrame {
|
|
168
170
|
/**
|
|
169
|
-
* How much of
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* the pose-freeze fade `CONTEXT.md` names.
|
|
171
|
+
* How much of this band is still showing: `1` at the moment of the write,
|
|
172
|
+
* falling to `0` across `fadeDuration`, and `0` once the transition is over.
|
|
173
|
+
* Wall clock — `1 - clamp((time - startTime) / fadeDuration, 0, 1)` — so a
|
|
174
|
+
* half-speed incoming clip does not stretch the transition.
|
|
174
175
|
*/
|
|
175
|
-
|
|
176
|
+
weight: number;
|
|
176
177
|
}
|
|
177
178
|
/**
|
|
178
179
|
* What the vertex shader computes, as a pure function of `(instance, time)` —
|
|
179
180
|
* the **one definition** of the playback semantics. Both decode paths
|
|
180
|
-
* transcribe it
|
|
181
|
-
*
|
|
181
|
+
* transcribe it — its band half in `vatBand` (src/webgl.ts) and `resolveBand`
|
|
182
|
+
* (src/tsl.ts), one function of a clip and playback texel pair, *called twice*
|
|
183
|
+
* where an instance is transitioning; its crossfade weight beside the call, in
|
|
184
|
+
* `vatRows` and `vatDecode` — and neither invents it.
|
|
182
185
|
*
|
|
183
186
|
* It exists in TypeScript because the arithmetic is otherwise reachable only
|
|
184
187
|
* inside a GLSL string and a TSL node graph, neither of which CI can evaluate
|
|
@@ -191,7 +194,7 @@ interface VATFrame {
|
|
|
191
194
|
*/
|
|
192
195
|
declare function resolveVATFrame(instance: VATInstance, time: number): VATFrame;
|
|
193
196
|
/**
|
|
194
|
-
* What carries the pack to the shader: one `DataTexture`,
|
|
197
|
+
* What carries the pack to the shader: one `DataTexture`, five texels wide,
|
|
195
198
|
* one row per instance, read by the instance's *logical* index (ADR-0016).
|
|
196
199
|
*
|
|
197
200
|
* Held by the caller rather than hidden behind the geometry, because a
|
|
@@ -207,24 +210,56 @@ interface VATPlaybackTexture {
|
|
|
207
210
|
* float, {@link PACK_WIDTH} texels wide.
|
|
208
211
|
*/
|
|
209
212
|
texture: DataTexture;
|
|
210
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* Rows of {@link texture} — the crowd's **capacity**, which is what the
|
|
215
|
+
* decode indexes and what {@link setVATInstance} bounds-checks against.
|
|
216
|
+
*
|
|
217
|
+
* Not a live population: for a crowd that spawns and dies, most of these
|
|
218
|
+
* rows may be reserved and empty at any moment, and the library has no
|
|
219
|
+
* notion of which (ADR-0022). The caller owns the indices, because the
|
|
220
|
+
* carrier already hands that numbering out.
|
|
221
|
+
*/
|
|
211
222
|
count: number;
|
|
212
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* How a playback texture is sized when the instances it is handed are not the
|
|
226
|
+
* whole story.
|
|
227
|
+
*/
|
|
228
|
+
interface VATPlaybackTextureOptions {
|
|
229
|
+
/**
|
|
230
|
+
* Rows to reserve — the crowd's ceiling, rather than its current population.
|
|
231
|
+
* Defaults to the number of instances given, which is a crowd placed once.
|
|
232
|
+
*
|
|
233
|
+
* At least that many, and at most `MAX_TEXTURE_SIZE`; both are refused by
|
|
234
|
+
* name. Fixed once the texture is made (ADR-0022): a texture does not grow
|
|
235
|
+
* in place, and growing one means rebuilding it and rebinding it on every
|
|
236
|
+
* patched material — `docs/usage.md` carries that recipe.
|
|
237
|
+
*/
|
|
238
|
+
capacity?: number;
|
|
239
|
+
}
|
|
213
240
|
/**
|
|
214
241
|
* Write a crowd's instance playback into a new playback texture. Call once,
|
|
215
242
|
* before rendering, and bind the result into the decode — `createVATMesh` does
|
|
216
243
|
* both for you.
|
|
217
244
|
*
|
|
218
245
|
* The layout below is the shared contract, spelled once in {@link PACK_TEXELS}.
|
|
219
|
-
* Both decode paths read exactly these
|
|
220
|
-
* `
|
|
221
|
-
* `src/tsl.ts` with `textureLoad
|
|
246
|
+
* Both decode paths read exactly these texels of row `instanceIndex` —
|
|
247
|
+
* `ROW_PRELUDE` in `src/webgl.ts` with `texelFetch`, `texturePlayback` in
|
|
248
|
+
* `src/tsl.ts` with `textureLoad` — the first three always, the last two only
|
|
249
|
+
* while a transition is running.
|
|
222
250
|
*
|
|
223
|
-
* | Texel
|
|
224
|
-
* |
|
|
225
|
-
* | `x = 0` clip
|
|
226
|
-
* | `x = 1` playback
|
|
227
|
-
* | `x = 2`
|
|
251
|
+
* | Texel | r | g | b | a |
|
|
252
|
+
* | ------------------------- | -------------- | ----------- | ----------- | -------- |
|
|
253
|
+
* | `x = 0` clip | clip start row | clip frames | clip fps | speed |
|
|
254
|
+
* | `x = 1` playback | start time | loop mode | repetitions | end mode |
|
|
255
|
+
* | `x = 2` crossfade | fade duration | 0 | 0 | 0 |
|
|
256
|
+
* | `x = 3` outgoing clip | clip start row | clip frames | clip fps | speed |
|
|
257
|
+
* | `x = 4` outgoing playback | start time | loop mode | repetitions | end mode |
|
|
258
|
+
*
|
|
259
|
+
* The outgoing pair is a full playback state — the same two texels, in the same
|
|
260
|
+
* order, with the same meaning — because that is the whole difference between a
|
|
261
|
+
* freeze and a crossfade (ADR-0025). The crossfade texel's three spare
|
|
262
|
+
* components are written as zero and read by nothing.
|
|
228
263
|
*
|
|
229
264
|
* **A texture, not three instanced attributes.** An attribute with divisor 1 is
|
|
230
265
|
* indexed by the *drawn slot*, and the drawn slot stops being the instance the
|
|
@@ -233,25 +268,35 @@ interface VATPlaybackTexture {
|
|
|
233
268
|
* (ADR-0016). A row keyed by the logical index is what three itself does for
|
|
234
269
|
* the same problem, in `_matricesTexture`.
|
|
235
270
|
*
|
|
236
|
-
* **
|
|
271
|
+
* **RGBA-shaped texels, not loose floats.** The move to a texture touched no
|
|
237
272
|
* decode arithmetic, because the layout did not change with it: the pack was
|
|
238
|
-
* already
|
|
239
|
-
*
|
|
240
|
-
* at once.
|
|
273
|
+
* already RGBA-shaped `vec4`s (ADR-0009). Published 1.x is the other story —
|
|
274
|
+
* five one-float attributes there, so a 1.x caller meets both changes at once.
|
|
241
275
|
*
|
|
242
276
|
* **`FloatType`, and it stays that way.** A `startTime` in seconds does not
|
|
243
|
-
* survive half precision — one second of resolution at 2 048 s —
|
|
244
|
-
* encoding for the VAT textures does not
|
|
277
|
+
* survive half precision — one second of resolution at 2 048 s — and there are
|
|
278
|
+
* now two of them per row, so a narrower encoding for the VAT textures does not
|
|
279
|
+
* reach this one.
|
|
245
280
|
*
|
|
246
281
|
* The policy fields, and the clip texel's speed, come from the instance where
|
|
247
282
|
* it names them and from the clip's baked defaults where it does not — resolved
|
|
248
283
|
* in the one place those tiers are spelled — and both decode paths read them as
|
|
249
|
-
* {@link resolveVATFrame} defines them. The
|
|
250
|
-
* which is what "not
|
|
251
|
-
*
|
|
252
|
-
*
|
|
284
|
+
* {@link resolveVATFrame} defines them. The crossfade texel and the outgoing
|
|
285
|
+
* pair are written as zeroes, which is what "not transitioning" is: a crowd
|
|
286
|
+
* being created has no animation to blend away from. Transitions belong to
|
|
287
|
+
* {@link setVATInstance}, where an instance's animation changes and there is
|
|
288
|
+
* something to blend out of.
|
|
289
|
+
*
|
|
290
|
+
* **A crowd that spawns and dies gives a capacity** instead of a census
|
|
291
|
+
* ({@link VATPlaybackTextureOptions}, ADR-0022): the rows are reserved once,
|
|
292
|
+
* from the ceiling, and filled with {@link setVATInstance} as instances appear.
|
|
293
|
+
* The list may then be empty — a level that starts with nothing alive in it —
|
|
294
|
+
* and the reserved rows hold a first frame, held. The library allocates no
|
|
295
|
+
* indices and follows no `setInstanceCount`: the carrier already numbers the
|
|
296
|
+
* instances, and a texture does not grow in place. `docs/usage.md` carries both,
|
|
297
|
+
* with **row recycling** — the hazard of reusing a row an instance has died on.
|
|
253
298
|
*/
|
|
254
|
-
declare function createVATPlaybackTexture(instances: VATInstance[]): VATPlaybackTexture;
|
|
299
|
+
declare function createVATPlaybackTexture(instances: VATInstance[], options?: VATPlaybackTextureOptions): VATPlaybackTexture;
|
|
255
300
|
/**
|
|
256
301
|
* Change one instance's animation, after the crowd is built. The single write
|
|
257
302
|
* the whole event-driven half of this library is made of: an enemy hit at
|
|
@@ -274,15 +319,21 @@ declare function createVATPlaybackTexture(instances: VATInstance[]): VATPlayback
|
|
|
274
319
|
* one small write rather than a full re-upload. Everything else about the
|
|
275
320
|
* instance — its matrix, its clip's defaults — is untouched. On the TSL path
|
|
276
321
|
* the range is recorded and ignored: three's WebGPU backend re-uploads the
|
|
277
|
-
* whole image on `needsUpdate`, which is
|
|
322
|
+
* whole image on `needsUpdate`, which is 80 bytes per instance once per frame
|
|
278
323
|
* in which anything changed (docs/usage.md says what that costs).
|
|
279
324
|
*
|
|
280
|
-
* Ask for a `fadeDuration` and the
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
325
|
+
* Ask for a `fadeDuration` and the animation the instance was playing **keeps
|
|
326
|
+
* playing**, blended away over that many wall-clock seconds from `startTime`,
|
|
327
|
+
* so the change is a transition rather than a pop (ADR-0025). Uncapped: a tenth
|
|
328
|
+
* of a second for a death, half a second for a walk into a run, and both clips
|
|
329
|
+
* move throughout. Zero, or none at all, is a cut.
|
|
330
|
+
*
|
|
331
|
+
* Two bands, and no more. Writing an instance that is *already* mid-transition
|
|
332
|
+
* replaces the outgoing band with the one it was switching to and drops the
|
|
333
|
+
* older band at whatever weight it still had — a pop proportional to how early
|
|
334
|
+
* the interruption came, and the one visible discontinuity a caller can
|
|
335
|
+
* produce. `startTime + fadeDuration` is when the transition ends, for a caller
|
|
336
|
+
* who would rather wait it out.
|
|
286
337
|
*
|
|
287
338
|
* A written instance is a pure function of the clock from here on, so what
|
|
288
339
|
* happens *after* it is a matter of scheduling one more of these writes —
|
|
@@ -374,7 +425,9 @@ interface VATClip extends VATClipDefaults {
|
|
|
374
425
|
/** Source clip duration in seconds. */
|
|
375
426
|
duration: number;
|
|
376
427
|
/**
|
|
377
|
-
* Largest
|
|
428
|
+
* Largest displacement (metres) of any vertex from the rest pose across the
|
|
429
|
+
* clip — the same number under either encoding, because the rig bake skins
|
|
430
|
+
* every vertex on the CPU for the bounds anyway and measures it there.
|
|
378
431
|
* Near-zero means the clip baked as a frozen pose — the diagnostic for a
|
|
379
432
|
* mis-targeted or genuinely static clip.
|
|
380
433
|
*/
|
|
@@ -396,22 +449,14 @@ interface VATClip extends VATClipDefaults {
|
|
|
396
449
|
* change it in a minor release. Move the buffer, hand it to {@link makeVATTexture};
|
|
397
450
|
* do not read numbers out of it.
|
|
398
451
|
*/
|
|
399
|
-
interface
|
|
400
|
-
/** RGBA float texture of per-vertex position deltas (`x = vertex`, `y = frame`). */
|
|
401
|
-
positionTexture: DataTexture;
|
|
402
|
-
/**
|
|
403
|
-
* RGBA float texture of per-vertex absolute normals (`x = vertex`, `y = frame`),
|
|
404
|
-
* or `null` when the bake was told to skip it (`bakeNormals: false`) — halving
|
|
405
|
-
* the VAT for a crowd that never reads a normal. Neither decode path samples
|
|
406
|
-
* it when it is absent; a smooth-shaded lit material paired with such a VAT is
|
|
407
|
-
* refused rather than lit by its rest pose.
|
|
408
|
-
*/
|
|
409
|
-
normalTexture: DataTexture | null;
|
|
452
|
+
interface VATBase {
|
|
410
453
|
/**
|
|
411
|
-
* Merged
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
454
|
+
* Merged rest-pose geometry, in whichever space the encoding stores it: root
|
|
455
|
+
* space under the vertex encoding, where its `position` is the delta
|
|
456
|
+
* reference; each part's own local space under the rig encoding, where the
|
|
457
|
+
* slot carries the placement and also keeps `skinIndex` and `skinWeight`.
|
|
458
|
+
* Carries `normal` always, and `uv`, `color` and `tangent` when every source
|
|
459
|
+
* mesh carried them.
|
|
415
460
|
*/
|
|
416
461
|
geometry: BufferGeometry;
|
|
417
462
|
/** Source materials, indexed by `geometry.groups[].materialIndex`. */
|
|
@@ -424,9 +469,58 @@ interface VAT {
|
|
|
424
469
|
vertexCount: number;
|
|
425
470
|
/** Total frame rows across all clips (texture height). */
|
|
426
471
|
totalFrames: number;
|
|
427
|
-
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* A VAT under the **vertex encoding**: a row holds where every vertex ended up,
|
|
475
|
+
* as a position delta and, unless the bake was told to skip it, a normal. The
|
|
476
|
+
* source-agnostic encoding (ADR-0008) and the default; the member every bake
|
|
477
|
+
* produced before there was a second one (ADR-0018).
|
|
478
|
+
*/
|
|
479
|
+
interface DeltaVAT extends VATBase {
|
|
480
|
+
/** Which encoding a row holds — the discriminant of {@link VAT}. */
|
|
428
481
|
encoding: 'delta';
|
|
482
|
+
/** RGBA float texture of per-vertex position deltas (`x = vertex`, `y = frame`). */
|
|
483
|
+
positionTexture: DataTexture;
|
|
484
|
+
/**
|
|
485
|
+
* RGBA float texture of per-vertex absolute normals (`x = vertex`, `y = frame`),
|
|
486
|
+
* or `null` when the bake was told to skip it (`bakeNormals: false`) — halving
|
|
487
|
+
* the VAT for a crowd that never reads a normal. Neither decode path samples
|
|
488
|
+
* it when it is absent; a smooth-shaded lit material paired with such a VAT is
|
|
489
|
+
* refused rather than lit by its rest pose.
|
|
490
|
+
*/
|
|
491
|
+
normalTexture: DataTexture | null;
|
|
429
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* A VAT under the **rig encoding** (ADR-0018): a row holds the posed rig — one
|
|
495
|
+
* **slot** per bone, as a rotation, a translation and a uniform scale — and the
|
|
496
|
+
* vertex shader skins the rest-pose geometry from it. Two orders of magnitude
|
|
497
|
+
* smaller than the vertex encoding and bake-cheap, at the price of source
|
|
498
|
+
* agnosticism: what a rig cannot express is refused at the bake.
|
|
499
|
+
*
|
|
500
|
+
* No normal texture, and none missing: normals and tangents come out of the
|
|
501
|
+
* skin matrix, as in three's own skinning.
|
|
502
|
+
*/
|
|
503
|
+
interface RigVAT extends VATBase {
|
|
504
|
+
/** Which encoding a row holds — the discriminant of {@link VAT}. */
|
|
505
|
+
encoding: 'rig';
|
|
506
|
+
/**
|
|
507
|
+
* RGBA float **rig texture**: two texels per slot — `(qx, qy, qz, qw)` then
|
|
508
|
+
* `(tx, ty, tz, s)` — laid out as `x = slot × 2 + texel`, `y = frame`, clips
|
|
509
|
+
* stacked as bands exactly as in the position texture. Consecutive rows of
|
|
510
|
+
* one slot sit on one quaternion hemisphere; the decode still flips the
|
|
511
|
+
* second row onto the first's side, because a looping clip blends a band's
|
|
512
|
+
* last row into its first and those are not neighbours.
|
|
513
|
+
*/
|
|
514
|
+
rigTexture: DataTexture;
|
|
515
|
+
/** Slots in the rig — the texture is `slotCount × 2` texels wide. */
|
|
516
|
+
slotCount: number;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* A baked VAT, discriminated on `encoding`. A decode that samples a texture
|
|
520
|
+
* narrows on `encoding` first, so an encoding it does not decode is refused by
|
|
521
|
+
* name rather than sampled as a texture the VAT does not have (ADR-0018).
|
|
522
|
+
*/
|
|
523
|
+
type VAT = DeltaVAT | RigVAT;
|
|
430
524
|
/**
|
|
431
525
|
* The shared playback clock: one `{ value }` in seconds, read by every material
|
|
432
526
|
* of every VAT mesh driven by it. Set it once per frame. Deliberately the
|
|
@@ -469,4 +563,4 @@ interface VATCrowd {
|
|
|
469
563
|
*/
|
|
470
564
|
type VATCarrier = InstancedMesh | BatchedMesh;
|
|
471
565
|
|
|
472
|
-
export { EndMode as E, INFINITE_REPETITIONS as I, LoopMode as L,
|
|
566
|
+
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 };
|
|
@@ -13,7 +13,7 @@ function needsBakedNormal(material) {
|
|
|
13
13
|
return NORMAL_READING_FLAGS.some((flag) => flags[flag] === true);
|
|
14
14
|
}
|
|
15
15
|
function assertBakedNormal(vat, material) {
|
|
16
|
-
if (vat.normalTexture !== null || !needsBakedNormal(material)) return;
|
|
16
|
+
if (vat.encoding !== "delta" || vat.normalTexture !== null || !needsBakedNormal(material)) return;
|
|
17
17
|
throw new Error(
|
|
18
18
|
`three-vat: material "${material.name || "(unnamed)"}" (${material.type}) shades from a normal, but this VAT was baked with \`bakeNormals: false\` and carries none \u2014 the crowd would be lit by its rest pose. Set \`flatShading: true\` on the material (three then derives the normal from the deformed position, per fragment, which is the right normal for a posed mesh), or use an unlit material such as MeshBasicMaterial, or bake with \`bakeNormals: true\`.`
|
|
19
19
|
);
|