babylon.quarks 0.25.0 → 0.26.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/CHANGELOG.md +85 -0
- package/dist/babylon.quarks.cjs +1 -1
- package/dist/babylon.quarks.cjs.map +1 -1
- package/dist/babylon.quarks.esm.js +1 -1
- package/dist/babylon.quarks.esm.js.map +1 -1
- package/dist/babylon.quarks.umd.min.js +1 -1
- package/dist/babylon.quarks.umd.min.js.map +1 -1
- package/dist/types/index.d.ts +279 -53
- package/package.json +1 -1
- package/src/BatchedRenderer.ts +23 -17
- package/src/ParticleSystem.ts +240 -38
- package/src/QuarksLoader.ts +42 -8
- package/src/SpriteBatch.ts +52 -1
- package/src/TrailBatch.ts +129 -20
- package/src/VFXBatch.ts +325 -61
- package/src/index.ts +19 -2
- package/src/shaders/chunks/surface.ts +122 -42
- package/src/shaders/local_particle_physics_vert.glsl.ts +15 -0
- package/src/shaders/local_particle_physics_vert.wgsl.ts +15 -0
- package/src/shaders/particle_frag.glsl.ts +3 -0
- package/src/shaders/particle_frag.wgsl.ts +3 -0
- package/src/shaders/particle_physics_frag.glsl.ts +10 -4
- package/src/shaders/particle_physics_frag.wgsl.ts +10 -4
- package/src/shaders/particle_vert.glsl.ts +7 -0
- package/src/shaders/particle_vert.wgsl.ts +7 -0
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +17 -9
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +17 -9
- package/src/shaders/trail_frag.glsl.ts +3 -0
- package/src/shaders/trail_frag.wgsl.ts +3 -0
- package/src/shaders/trail_vert.glsl.ts +8 -0
- package/src/shaders/trail_vert.wgsl.ts +8 -0
package/src/SpriteBatch.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
SpriteParticle,
|
|
16
16
|
StretchedBillBoardSettings,
|
|
17
17
|
Vector3,
|
|
18
|
+
Vector4,
|
|
18
19
|
} from 'quarks.core';
|
|
19
20
|
import {VFXBatchSettings} from './BatchedRenderer';
|
|
20
21
|
import {gather, sortByKey, SortScratch} from './instanceSort';
|
|
@@ -91,6 +92,9 @@ export class SpriteBatch extends VFXBatch {
|
|
|
91
92
|
} else {
|
|
92
93
|
vertexData.uvs = new Float32Array(vertexCount * 2);
|
|
93
94
|
}
|
|
95
|
+
if (this.meshUV2()) {
|
|
96
|
+
vertexData.uvs2 = this.settings.instancingUVs2!;
|
|
97
|
+
}
|
|
94
98
|
if (this.settings.instancingNormals) {
|
|
95
99
|
vertexData.normals = this.settings.instancingNormals;
|
|
96
100
|
}
|
|
@@ -99,6 +103,33 @@ export class SpriteBatch extends VFXBatch {
|
|
|
99
103
|
this.buildExpandableBuffers();
|
|
100
104
|
}
|
|
101
105
|
|
|
106
|
+
/**
|
|
107
|
+
* True when a mesh batch reads UV channel 1 and has a second UV set to
|
|
108
|
+
* read it from, two values per vertex. A set of any other length is left
|
|
109
|
+
* out with a warning, and channel 1 then reads the first set.
|
|
110
|
+
*/
|
|
111
|
+
private warnedUV2 = false;
|
|
112
|
+
|
|
113
|
+
private meshUV2(): boolean {
|
|
114
|
+
const uvs2 = this.settings.instancingUVs2;
|
|
115
|
+
if (this.settings.renderMode !== RenderMode.Mesh || !uvs2 || !this.usesUV1()) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const expected = (this.settings.instancingGeometry.length / 3) * 2;
|
|
119
|
+
if (uvs2.length !== expected) {
|
|
120
|
+
if (this.warnedUV2) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
this.warnedUV2 = true;
|
|
124
|
+
console.warn(
|
|
125
|
+
`[babylon.quarks] instancingUVs2 has ${uvs2.length} values for ${expected / 2} vertices ` +
|
|
126
|
+
'(two per vertex expected); UV channel 1 reads the first UV set instead.'
|
|
127
|
+
);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
102
133
|
/** Releases the instance vertex buffers so a resize does not leak GPU memory. */
|
|
103
134
|
private disposeExpandableBuffers(): void {
|
|
104
135
|
this.offsetVB?.dispose();
|
|
@@ -343,7 +374,9 @@ export class SpriteBatch extends VFXBatch {
|
|
|
343
374
|
const rim = isMesh ? this.settings.rim : undefined;
|
|
344
375
|
if (rim) {
|
|
345
376
|
defines.push('MESH_RIM');
|
|
346
|
-
if (rim.mode === '
|
|
377
|
+
if (rim.mode === 'fade') {
|
|
378
|
+
defines.push('MESH_RIM_FADE');
|
|
379
|
+
} else if (rim.mode === 'mix') {
|
|
347
380
|
defines.push('MESH_RIM_MIX');
|
|
348
381
|
}
|
|
349
382
|
}
|
|
@@ -357,6 +390,10 @@ export class SpriteBatch extends VFXBatch {
|
|
|
357
390
|
if (this.settings.renderMode === RenderMode.Mesh) {
|
|
358
391
|
attributes.push('normal');
|
|
359
392
|
}
|
|
393
|
+
if (this.usesUV1() && this.meshUV2()) {
|
|
394
|
+
defines.push('MESH_UV2');
|
|
395
|
+
attributes.push('uv2');
|
|
396
|
+
}
|
|
360
397
|
if (SpriteBatch.drawsAlongVelocity(this.settings.renderMode)) {
|
|
361
398
|
attributes.push('velocity');
|
|
362
399
|
}
|
|
@@ -913,6 +950,10 @@ export class SpriteBatch extends VFXBatch {
|
|
|
913
950
|
}
|
|
914
951
|
}
|
|
915
952
|
|
|
953
|
+
const tint = (system as ParticleSystem).tint;
|
|
954
|
+
if (tint !== undefined && index > firstIndex) {
|
|
955
|
+
SpriteBatch.applyTint(this.colorBuffer, firstIndex * 4, index - firstIndex, tint);
|
|
956
|
+
}
|
|
916
957
|
if (flipsSprites && system.flip !== undefined) {
|
|
917
958
|
if (store !== undefined) {
|
|
918
959
|
SpriteBatch.applyFlip(this.sizeBuffer, firstIndex * 3, store.flip, particleNum);
|
|
@@ -1020,6 +1061,16 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1020
1061
|
);
|
|
1021
1062
|
}
|
|
1022
1063
|
|
|
1064
|
+
/** Multiplies `count` RGBA colours starting at `base` by a system's {@link ParticleSystem.tint}. */
|
|
1065
|
+
private static applyTint(colors: Float32Array, base: number, count: number, tint: Vector4): void {
|
|
1066
|
+
for (let j = 0, o = base; j < count; j++, o += 4) {
|
|
1067
|
+
colors[o] *= tint.x;
|
|
1068
|
+
colors[o + 1] *= tint.y;
|
|
1069
|
+
colors[o + 2] *= tint.z;
|
|
1070
|
+
colors[o + 3] *= tint.w;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1023
1074
|
/**
|
|
1024
1075
|
* Negates the size of each flipped particle along the axes it flips on,
|
|
1025
1076
|
* starting at `base` in `sizes`. The billboard shader scales the quad by
|
package/src/TrailBatch.ts
CHANGED
|
@@ -19,6 +19,8 @@ export class TrailBatch extends VFXBatch {
|
|
|
19
19
|
private previousBuffer!: Float32Array;
|
|
20
20
|
private nextBuffer!: Float32Array;
|
|
21
21
|
private uvBuffer!: Float32Array;
|
|
22
|
+
/** UV channel 1: along the trail and across it, without the sheet's cell; only when a texture reads it. */
|
|
23
|
+
private uv1Buffer: Float32Array | null = null;
|
|
22
24
|
private sideBuffer!: Float32Array;
|
|
23
25
|
private widthBuffer!: Float32Array;
|
|
24
26
|
private colorBuffer!: Float32Array;
|
|
@@ -52,6 +54,7 @@ export class TrailBatch extends VFXBatch {
|
|
|
52
54
|
this.previousBuffer = new Float32Array(this.maxParticles * 6);
|
|
53
55
|
this.nextBuffer = new Float32Array(this.maxParticles * 6);
|
|
54
56
|
this.uvBuffer = new Float32Array(this.maxParticles * 4);
|
|
57
|
+
this.uv1Buffer = this.usesUV1() ? new Float32Array(this.maxParticles * 4) : null;
|
|
55
58
|
this.sideBuffer = new Float32Array(this.maxParticles * 2);
|
|
56
59
|
this.widthBuffer = new Float32Array(this.maxParticles * 2);
|
|
57
60
|
this.colorBuffer = new Float32Array(this.maxParticles * 8);
|
|
@@ -86,6 +89,9 @@ export class TrailBatch extends VFXBatch {
|
|
|
86
89
|
// Set standard vertex data with full buffer capacity (updatable)
|
|
87
90
|
this.mesh.setVerticesData(VertexBuffer.PositionKind, this.positionBuffer, true);
|
|
88
91
|
this.mesh.setVerticesData(VertexBuffer.UVKind, this.uvBuffer, true);
|
|
92
|
+
if (this.uv1Buffer) {
|
|
93
|
+
this.mesh.setVerticesData(VertexBuffer.UV2Kind, this.uv1Buffer, true);
|
|
94
|
+
}
|
|
89
95
|
this.mesh.setVerticesData(VertexBuffer.ColorKind, this.colorBuffer, true, 4);
|
|
90
96
|
// The whole array, not just the dummy triangle: this call is what sizes
|
|
91
97
|
// the GPU index buffer, and updateIndices later writes into it without
|
|
@@ -150,6 +156,9 @@ export class TrailBatch extends VFXBatch {
|
|
|
150
156
|
);
|
|
151
157
|
|
|
152
158
|
const attributes = ['position', 'previous', 'next', 'side', 'width', 'uv', 'color'];
|
|
159
|
+
if (this.uv1Buffer) {
|
|
160
|
+
attributes.push('uv2');
|
|
161
|
+
}
|
|
153
162
|
const uniforms = [
|
|
154
163
|
'world',
|
|
155
164
|
'view',
|
|
@@ -231,6 +240,30 @@ export class TrailBatch extends VFXBatch {
|
|
|
231
240
|
* frame costs a matrix compose per system; checking the transform cache
|
|
232
241
|
* first makes a still emitter free.
|
|
233
242
|
*/
|
|
243
|
+
/**
|
|
244
|
+
* Writes a swept ribbon's vertex pair at `pi`: the sample at (x, y, z)
|
|
245
|
+
* pushed `half` widths along its side (sx, sy, sz) one way for the first
|
|
246
|
+
* vertex and the other way for the second.
|
|
247
|
+
*/
|
|
248
|
+
private writeSweptPair(
|
|
249
|
+
pi: number,
|
|
250
|
+
x: number,
|
|
251
|
+
y: number,
|
|
252
|
+
z: number,
|
|
253
|
+
sx: number,
|
|
254
|
+
sy: number,
|
|
255
|
+
sz: number,
|
|
256
|
+
half: number
|
|
257
|
+
): void {
|
|
258
|
+
const positions = this.positionBuffer;
|
|
259
|
+
positions[pi] = x + sx * half;
|
|
260
|
+
positions[pi + 1] = y + sy * half;
|
|
261
|
+
positions[pi + 2] = z + sz * half;
|
|
262
|
+
positions[pi + 3] = x - sx * half;
|
|
263
|
+
positions[pi + 4] = y - sy * half;
|
|
264
|
+
positions[pi + 5] = z - sz * half;
|
|
265
|
+
}
|
|
266
|
+
|
|
234
267
|
private static ensureWorldMatrix(system: IParticleSystem): void {
|
|
235
268
|
const emitter = system.emitter as {
|
|
236
269
|
computeWorldMatrix?: (force: boolean) => void;
|
|
@@ -296,10 +329,15 @@ export class TrailBatch extends VFXBatch {
|
|
|
296
329
|
// rather than by the particle's velocity, so it is left alone.
|
|
297
330
|
const trailSettings = system.rendererEmitterSettings as TrailSettings;
|
|
298
331
|
const uvFromHead = Boolean(trailSettings.uvFromHead);
|
|
332
|
+
const uv1 = this.uv1Buffer;
|
|
299
333
|
const colorFromParticle = Boolean(trailSettings.colorFromParticle);
|
|
300
334
|
// Recorded samples already sit in world space; the live tip still comes
|
|
301
335
|
// from the particle's own (local) position.
|
|
302
336
|
const samplesInWorld = systemWorldSpace || Boolean(trailSettings.recordWorldSpace);
|
|
337
|
+
// A swept trail spans each sample's recorded side instead of facing
|
|
338
|
+
// the camera: its vertices are laid out here, and a zero width
|
|
339
|
+
// keeps the shader from pushing them.
|
|
340
|
+
const sideAxis = trailSettings.sideAxis;
|
|
303
341
|
const residual = trailSettings.followLocalOrigin ? 0 : (system.simulationResidual ?? 0);
|
|
304
342
|
// Measured from the step the particle just took, not from its
|
|
305
343
|
// velocity: a trail pulled along by an orbit or by a plugin's own
|
|
@@ -315,6 +353,8 @@ export class TrailBatch extends VFXBatch {
|
|
|
315
353
|
const historyCapacity = particle.historyCapacity;
|
|
316
354
|
const historyPositions = particle.historyPositions;
|
|
317
355
|
const historySizes = particle.historySizes;
|
|
356
|
+
const historySides = particle.historySides;
|
|
357
|
+
const swept = sideAxis !== undefined && historySides.length === historyCapacity * 3;
|
|
318
358
|
// Every sample takes its color from one place: its own recorded slot, or
|
|
319
359
|
// the particle's current row in the store.
|
|
320
360
|
const colors = colorFromParticle ? particle.store.color : particle.historyColors;
|
|
@@ -394,12 +434,27 @@ export class TrailBatch extends VFXBatch {
|
|
|
394
434
|
const currentColorW = colors[currentColorIndex + 3];
|
|
395
435
|
|
|
396
436
|
const pi = index * 3;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
437
|
+
if (swept) {
|
|
438
|
+
const si = currentSlot * 3;
|
|
439
|
+
let sx = historySides[si];
|
|
440
|
+
let sy = historySides[si + 1];
|
|
441
|
+
let sz = historySides[si + 2];
|
|
442
|
+
if (!samplesInWorld) {
|
|
443
|
+
const tx = m00 * sx + m10 * sy + m20 * sz;
|
|
444
|
+
const ty = m01 * sx + m11 * sy + m21 * sz;
|
|
445
|
+
sz = m02 * sx + m12 * sy + m22 * sz;
|
|
446
|
+
sx = tx;
|
|
447
|
+
sy = ty;
|
|
448
|
+
}
|
|
449
|
+
this.writeSweptPair(pi, currentX, currentY, currentZ, sx, sy, sz, currentSize * 0.5);
|
|
450
|
+
} else {
|
|
451
|
+
this.positionBuffer[pi] = currentX;
|
|
452
|
+
this.positionBuffer[pi + 1] = currentY;
|
|
453
|
+
this.positionBuffer[pi + 2] = currentZ;
|
|
454
|
+
this.positionBuffer[pi + 3] = currentX;
|
|
455
|
+
this.positionBuffer[pi + 4] = currentY;
|
|
456
|
+
this.positionBuffer[pi + 5] = currentZ;
|
|
457
|
+
}
|
|
403
458
|
|
|
404
459
|
this.previousBuffer[pi] = previousX;
|
|
405
460
|
this.previousBuffer[pi + 1] = previousY;
|
|
@@ -415,7 +470,10 @@ export class TrailBatch extends VFXBatch {
|
|
|
415
470
|
this.nextBuffer[pi + 4] = nextY;
|
|
416
471
|
this.nextBuffer[pi + 5] = nextZ;
|
|
417
472
|
|
|
418
|
-
if (
|
|
473
|
+
if (swept) {
|
|
474
|
+
this.widthBuffer[index] = 0;
|
|
475
|
+
this.widthBuffer[index + 1] = 0;
|
|
476
|
+
} else if (systemWorldSpace || particle.parentMatrix) {
|
|
419
477
|
this.widthBuffer[index] = currentSize;
|
|
420
478
|
this.widthBuffer[index + 1] = currentSize;
|
|
421
479
|
} else {
|
|
@@ -430,6 +488,13 @@ export class TrailBatch extends VFXBatch {
|
|
|
430
488
|
this.uvBuffer[ui + 1] = (vTileCount - row - 1) * tileHeight;
|
|
431
489
|
this.uvBuffer[ui + 2] = u;
|
|
432
490
|
this.uvBuffer[ui + 3] = (vTileCount - row) * tileHeight;
|
|
491
|
+
if (uv1) {
|
|
492
|
+
const u1 = uvFromHead ? 1 - along : along;
|
|
493
|
+
uv1[ui] = u1;
|
|
494
|
+
uv1[ui + 1] = 0;
|
|
495
|
+
uv1[ui + 2] = u1;
|
|
496
|
+
uv1[ui + 3] = 1;
|
|
497
|
+
}
|
|
433
498
|
|
|
434
499
|
const cci = index * 4;
|
|
435
500
|
this.colorBuffer[cci] = currentColorX;
|
|
@@ -470,9 +535,12 @@ export class TrailBatch extends VFXBatch {
|
|
|
470
535
|
let tipY = position.y + (position.y - previousHead.y) * stepFraction;
|
|
471
536
|
let tipZ = position.z + (position.z - previousHead.z) * stepFraction;
|
|
472
537
|
const headIndex = index - 2;
|
|
473
|
-
|
|
474
|
-
const
|
|
475
|
-
const
|
|
538
|
+
// The pair's midpoint: a swept pair lies to either side of its sample.
|
|
539
|
+
const headX = (this.positionBuffer[headIndex * 3] + this.positionBuffer[headIndex * 3 + 3]) * 0.5;
|
|
540
|
+
const headY =
|
|
541
|
+
(this.positionBuffer[headIndex * 3 + 1] + this.positionBuffer[headIndex * 3 + 4]) * 0.5;
|
|
542
|
+
const headZ =
|
|
543
|
+
(this.positionBuffer[headIndex * 3 + 2] + this.positionBuffer[headIndex * 3 + 5]) * 0.5;
|
|
476
544
|
|
|
477
545
|
if (!systemWorldSpace) {
|
|
478
546
|
const w = 1 / (m03 * tipX + m13 * tipY + m23 * tipZ + m33);
|
|
@@ -484,12 +552,30 @@ export class TrailBatch extends VFXBatch {
|
|
|
484
552
|
}
|
|
485
553
|
|
|
486
554
|
const tipPi = index * 3;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
555
|
+
const tipSize = particle.size.x;
|
|
556
|
+
if (swept) {
|
|
557
|
+
// The side as the emitter holds it now.
|
|
558
|
+
const ax = sideAxis!.x;
|
|
559
|
+
const ay = sideAxis!.y;
|
|
560
|
+
const az = sideAxis!.z;
|
|
561
|
+
this.writeSweptPair(
|
|
562
|
+
tipPi,
|
|
563
|
+
tipX,
|
|
564
|
+
tipY,
|
|
565
|
+
tipZ,
|
|
566
|
+
m00 * ax + m10 * ay + m20 * az,
|
|
567
|
+
m01 * ax + m11 * ay + m21 * az,
|
|
568
|
+
m02 * ax + m12 * ay + m22 * az,
|
|
569
|
+
tipSize * 0.5
|
|
570
|
+
);
|
|
571
|
+
} else {
|
|
572
|
+
this.positionBuffer[tipPi] = tipX;
|
|
573
|
+
this.positionBuffer[tipPi + 1] = tipY;
|
|
574
|
+
this.positionBuffer[tipPi + 2] = tipZ;
|
|
575
|
+
this.positionBuffer[tipPi + 3] = tipX;
|
|
576
|
+
this.positionBuffer[tipPi + 4] = tipY;
|
|
577
|
+
this.positionBuffer[tipPi + 5] = tipZ;
|
|
578
|
+
}
|
|
493
579
|
|
|
494
580
|
this.previousBuffer[tipPi] = headX;
|
|
495
581
|
this.previousBuffer[tipPi + 1] = headY;
|
|
@@ -516,8 +602,10 @@ export class TrailBatch extends VFXBatch {
|
|
|
516
602
|
this.nextBuffer[headPi + 4] = tipY;
|
|
517
603
|
this.nextBuffer[headPi + 5] = tipZ;
|
|
518
604
|
|
|
519
|
-
|
|
520
|
-
|
|
605
|
+
if (swept) {
|
|
606
|
+
this.widthBuffer[index] = 0;
|
|
607
|
+
this.widthBuffer[index + 1] = 0;
|
|
608
|
+
} else if (systemWorldSpace || particle.parentMatrix) {
|
|
521
609
|
this.widthBuffer[index] = tipSize;
|
|
522
610
|
this.widthBuffer[index + 1] = tipSize;
|
|
523
611
|
} else {
|
|
@@ -531,6 +619,13 @@ export class TrailBatch extends VFXBatch {
|
|
|
531
619
|
this.uvBuffer[tipUi + 1] = (vTileCount - row - 1) * tileHeight;
|
|
532
620
|
this.uvBuffer[tipUi + 2] = tipU;
|
|
533
621
|
this.uvBuffer[tipUi + 3] = (vTileCount - row) * tileHeight;
|
|
622
|
+
if (uv1) {
|
|
623
|
+
const tipU1 = uvFromHead ? 0 : 1;
|
|
624
|
+
uv1[tipUi] = tipU1;
|
|
625
|
+
uv1[tipUi + 1] = 0;
|
|
626
|
+
uv1[tipUi + 2] = tipU1;
|
|
627
|
+
uv1[tipUi + 3] = 1;
|
|
628
|
+
}
|
|
534
629
|
|
|
535
630
|
const tipColor = particle.color;
|
|
536
631
|
const tipCi = index * 4;
|
|
@@ -555,6 +650,17 @@ export class TrailBatch extends VFXBatch {
|
|
|
555
650
|
index += 2;
|
|
556
651
|
}
|
|
557
652
|
}
|
|
653
|
+
const tint = (system as ParticleSystem).tint;
|
|
654
|
+
if (tint !== undefined) {
|
|
655
|
+
// As SpriteBatch does: the system's tint over its ribbons' vertices.
|
|
656
|
+
const colors = this.colorBuffer;
|
|
657
|
+
for (let o = firstVertex * 4, end = index * 4; o < end; o += 4) {
|
|
658
|
+
colors[o] *= tint.x;
|
|
659
|
+
colors[o + 1] *= tint.y;
|
|
660
|
+
colors[o + 2] *= tint.z;
|
|
661
|
+
colors[o + 3] *= tint.w;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
558
664
|
this.measureSystemBounds(system as ParticleSystem, firstVertex, index);
|
|
559
665
|
}
|
|
560
666
|
|
|
@@ -578,8 +684,8 @@ export class TrailBatch extends VFXBatch {
|
|
|
578
684
|
let maxX = -Infinity;
|
|
579
685
|
let maxY = -Infinity;
|
|
580
686
|
let maxZ = -Infinity;
|
|
581
|
-
//
|
|
582
|
-
for (let o = fromVertex * 3, end = toVertex * 3; o < end; o +=
|
|
687
|
+
// Both of each pair: a swept ribbon's edges lie apart, a facing one's coincide.
|
|
688
|
+
for (let o = fromVertex * 3, end = toVertex * 3; o < end; o += 3) {
|
|
583
689
|
const x = positions[o];
|
|
584
690
|
const y = positions[o + 1];
|
|
585
691
|
const z = positions[o + 2];
|
|
@@ -614,6 +720,9 @@ export class TrailBatch extends VFXBatch {
|
|
|
614
720
|
// the high-water mark of every system in the batch.
|
|
615
721
|
this.mesh.updateVerticesData(VertexBuffer.PositionKind, this.positionBuffer.subarray(0, index * 3));
|
|
616
722
|
this.mesh.updateVerticesData(VertexBuffer.UVKind, this.uvBuffer.subarray(0, index * 2));
|
|
723
|
+
if (this.uv1Buffer) {
|
|
724
|
+
this.mesh.updateVerticesData(VertexBuffer.UV2Kind, this.uv1Buffer.subarray(0, index * 2));
|
|
725
|
+
}
|
|
617
726
|
// Straight to the GPU buffer. Going through `mesh.updateIndices` with a
|
|
618
727
|
// range whose length changes every frame makes Babylon copy the
|
|
619
728
|
// indices, rebuild the sub mesh and recompute its bounds from the
|