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/BatchedRenderer.ts
CHANGED
|
@@ -4,72 +4,103 @@ import {Texture} from '@babylonjs/core/Materials/Textures/texture';
|
|
|
4
4
|
import {Frustum} from '@babylonjs/core/Maths/math.frustum';
|
|
5
5
|
import {Plane} from '@babylonjs/core/Maths/math.plane';
|
|
6
6
|
import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
7
|
+
import type {DepthRenderer} from '@babylonjs/core/Rendering/depthRenderer';
|
|
7
8
|
import {Scene} from '@babylonjs/core/scene';
|
|
8
9
|
import {EmitSubParticleSystem, IParticleSystem, Vector3} from 'quarks.core';
|
|
10
|
+
import {batchSettingsEqual, shaderKeyEqual, storedBatchSettings} from './batchKey';
|
|
9
11
|
import {ParticleSystem} from './ParticleSystem';
|
|
12
|
+
import {depthBinding, DepthBinding, DepthTextureOptions, SoftDepthEncoding} from './softDepth';
|
|
10
13
|
import {SpriteBatch} from './SpriteBatch';
|
|
11
14
|
import {TrailBatch} from './TrailBatch';
|
|
12
15
|
import {
|
|
13
16
|
defaultMeshLighting,
|
|
14
|
-
erosionEqual,
|
|
15
|
-
gradientMapEqual,
|
|
16
|
-
mapUVEqual,
|
|
17
17
|
MeshLighting,
|
|
18
18
|
MeshLightingSettings,
|
|
19
19
|
MeshRim,
|
|
20
|
-
meshRimEqual,
|
|
21
20
|
ParticleErosion,
|
|
22
21
|
ParticleGradientMap,
|
|
23
22
|
ParticleUVDistortion,
|
|
24
|
-
|
|
23
|
+
RenderFace,
|
|
25
24
|
RenderMode,
|
|
26
25
|
SortMode,
|
|
27
|
-
StoredBatchSettings,
|
|
28
26
|
TextureUV,
|
|
29
|
-
uvDistortionEqual,
|
|
30
27
|
VFXBatch,
|
|
31
28
|
} from './VFXBatch';
|
|
32
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Everything about a system that decides how it is drawn: {@link ParticleSystem.getRendererSettings}
|
|
32
|
+
* returns it, and the renderer draws systems whose settings match from one batch. Only the
|
|
33
|
+
* settings a render mode reads count: two billboards that differ in a mesh-only setting, such as
|
|
34
|
+
* `rim`, share a batch.
|
|
35
|
+
*
|
|
36
|
+
* A batch is one draw with one material, so its settings fall into two groups, as a Unity
|
|
37
|
+
* material's keywords and texture slots against its MaterialPropertyBlock values, or an Unreal
|
|
38
|
+
* material against a dynamic material instance's parameters. The shader key (render mode,
|
|
39
|
+
* geometry, textures, blend and depth state, and which effects are on) decides the material.
|
|
40
|
+
* The live parameters (the fade distances, alpha cutoff, pivot, size limits, reflection level,
|
|
41
|
+
* rim colour, each effect's numbers, and each texture's tiling, offset and scroll) are uniforms:
|
|
42
|
+
* changing one on a system that is alone in its batch writes the material in place, with no new
|
|
43
|
+
* batch or material. A texture's UV channel is part of the shader key, so changing it rebuilds.
|
|
44
|
+
*
|
|
45
|
+
* Prefer the system's own setters, which also move it to a matching batch; after changing a
|
|
46
|
+
* field directly, set {@link ParticleSystem.neededToUpdateRender}.
|
|
47
|
+
*/
|
|
33
48
|
export interface VFXBatchSettings {
|
|
49
|
+
/** Mesh particles' vertex positions, see {@link ParticleSystem.instancingGeometry}. A quad for other modes. */
|
|
34
50
|
instancingGeometry: Float32Array;
|
|
51
|
+
/** Triangle indices into {@link instancingGeometry}. */
|
|
35
52
|
instancingIndices: Uint32Array | Uint16Array;
|
|
53
|
+
/** The mesh's first UV set, two values per vertex. */
|
|
36
54
|
instancingUVs?: Float32Array;
|
|
37
55
|
/** The mesh's second UV set, see {@link UVChannel}. */
|
|
38
56
|
instancingUVs2?: Float32Array;
|
|
57
|
+
/** The mesh's vertex normals; mesh particles fall back to +Z. */
|
|
39
58
|
instancingNormals?: Float32Array;
|
|
59
|
+
/** See {@link ParticleSystem.renderMode}. */
|
|
40
60
|
renderMode: RenderMode;
|
|
61
|
+
/** See {@link ParticleSystem.renderOrder}. */
|
|
41
62
|
renderOrder: number;
|
|
63
|
+
/** See {@link ParticleSystem.uTileCount}. */
|
|
42
64
|
uTileCount: number;
|
|
65
|
+
/** See {@link ParticleSystem.vTileCount}. */
|
|
43
66
|
vTileCount: number;
|
|
67
|
+
/** See {@link ParticleSystem.blendTiles}; read with a sheet, and not by trails. */
|
|
44
68
|
blendTiles: boolean;
|
|
69
|
+
/** See {@link ParticleSystem.softParticles}. */
|
|
45
70
|
softParticles: boolean;
|
|
71
|
+
/** See {@link ParticleSystem.softNearFade}. */
|
|
46
72
|
softNearFade: number;
|
|
73
|
+
/** See {@link ParticleSystem.softFarFade}. */
|
|
47
74
|
softFarFade: number;
|
|
75
|
+
/** Babylon alpha mode, see {@link ParticleSystem.blending}. */
|
|
48
76
|
materialBlendMode: number;
|
|
77
|
+
/** Whether the batch draws in the transparent pass. */
|
|
49
78
|
materialTransparent: boolean;
|
|
79
|
+
/** Whether particles are hidden behind nearer geometry. */
|
|
50
80
|
materialDepthTest: boolean;
|
|
81
|
+
/** Whether particles write depth. */
|
|
51
82
|
materialDepthWrite: boolean;
|
|
83
|
+
/** Alpha below which a fragment is discarded; 0 discards nothing. */
|
|
52
84
|
materialAlphaTest: number;
|
|
85
|
+
/** See {@link ParticleSystem.texture}. */
|
|
53
86
|
texture: Texture | null;
|
|
54
87
|
/**
|
|
55
|
-
*
|
|
56
|
-
* `
|
|
57
|
-
*
|
|
88
|
+
* The reflection a mesh particle's material carries (a Babylon material's `reflectionTexture`
|
|
89
|
+
* or `environmentTexture`). It is not sampled itself: a cube texture is unfolded into
|
|
90
|
+
* {@link reflectionAtlas} once it has loaded and sampled from there; anything else is kept
|
|
91
|
+
* only to tell materials apart. Other render modes ignore it.
|
|
58
92
|
*/
|
|
59
93
|
reflectionTexture: BaseTexture | null;
|
|
60
94
|
/** Multiplier for the reflection sample — mirrors `texture.level`. */
|
|
61
95
|
reflectionLevel: number;
|
|
62
96
|
/**
|
|
63
|
-
*
|
|
64
|
-
* `
|
|
65
|
-
* GL_INVALID_OPERATION
|
|
66
|
-
|
|
67
|
-
reflectionFaces: BaseTexture[] | null;
|
|
68
|
-
/**
|
|
69
|
-
* Single 3×2 atlas of cube faces (px py pz / nx ny nz). One sampler2D —
|
|
70
|
-
* the path that stays valid on iOS WebKit ShaderMaterial draws.
|
|
97
|
+
* A 3×2 atlas of cube faces (+X +Y +Z over -X -Y -Z) that mesh particles reflect, through one
|
|
98
|
+
* `sampler2D`: the path that stays valid on iOS WebKit ShaderMaterial draws, where a
|
|
99
|
+
* `samplerCube` or six face samplers raise GL_INVALID_OPERATION. Built from
|
|
100
|
+
* {@link reflectionTexture} when that is a cube texture. Other render modes ignore it.
|
|
71
101
|
*/
|
|
72
102
|
reflectionAtlas: BaseTexture | null;
|
|
103
|
+
/** Camera layer mask of the batch mesh, see {@link ParticleSystem.layers}. */
|
|
73
104
|
layerMask: number;
|
|
74
105
|
/**
|
|
75
106
|
* Babylon rendering group (`mesh.renderingGroupId`) the batch draws in,
|
|
@@ -78,8 +109,13 @@ export interface VFXBatchSettings {
|
|
|
78
109
|
renderingGroupId: number;
|
|
79
110
|
/**
|
|
80
111
|
* Whether the scene's fog applies to the particles, as it does to any other
|
|
81
|
-
* mesh; true by default.
|
|
82
|
-
*
|
|
112
|
+
* mesh; true by default. Particles that lay over the frame (`ALPHA_COMBINE`,
|
|
113
|
+
* `ALPHA_PREMULTIPLIED`, opaque ones) mix toward the fog colour; particles
|
|
114
|
+
* that add to it or subtract from it (`ALPHA_ADD`, `ALPHA_ONEONE`,
|
|
115
|
+
* `ALPHA_SCREENMODE`, `ALPHA_SUBTRACT`, …) fade toward black, since the fog
|
|
116
|
+
* colour would fog the whole screen; particles that multiply it
|
|
117
|
+
* (`ALPHA_MULTIPLY`) fade toward white. See {@link blendCategory}. Systems
|
|
118
|
+
* that differ are batched apart.
|
|
83
119
|
*/
|
|
84
120
|
applyFog: boolean;
|
|
85
121
|
/**
|
|
@@ -92,6 +128,11 @@ export interface VFXBatchSettings {
|
|
|
92
128
|
pivot?: Vector3;
|
|
93
129
|
/** Draw order within the batch, see {@link SortMode}. Unset is `'none'`. Systems that differ are batched apart. */
|
|
94
130
|
sortMode?: SortMode;
|
|
131
|
+
/**
|
|
132
|
+
* Which faces a mesh particle draws, see {@link RenderFace}. Unset is `'both'`. Mesh systems
|
|
133
|
+
* that differ are batched apart; other render modes ignore it.
|
|
134
|
+
*/
|
|
135
|
+
renderFace?: RenderFace;
|
|
95
136
|
/**
|
|
96
137
|
* Smallest size a billboard or stretched particle may have on screen, as a
|
|
97
138
|
* fraction of the viewport height (Unity's Min Particle Size); 0 by default.
|
|
@@ -121,47 +162,70 @@ export interface VFXBatchSettings {
|
|
|
121
162
|
erosion?: ParticleErosion;
|
|
122
163
|
}
|
|
123
164
|
|
|
165
|
+
/**
|
|
166
|
+
* How {@link BatchedRenderer.configureAdaptivePerformance} trades particles for frame time: each
|
|
167
|
+
* update that runs over `targetFrameMs` lowers every system's quality factor (see
|
|
168
|
+
* {@link ParticleSystem.setQualityFactor}) by `decreaseStep`, each one under it raises the factor
|
|
169
|
+
* by `increaseStep`, within `minQuality` and `maxQuality`.
|
|
170
|
+
*/
|
|
124
171
|
export interface AdaptivePerformanceOptions {
|
|
172
|
+
/** CPU time in milliseconds the renderer's update may take before quality drops. Default 16.7. */
|
|
125
173
|
targetFrameMs: number;
|
|
174
|
+
/** Lowest quality factor, 0.1 to 1 ({@link ParticleSystem.setQualityFactor} clamps lower values). Default 0.35. */
|
|
126
175
|
minQuality: number;
|
|
176
|
+
/** Highest quality factor, `minQuality` to 1. Default 1. */
|
|
127
177
|
maxQuality: number;
|
|
178
|
+
/** Quality lost per update over budget. Default 0.08. */
|
|
128
179
|
decreaseStep: number;
|
|
180
|
+
/** Quality regained per update within budget. Default 0.02. */
|
|
129
181
|
increaseStep: number;
|
|
130
182
|
}
|
|
131
183
|
|
|
132
|
-
/**
|
|
133
|
-
function pivotEqual(a: Vector3 | undefined, b: Vector3 | undefined): boolean {
|
|
134
|
-
return (a?.x ?? 0) === (b?.x ?? 0) && (a?.y ?? 0) === (b?.y ?? 0) && (a?.z ?? 0) === (b?.z ?? 0);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** True when both face lists are the same six texture references (or both empty). */
|
|
138
|
-
function reflectionFacesEqual(a: BaseTexture[] | null | undefined, b: BaseTexture[] | null | undefined): boolean {
|
|
139
|
-
if (a === b) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
if (!a || !b || a.length !== b.length) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
for (let i = 0; i < a.length; i++) {
|
|
146
|
-
if (a[i] !== b[i]) {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
|
|
184
|
+
/** A snapshot of the renderer's adaptive performance, from {@link BatchedRenderer.getAdaptivePerformanceState}. */
|
|
153
185
|
export interface AdaptivePerformanceState extends AdaptivePerformanceOptions {
|
|
186
|
+
/** Whether adaptive performance is on. */
|
|
154
187
|
enabled: boolean;
|
|
188
|
+
/** The quality factor the systems run at now. */
|
|
155
189
|
currentQuality: number;
|
|
190
|
+
/** CPU time the last measured update took, in milliseconds; kept from the last update while off. */
|
|
156
191
|
lastFrameCpuMs: number;
|
|
157
192
|
}
|
|
158
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Draws particle systems, batching the ones that draw alike into one instanced mesh each, and
|
|
196
|
+
* advances them every frame. It is a node in the scene; make one per scene, add each system with
|
|
197
|
+
* {@link addSystem}, and call {@link update} once per frame:
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* const renderer = new BatchedRenderer('particles', scene);
|
|
201
|
+
* renderer.addSystem(system);
|
|
202
|
+
* scene.onBeforeRenderObservable.add(() => renderer.update(scene.getEngine().getDeltaTime() / 1000));
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* Systems step on a fixed 60 Hz clock, so an effect plays out the same at any frame rate.
|
|
206
|
+
*
|
|
207
|
+
* The renderer is a node so the batches have a parent to live under: disabling it hides them,
|
|
208
|
+
* disposing it disposes them. Its transform moves nothing: particles are placed in the world by
|
|
209
|
+
* their emitters, and the batch meshes draw them there whatever the renderer's position, rotation
|
|
210
|
+
* or scale. A system belongs to one renderer at a time: adding it to another takes it out of the
|
|
211
|
+
* first.
|
|
212
|
+
*/
|
|
159
213
|
export class BatchedRenderer extends TransformNode {
|
|
160
|
-
|
|
214
|
+
private readonly batchList: Array<VFXBatch> = [];
|
|
215
|
+
private readonly batchIndexOf = new Map<IParticleSystem, number>();
|
|
216
|
+
|
|
217
|
+
/** The batches, one draw each, to inspect what is drawn; the renderer makes and frees them. */
|
|
218
|
+
get batches(): ReadonlyArray<VFXBatch> {
|
|
219
|
+
return this.batchList;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Index into {@link batches} of each system's batch, to inspect; the renderer keeps it. */
|
|
223
|
+
get systemToBatchIndex(): ReadonlyMap<IParticleSystem, number> {
|
|
224
|
+
return this.batchIndexOf;
|
|
225
|
+
}
|
|
161
226
|
/** Seconds this renderer has been updated for; the clock UV and erosion scrolling run on. */
|
|
162
227
|
private elapsed = 0;
|
|
163
|
-
|
|
164
|
-
depthTexture: BaseTexture | null = null;
|
|
228
|
+
private depth: DepthBinding | null = null;
|
|
165
229
|
/** Systems in insertion order — iterated every frame, unlike the map. */
|
|
166
230
|
private systems: Array<IParticleSystem> = [];
|
|
167
231
|
/**
|
|
@@ -223,8 +287,12 @@ export class BatchedRenderer extends TransformNode {
|
|
|
223
287
|
*/
|
|
224
288
|
simulationBudgetMs = Infinity;
|
|
225
289
|
|
|
226
|
-
/** Frustum planes of
|
|
227
|
-
private
|
|
290
|
+
/** Frustum planes of each active camera, six per camera, reused across updates. */
|
|
291
|
+
private readonly cameraPlanes: Plane[][] = [];
|
|
292
|
+
/** Each active camera's `layerMask`, parallel to {@link cameraPlanes}. */
|
|
293
|
+
private readonly cameraMasks: number[] = [];
|
|
294
|
+
/** How many cameras {@link cameraPlanes} holds this update. */
|
|
295
|
+
private cameraCount = 0;
|
|
228
296
|
private readonly boundsMin = new Vector3();
|
|
229
297
|
private readonly boundsMax = new Vector3();
|
|
230
298
|
|
|
@@ -233,21 +301,16 @@ export class BatchedRenderer extends TransformNode {
|
|
|
233
301
|
}
|
|
234
302
|
|
|
235
303
|
/**
|
|
236
|
-
* Gathers the frustum planes of the cameras the scene renders with,
|
|
237
|
-
*
|
|
304
|
+
* Gathers the frustum planes and layer masks of the cameras the scene renders with, and returns
|
|
305
|
+
* how many. 0 when there is no camera yet; systems are then never culled. Allocates only when a
|
|
306
|
+
* camera is added.
|
|
238
307
|
*/
|
|
239
|
-
private
|
|
308
|
+
private collectCameras(): number {
|
|
240
309
|
const scene = this.getScene();
|
|
241
|
-
const planes = this.frustumPlanes;
|
|
242
310
|
const activeCameras = scene.activeCameras;
|
|
243
311
|
const activeCamera = scene.activeCamera;
|
|
244
|
-
const cameras: Camera[] =
|
|
312
|
+
const cameras: readonly Camera[] =
|
|
245
313
|
activeCameras && activeCameras.length > 0 ? activeCameras : activeCamera ? [activeCamera] : [];
|
|
246
|
-
const needed = cameras.length * 6;
|
|
247
|
-
while (planes.length < needed) {
|
|
248
|
-
planes.push(new Plane(0, 0, 0, 0));
|
|
249
|
-
}
|
|
250
|
-
planes.length = needed;
|
|
251
314
|
for (let c = 0; c < cameras.length; c++) {
|
|
252
315
|
const camera = cameras[c];
|
|
253
316
|
// Both matrices are recomputed only on demand: a camera moved since
|
|
@@ -255,31 +318,33 @@ export class BatchedRenderer extends TransformNode {
|
|
|
255
318
|
// that has not rendered yet has no projection at all.
|
|
256
319
|
camera.getViewMatrix();
|
|
257
320
|
camera.getProjectionMatrix();
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (c !== 0) {
|
|
262
|
-
for (let p = 0; p < 6; p++) {
|
|
263
|
-
planes[c * 6 + p] = slice[p];
|
|
264
|
-
}
|
|
265
|
-
}
|
|
321
|
+
const planes = (this.cameraPlanes[c] ??= [0, 1, 2, 3, 4, 5].map(() => new Plane(0, 0, 0, 0)));
|
|
322
|
+
Frustum.GetPlanesToRef(camera.getTransformationMatrix(), planes);
|
|
323
|
+
this.cameraMasks[c] = camera.layerMask;
|
|
266
324
|
}
|
|
267
|
-
|
|
325
|
+
this.cameraCount = cameras.length;
|
|
326
|
+
return cameras.length;
|
|
268
327
|
}
|
|
269
328
|
|
|
270
329
|
/**
|
|
271
|
-
* True when `system`'s world bounds reach into at least one camera
|
|
272
|
-
*
|
|
330
|
+
* True when `system`'s world bounds reach into the frustum of at least one camera whose
|
|
331
|
+
* `layerMask` shares a bit with the system's (a camera that does not render the system's layer
|
|
332
|
+
* does not see it), or when it has no bounds to test (empty, or not yet measured).
|
|
273
333
|
*/
|
|
274
|
-
private isSystemInView(system: ParticleSystem
|
|
334
|
+
private isSystemInView(system: ParticleSystem): boolean {
|
|
275
335
|
const min = this.boundsMin;
|
|
276
336
|
const max = this.boundsMax;
|
|
277
337
|
if (!system.getWorldBounds(min, max)) {
|
|
278
338
|
return true;
|
|
279
339
|
}
|
|
280
|
-
|
|
340
|
+
const layerMask = system.layers.mask;
|
|
341
|
+
for (let c = 0; c < this.cameraCount; c++) {
|
|
342
|
+
if ((this.cameraMasks[c] & layerMask) === 0) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
const planes = this.cameraPlanes[c];
|
|
281
346
|
let inside = true;
|
|
282
|
-
for (let p =
|
|
347
|
+
for (let p = 0; p < 6; p++) {
|
|
283
348
|
const plane = planes[p];
|
|
284
349
|
const n = plane.normal;
|
|
285
350
|
// The box corner farthest along the plane's normal: if even it
|
|
@@ -303,52 +368,29 @@ export class BatchedRenderer extends TransformNode {
|
|
|
303
368
|
return Math.max(min, Math.min(max, value));
|
|
304
369
|
}
|
|
305
370
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
a.reflectionTexture === b.reflectionTexture &&
|
|
315
|
-
a.reflectionLevel === b.reflectionLevel &&
|
|
316
|
-
reflectionFacesEqual(a.reflectionFaces, b.reflectionFaces) &&
|
|
317
|
-
a.reflectionAtlas === b.reflectionAtlas &&
|
|
318
|
-
a.renderMode === b.renderMode &&
|
|
319
|
-
a.blendTiles === b.blendTiles &&
|
|
320
|
-
a.softParticles === b.softParticles &&
|
|
321
|
-
a.softFarFade === b.softFarFade &&
|
|
322
|
-
a.softNearFade === b.softNearFade &&
|
|
323
|
-
a.uTileCount === b.uTileCount &&
|
|
324
|
-
a.vTileCount === b.vTileCount &&
|
|
325
|
-
a.instancingGeometry === b.instancingGeometry &&
|
|
326
|
-
// Trails draw their own strip and read none of the instanced geometry's UVs.
|
|
327
|
-
(a.renderMode === RenderMode.Trail || a.instancingUVs === b.instancingUVs) &&
|
|
328
|
-
// Normals, and the second UV set when read, only matter to meshes.
|
|
329
|
-
(a.renderMode !== RenderMode.Mesh || a.instancingNormals === b.instancingNormals) &&
|
|
330
|
-
(a.renderMode !== RenderMode.Mesh || !readsUV1(a) || a.instancingUVs2 === b.instancingUVs2) &&
|
|
331
|
-
a.renderOrder === b.renderOrder &&
|
|
332
|
-
a.layerMask === b.layerMask &&
|
|
333
|
-
a.renderingGroupId === b.renderingGroupId &&
|
|
334
|
-
a.applyFog === b.applyFog &&
|
|
335
|
-
pivotEqual(a.pivot, b.pivot) &&
|
|
336
|
-
a.sortMode === (b.sortMode ?? 'none') &&
|
|
337
|
-
a.minParticleSize === (b.minParticleSize ?? 0) &&
|
|
338
|
-
a.maxParticleSize === (b.maxParticleSize ?? Infinity) &&
|
|
339
|
-
(a.lighting ?? 'fixed') === (b.lighting ?? 'fixed') &&
|
|
340
|
-
meshRimEqual(a.rim, b.rim) &&
|
|
341
|
-
mapUVEqual(a.mapUV, b.mapUV) &&
|
|
342
|
-
uvDistortionEqual(a.uvDistortion, b.uvDistortion) &&
|
|
343
|
-
gradientMapEqual(a.gradientMap, b.gradientMap) &&
|
|
344
|
-
erosionEqual(a.erosion, b.erosion)
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
addSystem(system: IParticleSystem) {
|
|
371
|
+
/**
|
|
372
|
+
* Starts drawing and updating `system`, in the batch whose settings match its own (a new one if
|
|
373
|
+
* none does). Add each system of an effect, or use `QuarksUtil.addToBatchRenderer` on its root.
|
|
374
|
+
*/
|
|
375
|
+
addSystem(system: IParticleSystem): void {
|
|
376
|
+
if (this.isDisposed()) {
|
|
377
|
+
throw new Error('BatchedRenderer.addSystem: the renderer has been disposed');
|
|
378
|
+
}
|
|
349
379
|
const particleSystem = system as ParticleSystem;
|
|
380
|
+
if (particleSystem.emitter.getScene() !== this.getScene()) {
|
|
381
|
+
// Its batch would hold meshes and materials of two scenes.
|
|
382
|
+
throw new Error('BatchedRenderer.addSystem: the system belongs to another scene');
|
|
383
|
+
}
|
|
384
|
+
const previous = particleSystem._renderer;
|
|
385
|
+
if (previous !== undefined && previous !== this) {
|
|
386
|
+
// One renderer at a time, or two would step and draw it.
|
|
387
|
+
previous.deleteSystem(system);
|
|
388
|
+
}
|
|
350
389
|
particleSystem._renderer = this;
|
|
351
|
-
|
|
390
|
+
// Added again after its settings changed: out of the old batch first, so it lands in the
|
|
391
|
+
// one that matches now rather than in both.
|
|
392
|
+
this.removeFromBatch(system);
|
|
393
|
+
if (this.systems.indexOf(system) === -1) {
|
|
352
394
|
this.systems.push(system);
|
|
353
395
|
this.orderedSystemsStale = true;
|
|
354
396
|
}
|
|
@@ -362,10 +404,11 @@ export class BatchedRenderer extends TransformNode {
|
|
|
362
404
|
private assignToBatch(particleSystem: ParticleSystem) {
|
|
363
405
|
const system = particleSystem as IParticleSystem;
|
|
364
406
|
const settings = particleSystem.getRendererSettings();
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
this.
|
|
407
|
+
const stored = storedBatchSettings(settings);
|
|
408
|
+
for (let i = 0; i < this.batchList.length; i++) {
|
|
409
|
+
if (batchSettingsEqual(this.batchList[i].settings, stored)) {
|
|
410
|
+
this.batchList[i].addSystem(system);
|
|
411
|
+
this.batchIndexOf.set(system, i);
|
|
369
412
|
return;
|
|
370
413
|
}
|
|
371
414
|
}
|
|
@@ -373,7 +416,7 @@ export class BatchedRenderer extends TransformNode {
|
|
|
373
416
|
const scene = this.getScene();
|
|
374
417
|
switch (settings.renderMode) {
|
|
375
418
|
case RenderMode.Trail:
|
|
376
|
-
batch = new TrailBatch(settings, scene);
|
|
419
|
+
batch = new TrailBatch(settings, scene, this.depth);
|
|
377
420
|
break;
|
|
378
421
|
case RenderMode.Mesh:
|
|
379
422
|
case RenderMode.BillBoard:
|
|
@@ -381,23 +424,24 @@ export class BatchedRenderer extends TransformNode {
|
|
|
381
424
|
case RenderMode.HorizontalBillBoard:
|
|
382
425
|
case RenderMode.StretchedBillBoard:
|
|
383
426
|
case RenderMode.Beam:
|
|
384
|
-
batch = new SpriteBatch(settings, scene);
|
|
427
|
+
batch = new SpriteBatch(settings, scene, this.depth);
|
|
385
428
|
break;
|
|
386
429
|
default:
|
|
387
430
|
throw new Error(`Unsupported render mode: ${settings.renderMode}`);
|
|
388
431
|
}
|
|
389
|
-
batch.
|
|
390
|
-
if (this.depthTexture) {
|
|
391
|
-
batch.applyDepthTexture(this.depthTexture);
|
|
392
|
-
}
|
|
432
|
+
batch.attachTo(this);
|
|
393
433
|
batch.applyMeshLighting(this.meshLighting);
|
|
394
434
|
batch.addSystem(system);
|
|
395
|
-
this.
|
|
435
|
+
this.batchList.push(batch);
|
|
396
436
|
this.batchEmptyFrames.push(0);
|
|
397
|
-
this.
|
|
437
|
+
this.batchIndexOf.set(system, this.batchList.length - 1);
|
|
398
438
|
}
|
|
399
439
|
|
|
400
|
-
|
|
440
|
+
/**
|
|
441
|
+
* Stops drawing and updating `system`; disposing the system calls it. Its particles are left as they are.
|
|
442
|
+
* @returns Whether the system was in one of this renderer's batches.
|
|
443
|
+
*/
|
|
444
|
+
deleteSystem(system: IParticleSystem): boolean {
|
|
401
445
|
const removed = this.removeFromBatch(system);
|
|
402
446
|
const index = this.systems.indexOf(system);
|
|
403
447
|
if (index !== -1) {
|
|
@@ -416,31 +460,76 @@ export class BatchedRenderer extends TransformNode {
|
|
|
416
460
|
|
|
417
461
|
/** Detaches a system from its batch, leaving the renderer's system list alone. */
|
|
418
462
|
private removeFromBatch(system: IParticleSystem): boolean {
|
|
419
|
-
const batchIndex = this.
|
|
463
|
+
const batchIndex = this.batchIndexOf.get(system);
|
|
420
464
|
if (batchIndex === undefined) {
|
|
421
465
|
return false;
|
|
422
466
|
}
|
|
423
|
-
this.
|
|
424
|
-
this.
|
|
467
|
+
this.batchList[batchIndex].removeSystem(system);
|
|
468
|
+
this.batchIndexOf.delete(system);
|
|
425
469
|
return true;
|
|
426
470
|
}
|
|
427
471
|
|
|
428
472
|
/**
|
|
429
|
-
*
|
|
430
|
-
*
|
|
473
|
+
* Takes a system's changed render settings. When only live parameters changed (see
|
|
474
|
+
* {@link VFXBatchSettings}) and the system is alone in its batch, the batch's material is
|
|
475
|
+
* written in place. Otherwise the system moves to the batch its settings now match. The
|
|
476
|
+
* system keeps its place in the update order, so this is safe to call mid-frame.
|
|
431
477
|
*/
|
|
432
478
|
updateSystem(system: IParticleSystem) {
|
|
479
|
+
const batchIndex = this.batchIndexOf.get(system);
|
|
480
|
+
if (batchIndex !== undefined) {
|
|
481
|
+
const batch = this.batchList[batchIndex];
|
|
482
|
+
const settings = storedBatchSettings((system as ParticleSystem).getRendererSettings());
|
|
483
|
+
if (
|
|
484
|
+
batch.systems.size === 1 &&
|
|
485
|
+
shaderKeyEqual(batch.settings, settings) &&
|
|
486
|
+
// A batch that already draws with these values takes the system instead, so two
|
|
487
|
+
// batches never hold the same settings.
|
|
488
|
+
!this.batchList.some((other) => other !== batch && batchSettingsEqual(other.settings, settings))
|
|
489
|
+
) {
|
|
490
|
+
batch.applyLiveSettings(settings);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
433
494
|
this.removeFromBatch(system);
|
|
434
495
|
this.addSystem(system);
|
|
435
496
|
}
|
|
436
497
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
498
|
+
/**
|
|
499
|
+
* Sets the scene depth that soft particles fade against. Without one, soft particles are not
|
|
500
|
+
* compiled in and draw hard, as Unity does for a camera with no depth texture.
|
|
501
|
+
*
|
|
502
|
+
* Pass the `DepthRenderer` itself to have its encoding read off it, for example
|
|
503
|
+
* `renderer.setDepthTexture(scene.enableDepthRenderer(camera))`. A bare texture is read as
|
|
504
|
+
* Babylon's default `'linear'` metric unless `options.encoding` says otherwise; see
|
|
505
|
+
* {@link SoftDepthEncoding}. The depth should be drawn by the camera that draws the particles.
|
|
506
|
+
* The texture is held, not the renderer: set it again if the `DepthRenderer` is disposed or
|
|
507
|
+
* replaced.
|
|
508
|
+
*
|
|
509
|
+
* @param depth - A depth texture, a Babylon `DepthRenderer`, or null to turn soft particles off.
|
|
510
|
+
* @param options - What the texture stores, when it cannot be read off a `DepthRenderer`.
|
|
511
|
+
*/
|
|
512
|
+
setDepthTexture(depth: BaseTexture | DepthRenderer | null, options: DepthTextureOptions = {}) {
|
|
513
|
+
this.depth = depth ? depthBinding(depth, options) : null;
|
|
514
|
+
for (const batch of this.batchList) {
|
|
515
|
+
batch.applyDepthTexture(this.depth);
|
|
441
516
|
}
|
|
442
517
|
}
|
|
443
518
|
|
|
519
|
+
/** The depth texture soft particles fade against, see {@link setDepthTexture}. */
|
|
520
|
+
get depthTexture(): BaseTexture | null {
|
|
521
|
+
return this.depth?.texture ?? null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
set depthTexture(texture: BaseTexture | null) {
|
|
525
|
+
this.setDepthTexture(texture);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** What the depth texture stores, see {@link setDepthTexture}. */
|
|
529
|
+
get depthEncoding(): SoftDepthEncoding | null {
|
|
530
|
+
return this.depth?.encoding ?? null;
|
|
531
|
+
}
|
|
532
|
+
|
|
444
533
|
/**
|
|
445
534
|
* Sets the light mesh particles with `lighting: 'fixed'` (the default) are
|
|
446
535
|
* lit by, for every mesh batch of this renderer, including ones created
|
|
@@ -463,7 +552,7 @@ export class BatchedRenderer extends TransformNode {
|
|
|
463
552
|
if (lighting.ambient) {
|
|
464
553
|
current.ambient = lighting.ambient.clone();
|
|
465
554
|
}
|
|
466
|
-
for (const batch of this.
|
|
555
|
+
for (const batch of this.batchList) {
|
|
467
556
|
batch.applyMeshLighting(current);
|
|
468
557
|
}
|
|
469
558
|
}
|
|
@@ -478,9 +567,14 @@ export class BatchedRenderer extends TransformNode {
|
|
|
478
567
|
};
|
|
479
568
|
}
|
|
480
569
|
|
|
481
|
-
|
|
570
|
+
/**
|
|
571
|
+
* Turns on adaptive performance: the renderer measures its own update and thins every system's
|
|
572
|
+
* emission while it runs over budget, see {@link AdaptivePerformanceOptions}. Options left out
|
|
573
|
+
* keep their current values.
|
|
574
|
+
*/
|
|
575
|
+
configureAdaptivePerformance(options: Partial<AdaptivePerformanceOptions> = {}): void {
|
|
482
576
|
const state = this.adaptivePerformanceState;
|
|
483
|
-
const minQuality = BatchedRenderer.clamp(options.minQuality ?? state.minQuality, 0.
|
|
577
|
+
const minQuality = BatchedRenderer.clamp(options.minQuality ?? state.minQuality, 0.1, 1);
|
|
484
578
|
const maxQuality = BatchedRenderer.clamp(options.maxQuality ?? state.maxQuality, minQuality, 1);
|
|
485
579
|
state.targetFrameMs = Math.max(options.targetFrameMs ?? state.targetFrameMs, 0.1);
|
|
486
580
|
state.minQuality = minQuality;
|
|
@@ -492,7 +586,8 @@ export class BatchedRenderer extends TransformNode {
|
|
|
492
586
|
this.lastAppliedQuality = Number.NaN;
|
|
493
587
|
}
|
|
494
588
|
|
|
495
|
-
|
|
589
|
+
/** Turns adaptive performance off, and by default puts every system back at `maxQuality`. */
|
|
590
|
+
disableAdaptivePerformance(resetQuality = true): void {
|
|
496
591
|
this.adaptivePerformanceState.enabled = false;
|
|
497
592
|
if (resetQuality) {
|
|
498
593
|
this.adaptivePerformanceState.currentQuality = this.adaptivePerformanceState.maxQuality;
|
|
@@ -500,6 +595,7 @@ export class BatchedRenderer extends TransformNode {
|
|
|
500
595
|
}
|
|
501
596
|
}
|
|
502
597
|
|
|
598
|
+
/** A copy of the adaptive performance settings and the current quality. */
|
|
503
599
|
getAdaptivePerformanceState(): AdaptivePerformanceState {
|
|
504
600
|
return {...this.adaptivePerformanceState};
|
|
505
601
|
}
|
|
@@ -543,7 +639,9 @@ export class BatchedRenderer extends TransformNode {
|
|
|
543
639
|
if (behavior.type !== 'EmitSubParticleSystem') {
|
|
544
640
|
continue;
|
|
545
641
|
}
|
|
546
|
-
|
|
642
|
+
// A uuid string is a link the loader could not resolve: no target.
|
|
643
|
+
const sub = (behavior as EmitSubParticleSystem).subParticleSystem;
|
|
644
|
+
const target = typeof sub === 'object' ? sub.system : undefined;
|
|
547
645
|
if (target !== undefined && known.has(target)) {
|
|
548
646
|
targets.push(target);
|
|
549
647
|
}
|
|
@@ -573,7 +671,12 @@ export class BatchedRenderer extends TransformNode {
|
|
|
573
671
|
return ordered;
|
|
574
672
|
}
|
|
575
673
|
|
|
576
|
-
|
|
674
|
+
/**
|
|
675
|
+
* Advances every visible system by `delta` seconds and refills the batches. Call it once per
|
|
676
|
+
* frame. A system whose emitter is disabled is frozen; one no camera sees pauses if its
|
|
677
|
+
* {@link ParticleSystem.cullingMode} says so.
|
|
678
|
+
*/
|
|
679
|
+
update(delta: number): void {
|
|
577
680
|
this.elapsed += delta;
|
|
578
681
|
const adaptiveState = this.adaptivePerformanceState;
|
|
579
682
|
const frameStart = adaptiveState.enabled ? performance.now() : 0;
|
|
@@ -581,7 +684,7 @@ export class BatchedRenderer extends TransformNode {
|
|
|
581
684
|
this.applyAdaptiveQuality();
|
|
582
685
|
}
|
|
583
686
|
const systems = this.orderSystems();
|
|
584
|
-
const
|
|
687
|
+
const cameraCount = this.cullingEnabled ? this.collectCameras() : 0;
|
|
585
688
|
const budgetStart =
|
|
586
689
|
this.simulationBudgetMs !== Infinity || this.catchUpBudgetMs !== Infinity ? performance.now() : 0;
|
|
587
690
|
const stepDeadline = this.simulationBudgetMs === Infinity ? Infinity : budgetStart + this.simulationBudgetMs;
|
|
@@ -595,11 +698,10 @@ export class BatchedRenderer extends TransformNode {
|
|
|
595
698
|
// A system no camera sees pauses, by its own choice. One that
|
|
596
699
|
// only serves as a sub emitter target follows its parent.
|
|
597
700
|
system.culled =
|
|
598
|
-
|
|
599
|
-
planes.length > 0 &&
|
|
701
|
+
cameraCount > 0 &&
|
|
600
702
|
system.cullingMode !== 'always' &&
|
|
601
703
|
system.onlyUsedByOther !== true &&
|
|
602
|
-
!this.isSystemInView(system
|
|
704
|
+
!this.isSystemInView(system);
|
|
603
705
|
system.update(delta, stepDeadline, catchUpDeadline);
|
|
604
706
|
}
|
|
605
707
|
// An autoDestroy system disposes itself from inside update and drops
|
|
@@ -632,8 +734,8 @@ export class BatchedRenderer extends TransformNode {
|
|
|
632
734
|
/** Rebuild instance buffers from current particle state without advancing simulation. */
|
|
633
735
|
refreshBatches(): void {
|
|
634
736
|
const emptyFrames = this.batchEmptyFrames;
|
|
635
|
-
for (let i = 0; i < this.
|
|
636
|
-
const batch = this.
|
|
737
|
+
for (let i = 0; i < this.batchList.length; i++) {
|
|
738
|
+
const batch = this.batchList[i];
|
|
637
739
|
batch.time = this.elapsed;
|
|
638
740
|
if (batch.systems.size > 0) {
|
|
639
741
|
emptyFrames[i] = 0;
|
|
@@ -657,25 +759,30 @@ export class BatchedRenderer extends TransformNode {
|
|
|
657
759
|
|
|
658
760
|
/** Disposes the batch at `index` and repairs the system→batch mapping. */
|
|
659
761
|
private removeBatch(index: number): void {
|
|
660
|
-
this.
|
|
661
|
-
this.
|
|
762
|
+
this.batchList[index].dispose();
|
|
763
|
+
this.batchList.splice(index, 1);
|
|
662
764
|
this.batchEmptyFrames.splice(index, 1);
|
|
663
|
-
for (const [system, batchIndex] of this.
|
|
765
|
+
for (const [system, batchIndex] of this.batchIndexOf) {
|
|
664
766
|
if (batchIndex > index) {
|
|
665
|
-
this.
|
|
767
|
+
this.batchIndexOf.set(system, batchIndex - 1);
|
|
666
768
|
}
|
|
667
769
|
}
|
|
668
770
|
}
|
|
669
771
|
|
|
670
772
|
/** Per-batch particle vs GPU instance counts — for diagnosing stale render state. */
|
|
671
773
|
getBatchRenderStats(): Array<{
|
|
774
|
+
/** Index into {@link batches}. */
|
|
672
775
|
batchIndex: number;
|
|
776
|
+
/** The batch's render mode. */
|
|
673
777
|
renderMode: RenderMode;
|
|
778
|
+
/** Instances the batch mesh draws. */
|
|
674
779
|
instanceCount: number;
|
|
780
|
+
/** Live particles of the batch's systems. */
|
|
675
781
|
particleCount: number;
|
|
782
|
+
/** The emitter names of the batch's systems. */
|
|
676
783
|
systemNames: string[];
|
|
677
784
|
}> {
|
|
678
|
-
return this.
|
|
785
|
+
return this.batchList.map((batch, batchIndex) => {
|
|
679
786
|
let particleCount = 0;
|
|
680
787
|
const systemNames: string[] = [];
|
|
681
788
|
for (const system of batch.systems) {
|
|
@@ -693,14 +800,25 @@ export class BatchedRenderer extends TransformNode {
|
|
|
693
800
|
});
|
|
694
801
|
}
|
|
695
802
|
|
|
803
|
+
/**
|
|
804
|
+
* Disposes the batches and their meshes and materials, and lets the systems go: they keep
|
|
805
|
+
* their particles and can be added to another renderer. The renderer cannot be used again.
|
|
806
|
+
*/
|
|
696
807
|
dispose(): void {
|
|
697
|
-
for (const batch of this.
|
|
808
|
+
for (const batch of this.batchList) {
|
|
698
809
|
batch.dispose();
|
|
699
810
|
}
|
|
700
|
-
this.
|
|
811
|
+
this.batchList.length = 0;
|
|
701
812
|
this.batchEmptyFrames.length = 0;
|
|
813
|
+
for (const system of this.systems) {
|
|
814
|
+
if ((system as ParticleSystem)._renderer === this) {
|
|
815
|
+
(system as ParticleSystem)._renderer = undefined;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
702
818
|
this.systems.length = 0;
|
|
703
|
-
this.
|
|
819
|
+
this.orderedSystems = [];
|
|
820
|
+
this.orderedSystemsStale = true;
|
|
821
|
+
this.batchIndexOf.clear();
|
|
704
822
|
super.dispose();
|
|
705
823
|
}
|
|
706
824
|
}
|