three-vat 2.1.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 +3 -4
- package/dist/{carrier-BXaLAPRO.d.ts → carrier-BxRuW47G.d.ts} +134 -83
- package/dist/{chunk-3PWAY6MD.js → chunk-J5IEUGSB.js} +71 -56
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/tsl.d.ts +59 -2
- package/dist/tsl.js +109 -73
- package/dist/webgl.d.ts +89 -7
- package/dist/webgl.js +244 -92
- package/package.json +1 -1
package/dist/webgl.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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-
|
|
3
|
-
import { MeshDepthMaterial, RGBADepthPacking, InstancedMesh, MeshDistanceMaterial } from 'three';
|
|
2
|
+
import { PACK_TEXELS, EndMode, LoopMode, RIG_TEXELS_PER_SLOT, RIG_TEXELS, INFINITE_REPETITIONS, createVATPlaybackTexture } from './chunk-J5IEUGSB.js';
|
|
3
|
+
import { MeshDepthMaterial, RGBADepthPacking, InstancedMesh, MeshDistanceMaterial, Material } from 'three';
|
|
4
4
|
|
|
5
5
|
function getMaxTextureSize(renderer) {
|
|
6
6
|
return renderer.capabilities.maxTextureSize;
|
|
@@ -15,40 +15,39 @@ var ROW_PRELUDE = (
|
|
|
15
15
|
uniform highp sampler2D uVatPlaybackTex;
|
|
16
16
|
uniform float uVatTime;
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
|
|
18
|
+
// One band resolved: the two rows an instance sits between and the blend
|
|
19
|
+
// toward the second, plus the two facts those rows cannot be read back out
|
|
20
|
+
// of \u2014 whether the sampling wrapped past the band's last row into its first,
|
|
21
|
+
// and whether the repetitions have run out.
|
|
22
|
+
struct VatBand {
|
|
22
23
|
int row0;
|
|
23
24
|
int row1;
|
|
24
25
|
float blend;
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
bool wraps;
|
|
27
|
+
bool finished;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// parameter: getIndirectIndex is declared by batching_pars_vertex, which
|
|
41
|
-
// three expands *after* this prelude, so naming it up here would not
|
|
42
|
-
// compile.
|
|
43
|
-
//
|
|
44
|
-
// Fetched inside the function, so each injection point stays
|
|
45
|
-
// self-contained (ADR-0006) \u2014 which costs a second set of fetches in the
|
|
46
|
-
// normal decode. Every vertex of an instance reads the same three texels,
|
|
47
|
-
// so the texture cache absorbs them; the 5% demo bench is what says so.
|
|
48
|
-
vec4 vatClip = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.clip}, vatInstance ), 0 );
|
|
49
|
-
vec4 vatPlayback = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.playback}, vatInstance ), 0 );
|
|
50
|
-
vec4 vatFade = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.fade}, vatInstance ), 0 );
|
|
30
|
+
// Where an instance is reading: the band it is playing, and how much of the
|
|
31
|
+
// band it is leaving still shows \u2014 a weight of zero being "not
|
|
32
|
+
// transitioning".
|
|
33
|
+
//
|
|
34
|
+
// The outgoing band is *not* a field here. Each sampler resolves it for
|
|
35
|
+
// itself, because what it costs to resolve is not the same on the two
|
|
36
|
+
// encodings and #72 measured the difference: see vatOutgoingBand below.
|
|
37
|
+
struct VatRows {
|
|
38
|
+
VatBand live;
|
|
39
|
+
float weight;
|
|
40
|
+
};
|
|
51
41
|
|
|
42
|
+
// resolveVATFrame for one (clip texel, playback texel) pair, branch for
|
|
43
|
+
// branch: not started, finished, ping-pong, repeat \u2014 the resolver's own
|
|
44
|
+
// order, each case falling out into the shared phase-to-row arithmetic below
|
|
45
|
+
// rather than returning early, so all of them land on the same two rows.
|
|
46
|
+
//
|
|
47
|
+
// A function of the pair rather than of the instance, because the pair is
|
|
48
|
+
// what there are two of: a crossfading instance resolves its outgoing band
|
|
49
|
+
// by calling this a second time, not by transcribing it a second time.
|
|
50
|
+
VatBand vatBand( const in vec4 vatClip, const in vec4 vatPlayback ) {
|
|
52
51
|
float frames = vatClip.y;
|
|
53
52
|
float last = frames - 1.0;
|
|
54
53
|
float duration = frames / vatClip.z;
|
|
@@ -84,44 +83,119 @@ var ROW_PRELUDE = (
|
|
|
84
83
|
float f0 = min( floor( f ), last );
|
|
85
84
|
float f1 = wraps ? mod( f0 + 1.0, frames ) : min( f0 + 1.0, last );
|
|
86
85
|
|
|
86
|
+
VatBand band;
|
|
87
|
+
band.row0 = int( vatClip.x + f0 );
|
|
88
|
+
band.row1 = int( vatClip.x + f1 );
|
|
89
|
+
band.blend = f - f0;
|
|
90
|
+
band.wraps = wraps;
|
|
91
|
+
band.finished = finished;
|
|
92
|
+
return band;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
VatRows vatRows( const in int vatInstance ) {
|
|
96
|
+
// The pack, fetched by this instance's *logical* index rather than read
|
|
97
|
+
// off an attribute indexed by the drawn slot (ADR-0016). Three texels of
|
|
98
|
+
// one row, in the order src/instance-playback.ts lays them out; the
|
|
99
|
+
// arithmetic above is untouched by where they came from, because the pack
|
|
100
|
+
// was already three vec4s.
|
|
101
|
+
//
|
|
102
|
+
// The index arrives as a parameter rather than being read here, because
|
|
103
|
+
// where it comes from is the carrier's business and not the decode's:
|
|
104
|
+
// gl_InstanceID on an InstancedMesh, getIndirectIndex( gl_DrawID ) on a
|
|
105
|
+
// BatchedMesh \u2014 see INSTANCE_ID in src/webgl.ts. It also has to be a
|
|
106
|
+
// parameter: getIndirectIndex is declared by batching_pars_vertex, which
|
|
107
|
+
// three expands *after* this prelude, so naming it up here would not
|
|
108
|
+
// compile.
|
|
109
|
+
//
|
|
110
|
+
// Fetched inside the function, so each injection point stays
|
|
111
|
+
// self-contained (ADR-0006) \u2014 which costs a second set of fetches in the
|
|
112
|
+
// normal decode. Every vertex of an instance reads the same texels of the
|
|
113
|
+
// same row, whichever branch it takes, so the texture cache absorbs them;
|
|
114
|
+
// the demo bench under ADR-0016's 5% bound is what said so for three of
|
|
115
|
+
// them, and ADR-0025's is what says so for a transitioning instance's five.
|
|
116
|
+
//
|
|
117
|
+
// Three texels, always \u2014 the crossfade one sits third exactly so that an
|
|
118
|
+
// instance that is not transitioning reads what it has always read.
|
|
119
|
+
vec4 vatClip = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.clip}, vatInstance ), 0 );
|
|
120
|
+
vec4 vatPlayback = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.playback}, vatInstance ), 0 );
|
|
121
|
+
vec4 vatCrossfade = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.crossfade}, vatInstance ), 0 );
|
|
122
|
+
|
|
87
123
|
VatRows rows;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
rows.
|
|
124
|
+
// The live band: the one clip this instance is playing, resolved from its
|
|
125
|
+
// own pair of texels.
|
|
126
|
+
rows.live = vatBand( vatClip, vatPlayback );
|
|
91
127
|
|
|
92
|
-
// The
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if ( vatFade.w > 0.0 ) {
|
|
100
|
-
float weight = 1.0 - clamp( ( uVatTime - vatPlayback.x ) / vatFade.w, 0.0, 1.0 );
|
|
101
|
-
float fromRow = max( min( floor( vatFade.z * vatFade.y ), vatFade.y - 1.0 ), 0.0 );
|
|
102
|
-
rows.fadeRow = int( vatFade.x + fromRow );
|
|
103
|
-
rows.fadeWeight = weight;
|
|
128
|
+
// The crossfade's weight, transcribed from the resolver: wall clock, not
|
|
129
|
+
// clip time \u2014 the incoming clip's speed does not stretch a transition \u2014 and
|
|
130
|
+
// a duration of zero is what a cut is, which is what the pack writes when
|
|
131
|
+
// there is no band to blend away.
|
|
132
|
+
rows.weight = 0.0;
|
|
133
|
+
if ( vatCrossfade.x > 0.0 ) {
|
|
134
|
+
rows.weight = 1.0 - clamp( ( uVatTime - vatPlayback.x ) / vatCrossfade.x, 0.0, 1.0 );
|
|
104
135
|
}
|
|
136
|
+
|
|
105
137
|
return rows;
|
|
106
138
|
}
|
|
139
|
+
|
|
140
|
+
// The band the instance is leaving: two more texels and a second call of the
|
|
141
|
+
// very same resolver, so the clip it is leaving keeps playing \u2014 keeping its
|
|
142
|
+
// own speed and its own end policy \u2014 rather than standing still (ADR-0025).
|
|
143
|
+
//
|
|
144
|
+
// While the weight is zero the pair selected *is* the live pair, so the two
|
|
145
|
+
// fetches land on texels this vertex has already read and the band resolves
|
|
146
|
+
// to the one it is playing. That is what lets a caller resolve it without a
|
|
147
|
+
// branch, and blending a pose into itself is what a weight of zero means.
|
|
148
|
+
//
|
|
149
|
+
// Selecting the live pair is also why this needs no counterpart to the TSL
|
|
150
|
+
// path's max-of-one on the outgoing frames and fps (src/tsl.ts): that path
|
|
151
|
+
// resolves a band from the zeroes "not transitioning" is written as, which is
|
|
152
|
+
// a 0/0 duration and a NaN row. The pair selected here is always real.
|
|
153
|
+
//
|
|
154
|
+
// Whether the instance is transitioning is the parameter, not the weight, so
|
|
155
|
+
// the caller is the one that says so \u2014 and the two encodings say it from
|
|
156
|
+
// different places (#72): the vertex sampler calls this unconditionally,
|
|
157
|
+
// because guarding two texel fetches cost an idle crowd 10%; the rig sampler
|
|
158
|
+
// calls it unconditionally too but guards what it *does* with the band, where
|
|
159
|
+
// what a guard skips is sixteen dependent fetches per vertex.
|
|
160
|
+
VatBand vatOutgoingBand( const in int vatInstance, const in bool transitioning ) {
|
|
161
|
+
int clipX = transitioning ? ${PACK_TEXELS.outgoingClip} : ${PACK_TEXELS.clip};
|
|
162
|
+
int playbackX = transitioning ? ${PACK_TEXELS.outgoingPlayback} : ${PACK_TEXELS.playback};
|
|
163
|
+
vec4 vatOutClip = texelFetch( uVatPlaybackTex, ivec2( clipX, vatInstance ), 0 );
|
|
164
|
+
vec4 vatOutPlayback = texelFetch( uVatPlaybackTex, ivec2( playbackX, vatInstance ), 0 );
|
|
165
|
+
return vatBand( vatOutClip, vatOutPlayback );
|
|
166
|
+
}
|
|
107
167
|
`
|
|
108
168
|
);
|
|
109
169
|
var VERTEX_PRELUDE = (
|
|
110
170
|
/* glsl */
|
|
111
171
|
`
|
|
112
172
|
uniform highp sampler2D uVatPosTex;
|
|
173
|
+
|
|
174
|
+
// One band of one layer: the two rows this band sits between, mixed. The
|
|
175
|
+
// same function for the live band and the outgoing one, as vatBand is the same
|
|
176
|
+
// function for both pairs.
|
|
177
|
+
vec3 vatBandSample( const in sampler2D tex, const in VatBand band ) {
|
|
178
|
+
vec3 s0 = texelFetch( tex, ivec2( gl_VertexID, band.row0 ), 0 ).xyz;
|
|
179
|
+
vec3 s1 = texelFetch( tex, ivec2( gl_VertexID, band.row1 ), 0 ).xyz;
|
|
180
|
+
return mix( s0, s1, band.blend );
|
|
181
|
+
}
|
|
182
|
+
|
|
113
183
|
vec3 vatSample( const in sampler2D tex, const in int vatInstance ) {
|
|
114
184
|
VatRows rows = vatRows( vatInstance );
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// The
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
185
|
+
// The outgoing band \u2014 still playing, two rows of its own \u2014 mixed in by the
|
|
186
|
+
// weight the rows resolved, and mixed in *unconditionally*: at a weight of
|
|
187
|
+
// zero the band resolved is the live one, so this blends a pose into
|
|
188
|
+
// itself. The normal layer is renormalised by the caller after the mix, as
|
|
189
|
+
// it is for a single band.
|
|
190
|
+
//
|
|
191
|
+
// No branch, and that is measured rather than reasoned (#72): here the
|
|
192
|
+
// outgoing band is two more fetches of one layer, and guarding them cost an
|
|
193
|
+
// idle crowd 10% where paying them costs nothing measurable \u2014 0.283 ms
|
|
194
|
+
// against the 0.282 ms it cost before the crossfade existed. A branch is
|
|
195
|
+
// not free because it is not taken: the compiler still holds registers for
|
|
196
|
+
// the side it skips, and that is what an idle crowd was paying for.
|
|
197
|
+
VatBand outgoing = vatOutgoingBand( vatInstance, rows.weight > 0.0 );
|
|
198
|
+
return mix( vatBandSample( tex, rows.live ), vatBandSample( tex, outgoing ), rows.weight );
|
|
125
199
|
}
|
|
126
200
|
`
|
|
127
201
|
);
|
|
@@ -155,34 +229,58 @@ var RIG_PRELUDE = (
|
|
|
155
229
|
);
|
|
156
230
|
}
|
|
157
231
|
|
|
158
|
-
// One slot
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
vec4
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
232
|
+
// One slot of the posed rig, at one band: a rotation and a placement, each
|
|
233
|
+
// between the two rows that band sits between. The same function for the live
|
|
234
|
+
// band and the outgoing one, as vatBand is the same function for both pairs.
|
|
235
|
+
struct VatPose {
|
|
236
|
+
vec4 q;
|
|
237
|
+
vec4 ts;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
VatPose vatSlotPose( const in int rotation, const in int placement, const in VatBand band ) {
|
|
241
|
+
vec4 q0 = texelFetch( uVatRigTex, ivec2( rotation, band.row0 ), 0 );
|
|
242
|
+
vec4 ts0 = texelFetch( uVatRigTex, ivec2( placement, band.row0 ), 0 );
|
|
243
|
+
vec4 q1 = texelFetch( uVatRigTex, ivec2( rotation, band.row1 ), 0 );
|
|
244
|
+
vec4 ts1 = texelFetch( uVatRigTex, ivec2( placement, band.row1 ), 0 );
|
|
167
245
|
// The bake keeps consecutive rows on one hemisphere, but a looping clip
|
|
168
246
|
// blends its band's last row into its first, and a bone that turned a full
|
|
169
247
|
// circle over the clip arrives there on the far side: one dot product per
|
|
170
248
|
// slot, or the blend passes through zero on the wrap frame.
|
|
171
249
|
if ( dot( q0, q1 ) < 0.0 ) q1 = -q1;
|
|
250
|
+
VatPose pose;
|
|
172
251
|
// A normalised lerp, not a slerp: at a bake's frame step the angular error
|
|
173
252
|
// against a true slerp is far below anything visible. It is still a
|
|
174
253
|
// *rotation* at every blend, which is what a componentwise matrix lerp is
|
|
175
254
|
// not \u2014 that one shortens a limb as it turns (ADR-0018).
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
255
|
+
pose.q = normalize( mix( q0, q1, band.blend ) );
|
|
256
|
+
pose.ts = mix( ts0, ts1, band.blend );
|
|
257
|
+
return pose;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// One slot's matrix: its pose in the band the instance is playing and, while
|
|
261
|
+
// it is transitioning, its pose in the band it is leaving \u2014 blended per slot
|
|
262
|
+
// before the matrix is composed, so the crowd skins from one rig rather than
|
|
263
|
+
// from the average of two matrices.
|
|
264
|
+
//
|
|
265
|
+
// The guard stays here, where the vertex sampler dropped its own. What it
|
|
266
|
+
// skips is four dependent fetches of the rig texture per slot, sixteen per
|
|
267
|
+
// vertex, against the two of one layer the vertex encoding skips \u2014 and #72
|
|
268
|
+
// measured it worth keeping: this encoding did not get slower when the
|
|
269
|
+
// crossfade landed, and the vertex encoding did.
|
|
270
|
+
mat4 vatSlot( const in int slot, const in VatRows rows, const in VatBand outgoing ) {
|
|
271
|
+
int rotation = slot * ${RIG_TEXELS_PER_SLOT} + ${RIG_TEXELS.rotation};
|
|
272
|
+
int placement = slot * ${RIG_TEXELS_PER_SLOT} + ${RIG_TEXELS.placement};
|
|
273
|
+
VatPose pose = vatSlotPose( rotation, placement, rows.live );
|
|
274
|
+
vec4 q = pose.q;
|
|
275
|
+
vec4 ts = pose.ts;
|
|
276
|
+
if ( rows.weight > 0.0 ) {
|
|
277
|
+
VatPose leaving = vatSlotPose( rotation, placement, outgoing );
|
|
278
|
+
vec4 qo = leaving.q;
|
|
279
|
+
// The outgoing band is any row of the bake, not this row's neighbour, so
|
|
182
280
|
// the same check.
|
|
183
|
-
if ( dot( q,
|
|
184
|
-
q = normalize( mix( q,
|
|
185
|
-
ts = mix( ts,
|
|
281
|
+
if ( dot( q, qo ) < 0.0 ) qo = -qo;
|
|
282
|
+
q = normalize( mix( q, qo, rows.weight ) );
|
|
283
|
+
ts = mix( ts, leaving.ts, rows.weight );
|
|
186
284
|
}
|
|
187
285
|
return vatCompose( q, ts );
|
|
188
286
|
}
|
|
@@ -190,13 +288,20 @@ var RIG_PRELUDE = (
|
|
|
190
288
|
// Linear blend skinning: the weighted sum of slot matrices, which is the
|
|
191
289
|
// blend the bake did on the CPU for the bounds and three's own
|
|
192
290
|
// skinning_vertex does on the GPU. A zero weight skips its four fetches.
|
|
291
|
+
//
|
|
292
|
+
// The band being left is resolved once for the vertex rather than once per
|
|
293
|
+
// slot, and unconditionally \u2014 two pack texels this encoding's old guard
|
|
294
|
+
// skipped, against the sixteen rig fetches per vertex the guard inside
|
|
295
|
+
// vatSlot still skips. At a weight of zero those two land on texels already
|
|
296
|
+
// read, and every slot below skips the pose.
|
|
193
297
|
mat4 vatSkinMatrix( const in int vatInstance ) {
|
|
194
298
|
VatRows rows = vatRows( vatInstance );
|
|
299
|
+
VatBand outgoing = vatOutgoingBand( vatInstance, rows.weight > 0.0 );
|
|
195
300
|
mat4 skin = mat4( 0.0 );
|
|
196
301
|
for ( int i = 0; i < 4; i ++ ) {
|
|
197
302
|
float w = skinWeight[ i ];
|
|
198
303
|
if ( w == 0.0 ) continue;
|
|
199
|
-
skin += w * vatSlot( int( skinIndex[ i ] ), rows );
|
|
304
|
+
skin += w * vatSlot( int( skinIndex[ i ] ), rows, outgoing );
|
|
200
305
|
}
|
|
201
306
|
return skin;
|
|
202
307
|
}
|
|
@@ -245,13 +350,16 @@ function vertexDecode({ positionTexture, normalTexture }, id) {
|
|
|
245
350
|
uniforms.uVatPosTex = { value: positionTexture };
|
|
246
351
|
if (normalTexture) uniforms.uVatNrmTex = { value: normalTexture };
|
|
247
352
|
},
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
353
|
+
position: vertexPosition(id),
|
|
354
|
+
// No normal texture, no normal decode, and no uniform bound for one: the
|
|
355
|
+
// material either does not read a normal or derives it from the deformed
|
|
356
|
+
// position itself (`flatShading`), so three's own `beginnormal_vertex` is
|
|
357
|
+
// left exactly where it is.
|
|
358
|
+
normal: normalTexture ? vertexNormal(id) : null,
|
|
252
359
|
// A normal-less VAT injects a different vertex shader off the same material
|
|
253
360
|
// parameters, so the variant is in the key (see `patchVATMaterial`).
|
|
254
|
-
key: `three-vat:${id}${normalTexture ? "" : ":no-normal"}
|
|
361
|
+
key: `three-vat:${id}${normalTexture ? "" : ":no-normal"}`,
|
|
362
|
+
instanceIndex: INSTANCE_ID[id]
|
|
255
363
|
};
|
|
256
364
|
}
|
|
257
365
|
function rigDecode({ rigTexture }, id) {
|
|
@@ -260,12 +368,44 @@ function rigDecode({ rigTexture }, id) {
|
|
|
260
368
|
bind(uniforms) {
|
|
261
369
|
uniforms.uVatRigTex = { value: rigTexture };
|
|
262
370
|
},
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
371
|
+
position: rigPosition(id),
|
|
372
|
+
normal: rigNormal(id),
|
|
373
|
+
key: `three-vat:rig:${id}`,
|
|
374
|
+
instanceIndex: INSTANCE_ID[id]
|
|
267
375
|
};
|
|
268
376
|
}
|
|
377
|
+
function patchOptionsOf(fifth) {
|
|
378
|
+
if (!fifth) return {};
|
|
379
|
+
return fifth.isObject3D ? { carrier: fifth } : fifth;
|
|
380
|
+
}
|
|
381
|
+
function assertHook(hook) {
|
|
382
|
+
if (!hook.key.trim()) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
"three-vat: a post-decode hook needs a non-empty `key`. It is folded into the library\u2019s own program cache key, and two hooks that fold in nothing share a compiled program \u2014 so one crowd renders the other\u2019s GLSL."
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
if (!hook.position && !hook.normal) {
|
|
388
|
+
throw new Error(
|
|
389
|
+
"three-vat: a post-decode hook with neither `position` nor `normal` injects nothing. Give it the chunk you meant \u2014 `position` deforms the posed vertex, `normal` repairs the normal that was taken before it, and a deformation wants both."
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
var hookChunk = (chunk, instanceIndex) => chunk ? (
|
|
394
|
+
/* glsl */
|
|
395
|
+
`
|
|
396
|
+
{
|
|
397
|
+
int vatInstanceIndex = ${instanceIndex};
|
|
398
|
+
${chunk}
|
|
399
|
+
}
|
|
400
|
+
`
|
|
401
|
+
) : "";
|
|
402
|
+
function injectVAT(vertexShader, decode, hook) {
|
|
403
|
+
const position = decode.position + hookChunk(hook?.position, decode.instanceIndex);
|
|
404
|
+
const normal = (decode.normal ?? "#include <beginnormal_vertex>") + hookChunk(hook?.normal, decode.instanceIndex);
|
|
405
|
+
return vertexShader.replace("#include <begin_vertex>", () => position).replace("#include <beginnormal_vertex>", () => normal);
|
|
406
|
+
}
|
|
407
|
+
var VAT_PATCHES = /* @__PURE__ */ new WeakSet();
|
|
408
|
+
var ours = (onBeforeCompile) => onBeforeCompile === Material.prototype.onBeforeCompile || VAT_PATCHES.has(onBeforeCompile);
|
|
269
409
|
function decodeFor(vat, id) {
|
|
270
410
|
switch (vat.encoding) {
|
|
271
411
|
case "delta":
|
|
@@ -280,18 +420,29 @@ function decodeFor(vat, id) {
|
|
|
280
420
|
}
|
|
281
421
|
}
|
|
282
422
|
}
|
|
283
|
-
function patchVATMaterial(material, vat, uniforms, playback,
|
|
423
|
+
function patchVATMaterial(material, vat, uniforms, playback, options) {
|
|
424
|
+
const { carrier, hook } = patchOptionsOf(options);
|
|
284
425
|
assertBakedNormal(vat, material);
|
|
285
426
|
if (carrier) assertVATCarrier(carrier, vat);
|
|
427
|
+
if (hook) assertHook(hook);
|
|
286
428
|
const id = isBatchedCarrier(carrier) ? "batch" : "instance";
|
|
287
429
|
const decode = decodeFor(vat, id);
|
|
288
|
-
material.onBeforeCompile
|
|
430
|
+
const previous = ours(material.onBeforeCompile) ? null : material.onBeforeCompile.bind(material);
|
|
431
|
+
const patch = (shader, renderer) => {
|
|
432
|
+
previous?.(shader, renderer);
|
|
433
|
+
if (hook?.uniforms) Object.assign(shader.uniforms, hook.uniforms);
|
|
289
434
|
shader.uniforms.uVatPlaybackTex = { value: playback.texture };
|
|
290
435
|
shader.uniforms.uVatTime = uniforms.uVatTime;
|
|
291
436
|
decode.bind(shader.uniforms);
|
|
292
|
-
shader.vertexShader = ROW_PRELUDE + decode.prelude +
|
|
437
|
+
shader.vertexShader = ROW_PRELUDE + decode.prelude + // Ahead of three's shader, where a declaration is safe from a dead block.
|
|
438
|
+
(hook?.prelude ? `
|
|
439
|
+
${hook.prelude}
|
|
440
|
+
` : "") + injectVAT(shader.vertexShader, decode, hook);
|
|
293
441
|
};
|
|
294
|
-
material.
|
|
442
|
+
material.onBeforeCompile = patch;
|
|
443
|
+
VAT_PATCHES.add(patch);
|
|
444
|
+
const key = hook ? `${decode.key}+${hook.key}` : decode.key;
|
|
445
|
+
material.customProgramCacheKey = () => key;
|
|
295
446
|
guardCarrierMismatch(material, id);
|
|
296
447
|
return material;
|
|
297
448
|
}
|
|
@@ -315,18 +466,19 @@ var CARRIER_NAME = {
|
|
|
315
466
|
instance: "an InstancedMesh",
|
|
316
467
|
batch: "a BatchedMesh"
|
|
317
468
|
};
|
|
318
|
-
function createVATDepthMaterial(vat, uniforms, playback,
|
|
469
|
+
function createVATDepthMaterial(vat, uniforms, playback, options) {
|
|
319
470
|
const depth = new MeshDepthMaterial({ depthPacking: RGBADepthPacking });
|
|
320
|
-
patchVATMaterial(depth, vat, uniforms, playback,
|
|
471
|
+
patchVATMaterial(depth, vat, uniforms, playback, options);
|
|
321
472
|
return depth;
|
|
322
473
|
}
|
|
323
474
|
function createVATMesh(vat, instances, options = {}) {
|
|
324
475
|
const uniforms = options.time ? { uVatTime: options.time } : createVATUniforms();
|
|
325
476
|
const playback = createVATPlaybackTexture(instances);
|
|
326
|
-
const
|
|
477
|
+
const patch = { hook: options.hook };
|
|
478
|
+
const materials = vat.materials.map((source) => patchVATMaterial(source.clone(), vat, uniforms, playback, patch));
|
|
327
479
|
const mesh = new InstancedMesh(vat.geometry, materials, instances.length);
|
|
328
|
-
mesh.customDepthMaterial = createVATDepthMaterial(vat, uniforms, playback);
|
|
329
|
-
mesh.customDistanceMaterial = patchVATMaterial(new MeshDistanceMaterial(), vat, uniforms, playback);
|
|
480
|
+
mesh.customDepthMaterial = createVATDepthMaterial(vat, uniforms, playback, patch);
|
|
481
|
+
mesh.customDistanceMaterial = patchVATMaterial(new MeshDistanceMaterial(), vat, uniforms, playback, patch);
|
|
330
482
|
return { mesh, time: uniforms.uVatTime, playback };
|
|
331
483
|
}
|
|
332
484
|
|