babylon.quarks 0.26.0 → 0.27.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 +769 -8
- package/README.md +10 -8
- 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.cts +6683 -0
- package/dist/types/index.d.ts +1959 -588
- package/package.json +12 -6
- package/src/BatchedRenderer.ts +204 -101
- package/src/EffectPool.ts +4 -1
- package/src/MeshSurfaceEmitter.ts +29 -4
- package/src/ParticleEmitter.ts +45 -15
- package/src/ParticleSystem.ts +909 -2061
- package/src/QuarksLoader.ts +421 -67
- package/src/QuarksPrefab.ts +91 -4
- package/src/QuarksSerializer.ts +484 -0
- package/src/QuarksUtil.ts +15 -0
- package/src/SpriteBatch.ts +106 -246
- package/src/TrailBatch.ts +533 -342
- package/src/VFXBatch.ts +236 -136
- package/src/batchKey.ts +237 -0
- package/src/effectResources.ts +79 -0
- package/src/envAtlas.ts +119 -17
- package/src/index.ts +32 -33
- package/src/nodeId.ts +13 -0
- package/src/shaderProgram.ts +325 -0
- package/src/shaderVariants.ts +225 -0
- package/src/shaders/chunks/fog.ts +4 -2
- package/src/shaders/chunks/sheet.ts +111 -0
- package/src/shaders/chunks/soft.ts +84 -0
- package/src/shaders/chunks/surface.ts +10 -0
- package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
- package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
- package/src/shaders/particle_frag.glsl.ts +3 -22
- package/src/shaders/particle_frag.wgsl.ts +3 -23
- package/src/shaders/particle_physics_frag.glsl.ts +12 -25
- package/src/shaders/particle_physics_frag.wgsl.ts +12 -26
- package/src/shaders/particle_vert.glsl.ts +5 -27
- package/src/shaders/particle_vert.wgsl.ts +5 -26
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
- package/src/shaders/trail_frag.glsl.ts +11 -1
- package/src/shaders/trail_frag.wgsl.ts +11 -0
- package/src/shaders/trail_vert.glsl.ts +45 -36
- package/src/shaders/trail_vert.wgsl.ts +46 -40
- package/src/softDepth.ts +136 -0
- package/src/system/BehaviorPlan.ts +85 -0
- package/src/system/BoundsTracker.ts +181 -0
- package/src/system/EmissionController.ts +360 -0
- package/src/system/EventDispatcher.ts +44 -0
- package/src/system/ParticleSpawner.ts +274 -0
- package/src/system/SimulationClock.ts +130 -0
- package/src/system/geometry.ts +111 -0
- package/src/system/integrate.ts +428 -0
- package/src/system/materialResolve.ts +116 -0
- package/src/system/normalize.ts +95 -0
- package/src/system/serialization.ts +401 -0
- package/src/system/settingsSchema.ts +317 -0
- package/src/system/types.ts +410 -0
- package/src/textureSource.ts +30 -0
- package/src/trailSettings.ts +51 -0
- package/src/BatchedParticleRenderer.ts +0 -3
- package/src/materials/ParticleMaterials.ts +0 -23
- package/src/materials/index.ts +0 -1
- package/src/shaders/chunks/index.ts +0 -1
- package/src/shaders/chunks/register-shader-chunks.ts +0 -32
- package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
- package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
- package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
- package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
- package/src/shaders/index.ts +0 -9
- package/src/shaders/local_particle_vert.glsl.ts +0 -50
package/src/SpriteBatch.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {VertexBuffer} from '@babylonjs/core/Buffers/buffer';
|
|
2
2
|
import {Constants} from '@babylonjs/core/Engines/constants';
|
|
3
|
+
import type {Effect} from '@babylonjs/core/Materials/effect';
|
|
3
4
|
import {ShaderMaterial} from '@babylonjs/core/Materials/shaderMaterial';
|
|
4
5
|
import {RawTexture} from '@babylonjs/core/Materials/Textures/rawTexture';
|
|
5
|
-
import {Vector2 as BVector2, Vector3 as BVector3
|
|
6
|
+
import {Vector2 as BVector2, Vector3 as BVector3} from '@babylonjs/core/Maths/math.vector';
|
|
6
7
|
import {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
7
8
|
import {VertexData} from '@babylonjs/core/Meshes/mesh.vertexData';
|
|
8
9
|
import {Scene} from '@babylonjs/core/scene';
|
|
9
10
|
import {
|
|
10
11
|
IParticle,
|
|
11
|
-
IParticleSystem,
|
|
12
12
|
Matrix3,
|
|
13
13
|
ParticleStore,
|
|
14
14
|
Quaternion,
|
|
@@ -18,21 +18,15 @@ import {
|
|
|
18
18
|
Vector4,
|
|
19
19
|
} from 'quarks.core';
|
|
20
20
|
import {VFXBatchSettings} from './BatchedRenderer';
|
|
21
|
+
import {hasSizeLimits, takesPivotAndSizeLimits, tilesSheet} from './batchKey';
|
|
21
22
|
import {gather, sortByKey, SortScratch} from './instanceSort';
|
|
22
23
|
import type {ParticleSystem} from './ParticleSystem';
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import particle_frag_wgsl from './shaders/particle_frag.wgsl';
|
|
27
|
-
import particle_physics_frag from './shaders/particle_physics_frag.glsl';
|
|
28
|
-
import particle_physics_frag_wgsl from './shaders/particle_physics_frag.wgsl';
|
|
29
|
-
import particle_vert from './shaders/particle_vert.glsl';
|
|
30
|
-
import particle_vert_wgsl from './shaders/particle_vert.wgsl';
|
|
31
|
-
import {registerShaders, shaderLanguageFor, ShaderSources} from './shaders/shaderLanguageSupport';
|
|
32
|
-
import stretched_bb_particle_vert from './shaders/stretched_bb_particle_vert.glsl';
|
|
33
|
-
import stretched_bb_particle_vert_wgsl from './shaders/stretched_bb_particle_vert.wgsl';
|
|
24
|
+
import {drawsAlongVelocity, shaderProgram} from './shaderProgram';
|
|
25
|
+
import {registerShaders, shaderLanguageFor} from './shaders/shaderLanguageSupport';
|
|
26
|
+
import type {DepthBinding} from './softDepth';
|
|
34
27
|
import {defaultMeshLighting, MeshLightingSettings, RenderMode, VFXBatch} from './VFXBatch';
|
|
35
28
|
|
|
29
|
+
/** The batch of billboard, stretched, mesh and beam particles. @internal */
|
|
36
30
|
export class SpriteBatch extends VFXBatch {
|
|
37
31
|
private static whiteTextureByScene = new WeakMap<Scene, RawTexture>();
|
|
38
32
|
|
|
@@ -53,8 +47,11 @@ export class SpriteBatch extends VFXBatch {
|
|
|
53
47
|
private uvTileBuffer!: Float32Array;
|
|
54
48
|
private velocityBuffer?: Float32Array;
|
|
55
49
|
|
|
56
|
-
/** Concretely typed handle on the batch material,
|
|
50
|
+
/** Concretely typed handle on the batch material, for the lighting and live-parameter writes. */
|
|
57
51
|
private shaderMaterial: ShaderMaterial | null = null;
|
|
52
|
+
/** Cancels the pending rebuild for an env atlas that has not loaded yet. */
|
|
53
|
+
private cancelAtlasRebuild: (() => void) | null = null;
|
|
54
|
+
private disposed = false;
|
|
58
55
|
/** The `'fixed'` mesh light, as shader uniforms; see applyMeshLighting. */
|
|
59
56
|
private readonly fixedLightDirection = new BVector3();
|
|
60
57
|
private readonly fixedLightColor = new BVector3();
|
|
@@ -67,14 +64,15 @@ export class SpriteBatch extends VFXBatch {
|
|
|
67
64
|
private uvTileVB!: VertexBuffer;
|
|
68
65
|
private velocityVB?: VertexBuffer;
|
|
69
66
|
|
|
70
|
-
constructor(settings: VFXBatchSettings, scene: Scene) {
|
|
71
|
-
super(settings, scene);
|
|
67
|
+
constructor(settings: VFXBatchSettings, scene: Scene, depth: DepthBinding | null = null) {
|
|
68
|
+
super(settings, scene, depth);
|
|
72
69
|
this.storeFixedLighting(defaultMeshLighting());
|
|
73
70
|
this.maxParticles = 1000;
|
|
74
71
|
this.setupBuffers();
|
|
75
72
|
this.rebuildMaterial();
|
|
76
73
|
}
|
|
77
74
|
|
|
75
|
+
/** @internal */
|
|
78
76
|
setupBuffers(): void {
|
|
79
77
|
this.mesh.dispose();
|
|
80
78
|
this.mesh = new Mesh('spriteBatch', this.scene);
|
|
@@ -170,7 +168,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
170
168
|
}
|
|
171
169
|
this.mesh.setVerticesBuffer(this.rotationVB);
|
|
172
170
|
|
|
173
|
-
if (
|
|
171
|
+
if (drawsAlongVelocity(this.settings.renderMode)) {
|
|
174
172
|
this.velocityBuffer = new Float32Array(this.maxParticles * 4);
|
|
175
173
|
this.velocityVB = new VertexBuffer(engine, this.velocityBuffer, 'velocity', true, false, 4, true);
|
|
176
174
|
this.mesh.setVerticesBuffer(this.velocityVB);
|
|
@@ -180,6 +178,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
180
178
|
this.mesh.doNotSyncBoundingInfo = true;
|
|
181
179
|
}
|
|
182
180
|
|
|
181
|
+
/** @internal */
|
|
183
182
|
expandBuffers(target: number): void {
|
|
184
183
|
while (target >= this.maxParticles) {
|
|
185
184
|
this.maxParticles *= 2;
|
|
@@ -193,10 +192,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
193
192
|
/** The screen-size limits as a uniform value, or null when the batch has none that apply. */
|
|
194
193
|
private sizeLimits(): BVector2 | null {
|
|
195
194
|
const {minParticleSize, maxParticleSize} = this.settings;
|
|
196
|
-
if (!
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
if (minParticleSize <= 0 && maxParticleSize === Infinity) {
|
|
195
|
+
if (!takesPivotAndSizeLimits(this.settings.renderMode) || !hasSizeLimits(this.settings)) {
|
|
200
196
|
return null;
|
|
201
197
|
}
|
|
202
198
|
// The shader clamps; an unlimited maximum is just a very large one.
|
|
@@ -295,153 +291,47 @@ export class SpriteBatch extends VFXBatch {
|
|
|
295
291
|
return spare;
|
|
296
292
|
}
|
|
297
293
|
|
|
294
|
+
/** @internal */
|
|
298
295
|
rebuildMaterial(): void {
|
|
299
|
-
|
|
300
|
-
// registered once per mode. A unique name per rebuild would leak entries in
|
|
301
|
-
// Effect.ShadersStore and defeat Babylon's compiled-effect cache.
|
|
302
|
-
const shaderName = `quarksParticle_${this.settings.renderMode}`;
|
|
303
|
-
this.lastStretchedSpeedFactor = Number.NaN;
|
|
304
|
-
const defines: string[] = this.fogDefines();
|
|
305
|
-
|
|
306
|
-
let vertexShader: ShaderSources;
|
|
307
|
-
let fragmentShader: ShaderSources;
|
|
308
|
-
if (this.settings.renderMode === RenderMode.Mesh) {
|
|
309
|
-
vertexShader = {glsl: local_particle_physics_vert, wgsl: local_particle_physics_vert_wgsl};
|
|
310
|
-
fragmentShader = {glsl: particle_physics_frag, wgsl: particle_physics_frag_wgsl};
|
|
311
|
-
} else if (SpriteBatch.drawsAlongVelocity(this.settings.renderMode)) {
|
|
312
|
-
vertexShader = {glsl: stretched_bb_particle_vert, wgsl: stretched_bb_particle_vert_wgsl};
|
|
313
|
-
fragmentShader = {glsl: particle_frag, wgsl: particle_frag_wgsl};
|
|
314
|
-
} else {
|
|
315
|
-
vertexShader = {glsl: particle_vert, wgsl: particle_vert_wgsl};
|
|
316
|
-
fragmentShader = {glsl: particle_frag, wgsl: particle_frag_wgsl};
|
|
317
|
-
}
|
|
318
|
-
|
|
296
|
+
const isMesh = this.settings.renderMode === RenderMode.Mesh;
|
|
319
297
|
// Mesh batches without a diffuse map still need a sampler2D on iOS WebKit —
|
|
320
298
|
// a zero-sampler particle mesh effect often never becomes drawable there.
|
|
321
|
-
const mapTexture =
|
|
322
|
-
this.settings.texture ??
|
|
323
|
-
(this.settings.renderMode === RenderMode.Mesh ? SpriteBatch.whiteTexture(this.scene) : null);
|
|
324
|
-
if (mapTexture) {
|
|
325
|
-
defines.push('USE_MAP');
|
|
326
|
-
}
|
|
299
|
+
const mapTexture = this.settings.texture ?? (isMesh ? SpriteBatch.whiteTexture(this.scene) : null);
|
|
327
300
|
const atlas = this.settings.reflectionAtlas;
|
|
328
|
-
const atlasPending =
|
|
301
|
+
const atlasPending = isMesh && !!atlas && !atlas.isReady();
|
|
302
|
+
this.cancelAtlasRebuild?.();
|
|
303
|
+
this.cancelAtlasRebuild = null;
|
|
329
304
|
if (atlasPending && atlas) {
|
|
330
|
-
const onLoad = (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
if (this.settings.softParticles) {
|
|
346
|
-
defines.push('SOFT_PARTICLES');
|
|
347
|
-
}
|
|
348
|
-
if (this.settings.materialAlphaTest > 0) {
|
|
349
|
-
defines.push('USE_ALPHATEST');
|
|
350
|
-
}
|
|
351
|
-
if (this.settings.renderMode === RenderMode.VerticalBillBoard) {
|
|
352
|
-
defines.push('VERTICAL');
|
|
353
|
-
}
|
|
354
|
-
if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {
|
|
355
|
-
defines.push('HORIZONTAL');
|
|
356
|
-
}
|
|
357
|
-
if (this.settings.renderMode === RenderMode.Beam) {
|
|
358
|
-
defines.push('BEAM');
|
|
359
|
-
}
|
|
360
|
-
const pivot = SpriteBatch.takesPivotAndFlip(this.settings.renderMode) ? this.settings.pivot : undefined;
|
|
361
|
-
if (pivot) {
|
|
362
|
-
defines.push('PIVOT');
|
|
363
|
-
}
|
|
364
|
-
const sizeLimits = this.sizeLimits();
|
|
365
|
-
if (sizeLimits) {
|
|
366
|
-
defines.push('SIZE_LIMITS');
|
|
305
|
+
const onLoad = (
|
|
306
|
+
atlas as {
|
|
307
|
+
onLoadObservable?: {addOnce: (cb: () => void) => unknown; remove: (observer: unknown) => boolean};
|
|
308
|
+
}
|
|
309
|
+
).onLoadObservable;
|
|
310
|
+
if (onLoad) {
|
|
311
|
+
const observer = onLoad.addOnce(() => {
|
|
312
|
+
this.cancelAtlasRebuild = null;
|
|
313
|
+
if (!this.disposed) {
|
|
314
|
+
this.rebuildMaterial();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
this.cancelAtlasRebuild = () => onLoad.remove(observer);
|
|
318
|
+
}
|
|
367
319
|
}
|
|
368
|
-
const
|
|
320
|
+
const useEnvAtlas = this.usesEnvAtlas();
|
|
321
|
+
const program = shaderProgram(this.settings, {
|
|
322
|
+
depth: this.depth,
|
|
323
|
+
envAtlasReady: useEnvAtlas,
|
|
324
|
+
meshUV2: this.meshUV2(),
|
|
325
|
+
});
|
|
326
|
+
const {defines, attributes, uniforms, samplers} = program;
|
|
327
|
+
const shaderName = program.name;
|
|
369
328
|
const lighting = this.settings.lighting ?? 'fixed';
|
|
370
329
|
const lit = isMesh && lighting !== 'unlit';
|
|
371
|
-
if (isMesh && !lit) {
|
|
372
|
-
defines.push('MESH_UNLIT');
|
|
373
|
-
}
|
|
374
330
|
const rim = isMesh ? this.settings.rim : undefined;
|
|
375
|
-
if (rim) {
|
|
376
|
-
defines.push('MESH_RIM');
|
|
377
|
-
if (rim.mode === 'fade') {
|
|
378
|
-
defines.push('MESH_RIM_FADE');
|
|
379
|
-
} else if (rim.mode === 'mix') {
|
|
380
|
-
defines.push('MESH_RIM_MIX');
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
331
|
const needsEyePosition = useEnvAtlas || !!rim;
|
|
384
|
-
defines.push(...this.surfaceDefines());
|
|
385
332
|
|
|
386
333
|
const shaderLanguage = shaderLanguageFor(this.scene.getEngine());
|
|
387
|
-
registerShaders(shaderName,
|
|
388
|
-
|
|
389
|
-
const attributes = ['position', 'uv', 'offset', 'color', 'size', 'rotation', 'uvTile'];
|
|
390
|
-
if (this.settings.renderMode === RenderMode.Mesh) {
|
|
391
|
-
attributes.push('normal');
|
|
392
|
-
}
|
|
393
|
-
if (this.usesUV1() && this.meshUV2()) {
|
|
394
|
-
defines.push('MESH_UV2');
|
|
395
|
-
attributes.push('uv2');
|
|
396
|
-
}
|
|
397
|
-
if (SpriteBatch.drawsAlongVelocity(this.settings.renderMode)) {
|
|
398
|
-
attributes.push('velocity');
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const uniforms = ['world', 'view', 'projection', 'worldView', 'worldViewProjection', ...VFXBatch.FOG_UNIFORMS];
|
|
402
|
-
const samplers: string[] = [];
|
|
403
|
-
|
|
404
|
-
if (this.settings.uTileCount > 1 || this.settings.vTileCount > 1) {
|
|
405
|
-
uniforms.push('tileCountX');
|
|
406
|
-
uniforms.push('tileCountY');
|
|
407
|
-
}
|
|
408
|
-
if (mapTexture) {
|
|
409
|
-
samplers.push('map');
|
|
410
|
-
}
|
|
411
|
-
if (useEnvAtlas) {
|
|
412
|
-
samplers.push('envAtlas');
|
|
413
|
-
uniforms.push('reflectionLevel');
|
|
414
|
-
}
|
|
415
|
-
if (needsEyePosition) {
|
|
416
|
-
uniforms.push('eyePosition');
|
|
417
|
-
}
|
|
418
|
-
if (rim) {
|
|
419
|
-
uniforms.push('rimColor');
|
|
420
|
-
uniforms.push('rimParams');
|
|
421
|
-
}
|
|
422
|
-
this.surfaceUniforms(uniforms, samplers);
|
|
423
|
-
if (SpriteBatch.drawsAlongVelocity(this.settings.renderMode)) {
|
|
424
|
-
uniforms.push('speedFactor');
|
|
425
|
-
}
|
|
426
|
-
if (this.settings.softParticles) {
|
|
427
|
-
uniforms.push('softParams');
|
|
428
|
-
uniforms.push('projParams');
|
|
429
|
-
samplers.push('depthTexture');
|
|
430
|
-
}
|
|
431
|
-
if (this.settings.materialAlphaTest > 0) {
|
|
432
|
-
uniforms.push('alphaTest');
|
|
433
|
-
}
|
|
434
|
-
if (pivot) {
|
|
435
|
-
uniforms.push('pivot');
|
|
436
|
-
}
|
|
437
|
-
if (sizeLimits) {
|
|
438
|
-
uniforms.push('sizeLimits');
|
|
439
|
-
}
|
|
440
|
-
if (lit) {
|
|
441
|
-
uniforms.push('lightDirection');
|
|
442
|
-
uniforms.push('lightColor');
|
|
443
|
-
uniforms.push('ambientColor');
|
|
444
|
-
}
|
|
334
|
+
registerShaders(shaderName, program.vertex, program.fragment, shaderLanguage);
|
|
445
335
|
|
|
446
336
|
const mat = new ShaderMaterial(
|
|
447
337
|
shaderName,
|
|
@@ -462,11 +352,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
462
352
|
}
|
|
463
353
|
if (useEnvAtlas && atlas) {
|
|
464
354
|
mat.setTexture('envAtlas', atlas);
|
|
465
|
-
mat.setFloat('reflectionLevel', this.settings.reflectionLevel);
|
|
466
|
-
}
|
|
467
|
-
if (rim) {
|
|
468
|
-
mat.setVector3('rimColor', new BVector3(rim.color[0], rim.color[1], rim.color[2]));
|
|
469
|
-
mat.setVector2('rimParams', new BVector2(rim.power, rim.strength));
|
|
470
355
|
}
|
|
471
356
|
if (needsEyePosition) {
|
|
472
357
|
const eyePosition = new BVector3();
|
|
@@ -477,44 +362,15 @@ export class SpriteBatch extends VFXBatch {
|
|
|
477
362
|
return;
|
|
478
363
|
}
|
|
479
364
|
eyePosition.copyFrom(camera.globalPosition);
|
|
480
|
-
mat.setVector3('eyePosition', eyePosition);
|
|
365
|
+
VFXBatch.writeBound(mat, (target) => target.setVector3('eyePosition', eyePosition));
|
|
481
366
|
});
|
|
482
367
|
}
|
|
483
|
-
if (this.settings
|
|
368
|
+
if (tilesSheet(this.settings)) {
|
|
484
369
|
mat.setFloat('tileCountX', this.settings.uTileCount);
|
|
485
370
|
mat.setFloat('tileCountY', this.settings.vTileCount);
|
|
486
371
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
if (this.settings.softParticles) {
|
|
491
|
-
mat.setVector2(
|
|
492
|
-
'softParams',
|
|
493
|
-
new BVector2(
|
|
494
|
-
this.settings.softNearFade,
|
|
495
|
-
1.0 / Math.max(this.settings.softFarFade - this.settings.softNearFade, 0.0001)
|
|
496
|
-
)
|
|
497
|
-
);
|
|
498
|
-
// Reused across binds: this runs on every draw call.
|
|
499
|
-
const projParams = new BVector4(0, 0, 0, 0);
|
|
500
|
-
mat.onBindObservable.add(() => {
|
|
501
|
-
const camera = this.scene.activeCamera;
|
|
502
|
-
if (camera) {
|
|
503
|
-
projParams.x = camera.minZ;
|
|
504
|
-
projParams.y = camera.maxZ;
|
|
505
|
-
mat.setVector4('projParams', projParams);
|
|
506
|
-
}
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
if (this.settings.materialAlphaTest > 0) {
|
|
510
|
-
mat.setFloat('alphaTest', this.settings.materialAlphaTest);
|
|
511
|
-
}
|
|
512
|
-
if (pivot) {
|
|
513
|
-
mat.setVector3('pivot', new BVector3(pivot.x, pivot.y, pivot.z));
|
|
514
|
-
}
|
|
515
|
-
if (sizeLimits) {
|
|
516
|
-
mat.setVector2('sizeLimits', sizeLimits);
|
|
517
|
-
}
|
|
372
|
+
this.bindSoft(mat);
|
|
373
|
+
this.writeSpriteParams(mat, useEnvAtlas);
|
|
518
374
|
if (lit) {
|
|
519
375
|
this.writeFixedLighting(mat);
|
|
520
376
|
if (lighting === 'scene') {
|
|
@@ -522,13 +378,15 @@ export class SpriteBatch extends VFXBatch {
|
|
|
522
378
|
const color = new BVector3();
|
|
523
379
|
const ambient = new BVector3();
|
|
524
380
|
mat.onBindObservable.add(() => {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
381
|
+
const found = SpriteBatch.readSceneLight(this.scene, direction, color, ambient);
|
|
382
|
+
VFXBatch.writeBound(mat, (target) =>
|
|
383
|
+
found
|
|
384
|
+
? target
|
|
385
|
+
.setVector3('lightDirection', direction)
|
|
386
|
+
.setVector3('lightColor', color)
|
|
387
|
+
.setVector3('ambientColor', ambient)
|
|
388
|
+
: this.writeFixedLighting(target)
|
|
389
|
+
);
|
|
532
390
|
});
|
|
533
391
|
}
|
|
534
392
|
}
|
|
@@ -542,14 +400,52 @@ export class SpriteBatch extends VFXBatch {
|
|
|
542
400
|
} else {
|
|
543
401
|
mat.alphaMode = Constants.ALPHA_DISABLE;
|
|
544
402
|
}
|
|
545
|
-
|
|
403
|
+
// A transparent surface that writes depth is drawn into the depth buffer first, so its own
|
|
404
|
+
// overlapping pieces do not fight; an opaque one writes depth as it draws.
|
|
405
|
+
mat.needDepthPrePass = this.settings.materialDepthWrite && this.settings.materialTransparent;
|
|
546
406
|
mat.forceDepthWrite = this.settings.materialDepthWrite;
|
|
547
407
|
mat.disableDepthWrite = !this.settings.materialDepthWrite;
|
|
408
|
+
// 0 keeps the engine's depth function (LEQUAL); ALWAYS draws over everything, like ZTest Always.
|
|
409
|
+
mat.depthFunction = this.settings.materialDepthTest ? 0 : Constants.ALWAYS;
|
|
548
410
|
|
|
411
|
+
// A rebuild (an env atlas that finished loading) replaces the material; the old one and its
|
|
412
|
+
// bind observers go with it. Textures belong to the settings, so they stay.
|
|
413
|
+
this.shaderMaterial?.dispose(false, false);
|
|
549
414
|
this.mesh.material = mat;
|
|
550
415
|
this.shaderMaterial = mat;
|
|
551
416
|
}
|
|
552
417
|
|
|
418
|
+
/** Writes the live parameters only sprite and mesh batches have: reflection level, rim, pivot and size limits. */
|
|
419
|
+
private writeSpriteParams(material: ShaderMaterial, envAtlas: boolean): void {
|
|
420
|
+
if (envAtlas) {
|
|
421
|
+
material.setFloat('reflectionLevel', this.settings.reflectionLevel);
|
|
422
|
+
}
|
|
423
|
+
const rim = this.settings.renderMode === RenderMode.Mesh ? this.settings.rim : undefined;
|
|
424
|
+
if (rim) {
|
|
425
|
+
material.setVector3('rimColor', new BVector3(rim.color[0], rim.color[1], rim.color[2]));
|
|
426
|
+
material.setVector2('rimParams', new BVector2(rim.power, rim.strength));
|
|
427
|
+
}
|
|
428
|
+
const pivot = takesPivotAndSizeLimits(this.settings.renderMode) ? this.settings.pivot : undefined;
|
|
429
|
+
if (pivot) {
|
|
430
|
+
material.setVector3('pivot', new BVector3(pivot.x, pivot.y, pivot.z));
|
|
431
|
+
}
|
|
432
|
+
const sizeLimits = this.sizeLimits();
|
|
433
|
+
if (sizeLimits) {
|
|
434
|
+
material.setVector2('sizeLimits', sizeLimits);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
protected override writeLiveUniforms(material: ShaderMaterial): void {
|
|
439
|
+
super.writeLiveUniforms(material);
|
|
440
|
+
this.writeSpriteParams(material, this.usesEnvAtlas());
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** True when the material samples the env atlas: a mesh batch whose atlas has loaded. */
|
|
444
|
+
private usesEnvAtlas(): boolean {
|
|
445
|
+
const atlas = this.settings.reflectionAtlas;
|
|
446
|
+
return this.settings.renderMode === RenderMode.Mesh && !!atlas && atlas.isReady();
|
|
447
|
+
}
|
|
448
|
+
|
|
553
449
|
applyMeshLighting(lighting: MeshLightingSettings): void {
|
|
554
450
|
this.storeFixedLighting(lighting);
|
|
555
451
|
const material = this.shaderMaterial;
|
|
@@ -564,7 +460,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
564
460
|
this.fixedAmbientColor.set(lighting.ambient.r, lighting.ambient.g, lighting.ambient.b);
|
|
565
461
|
}
|
|
566
462
|
|
|
567
|
-
private writeFixedLighting(material: ShaderMaterial): void {
|
|
463
|
+
private writeFixedLighting(material: ShaderMaterial | Effect): void {
|
|
568
464
|
material.setVector3('lightDirection', this.fixedLightDirection);
|
|
569
465
|
material.setVector3('lightColor', this.fixedLightColor);
|
|
570
466
|
material.setVector3('ambientColor', this.fixedAmbientColor);
|
|
@@ -618,10 +514,10 @@ export class SpriteBatch extends VFXBatch {
|
|
|
618
514
|
private quaternion5_ = new Quaternion();
|
|
619
515
|
private rotationMat_ = new Matrix3();
|
|
620
516
|
private rotationMat2_ = new Matrix3();
|
|
621
|
-
private lastStretchedSpeedFactor = Number.NaN;
|
|
622
517
|
/** Slot 0 holds the zero-alpha instance since the batch last went empty. */
|
|
623
518
|
private clearedInstanceUploaded = false;
|
|
624
519
|
|
|
520
|
+
/** @internal */
|
|
625
521
|
update(): void {
|
|
626
522
|
let index = 0;
|
|
627
523
|
let particleCount = 0;
|
|
@@ -630,7 +526,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
630
526
|
const isStretchedRender = renderMode === RenderMode.StretchedBillBoard;
|
|
631
527
|
const isBeamRender = renderMode === RenderMode.Beam;
|
|
632
528
|
const usesVelocity = isStretchedRender || isBeamRender;
|
|
633
|
-
const flipsSprites =
|
|
529
|
+
const flipsSprites = takesPivotAndSizeLimits(renderMode);
|
|
634
530
|
|
|
635
531
|
const visibleSystems = this.getVisibleSystems();
|
|
636
532
|
for (let i = 0; i < visibleSystems.length; i++) {
|
|
@@ -931,7 +827,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
931
827
|
const vx = velocity.x + (velocity.x - previousVelocity.x) * stepFraction;
|
|
932
828
|
const vy = velocity.y + (velocity.y - previousVelocity.y) * stepFraction;
|
|
933
829
|
const vz = velocity.z + (velocity.z - previousVelocity.z) * stepFraction;
|
|
934
|
-
|
|
830
|
+
const vel: Vector3 = this.vector_;
|
|
935
831
|
vel.set(vx, vy, vz);
|
|
936
832
|
if (!systemWorldSpace) {
|
|
937
833
|
if (particle.parentMatrix) {
|
|
@@ -966,10 +862,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
966
862
|
}
|
|
967
863
|
}
|
|
968
864
|
|
|
969
|
-
if (isStretchedRender && visibleSystems.length > 0) {
|
|
970
|
-
this.syncStretchedSpeedFactor(visibleSystems[0]);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
865
|
// Keep the mesh enabled even when count is 0 — toggling visibility off for a
|
|
974
866
|
// single frame (common with short lifetimes + low emission overlap) causes flicker.
|
|
975
867
|
this.mesh.forcedInstanceCount = index;
|
|
@@ -1002,32 +894,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1002
894
|
}
|
|
1003
895
|
}
|
|
1004
896
|
|
|
1005
|
-
/**
|
|
1006
|
-
* Pushes the stretched-billboard speed factor to the shader when it changed.
|
|
1007
|
-
*
|
|
1008
|
-
* Deliberately kept out of `update`: touching the loosely typed
|
|
1009
|
-
* `mesh.material` from inside that function gives V8 a generic property
|
|
1010
|
-
* access it has no feedback for, which knocks the whole particle loop out of
|
|
1011
|
-
* optimised code on every frame.
|
|
1012
|
-
*/
|
|
1013
|
-
private syncStretchedSpeedFactor(system: IParticleSystem): void {
|
|
1014
|
-
const material = this.shaderMaterial;
|
|
1015
|
-
if (material === null) {
|
|
1016
|
-
return;
|
|
1017
|
-
}
|
|
1018
|
-
const speedFactor = (system.rendererEmitterSettings as StretchedBillBoardSettings).speedFactor ?? 1.0;
|
|
1019
|
-
const clampedSpeedFactor = speedFactor === 0 ? 0.001 : speedFactor;
|
|
1020
|
-
if (clampedSpeedFactor !== this.lastStretchedSpeedFactor) {
|
|
1021
|
-
material.setFloat('speedFactor', clampedSpeedFactor);
|
|
1022
|
-
this.lastStretchedSpeedFactor = clampedSpeedFactor;
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
/** Stretched billboards and beams: drawn along a per-instance vector, by the stretched shader. */
|
|
1027
|
-
private static drawsAlongVelocity(renderMode: RenderMode): boolean {
|
|
1028
|
-
return renderMode === RenderMode.StretchedBillBoard || renderMode === RenderMode.Beam;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
897
|
private readonly beamEnd_ = new Vector3();
|
|
1032
898
|
|
|
1033
899
|
/**
|
|
@@ -1051,16 +917,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1051
917
|
}
|
|
1052
918
|
}
|
|
1053
919
|
|
|
1054
|
-
/** The quads that take a pivot and flip: every billboard kind, stretched included, but not meshes or beams. */
|
|
1055
|
-
private static takesPivotAndFlip(renderMode: RenderMode): boolean {
|
|
1056
|
-
return (
|
|
1057
|
-
renderMode === RenderMode.BillBoard ||
|
|
1058
|
-
renderMode === RenderMode.StretchedBillBoard ||
|
|
1059
|
-
renderMode === RenderMode.VerticalBillBoard ||
|
|
1060
|
-
renderMode === RenderMode.HorizontalBillBoard
|
|
1061
|
-
);
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
920
|
/** Multiplies `count` RGBA colours starting at `base` by a system's {@link ParticleSystem.tint}. */
|
|
1065
921
|
private static applyTint(colors: Float32Array, base: number, count: number, tint: Vector4): void {
|
|
1066
922
|
for (let j = 0, o = base; j < count; j++, o += 4) {
|
|
@@ -1332,7 +1188,11 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1332
1188
|
}
|
|
1333
1189
|
}
|
|
1334
1190
|
|
|
1191
|
+
/** @internal */
|
|
1335
1192
|
dispose(): void {
|
|
1193
|
+
this.disposed = true;
|
|
1194
|
+
this.cancelAtlasRebuild?.();
|
|
1195
|
+
this.cancelAtlasRebuild = null;
|
|
1336
1196
|
if (this.mesh.material) {
|
|
1337
1197
|
this.mesh.material.dispose();
|
|
1338
1198
|
}
|