babylon.quarks 0.17.3 → 0.17.6

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/LICENSE +21 -21
  3. package/README.md +165 -33
  4. package/dist/babylon.quarks.cjs +1 -1
  5. package/dist/babylon.quarks.cjs.map +1 -1
  6. package/dist/babylon.quarks.esm.js +1 -1
  7. package/dist/babylon.quarks.esm.js.map +1 -1
  8. package/dist/babylon.quarks.umd.min.js +2 -0
  9. package/dist/babylon.quarks.umd.min.js.map +1 -0
  10. package/dist/types/index.d.ts +2829 -19
  11. package/package.json +62 -60
  12. package/src/BatchedParticleRenderer.ts +3 -3
  13. package/src/BatchedRenderer.ts +228 -227
  14. package/src/MeshSurfaceEmitter.ts +137 -137
  15. package/src/ParticleEmitter.ts +86 -86
  16. package/src/ParticleSystem.ts +1322 -1274
  17. package/src/QuarksLoader.ts +529 -507
  18. package/src/QuarksPrefab.ts +307 -307
  19. package/src/QuarksUtil.ts +66 -66
  20. package/src/SpriteBatch.ts +403 -404
  21. package/src/TrailBatch.ts +420 -419
  22. package/src/VFXBatch.ts +108 -108
  23. package/src/geometryUtil.ts +15 -0
  24. package/src/index.ts +59 -52
  25. package/src/materials/ParticleMaterials.ts +23 -23
  26. package/src/materials/index.ts +1 -1
  27. package/src/shaders/chunks/index.ts +1 -1
  28. package/src/shaders/chunks/register-shader-chunks.ts +32 -32
  29. package/src/shaders/chunks/soft_fragment.glsl.ts +13 -13
  30. package/src/shaders/chunks/soft_pars_fragment.glsl.ts +20 -20
  31. package/src/shaders/chunks/soft_pars_vertex.glsl.ts +6 -6
  32. package/src/shaders/chunks/soft_vertex.glsl.ts +6 -6
  33. package/src/shaders/chunks/tile_fragment.glsl.ts +9 -9
  34. package/src/shaders/chunks/tile_pars_fragment.glsl.ts +9 -9
  35. package/src/shaders/chunks/tile_pars_vertex.glsl.ts +28 -28
  36. package/src/shaders/chunks/tile_vertex.glsl.ts +18 -18
  37. package/src/shaders/index.ts +9 -9
  38. package/src/shaders/local_particle_physics_vert.glsl.ts +79 -79
  39. package/src/shaders/local_particle_vert.glsl.ts +50 -50
  40. package/src/shaders/particle_frag.glsl.ts +61 -61
  41. package/src/shaders/particle_physics_frag.glsl.ts +67 -67
  42. package/src/shaders/particle_vert.glsl.ts +85 -85
  43. package/src/shaders/stretched_bb_particle_vert.glsl.ts +74 -74
  44. package/src/shaders/trail_frag.glsl.ts +21 -21
  45. package/src/shaders/trail_vert.glsl.ts +58 -58
  46. package/dist/types/BatchedParticleRenderer.d.ts +0 -2
  47. package/dist/types/BatchedRenderer.d.ts +0 -40
  48. package/dist/types/MeshSurfaceEmitter.d.ts +0 -19
  49. package/dist/types/ParticleEmitter.d.ts +0 -16
  50. package/dist/types/ParticleSystem.d.ts +0 -226
  51. package/dist/types/QuarksLoader.d.ts +0 -28
  52. package/dist/types/QuarksPrefab.d.ts +0 -48
  53. package/dist/types/QuarksUtil.d.ts +0 -14
  54. package/dist/types/SpriteBatch.d.ts +0 -32
  55. package/dist/types/TrailBatch.d.ts +0 -27
  56. package/dist/types/VFXBatch.d.ts +0 -51
  57. package/dist/types/materials/ParticleMaterials.d.ts +0 -12
  58. package/dist/types/materials/index.d.ts +0 -1
  59. package/dist/types/shaders/chunks/index.d.ts +0 -1
  60. package/dist/types/shaders/chunks/register-shader-chunks.d.ts +0 -3
  61. package/dist/types/shaders/chunks/soft_fragment.glsl.d.ts +0 -2
  62. package/dist/types/shaders/chunks/soft_pars_fragment.glsl.d.ts +0 -2
  63. package/dist/types/shaders/chunks/soft_pars_vertex.glsl.d.ts +0 -2
  64. package/dist/types/shaders/chunks/soft_vertex.glsl.d.ts +0 -2
  65. package/dist/types/shaders/chunks/tile_fragment.glsl.d.ts +0 -2
  66. package/dist/types/shaders/chunks/tile_pars_fragment.glsl.d.ts +0 -2
  67. package/dist/types/shaders/chunks/tile_pars_vertex.glsl.d.ts +0 -2
  68. package/dist/types/shaders/chunks/tile_vertex.glsl.d.ts +0 -2
  69. package/dist/types/shaders/index.d.ts +0 -9
  70. package/dist/types/shaders/local_particle_physics_vert.glsl.d.ts +0 -2
  71. package/dist/types/shaders/local_particle_vert.glsl.d.ts +0 -2
  72. package/dist/types/shaders/particle_frag.glsl.d.ts +0 -2
  73. package/dist/types/shaders/particle_physics_frag.glsl.d.ts +0 -2
  74. package/dist/types/shaders/particle_vert.glsl.d.ts +0 -2
  75. package/dist/types/shaders/stretched_bb_particle_vert.glsl.d.ts +0 -2
  76. package/dist/types/shaders/trail_frag.glsl.d.ts +0 -2
  77. package/dist/types/shaders/trail_vert.glsl.d.ts +0 -2
