babylon.quarks 0.26.0 → 0.28.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 +1194 -8
- package/README.md +13 -9
- 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 +10108 -0
- package/dist/types/index.d.ts +5794 -998
- package/package.json +18 -7
- package/src/BatchedRenderer.ts +274 -156
- package/src/EffectPool.ts +94 -12
- package/src/MeshSurfaceEmitter.ts +50 -14
- package/src/ParticleEmitter.ts +75 -17
- package/src/ParticleSystem.ts +981 -2066
- package/src/QuarksLoader.ts +666 -194
- package/src/QuarksPrefab.ts +367 -73
- package/src/QuarksSerializer.ts +512 -0
- package/src/QuarksUtil.ts +77 -7
- package/src/SpriteBatch.ts +127 -252
- package/src/TrailBatch.ts +545 -348
- package/src/VFXBatch.ts +238 -596
- package/src/batchKey.ts +239 -0
- package/src/batchSettings.ts +546 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +76 -0
- package/src/envAtlas.ts +126 -18
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +69 -35
- package/src/nodeId.ts +36 -0
- package/src/shaderProgram.ts +419 -0
- package/src/shaderVariants.ts +245 -0
- package/src/shaders/chunks/blend.ts +31 -0
- package/src/shaders/chunks/fog.ts +12 -5
- package/src/shaders/chunks/sheet.ts +111 -0
- package/src/shaders/chunks/soft.ts +85 -0
- package/src/shaders/chunks/surface.ts +12 -2
- 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 +5 -22
- package/src/shaders/particle_frag.wgsl.ts +5 -23
- package/src/shaders/particle_physics_frag.glsl.ts +16 -26
- package/src/shaders/particle_physics_frag.wgsl.ts +14 -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 +13 -0
- package/src/shaders/trail_frag.wgsl.ts +13 -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 +358 -0
- package/src/system/EventDispatcher.ts +60 -0
- package/src/system/ParticleSpawner.ts +274 -0
- package/src/system/SimulationClock.ts +130 -0
- package/src/system/geometry.ts +104 -0
- package/src/system/integrate.ts +428 -0
- package/src/system/materialResolve.ts +165 -0
- package/src/system/normalize.ts +100 -0
- package/src/system/serialization.ts +422 -0
- package/src/system/settingsSchema.ts +339 -0
- package/src/system/types.ts +449 -0
- package/src/textureSource.ts +30 -0
- package/src/trailSettings.ts +54 -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
|
|
|
@@ -43,6 +37,13 @@ export class SpriteBatch extends VFXBatch {
|
|
|
43
37
|
texture = RawTexture.CreateRGBATexture(new Uint8Array([255, 255, 255, 255]), 1, 1, scene);
|
|
44
38
|
texture.name = 'quarksMeshWhite';
|
|
45
39
|
SpriteBatch.whiteTextureByScene.set(scene, texture);
|
|
40
|
+
// Disposed from outside (a scene-wide texture sweep): the next batch makes a new one.
|
|
41
|
+
const made = texture;
|
|
42
|
+
texture.onDisposeObservable.addOnce(() => {
|
|
43
|
+
if (SpriteBatch.whiteTextureByScene.get(scene) === made) {
|
|
44
|
+
SpriteBatch.whiteTextureByScene.delete(scene);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
46
47
|
}
|
|
47
48
|
return texture;
|
|
48
49
|
}
|
|
@@ -53,8 +54,11 @@ export class SpriteBatch extends VFXBatch {
|
|
|
53
54
|
private uvTileBuffer!: Float32Array;
|
|
54
55
|
private velocityBuffer?: Float32Array;
|
|
55
56
|
|
|
56
|
-
/** Concretely typed handle on the batch material,
|
|
57
|
+
/** Concretely typed handle on the batch material, for the lighting and live-parameter writes. */
|
|
57
58
|
private shaderMaterial: ShaderMaterial | null = null;
|
|
59
|
+
/** Cancels the pending rebuild for an env atlas that has not loaded yet. */
|
|
60
|
+
private cancelAtlasRebuild: (() => void) | null = null;
|
|
61
|
+
private disposed = false;
|
|
58
62
|
/** The `'fixed'` mesh light, as shader uniforms; see applyMeshLighting. */
|
|
59
63
|
private readonly fixedLightDirection = new BVector3();
|
|
60
64
|
private readonly fixedLightColor = new BVector3();
|
|
@@ -67,16 +71,17 @@ export class SpriteBatch extends VFXBatch {
|
|
|
67
71
|
private uvTileVB!: VertexBuffer;
|
|
68
72
|
private velocityVB?: VertexBuffer;
|
|
69
73
|
|
|
70
|
-
constructor(settings: VFXBatchSettings, scene: Scene) {
|
|
71
|
-
super(settings, scene);
|
|
74
|
+
constructor(settings: VFXBatchSettings, scene: Scene, depth: DepthBinding | null = null) {
|
|
75
|
+
super(settings, scene, depth);
|
|
72
76
|
this.storeFixedLighting(defaultMeshLighting());
|
|
73
|
-
this.maxParticles =
|
|
77
|
+
this.maxParticles = this.initialCapacity;
|
|
74
78
|
this.setupBuffers();
|
|
75
79
|
this.rebuildMaterial();
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
/** @internal */
|
|
78
83
|
setupBuffers(): void {
|
|
79
|
-
this.mesh
|
|
84
|
+
this.mesh?.dispose();
|
|
80
85
|
this.mesh = new Mesh('spriteBatch', this.scene);
|
|
81
86
|
this.configureMesh();
|
|
82
87
|
|
|
@@ -170,7 +175,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
170
175
|
}
|
|
171
176
|
this.mesh.setVerticesBuffer(this.rotationVB);
|
|
172
177
|
|
|
173
|
-
if (
|
|
178
|
+
if (drawsAlongVelocity(this.settings.renderMode)) {
|
|
174
179
|
this.velocityBuffer = new Float32Array(this.maxParticles * 4);
|
|
175
180
|
this.velocityVB = new VertexBuffer(engine, this.velocityBuffer, 'velocity', true, false, 4, true);
|
|
176
181
|
this.mesh.setVerticesBuffer(this.velocityVB);
|
|
@@ -180,10 +185,19 @@ export class SpriteBatch extends VFXBatch {
|
|
|
180
185
|
this.mesh.doNotSyncBoundingInfo = true;
|
|
181
186
|
}
|
|
182
187
|
|
|
188
|
+
/** @internal */
|
|
189
|
+
protected readonly initialCapacity = 1000;
|
|
190
|
+
|
|
191
|
+
/** @internal */
|
|
183
192
|
expandBuffers(target: number): void {
|
|
184
193
|
while (target >= this.maxParticles) {
|
|
185
194
|
this.maxParticles *= 2;
|
|
186
195
|
}
|
|
196
|
+
this.resizeBuffers();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @internal */
|
|
200
|
+
protected resizeBuffers(): void {
|
|
187
201
|
this.disposeExpandableBuffers();
|
|
188
202
|
this.buildExpandableBuffers();
|
|
189
203
|
this.sortScratch = undefined;
|
|
@@ -193,10 +207,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
193
207
|
/** The screen-size limits as a uniform value, or null when the batch has none that apply. */
|
|
194
208
|
private sizeLimits(): BVector2 | null {
|
|
195
209
|
const {minParticleSize, maxParticleSize} = this.settings;
|
|
196
|
-
if (!
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
if (minParticleSize <= 0 && maxParticleSize === Infinity) {
|
|
210
|
+
if (!takesPivotAndSizeLimits(this.settings.renderMode) || !hasSizeLimits(this.settings)) {
|
|
200
211
|
return null;
|
|
201
212
|
}
|
|
202
213
|
// The shader clamps; an unlimited maximum is just a very large one.
|
|
@@ -295,153 +306,47 @@ export class SpriteBatch extends VFXBatch {
|
|
|
295
306
|
return spare;
|
|
296
307
|
}
|
|
297
308
|
|
|
309
|
+
/** @internal */
|
|
298
310
|
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
|
-
|
|
311
|
+
const isMesh = this.settings.renderMode === RenderMode.Mesh;
|
|
319
312
|
// Mesh batches without a diffuse map still need a sampler2D on iOS WebKit —
|
|
320
313
|
// 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
|
-
}
|
|
314
|
+
const mapTexture = this.settings.texture ?? (isMesh ? SpriteBatch.whiteTexture(this.scene) : null);
|
|
327
315
|
const atlas = this.settings.reflectionAtlas;
|
|
328
|
-
const atlasPending =
|
|
316
|
+
const atlasPending = isMesh && !!atlas && !atlas.isReady();
|
|
317
|
+
this.cancelAtlasRebuild?.();
|
|
318
|
+
this.cancelAtlasRebuild = null;
|
|
329
319
|
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');
|
|
320
|
+
const onLoad = (
|
|
321
|
+
atlas as {
|
|
322
|
+
onLoadObservable?: {addOnce: (cb: () => void) => unknown; remove: (observer: unknown) => boolean};
|
|
323
|
+
}
|
|
324
|
+
).onLoadObservable;
|
|
325
|
+
if (onLoad) {
|
|
326
|
+
const observer = onLoad.addOnce(() => {
|
|
327
|
+
this.cancelAtlasRebuild = null;
|
|
328
|
+
if (!this.disposed) {
|
|
329
|
+
this.rebuildMaterial();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
this.cancelAtlasRebuild = () => onLoad.remove(observer);
|
|
333
|
+
}
|
|
367
334
|
}
|
|
368
|
-
const
|
|
335
|
+
const useEnvAtlas = this.usesEnvAtlas();
|
|
336
|
+
const program = shaderProgram(this.settings, {
|
|
337
|
+
depth: this.depth,
|
|
338
|
+
envAtlasReady: useEnvAtlas,
|
|
339
|
+
meshUV2: this.meshUV2(),
|
|
340
|
+
});
|
|
341
|
+
const {defines, attributes, uniforms, samplers} = program;
|
|
342
|
+
const shaderName = program.name;
|
|
369
343
|
const lighting = this.settings.lighting ?? 'fixed';
|
|
370
344
|
const lit = isMesh && lighting !== 'unlit';
|
|
371
|
-
if (isMesh && !lit) {
|
|
372
|
-
defines.push('MESH_UNLIT');
|
|
373
|
-
}
|
|
374
345
|
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
346
|
const needsEyePosition = useEnvAtlas || !!rim;
|
|
384
|
-
defines.push(...this.surfaceDefines());
|
|
385
347
|
|
|
386
348
|
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
|
-
}
|
|
349
|
+
registerShaders(shaderName, program.vertex, program.fragment, shaderLanguage);
|
|
445
350
|
|
|
446
351
|
const mat = new ShaderMaterial(
|
|
447
352
|
shaderName,
|
|
@@ -462,11 +367,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
462
367
|
}
|
|
463
368
|
if (useEnvAtlas && atlas) {
|
|
464
369
|
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
370
|
}
|
|
471
371
|
if (needsEyePosition) {
|
|
472
372
|
const eyePosition = new BVector3();
|
|
@@ -477,44 +377,15 @@ export class SpriteBatch extends VFXBatch {
|
|
|
477
377
|
return;
|
|
478
378
|
}
|
|
479
379
|
eyePosition.copyFrom(camera.globalPosition);
|
|
480
|
-
mat.setVector3('eyePosition', eyePosition);
|
|
380
|
+
VFXBatch.writeBound(mat, (target) => target.setVector3('eyePosition', eyePosition));
|
|
481
381
|
});
|
|
482
382
|
}
|
|
483
|
-
if (this.settings
|
|
383
|
+
if (tilesSheet(this.settings)) {
|
|
484
384
|
mat.setFloat('tileCountX', this.settings.uTileCount);
|
|
485
385
|
mat.setFloat('tileCountY', this.settings.vTileCount);
|
|
486
386
|
}
|
|
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
|
-
}
|
|
387
|
+
this.bindSoft(mat);
|
|
388
|
+
this.writeSpriteParams(mat, useEnvAtlas);
|
|
518
389
|
if (lit) {
|
|
519
390
|
this.writeFixedLighting(mat);
|
|
520
391
|
if (lighting === 'scene') {
|
|
@@ -522,34 +393,76 @@ export class SpriteBatch extends VFXBatch {
|
|
|
522
393
|
const color = new BVector3();
|
|
523
394
|
const ambient = new BVector3();
|
|
524
395
|
mat.onBindObservable.add(() => {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
396
|
+
const found = SpriteBatch.readSceneLight(this.scene, direction, color, ambient);
|
|
397
|
+
VFXBatch.writeBound(mat, (target) =>
|
|
398
|
+
found
|
|
399
|
+
? target
|
|
400
|
+
.setVector3('lightDirection', direction)
|
|
401
|
+
.setVector3('lightColor', color)
|
|
402
|
+
.setVector3('ambientColor', ambient)
|
|
403
|
+
: this.writeFixedLighting(target)
|
|
404
|
+
);
|
|
532
405
|
});
|
|
533
406
|
}
|
|
534
407
|
}
|
|
535
408
|
|
|
536
409
|
this.bindFog(mat);
|
|
537
410
|
this.bindSurface(mat);
|
|
538
|
-
|
|
411
|
+
// Billboards, beams and the like are flat sheets: both faces, whatever the setting says.
|
|
412
|
+
mat.backFaceCulling = this.settings.renderFace !== 'both';
|
|
413
|
+
mat.cullBackFaces = this.settings.renderFace !== 'back';
|
|
539
414
|
|
|
540
415
|
if (this.settings.materialTransparent) {
|
|
541
416
|
mat.alphaMode = this.settings.materialBlendMode;
|
|
542
417
|
} else {
|
|
543
418
|
mat.alphaMode = Constants.ALPHA_DISABLE;
|
|
544
419
|
}
|
|
545
|
-
|
|
420
|
+
// A transparent surface that writes depth is drawn into the depth buffer first, so its own
|
|
421
|
+
// overlapping pieces do not fight; an opaque one writes depth as it draws.
|
|
422
|
+
mat.needDepthPrePass = this.settings.materialDepthWrite && this.settings.materialTransparent;
|
|
546
423
|
mat.forceDepthWrite = this.settings.materialDepthWrite;
|
|
547
424
|
mat.disableDepthWrite = !this.settings.materialDepthWrite;
|
|
425
|
+
// 0 keeps the engine's depth function (LEQUAL); ALWAYS draws over everything, like ZTest Always.
|
|
426
|
+
mat.depthFunction = this.settings.materialDepthTest ? 0 : Constants.ALWAYS;
|
|
548
427
|
|
|
428
|
+
// A rebuild (an env atlas that finished loading) replaces the material; the old one and its
|
|
429
|
+
// bind observers go with it. Textures belong to the settings, so they stay.
|
|
430
|
+
this.shaderMaterial?.dispose(false, false);
|
|
549
431
|
this.mesh.material = mat;
|
|
550
432
|
this.shaderMaterial = mat;
|
|
551
433
|
}
|
|
552
434
|
|
|
435
|
+
/** Writes the live parameters only sprite and mesh batches have: reflection level, rim, pivot and size limits. */
|
|
436
|
+
private writeSpriteParams(material: ShaderMaterial, envAtlas: boolean): void {
|
|
437
|
+
if (envAtlas) {
|
|
438
|
+
material.setFloat('reflectionLevel', this.settings.reflectionLevel);
|
|
439
|
+
}
|
|
440
|
+
const rim = this.settings.renderMode === RenderMode.Mesh ? this.settings.rim : undefined;
|
|
441
|
+
if (rim) {
|
|
442
|
+
material.setVector3('rimColor', new BVector3(rim.color[0], rim.color[1], rim.color[2]));
|
|
443
|
+
material.setVector2('rimParams', new BVector2(rim.power, rim.strength));
|
|
444
|
+
}
|
|
445
|
+
const pivot = takesPivotAndSizeLimits(this.settings.renderMode) ? this.settings.pivot : undefined;
|
|
446
|
+
if (pivot) {
|
|
447
|
+
material.setVector3('pivot', new BVector3(pivot.x, pivot.y, pivot.z));
|
|
448
|
+
}
|
|
449
|
+
const sizeLimits = this.sizeLimits();
|
|
450
|
+
if (sizeLimits) {
|
|
451
|
+
material.setVector2('sizeLimits', sizeLimits);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
protected override writeLiveUniforms(material: ShaderMaterial): void {
|
|
456
|
+
super.writeLiveUniforms(material);
|
|
457
|
+
this.writeSpriteParams(material, this.usesEnvAtlas());
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** True when the material samples the env atlas: a mesh batch whose atlas has loaded. */
|
|
461
|
+
private usesEnvAtlas(): boolean {
|
|
462
|
+
const atlas = this.settings.reflectionAtlas;
|
|
463
|
+
return this.settings.renderMode === RenderMode.Mesh && !!atlas && atlas.isReady();
|
|
464
|
+
}
|
|
465
|
+
|
|
553
466
|
applyMeshLighting(lighting: MeshLightingSettings): void {
|
|
554
467
|
this.storeFixedLighting(lighting);
|
|
555
468
|
const material = this.shaderMaterial;
|
|
@@ -564,7 +477,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
564
477
|
this.fixedAmbientColor.set(lighting.ambient.r, lighting.ambient.g, lighting.ambient.b);
|
|
565
478
|
}
|
|
566
479
|
|
|
567
|
-
private writeFixedLighting(material: ShaderMaterial): void {
|
|
480
|
+
private writeFixedLighting(material: ShaderMaterial | Effect): void {
|
|
568
481
|
material.setVector3('lightDirection', this.fixedLightDirection);
|
|
569
482
|
material.setVector3('lightColor', this.fixedLightColor);
|
|
570
483
|
material.setVector3('ambientColor', this.fixedAmbientColor);
|
|
@@ -618,10 +531,10 @@ export class SpriteBatch extends VFXBatch {
|
|
|
618
531
|
private quaternion5_ = new Quaternion();
|
|
619
532
|
private rotationMat_ = new Matrix3();
|
|
620
533
|
private rotationMat2_ = new Matrix3();
|
|
621
|
-
private lastStretchedSpeedFactor = Number.NaN;
|
|
622
534
|
/** Slot 0 holds the zero-alpha instance since the batch last went empty. */
|
|
623
535
|
private clearedInstanceUploaded = false;
|
|
624
536
|
|
|
537
|
+
/** @internal */
|
|
625
538
|
update(): void {
|
|
626
539
|
let index = 0;
|
|
627
540
|
let particleCount = 0;
|
|
@@ -630,15 +543,13 @@ export class SpriteBatch extends VFXBatch {
|
|
|
630
543
|
const isStretchedRender = renderMode === RenderMode.StretchedBillBoard;
|
|
631
544
|
const isBeamRender = renderMode === RenderMode.Beam;
|
|
632
545
|
const usesVelocity = isStretchedRender || isBeamRender;
|
|
633
|
-
const flipsSprites =
|
|
546
|
+
const flipsSprites = takesPivotAndSizeLimits(renderMode);
|
|
634
547
|
|
|
635
548
|
const visibleSystems = this.getVisibleSystems();
|
|
636
549
|
for (let i = 0; i < visibleSystems.length; i++) {
|
|
637
550
|
particleCount += visibleSystems[i].particleNum;
|
|
638
551
|
}
|
|
639
|
-
|
|
640
|
-
this.expandBuffers(particleCount);
|
|
641
|
-
}
|
|
552
|
+
this.fitBuffers(particleCount);
|
|
642
553
|
|
|
643
554
|
for (let s = 0; s < visibleSystems.length; s++) {
|
|
644
555
|
const system = visibleSystems[s];
|
|
@@ -931,7 +842,7 @@ export class SpriteBatch extends VFXBatch {
|
|
|
931
842
|
const vx = velocity.x + (velocity.x - previousVelocity.x) * stepFraction;
|
|
932
843
|
const vy = velocity.y + (velocity.y - previousVelocity.y) * stepFraction;
|
|
933
844
|
const vz = velocity.z + (velocity.z - previousVelocity.z) * stepFraction;
|
|
934
|
-
|
|
845
|
+
const vel: Vector3 = this.vector_;
|
|
935
846
|
vel.set(vx, vy, vz);
|
|
936
847
|
if (!systemWorldSpace) {
|
|
937
848
|
if (particle.parentMatrix) {
|
|
@@ -966,10 +877,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
966
877
|
}
|
|
967
878
|
}
|
|
968
879
|
|
|
969
|
-
if (isStretchedRender && visibleSystems.length > 0) {
|
|
970
|
-
this.syncStretchedSpeedFactor(visibleSystems[0]);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
880
|
// Keep the mesh enabled even when count is 0 — toggling visibility off for a
|
|
974
881
|
// single frame (common with short lifetimes + low emission overlap) causes flicker.
|
|
975
882
|
this.mesh.forcedInstanceCount = index;
|
|
@@ -1002,32 +909,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1002
909
|
}
|
|
1003
910
|
}
|
|
1004
911
|
|
|
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
912
|
private readonly beamEnd_ = new Vector3();
|
|
1032
913
|
|
|
1033
914
|
/**
|
|
@@ -1051,16 +932,6 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1051
932
|
}
|
|
1052
933
|
}
|
|
1053
934
|
|
|
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
935
|
/** Multiplies `count` RGBA colours starting at `base` by a system's {@link ParticleSystem.tint}. */
|
|
1065
936
|
private static applyTint(colors: Float32Array, base: number, count: number, tint: Vector4): void {
|
|
1066
937
|
for (let j = 0, o = base; j < count; j++, o += 4) {
|
|
@@ -1332,7 +1203,11 @@ export class SpriteBatch extends VFXBatch {
|
|
|
1332
1203
|
}
|
|
1333
1204
|
}
|
|
1334
1205
|
|
|
1206
|
+
/** @internal */
|
|
1335
1207
|
dispose(): void {
|
|
1208
|
+
this.disposed = true;
|
|
1209
|
+
this.cancelAtlasRebuild?.();
|
|
1210
|
+
this.cancelAtlasRebuild = null;
|
|
1336
1211
|
if (this.mesh.material) {
|
|
1337
1212
|
this.mesh.material.dispose();
|
|
1338
1213
|
}
|