three-vat 3.0.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.
- package/README.md +20 -16
- package/dist/{carrier-BxRuW47G.d.ts → carrier-D4pkftSk.d.ts} +50 -16
- package/dist/{chunk-J5IEUGSB.js → chunk-BSAJL5PG.js} +37 -14
- package/dist/index.d.ts +191 -30
- package/dist/index.js +848 -222
- package/dist/tsl.d.ts +7 -1
- package/dist/tsl.js +26 -8
- package/dist/webgl.d.ts +7 -1
- package/dist/webgl.js +53 -17
- package/package.json +1 -1
package/dist/tsl.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Node } from 'three/webgpu';
|
|
2
|
-
import { e as VATClock, k as VATPlaybackTexture, b as VATCarrier, V as VAT, h as VATInstance, f as VATCrowd } from './carrier-
|
|
2
|
+
import { e as VATClock, k as VATPlaybackTexture, b as VATCarrier, V as VAT, h as VATInstance, f as VATCrowd } from './carrier-D4pkftSk.js';
|
|
3
3
|
import 'three';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -225,6 +225,12 @@ interface CreateVATMeshOptions {
|
|
|
225
225
|
* call make its own is identical on either path.
|
|
226
226
|
*/
|
|
227
227
|
time?: VATTimeUniform;
|
|
228
|
+
/**
|
|
229
|
+
* The GPU's real texture ceiling — {@link getMaxTextureSize} — which the
|
|
230
|
+
* playback texture checks the crowd against, one row per instance. Defaults
|
|
231
|
+
* to `MAX_TEXTURE_SIZE`, a desktop figure (`VATPlaybackTextureOptions`).
|
|
232
|
+
*/
|
|
233
|
+
maxTextureSize?: number;
|
|
228
234
|
}
|
|
229
235
|
/**
|
|
230
236
|
* Turn a baked VAT and a list of instances into a crowd ready to render: an
|
package/dist/tsl.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isBatchedCarrier, assertVATCarrier, assertBakedNormal } from './chunk-I4STYOD5.js';
|
|
2
|
-
import { INFINITE_REPETITIONS, LoopMode, EndMode, createVATPlaybackTexture, PACK_TEXELS, RIG_TEXELS, RIG_TEXELS_PER_SLOT } from './chunk-
|
|
2
|
+
import { INFINITE_REPETITIONS, LoopMode, EndMode, createVATPlaybackTexture, PACK_TEXELS, RIG_TEXELS, RIG_TEXELS_PER_SLOT } from './chunk-BSAJL5PG.js';
|
|
3
3
|
import { InstancedMesh } from 'three';
|
|
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 } from 'three/tsl';
|
|
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
|
|
|
6
6
|
function getMaxTextureSize(renderer) {
|
|
7
7
|
const backend = renderer.backend;
|
|
@@ -103,20 +103,23 @@ function resolveBand(clip, playback, time) {
|
|
|
103
103
|
const started = local.greaterThanEqual(0);
|
|
104
104
|
const finished = started.and(playback.repetitions.notEqual(INFINITE_REPETITIONS)).and(loops.greaterThanEqual(playback.repetitions));
|
|
105
105
|
const isPingPong = playback.loopMode.equal(LoopMode.PingPong);
|
|
106
|
-
const bounce = loops.
|
|
106
|
+
const bounce = loops.sub(loops.mul(0.5).floor().mul(2));
|
|
107
107
|
const pingPongPhase = bounce.lessThan(1).select(bounce, float(2).sub(bounce));
|
|
108
108
|
const endPhase = playback.endMode.equal(EndMode.Clamp).select(float(1), float(0));
|
|
109
109
|
const phase = started.select(
|
|
110
110
|
finished.select(endPhase, isPingPong.select(pingPongPhase, loops.fract())),
|
|
111
111
|
float(0)
|
|
112
112
|
);
|
|
113
|
-
const
|
|
113
|
+
const looping = started.select(
|
|
114
114
|
finished.select(bool(false), isPingPong.select(bool(false), bool(true))),
|
|
115
115
|
bool(false)
|
|
116
116
|
);
|
|
117
|
-
const
|
|
117
|
+
const holds = looping.and(playback.endMode.equal(EndMode.Clamp)).and(playback.repetitions.notEqual(INFINITE_REPETITIONS)).and(loops.floor().add(1).greaterThanEqual(playback.repetitions));
|
|
118
|
+
const wraps = looping.and(holds.not());
|
|
119
|
+
const f = phase.mul(looping.select(frames, last));
|
|
118
120
|
const f0 = f.floor().min(last);
|
|
119
|
-
const
|
|
121
|
+
const next = f0.add(1);
|
|
122
|
+
const f1 = wraps.select(next.greaterThanEqual(frames).select(float(0), next), next.min(last));
|
|
120
123
|
const bandRow = (offset) => int(offset).add(clip.startFrame);
|
|
121
124
|
return {
|
|
122
125
|
row0: bandRow(f0),
|
|
@@ -164,6 +167,15 @@ function vatDecode(vat, options = {}) {
|
|
|
164
167
|
}
|
|
165
168
|
}
|
|
166
169
|
}
|
|
170
|
+
function octDecode(stored) {
|
|
171
|
+
const e = stored.mul(2).sub(1);
|
|
172
|
+
const ex = e.x;
|
|
173
|
+
const ey = e.y;
|
|
174
|
+
const z = float(1).sub(ex.abs()).sub(ey.abs());
|
|
175
|
+
const overshoot = z.negate().max(0);
|
|
176
|
+
const back = (component) => component.sub(component.greaterThanEqual(0).select(overshoot, overshoot.negate()));
|
|
177
|
+
return vec3(back(ex), back(ey), z).normalize();
|
|
178
|
+
}
|
|
167
179
|
function vertexDecode({ positionTexture, normalTexture }, rows) {
|
|
168
180
|
const vertexRow = int(vertexIndex);
|
|
169
181
|
const band = (tex, of) => {
|
|
@@ -172,10 +184,16 @@ function vertexDecode({ positionTexture, normalTexture }, rows) {
|
|
|
172
184
|
return mix(s0, s1, of.blend);
|
|
173
185
|
};
|
|
174
186
|
const sample = (tex) => mix(band(tex, rows.live), band(tex, rows.outgoing), rows.weight);
|
|
187
|
+
const bandNormal = (tex, of) => {
|
|
188
|
+
const s0 = octDecode(textureLoad(tex, ivec2(vertexRow, of.row0)).xy);
|
|
189
|
+
const s1 = octDecode(textureLoad(tex, ivec2(vertexRow, of.row1)).xy);
|
|
190
|
+
return mix(s0, s1, of.blend);
|
|
191
|
+
};
|
|
192
|
+
const sampleNormal = (tex) => mix(bandNormal(tex, rows.live), bandNormal(tex, rows.outgoing), rows.weight);
|
|
175
193
|
return {
|
|
176
194
|
encoding: "delta",
|
|
177
195
|
position: sample(positionTexture),
|
|
178
|
-
normal: normalTexture ?
|
|
196
|
+
normal: normalTexture ? sampleNormal(normalTexture).normalize() : null
|
|
179
197
|
};
|
|
180
198
|
}
|
|
181
199
|
function compose(q, ts) {
|
|
@@ -247,7 +265,7 @@ function rigDecode({ rigTexture, geometry }, rows) {
|
|
|
247
265
|
}
|
|
248
266
|
function createVATMesh(vat, instances, options = {}) {
|
|
249
267
|
const time = options.time ?? uniform(0);
|
|
250
|
-
const playback = createVATPlaybackTexture(instances);
|
|
268
|
+
const playback = createVATPlaybackTexture(instances, { maxTextureSize: options.maxTextureSize });
|
|
251
269
|
for (const source of vat.materials) assertBakedNormal(vat, source);
|
|
252
270
|
const materials = vat.materials.map((source) => source.clone());
|
|
253
271
|
const mesh = new InstancedMesh(vat.geometry, materials, instances.length);
|
package/dist/webgl.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IUniform, MeshDepthMaterial, WebGLRenderer, Material } from 'three';
|
|
2
|
-
import { b as VATCarrier, V as VAT, k as VATPlaybackTexture, h as VATInstance, f as VATCrowd } from './carrier-
|
|
2
|
+
import { b as VATCarrier, V as VAT, k as VATPlaybackTexture, h as VATInstance, f as VATCrowd } from './carrier-D4pkftSk.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The real maximum texture dimension this GPU accepts, for
|
|
@@ -137,6 +137,12 @@ interface CreateVATMeshOptions {
|
|
|
137
137
|
* untwisted shadow.
|
|
138
138
|
*/
|
|
139
139
|
hook?: VATPostDecodeHook;
|
|
140
|
+
/**
|
|
141
|
+
* The GPU's real texture ceiling — {@link getMaxTextureSize} — which the
|
|
142
|
+
* playback texture checks the crowd against, one row per instance. Defaults
|
|
143
|
+
* to `MAX_TEXTURE_SIZE`, a desktop figure (`VATPlaybackTextureOptions`).
|
|
144
|
+
*/
|
|
145
|
+
maxTextureSize?: number;
|
|
140
146
|
}
|
|
141
147
|
/**
|
|
142
148
|
* Turn a baked VAT and a list of instances into a crowd ready to render: an
|
package/dist/webgl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertBakedNormal, assertVATCarrier, isBatchedCarrier } from './chunk-I4STYOD5.js';
|
|
2
|
-
import { PACK_TEXELS, EndMode, LoopMode, RIG_TEXELS_PER_SLOT, RIG_TEXELS, INFINITE_REPETITIONS, createVATPlaybackTexture } from './chunk-
|
|
2
|
+
import { PACK_TEXELS, EndMode, LoopMode, RIG_TEXELS_PER_SLOT, RIG_TEXELS, INFINITE_REPETITIONS, createVATPlaybackTexture } from './chunk-BSAJL5PG.js';
|
|
3
3
|
import { MeshDepthMaterial, RGBADepthPacking, InstancedMesh, MeshDistanceMaterial, Material } from 'three';
|
|
4
4
|
|
|
5
5
|
function getMaxTextureSize(renderer) {
|
|
@@ -62,26 +62,35 @@ var ROW_PRELUDE = (
|
|
|
62
62
|
bool finished = started && repetitions != ${glslFloat(INFINITE_REPETITIONS)} && loops >= repetitions;
|
|
63
63
|
|
|
64
64
|
float phase;
|
|
65
|
-
bool
|
|
65
|
+
bool looping;
|
|
66
66
|
if ( !started ) {
|
|
67
67
|
phase = 0.0;
|
|
68
|
-
|
|
68
|
+
looping = false;
|
|
69
69
|
} else 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;
|
|
73
73
|
} else if ( vatPlayback.y == ${glslFloat(LoopMode.PingPong)} ) {
|
|
74
|
-
|
|
74
|
+
// Halved rather than divided, so the remainder cannot land below zero.
|
|
75
|
+
float m = loops - 2.0 * floor( loops * 0.5 );
|
|
75
76
|
phase = m < 1.0 ? m : 2.0 - m;
|
|
76
|
-
|
|
77
|
+
looping = false; // a ping-pong bounces; it does not wrap
|
|
77
78
|
} else {
|
|
78
79
|
phase = fract( loops );
|
|
79
|
-
|
|
80
|
+
looping = true; // and here the interpolation crossing back is correct
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
|
|
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
|
+
bool holds = looping && vatPlayback.w == ${glslFloat(EndMode.Clamp)} && repetitions != ${glslFloat(INFINITE_REPETITIONS)} && floor( loops ) + 1.0 >= repetitions;
|
|
86
|
+
bool wraps = looping && !holds;
|
|
87
|
+
|
|
88
|
+
float f = phase * ( looping ? frames : last );
|
|
83
89
|
float f0 = min( floor( f ), last );
|
|
84
|
-
|
|
90
|
+
// A compare, not a mod: a mod divides, and at the last row a quotient a hair
|
|
91
|
+
// under 1 leaves f1 at frames, one row past the band (#79).
|
|
92
|
+
float next = f0 + 1.0;
|
|
93
|
+
float f1 = wraps ? ( next >= frames ? 0.0 : next ) : min( next, last );
|
|
85
94
|
|
|
86
95
|
VatBand band;
|
|
87
96
|
band.row0 = int( vatClip.x + f0 );
|
|
@@ -112,10 +121,10 @@ var ROW_PRELUDE = (
|
|
|
112
121
|
// normal decode. Every vertex of an instance reads the same texels of the
|
|
113
122
|
// same row, whichever branch it takes, so the texture cache absorbs them;
|
|
114
123
|
// the demo bench under ADR-0016's 5% bound is what said so for three of
|
|
115
|
-
// them, and
|
|
124
|
+
// them, and #72's idle-crowd bench is what says so for all five.
|
|
116
125
|
//
|
|
117
|
-
// Three texels,
|
|
118
|
-
//
|
|
126
|
+
// Three texels here, the live pair and the crossfade; the other two are
|
|
127
|
+
// vatOutgoingBand's, fetched every frame by every sampler that calls it.
|
|
119
128
|
vec4 vatClip = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.clip}, vatInstance ), 0 );
|
|
120
129
|
vec4 vatPlayback = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.playback}, vatInstance ), 0 );
|
|
121
130
|
vec4 vatCrossfade = texelFetch( uVatPlaybackTex, ivec2( ${PACK_TEXELS.crossfade}, vatInstance ), 0 );
|
|
@@ -171,9 +180,9 @@ var VERTEX_PRELUDE = (
|
|
|
171
180
|
`
|
|
172
181
|
uniform highp sampler2D uVatPosTex;
|
|
173
182
|
|
|
174
|
-
// One band of
|
|
175
|
-
// same function for the live band and the outgoing one, as
|
|
176
|
-
// function for both pairs.
|
|
183
|
+
// One band of the position layer: the two rows this band sits between,
|
|
184
|
+
// mixed. The same function for the live band and the outgoing one, as
|
|
185
|
+
// vatBand is the same function for both pairs.
|
|
177
186
|
vec3 vatBandSample( const in sampler2D tex, const in VatBand band ) {
|
|
178
187
|
vec3 s0 = texelFetch( tex, ivec2( gl_VertexID, band.row0 ), 0 ).xyz;
|
|
179
188
|
vec3 s1 = texelFetch( tex, ivec2( gl_VertexID, band.row1 ), 0 ).xyz;
|
|
@@ -203,6 +212,33 @@ var NORMAL_PRELUDE = (
|
|
|
203
212
|
/* glsl */
|
|
204
213
|
`
|
|
205
214
|
uniform highp sampler2D uVatNrmTex;
|
|
215
|
+
|
|
216
|
+
vec2 vatOctSign( const in vec2 v ) {
|
|
217
|
+
return vec2( v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0 );
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
vec3 vatOctDecode( const in vec2 stored ) {
|
|
221
|
+
vec2 e = stored * 2.0 - 1.0;
|
|
222
|
+
float z = 1.0 - abs( e.x ) - abs( e.y );
|
|
223
|
+
vec2 xy = e - vatOctSign( e ) * max( -z, 0.0 );
|
|
224
|
+
return normalize( vec3( xy, z ) );
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// One band of the normal layer \u2014 vatBandSample, over decoded normals.
|
|
228
|
+
vec3 vatBandSampleNormal( const in VatBand band ) {
|
|
229
|
+
vec3 s0 = vatOctDecode( texelFetch( uVatNrmTex, ivec2( gl_VertexID, band.row0 ), 0 ).xy );
|
|
230
|
+
vec3 s1 = vatOctDecode( texelFetch( uVatNrmTex, ivec2( gl_VertexID, band.row1 ), 0 ).xy );
|
|
231
|
+
return mix( s0, s1, band.blend );
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// vatSample, for the one layer whose texel is not what it decodes to. The
|
|
235
|
+
// outgoing band is resolved and mixed unconditionally here too, for the
|
|
236
|
+
// reason spelled out on vatSample.
|
|
237
|
+
vec3 vatSampleNormal( const in int vatInstance ) {
|
|
238
|
+
VatRows rows = vatRows( vatInstance );
|
|
239
|
+
VatBand outgoing = vatOutgoingBand( vatInstance, rows.weight > 0.0 );
|
|
240
|
+
return mix( vatBandSampleNormal( rows.live ), vatBandSampleNormal( outgoing ), rows.weight );
|
|
241
|
+
}
|
|
206
242
|
`
|
|
207
243
|
);
|
|
208
244
|
var RIG_PRELUDE = (
|
|
@@ -320,7 +356,7 @@ var vertexPosition = (id) => (
|
|
|
320
356
|
var vertexNormal = (id) => (
|
|
321
357
|
/* glsl */
|
|
322
358
|
`
|
|
323
|
-
vec3 objectNormal = normalize(
|
|
359
|
+
vec3 objectNormal = normalize( vatSampleNormal( ${INSTANCE_ID[id]} ) );
|
|
324
360
|
#ifdef USE_TANGENT
|
|
325
361
|
vec3 objectTangent = vec3( tangent.xyz );
|
|
326
362
|
#endif
|
|
@@ -473,7 +509,7 @@ function createVATDepthMaterial(vat, uniforms, playback, options) {
|
|
|
473
509
|
}
|
|
474
510
|
function createVATMesh(vat, instances, options = {}) {
|
|
475
511
|
const uniforms = options.time ? { uVatTime: options.time } : createVATUniforms();
|
|
476
|
-
const playback = createVATPlaybackTexture(instances);
|
|
512
|
+
const playback = createVATPlaybackTexture(instances, { maxTextureSize: options.maxTextureSize });
|
|
477
513
|
const patch = { hook: options.hook };
|
|
478
514
|
const materials = vat.materials.map((source) => patchVATMaterial(source.clone(), vat, uniforms, playback, patch));
|
|
479
515
|
const mesh = new InstancedMesh(vat.geometry, materials, instances.length);
|