package/package.json CHANGED
@@ -1,60 +1,62 @@
1
- {
2
- "name": "babylon.quarks",
3
- "version": "0.17.3",
4
- "description": "A High-Performance Particle System for Babylon.js",
5
- "type": "module",
6
- "types": "./dist/types/index.d.ts",
7
- "main": "./dist/babylon.quarks.cjs",
8
- "module": "./dist/babylon.quarks.esm.js",
9
- "exports": {
10
- ".": {
11
- "import": "./dist/babylon.quarks.esm.js",
12
- "require": "./dist/babylon.quarks.cjs",
13
- "types": "./dist/types/index.d.ts"
14
- }
15
- },
16
- "files": [
17
- "dist",
18
- "src",
19
- "LICENSE",
20
- "README.md"
21
- ],
22
- "scripts": {
23
- "clean": "rimraf dist",
24
- "build:types": "tsc --emitDeclarationOnly",
25
- "build:js": "cross-env NODE_ENV=production rollup -c",
26
- "build": "npm run clean && npm run build:types && npm run build:js",
27
- "test": "jest",
28
- "test:coverage": "jest --coverage",
29
- "prettier": "prettier --write --ignore-unknown **/*.ts"
30
- },
31
- "repository": {
32
- "type": "git",
33
- "url": "git+https://github.com/Soullnik/babylon.quarks-standalone.git"
34
- },
35
- "keywords": [
36
- "particle",
37
- "library",
38
- "3d",
39
- "game",
40
- "performance",
41
- "babylonjs",
42
- "vfx"
43
- ],
44
- "author": "soullnik <soullnik96@gmail.com>",
45
- "license": "MIT",
46
- "bugs": {
47
- "url": "https://github.com/Soullnik/babylon.quarks-standalone/issues"
48
- },
49
- "homepage": "https://github.com/Soullnik/babylon.quarks-standalone#readme",
50
- "peerDependencies": {
51
- "@babylonjs/core": ">=9.0.0"
52
- },
53
- "dependencies": {
54
- "quarks.core": "^0.17.0"
55
- },
56
- "devDependencies": {
57
- "@babylonjs/core": "^9.0.0",
58
- "@types/jest": "^30.0.0"
59
- }
60
- }
1
+ {
2
+ "name": "babylon.quarks",
3
+ "version": "0.17.6",
4
+ "description": "A High-Performance Particle System for Babylon.js",
5
+ "type": "module",
6
+ "types": "./dist/types/index.d.ts",
7
+ "main": "./dist/babylon.quarks.cjs",
8
+ "module": "./dist/babylon.quarks.esm.js",
9
+ "unpkg": "./dist/babylon.quarks.umd.min.js",
10
+ "jsdelivr": "./dist/babylon.quarks.umd.min.js",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/babylon.quarks.esm.js",
14
+ "require": "./dist/babylon.quarks.cjs",
15
+ "types": "./dist/types/index.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src",
21
+ "LICENSE",
22
+ "README.md",
23
+ "CHANGELOG.md"
24
+ ],
25
+ "scripts": {
26
+ "clean": "rimraf dist dist-types tsconfig.tsbuildinfo",
27
+ "build:types": "tsc --emitDeclarationOnly",
28
+ "build:js": "cross-env NODE_ENV=production rollup -c",
29
+ "build": "npm run clean && npm run build:types && npm run build:js",
30
+ "test": "jest",
31
+ "docs": "typedoc",
32
+ "test:coverage": "jest --coverage",
33
+ "prettier": "prettier --write --ignore-unknown **/*.ts"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/Soullnik/babylon.quarks-standalone.git"
38
+ },
39
+ "keywords": [
40
+ "particle",
41
+ "library",
42
+ "3d",
43
+ "game",
44
+ "performance",
45
+ "babylonjs",
46
+ "vfx"
47
+ ],
48
+ "author": "soullnik <soullnik96@gmail.com>",
49
+ "license": "MIT",
50
+ "bugs": {
51
+ "url": "https://github.com/Soullnik/babylon.quarks-standalone/issues"
52
+ },
53
+ "homepage": "https://github.com/Soullnik/babylon.quarks-standalone#readme",
54
+ "peerDependencies": {
55
+ "@babylonjs/core": ">=9.0.0"
56
+ },
57
+ "devDependencies": {
58
+ "@babylonjs/core": "^9.0.0",
59
+ "@types/jest": "^30.0.0",
60
+ "quarks.core": "*"
61
+ }
62
+ }
@@ -1,3 +1,3 @@
1
- import {BatchedRenderer} from './BatchedRenderer';
2
-
3
- export const BatchedParticleRenderer = BatchedRenderer;
1
+ import {BatchedRenderer} from './BatchedRenderer';
2
+
3
+ export const BatchedParticleRenderer = BatchedRenderer;
@@ -1,227 +1,228 @@
1
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
2
- import {Scene} from '@babylonjs/core/scene';
3
- import {Texture} from '@babylonjs/core/Materials/Textures/texture';
4
- import {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
5
- import {IParticleSystem} from 'quarks.core';
6
- import {VFXBatch, RenderMode, StoredBatchSettings} from './VFXBatch';
7
- import {SpriteBatch} from './SpriteBatch';
8
- import {TrailBatch} from './TrailBatch';
9
- import {ParticleSystem} from './ParticleSystem';
10
-
11
- export interface VFXBatchSettings {
12
- instancingGeometry: Float32Array;
13
- instancingIndices: Uint32Array | Uint16Array;
14
- instancingUVs?: Float32Array;
15
- instancingNormals?: Float32Array;
16
- renderMode: RenderMode;
17
- renderOrder: number;
18
- uTileCount: number;
19
- vTileCount: number;
20
- blendTiles: boolean;
21
- softParticles: boolean;
22
- softNearFade: number;
23
- softFarFade: number;
24
- materialBlendMode: number;
25
- materialTransparent: boolean;
26
- materialDepthTest: boolean;
27
- materialDepthWrite: boolean;
28
- materialAlphaTest: number;
29
- texture: Texture | null;
30
- layerMask: number;
31
- }
32
-
33
- export interface AdaptivePerformanceOptions {
34
- targetFrameMs: number;
35
- minQuality: number;
36
- maxQuality: number;
37
- decreaseStep: number;
38
- increaseStep: number;
39
- }
40
-
41
- export interface AdaptivePerformanceState extends AdaptivePerformanceOptions {
42
- enabled: boolean;
43
- currentQuality: number;
44
- lastFrameCpuMs: number;
45
- }
46
-
47
- export class BatchedRenderer extends TransformNode {
48
- batches: Array<VFXBatch> = [];
49
- systemToBatchIndex: Map<IParticleSystem, number> = new Map<IParticleSystem, number>();
50
- depthTexture: BaseTexture | null = null;
51
- private adaptivePerformanceState: AdaptivePerformanceState = {
52
- enabled: false,
53
- targetFrameMs: 16.7,
54
- minQuality: 0.35,
55
- maxQuality: 1,
56
- decreaseStep: 0.08,
57
- increaseStep: 0.02,
58
- currentQuality: 1,
59
- lastFrameCpuMs: 0,
60
- };
61
- private lastAppliedQuality = Number.NaN;
62
-
63
- constructor(name: string, scene: Scene) {
64
- super(name, scene);
65
- }
66
-
67
- private static clamp(value: number, min: number, max: number): number {
68
- return Math.max(min, Math.min(max, value));
69
- }
70
-
71
- private static equals(a: StoredBatchSettings, b: VFXBatchSettings): boolean {
72
- return (
73
- a.materialBlendMode === b.materialBlendMode &&
74
- a.materialTransparent === b.materialTransparent &&
75
- a.materialDepthTest === b.materialDepthTest &&
76
- a.materialDepthWrite === b.materialDepthWrite &&
77
- a.materialAlphaTest === b.materialAlphaTest &&
78
- a.texture === b.texture &&
79
- a.renderMode === b.renderMode &&
80
- a.blendTiles === b.blendTiles &&
81
- a.softParticles === b.softParticles &&
82
- a.softFarFade === b.softFarFade &&
83
- a.softNearFade === b.softNearFade &&
84
- a.uTileCount === b.uTileCount &&
85
- a.vTileCount === b.vTileCount &&
86
- a.instancingGeometry === b.instancingGeometry &&
87
- a.renderOrder === b.renderOrder &&
88
- a.layerMask === b.layerMask
89
- );
90
- }
91
-
92
- addSystem(system: IParticleSystem) {
93
- (system as unknown as ParticleSystem)._renderer = this;
94
- if (this.adaptivePerformanceState.enabled) {
95
- (system as any).setQualityFactor?.(this.adaptivePerformanceState.currentQuality);
96
- }
97
- const settings = (system as unknown as ParticleSystem).getRendererSettings();
98
- for (let i = 0; i < this.batches.length; i++) {
99
- if (BatchedRenderer.equals(this.batches[i].settings, settings)) {
100
- this.batches[i].addSystem(system);
101
- this.systemToBatchIndex.set(system, i);
102
- return;
103
- }
104
- }
105
- let batch: VFXBatch;
106
- const scene = this.getScene();
107
- switch (settings.renderMode) {
108
- case RenderMode.Trail:
109
- batch = new TrailBatch(settings, scene);
110
- break;
111
- case RenderMode.Mesh:
112
- case RenderMode.BillBoard:
113
- case RenderMode.VerticalBillBoard:
114
- case RenderMode.HorizontalBillBoard:
115
- case RenderMode.StretchedBillBoard:
116
- batch = new SpriteBatch(settings, scene);
117
- break;
118
- default:
119
- throw new Error(`Unsupported render mode: ${settings.renderMode}`);
120
- }
121
- batch.mesh.parent = this;
122
- if (this.depthTexture) {
123
- batch.applyDepthTexture(this.depthTexture);
124
- }
125
- batch.addSystem(system);
126
- this.batches.push(batch);
127
- this.systemToBatchIndex.set(system, this.batches.length - 1);
128
- }
129
-
130
- deleteSystem(system: IParticleSystem) {
131
- const batchIndex = this.systemToBatchIndex.get(system);
132
- if (batchIndex !== undefined) {
133
- this.batches[batchIndex].removeSystem(system);
134
- this.systemToBatchIndex.delete(system);
135
- }
136
- }
137
-
138
- updateSystem(system: IParticleSystem) {
139
- this.deleteSystem(system);
140
- this.addSystem(system);
141
- }
142
-
143
- setDepthTexture(depthTexture: BaseTexture | null) {
144
- this.depthTexture = depthTexture;
145
- for (const batch of this.batches) {
146
- batch.applyDepthTexture(depthTexture);
147
- }
148
- }
149
-
150
- configureAdaptivePerformance(options: Partial<AdaptivePerformanceOptions> = {}) {
151
- const state = this.adaptivePerformanceState;
152
- const minQuality = BatchedRenderer.clamp(options.minQuality ?? state.minQuality, 0.05, 1);
153
- const maxQuality = BatchedRenderer.clamp(options.maxQuality ?? state.maxQuality, minQuality, 1);
154
- state.targetFrameMs = Math.max(options.targetFrameMs ?? state.targetFrameMs, 0.1);
155
- state.minQuality = minQuality;
156
- state.maxQuality = maxQuality;
157
- state.decreaseStep = Math.max(options.decreaseStep ?? state.decreaseStep, 0.001);
158
- state.increaseStep = Math.max(options.increaseStep ?? state.increaseStep, 0.001);
159
- state.currentQuality = BatchedRenderer.clamp(state.currentQuality, state.minQuality, state.maxQuality);
160
- state.enabled = true;
161
- this.lastAppliedQuality = Number.NaN;
162
- }
163
-
164
- disableAdaptivePerformance(resetQuality = true) {
165
- this.adaptivePerformanceState.enabled = false;
166
- if (resetQuality) {
167
- this.adaptivePerformanceState.currentQuality = this.adaptivePerformanceState.maxQuality;
168
- this.applyAdaptiveQuality();
169
- }
170
- }
171
-
172
- getAdaptivePerformanceState(): AdaptivePerformanceState {
173
- return {...this.adaptivePerformanceState};
174
- }
175
-
176
- private applyAdaptiveQuality() {
177
- const quality = this.adaptivePerformanceState.currentQuality;
178
- if (quality === this.lastAppliedQuality) {
179
- return;
180
- }
181
- for (const ps of this.systemToBatchIndex.keys()) {
182
- (ps as any).setQualityFactor?.(quality);
183
- }
184
- this.lastAppliedQuality = quality;
185
- }
186
-
187
- update(delta: number) {
188
- const adaptiveState = this.adaptivePerformanceState;
189
- const frameStart = adaptiveState.enabled ? performance.now() : 0;
190
- if (adaptiveState.enabled) {
191
- this.applyAdaptiveQuality();
192
- }
193
- for (const ps of this.systemToBatchIndex.keys()) {
194
- (ps as any).update(delta);
195
- }
196
- for (let i = 0; i < this.batches.length; i++) {
197
- this.batches[i].update();
198
- }
199
- if (!adaptiveState.enabled) {
200
- return;
201
- }
202
-
203
- adaptiveState.lastFrameCpuMs = performance.now() - frameStart;
204
- if (adaptiveState.lastFrameCpuMs > adaptiveState.targetFrameMs) {
205
- adaptiveState.currentQuality = BatchedRenderer.clamp(
206
- adaptiveState.currentQuality - adaptiveState.decreaseStep,
207
- adaptiveState.minQuality,
208
- adaptiveState.maxQuality
209
- );
210
- } else {
211
- adaptiveState.currentQuality = BatchedRenderer.clamp(
212
- adaptiveState.currentQuality + adaptiveState.increaseStep,
213
- adaptiveState.minQuality,
214
- adaptiveState.maxQuality
215
- );
216
- }
217
- }
218
-
219
- dispose(): void {
220
- for (const batch of this.batches) {
221
- batch.dispose();
222
- }
223
- this.batches = [];
224
- this.systemToBatchIndex.clear();
225
- super.dispose();
226
- }
227
- }
1
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
2
+ import {Scene} from '@babylonjs/core/scene';
3
+ import {Texture} from '@babylonjs/core/Materials/Textures/texture';
4
+ import {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
5
+ import {IParticleSystem} from 'quarks.core';
6
+ import {VFXBatch, RenderMode, StoredBatchSettings} from './VFXBatch';
7
+ import {SpriteBatch} from './SpriteBatch';
8
+ import {TrailBatch} from './TrailBatch';
9
+ import {ParticleSystem} from './ParticleSystem';
10
+
11
+ export interface VFXBatchSettings {
12
+ instancingGeometry: Float32Array;
13
+ instancingIndices: Uint32Array | Uint16Array;
14
+ instancingUVs?: Float32Array;
15
+ instancingNormals?: Float32Array;
16
+ renderMode: RenderMode;
17
+ renderOrder: number;
18
+ uTileCount: number;
19
+ vTileCount: number;
20
+ blendTiles: boolean;
21
+ softParticles: boolean;
22
+ softNearFade: number;
23
+ softFarFade: number;
24
+ materialBlendMode: number;
25
+ materialTransparent: boolean;
26
+ materialDepthTest: boolean;
27
+ materialDepthWrite: boolean;
28
+ materialAlphaTest: number;
29
+ texture: Texture | null;
30
+ layerMask: number;
31
+ }
32
+
33
+ export interface AdaptivePerformanceOptions {
34
+ targetFrameMs: number;
35
+ minQuality: number;
36
+ maxQuality: number;
37
+ decreaseStep: number;
38
+ increaseStep: number;
39
+ }
40
+
41
+ export interface AdaptivePerformanceState extends AdaptivePerformanceOptions {
42
+ enabled: boolean;
43
+ currentQuality: number;
44
+ lastFrameCpuMs: number;
45
+ }
46
+
47
+ export class BatchedRenderer extends TransformNode {
48
+ batches: Array<VFXBatch> = [];
49
+ systemToBatchIndex: Map<IParticleSystem, number> = new Map<IParticleSystem, number>();
50
+ depthTexture: BaseTexture | null = null;
51
+ private adaptivePerformanceState: AdaptivePerformanceState = {
52
+ enabled: false,
53
+ targetFrameMs: 16.7,
54
+ minQuality: 0.35,
55
+ maxQuality: 1,
56
+ decreaseStep: 0.08,
57
+ increaseStep: 0.02,
58
+ currentQuality: 1,
59
+ lastFrameCpuMs: 0,
60
+ };
61
+ private lastAppliedQuality = Number.NaN;
62
+
63
+ constructor(name: string, scene: Scene) {
64
+ super(name, scene);
65
+ }
66
+
67
+ private static clamp(value: number, min: number, max: number): number {
68
+ return Math.max(min, Math.min(max, value));
69
+ }
70
+
71
+ private static equals(a: StoredBatchSettings, b: VFXBatchSettings): boolean {
72
+ return (
73
+ a.materialBlendMode === b.materialBlendMode &&
74
+ a.materialTransparent === b.materialTransparent &&
75
+ a.materialDepthTest === b.materialDepthTest &&
76
+ a.materialDepthWrite === b.materialDepthWrite &&
77
+ a.materialAlphaTest === b.materialAlphaTest &&
78
+ a.texture === b.texture &&
79
+ a.renderMode === b.renderMode &&
80
+ a.blendTiles === b.blendTiles &&
81
+ a.softParticles === b.softParticles &&
82
+ a.softFarFade === b.softFarFade &&
83
+ a.softNearFade === b.softNearFade &&
84
+ a.uTileCount === b.uTileCount &&
85
+ a.vTileCount === b.vTileCount &&
86
+ a.instancingGeometry === b.instancingGeometry &&
87
+ a.renderOrder === b.renderOrder &&
88
+ a.layerMask === b.layerMask
89
+ );
90
+ }
91
+
92
+ addSystem(system: IParticleSystem) {
93
+ const particleSystem = system as ParticleSystem;
94
+ particleSystem._renderer = this;
95
+ if (this.adaptivePerformanceState.enabled) {
96
+ particleSystem.setQualityFactor(this.adaptivePerformanceState.currentQuality);
97
+ }
98
+ const settings = particleSystem.getRendererSettings();
99
+ for (let i = 0; i < this.batches.length; i++) {
100
+ if (BatchedRenderer.equals(this.batches[i].settings, settings)) {
101
+ this.batches[i].addSystem(system);
102
+ this.systemToBatchIndex.set(system, i);
103
+ return;
104
+ }
105
+ }
106
+ let batch: VFXBatch;
107
+ const scene = this.getScene();
108
+ switch (settings.renderMode) {
109
+ case RenderMode.Trail:
110
+ batch = new TrailBatch(settings, scene);
111
+ break;
112
+ case RenderMode.Mesh:
113
+ case RenderMode.BillBoard:
114
+ case RenderMode.VerticalBillBoard:
115
+ case RenderMode.HorizontalBillBoard:
116
+ case RenderMode.StretchedBillBoard:
117
+ batch = new SpriteBatch(settings, scene);
118
+ break;
119
+ default:
120
+ throw new Error(`Unsupported render mode: ${settings.renderMode}`);
121
+ }
122
+ batch.mesh.parent = this;
123
+ if (this.depthTexture) {
124
+ batch.applyDepthTexture(this.depthTexture);
125
+ }
126
+ batch.addSystem(system);
127
+ this.batches.push(batch);
128
+ this.systemToBatchIndex.set(system, this.batches.length - 1);
129
+ }
130
+
131
+ deleteSystem(system: IParticleSystem) {
132
+ const batchIndex = this.systemToBatchIndex.get(system);
133
+ if (batchIndex !== undefined) {
134
+ this.batches[batchIndex].removeSystem(system);
135
+ this.systemToBatchIndex.delete(system);
136
+ }
137
+ }
138
+
139
+ updateSystem(system: IParticleSystem) {
140
+ this.deleteSystem(system);
141
+ this.addSystem(system);
142
+ }
143
+
144
+ setDepthTexture(depthTexture: BaseTexture | null) {
145
+ this.depthTexture = depthTexture;
146
+ for (const batch of this.batches) {
147
+ batch.applyDepthTexture(depthTexture);
148
+ }
149
+ }
150
+
151
+ configureAdaptivePerformance(options: Partial<AdaptivePerformanceOptions> = {}) {
152
+ const state = this.adaptivePerformanceState;
153
+ const minQuality = BatchedRenderer.clamp(options.minQuality ?? state.minQuality, 0.05, 1);
154
+ const maxQuality = BatchedRenderer.clamp(options.maxQuality ?? state.maxQuality, minQuality, 1);
155
+ state.targetFrameMs = Math.max(options.targetFrameMs ?? state.targetFrameMs, 0.1);
156
+ state.minQuality = minQuality;
157
+ state.maxQuality = maxQuality;
158
+ state.decreaseStep = Math.max(options.decreaseStep ?? state.decreaseStep, 0.001);
159
+ state.increaseStep = Math.max(options.increaseStep ?? state.increaseStep, 0.001);
160
+ state.currentQuality = BatchedRenderer.clamp(state.currentQuality, state.minQuality, state.maxQuality);
161
+ state.enabled = true;
162
+ this.lastAppliedQuality = Number.NaN;
163
+ }
164
+
165
+ disableAdaptivePerformance(resetQuality = true) {
166
+ this.adaptivePerformanceState.enabled = false;
167
+ if (resetQuality) {
168
+ this.adaptivePerformanceState.currentQuality = this.adaptivePerformanceState.maxQuality;
169
+ this.applyAdaptiveQuality();
170
+ }
171
+ }
172
+
173
+ getAdaptivePerformanceState(): AdaptivePerformanceState {
174
+ return {...this.adaptivePerformanceState};
175
+ }
176
+
177
+ private applyAdaptiveQuality() {
178
+ const quality = this.adaptivePerformanceState.currentQuality;
179
+ if (quality === this.lastAppliedQuality) {
180
+ return;
181
+ }
182
+ for (const ps of this.systemToBatchIndex.keys()) {
183
+ (ps as ParticleSystem).setQualityFactor(quality);
184
+ }
185
+ this.lastAppliedQuality = quality;
186
+ }
187
+
188
+ update(delta: number) {
189
+ const adaptiveState = this.adaptivePerformanceState;
190
+ const frameStart = adaptiveState.enabled ? performance.now() : 0;
191
+ if (adaptiveState.enabled) {
192
+ this.applyAdaptiveQuality();
193
+ }
194
+ for (const ps of this.systemToBatchIndex.keys()) {
195
+ (ps as ParticleSystem).update(delta);
196
+ }
197
+ for (let i = 0; i < this.batches.length; i++) {
198
+ this.batches[i].update();
199
+ }
200
+ if (!adaptiveState.enabled) {
201
+ return;
202
+ }
203
+
204
+ adaptiveState.lastFrameCpuMs = performance.now() - frameStart;
205
+ if (adaptiveState.lastFrameCpuMs > adaptiveState.targetFrameMs) {
206
+ adaptiveState.currentQuality = BatchedRenderer.clamp(
207
+ adaptiveState.currentQuality - adaptiveState.decreaseStep,
208
+ adaptiveState.minQuality,
209
+ adaptiveState.maxQuality
210
+ );
211
+ } else {
212
+ adaptiveState.currentQuality = BatchedRenderer.clamp(
213
+ adaptiveState.currentQuality + adaptiveState.increaseStep,
214
+ adaptiveState.minQuality,
215
+ adaptiveState.maxQuality
216
+ );
217
+ }
218
+ }
219
+
220
+ dispose(): void {
221
+ for (const batch of this.batches) {
222
+ batch.dispose();
223
+ }
224
+ this.batches = [];
225
+ this.systemToBatchIndex.clear();
226
+ super.dispose();
227
+ }
228
+ }