babylon.quarks 0.24.1 → 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/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
@@ -139,6 +145,7 @@ export class TrailBatch extends VFXBatch {
139
145
  if (this.settings.texture) {
140
146
  defines.push('USE_MAP');
141
147
  }
148
+ defines.push(...this.surfaceDefines());
142
149
 
143
150
  const shaderLanguage = shaderLanguageFor(this.scene.getEngine());
144
151
  registerShaders(
@@ -149,6 +156,9 @@ export class TrailBatch extends VFXBatch {
149
156
  );
150
157
 
151
158
  const attributes = ['position', 'previous', 'next', 'side', 'width', 'uv', 'color'];
159
+ if (this.uv1Buffer) {
160
+ attributes.push('uv2');
161
+ }
152
162
  const uniforms = [
153
163
  'world',
154
164
  'view',
@@ -164,6 +174,7 @@ export class TrailBatch extends VFXBatch {
164
174
  if (this.settings.texture) {
165
175
  samplers.push('map');
166
176
  }
177
+ this.surfaceUniforms(uniforms, samplers);
167
178
 
168
179
  const mat = new ShaderMaterial(
169
180
  shaderName,
@@ -200,6 +211,7 @@ export class TrailBatch extends VFXBatch {
200
211
  });
201
212
 
202
213
  this.bindFog(mat);
214
+ this.bindSurface(mat);
203
215
  mat.backFaceCulling = false;
204
216
 
205
217
  if (this.settings.materialTransparent) {
@@ -228,6 +240,30 @@ export class TrailBatch extends VFXBatch {
228
240
  * frame costs a matrix compose per system; checking the transform cache
229
241
  * first makes a still emitter free.
230
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
+
231
267
  private static ensureWorldMatrix(system: IParticleSystem): void {
232
268
  const emitter = system.emitter as {
233
269
  computeWorldMatrix?: (force: boolean) => void;
@@ -293,10 +329,15 @@ export class TrailBatch extends VFXBatch {
293
329
  // rather than by the particle's velocity, so it is left alone.
294
330
  const trailSettings = system.rendererEmitterSettings as TrailSettings;
295
331
  const uvFromHead = Boolean(trailSettings.uvFromHead);
332
+ const uv1 = this.uv1Buffer;
296
333
  const colorFromParticle = Boolean(trailSettings.colorFromParticle);
297
334
  // Recorded samples already sit in world space; the live tip still comes
298
335
  // from the particle's own (local) position.
299
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;
300
341
  const residual = trailSettings.followLocalOrigin ? 0 : (system.simulationResidual ?? 0);
301
342
  // Measured from the step the particle just took, not from its
302
343
  // velocity: a trail pulled along by an orbit or by a plugin's own
@@ -312,6 +353,8 @@ export class TrailBatch extends VFXBatch {
312
353
  const historyCapacity = particle.historyCapacity;
313
354
  const historyPositions = particle.historyPositions;
314
355
  const historySizes = particle.historySizes;
356
+ const historySides = particle.historySides;
357
+ const swept = sideAxis !== undefined && historySides.length === historyCapacity * 3;
315
358
  // Every sample takes its color from one place: its own recorded slot, or
316
359
  // the particle's current row in the store.
317
360
  const colors = colorFromParticle ? particle.store.color : particle.historyColors;
@@ -391,12 +434,27 @@ export class TrailBatch extends VFXBatch {
391
434
  const currentColorW = colors[currentColorIndex + 3];
392
435
 
393
436
  const pi = index * 3;
394
- this.positionBuffer[pi] = currentX;
395
- this.positionBuffer[pi + 1] = currentY;
396
- this.positionBuffer[pi + 2] = currentZ;
397
- this.positionBuffer[pi + 3] = currentX;
398
- this.positionBuffer[pi + 4] = currentY;
399
- this.positionBuffer[pi + 5] = currentZ;
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
+ }
400
458
 
401
459
  this.previousBuffer[pi] = previousX;
402
460
  this.previousBuffer[pi + 1] = previousY;
@@ -412,7 +470,10 @@ export class TrailBatch extends VFXBatch {
412
470
  this.nextBuffer[pi + 4] = nextY;
413
471
  this.nextBuffer[pi + 5] = nextZ;
414
472
 
415
- if (systemWorldSpace || particle.parentMatrix) {
473
+ if (swept) {
474
+ this.widthBuffer[index] = 0;
475
+ this.widthBuffer[index + 1] = 0;
476
+ } else if (systemWorldSpace || particle.parentMatrix) {
416
477
  this.widthBuffer[index] = currentSize;
417
478
  this.widthBuffer[index + 1] = currentSize;
418
479
  } else {
@@ -427,6 +488,13 @@ export class TrailBatch extends VFXBatch {
427
488
  this.uvBuffer[ui + 1] = (vTileCount - row - 1) * tileHeight;
428
489
  this.uvBuffer[ui + 2] = u;
429
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
+ }
430
498
 
431
499
  const cci = index * 4;
432
500
  this.colorBuffer[cci] = currentColorX;
@@ -467,9 +535,12 @@ export class TrailBatch extends VFXBatch {
467
535
  let tipY = position.y + (position.y - previousHead.y) * stepFraction;
468
536
  let tipZ = position.z + (position.z - previousHead.z) * stepFraction;
469
537
  const headIndex = index - 2;
470
- const headX = this.positionBuffer[headIndex * 3];
471
- const headY = this.positionBuffer[headIndex * 3 + 1];
472
- const headZ = this.positionBuffer[headIndex * 3 + 2];
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;
473
544
 
474
545
  if (!systemWorldSpace) {
475
546
  const w = 1 / (m03 * tipX + m13 * tipY + m23 * tipZ + m33);
@@ -481,12 +552,30 @@ export class TrailBatch extends VFXBatch {
481
552
  }
482
553
 
483
554
  const tipPi = index * 3;
484
- this.positionBuffer[tipPi] = tipX;
485
- this.positionBuffer[tipPi + 1] = tipY;
486
- this.positionBuffer[tipPi + 2] = tipZ;
487
- this.positionBuffer[tipPi + 3] = tipX;
488
- this.positionBuffer[tipPi + 4] = tipY;
489
- this.positionBuffer[tipPi + 5] = tipZ;
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
+ }
490
579
 
491
580
  this.previousBuffer[tipPi] = headX;
492
581
  this.previousBuffer[tipPi + 1] = headY;
@@ -513,8 +602,10 @@ export class TrailBatch extends VFXBatch {
513
602
  this.nextBuffer[headPi + 4] = tipY;
514
603
  this.nextBuffer[headPi + 5] = tipZ;
515
604
 
516
- const tipSize = particle.size.x;
517
- if (systemWorldSpace || particle.parentMatrix) {
605
+ if (swept) {
606
+ this.widthBuffer[index] = 0;
607
+ this.widthBuffer[index + 1] = 0;
608
+ } else if (systemWorldSpace || particle.parentMatrix) {
518
609
  this.widthBuffer[index] = tipSize;
519
610
  this.widthBuffer[index + 1] = tipSize;
520
611
  } else {
@@ -528,6 +619,13 @@ export class TrailBatch extends VFXBatch {
528
619
  this.uvBuffer[tipUi + 1] = (vTileCount - row - 1) * tileHeight;
529
620
  this.uvBuffer[tipUi + 2] = tipU;
530
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
+ }
531
629
 
532
630
  const tipColor = particle.color;
533
631
  const tipCi = index * 4;
@@ -552,6 +650,17 @@ export class TrailBatch extends VFXBatch {
552
650
  index += 2;
553
651
  }
554
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
+ }
555
664
  this.measureSystemBounds(system as ParticleSystem, firstVertex, index);
556
665
  }
557
666
 
@@ -575,8 +684,8 @@ export class TrailBatch extends VFXBatch {
575
684
  let maxX = -Infinity;
576
685
  let maxY = -Infinity;
577
686
  let maxZ = -Infinity;
578
- // Each sample writes its position twice (both ribbon edges); one of the pair is enough.
579
- for (let o = fromVertex * 3, end = toVertex * 3; o < end; o += 6) {
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) {
580
689
  const x = positions[o];
581
690
  const y = positions[o + 1];
582
691
  const z = positions[o + 2];
@@ -611,6 +720,9 @@ export class TrailBatch extends VFXBatch {
611
720
  // the high-water mark of every system in the batch.
612
721
  this.mesh.updateVerticesData(VertexBuffer.PositionKind, this.positionBuffer.subarray(0, index * 3));
613
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
+ }
614
726
  // Straight to the GPU buffer. Going through `mesh.updateIndices` with a
615
727
  // range whose length changes every frame makes Babylon copy the
616
728
  // indices, rebuild the sub mesh and recompute its bounds from the