@needle-tools/engine 4.6.0-next.ffc175e → 4.6.1-next.1e3d612

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 (85) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/generateMeshBVH.worker-BaNp_Xtp.js +25 -0
  3. package/dist/{gltf-progressive-Bm9eEfgu.min.js → gltf-progressive-Bl4okF1b.min.js} +1 -1
  4. package/dist/{gltf-progressive-GjIqwSG3.js → gltf-progressive-DSpdn0QT.js} +2 -2
  5. package/dist/{gltf-progressive-Dn6o99rH.umd.cjs → gltf-progressive-P8b8a0qY.umd.cjs} +1 -1
  6. package/dist/needle-engine.bundle-BYdv-DmQ.min.js +1563 -0
  7. package/dist/{needle-engine.bundle-DUkg_M5A.umd.cjs → needle-engine.bundle-DsI9yYlp.umd.cjs} +152 -148
  8. package/dist/{needle-engine.bundle-B3vWYoFH.js → needle-engine.bundle-Dx31Hfkf.js} +8129 -8036
  9. package/dist/needle-engine.js +343 -342
  10. package/dist/needle-engine.min.js +1 -1
  11. package/dist/needle-engine.umd.cjs +1 -1
  12. package/dist/{postprocessing-CRQa6Qxn.umd.cjs → postprocessing-CjW23fio.umd.cjs} +18 -18
  13. package/dist/{postprocessing-D6W1EyZ-.js → postprocessing-DYLNOL3W.js} +4 -3
  14. package/dist/{postprocessing-DF8AlRgW.min.js → postprocessing-xYQWCHFu.min.js} +26 -26
  15. package/dist/{three-DMrv-4ar.umd.cjs → three-B_hneGZr.umd.cjs} +4 -4
  16. package/dist/{three-Bz6X1mrw.js → three-DrqIzZTH.js} +4198 -4198
  17. package/dist/{three-Boa-jOq-.min.js → three-DuDKwKB8.min.js} +33 -33
  18. package/dist/{three-examples-GggCDHv0.js → three-examples-B50TT3Iu.js} +5 -5
  19. package/dist/{three-examples-DuVhxqft.min.js → three-examples-DaDLBuy6.min.js} +14 -14
  20. package/dist/{three-examples-C7ryg8vN.umd.cjs → three-examples-X3OadjXB.umd.cjs} +3 -3
  21. package/dist/{three-mesh-ui-CY6Izc7C.min.js → three-mesh-ui-B3p3gyUz.min.js} +1 -1
  22. package/dist/{three-mesh-ui-CwlN0FUC.umd.cjs → three-mesh-ui-CQiIQIlA.umd.cjs} +1 -1
  23. package/dist/{three-mesh-ui-CLNOfsWn.js → three-mesh-ui-CxuWt7m-.js} +1 -1
  24. package/dist/{vendor-zxXa3Dmr.min.js → vendor-BlSxe9JJ.min.js} +3 -3
  25. package/dist/{vendor-BSD1RQIh.js → vendor-BmYIgaS1.js} +3 -3
  26. package/dist/{vendor-DHr4aqIZ.umd.cjs → vendor-Cavtu3CP.umd.cjs} +3 -3
  27. package/lib/engine/engine_assetdatabase.js +3 -1
  28. package/lib/engine/engine_assetdatabase.js.map +1 -1
  29. package/lib/engine/engine_context.d.ts +4 -2
  30. package/lib/engine/engine_context.js +17 -10
  31. package/lib/engine/engine_context.js.map +1 -1
  32. package/lib/engine/engine_utils_screenshot.d.ts +1 -1
  33. package/lib/engine/engine_utils_screenshot.js +11 -2
  34. package/lib/engine/engine_utils_screenshot.js.map +1 -1
  35. package/lib/engine-components/ReflectionProbe.d.ts +2 -1
  36. package/lib/engine-components/ReflectionProbe.js +4 -1
  37. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  38. package/lib/engine-components/Renderer.js +9 -5
  39. package/lib/engine-components/Renderer.js.map +1 -1
  40. package/lib/engine-components/postprocessing/Effects/Antialiasing.js +3 -1
  41. package/lib/engine-components/postprocessing/Effects/Antialiasing.js.map +1 -1
  42. package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
  43. package/lib/engine-components/postprocessing/Effects/BloomEffect.js +3 -0
  44. package/lib/engine-components/postprocessing/Effects/BloomEffect.js.map +1 -1
  45. package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.js +1 -0
  46. package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.js.map +1 -1
  47. package/lib/engine-components/postprocessing/Effects/Sharpening.d.ts +1 -0
  48. package/lib/engine-components/postprocessing/Effects/Sharpening.js +4 -0
  49. package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
  50. package/lib/engine-components/postprocessing/Effects/Tonemapping.js +10 -6
  51. package/lib/engine-components/postprocessing/Effects/Tonemapping.js.map +1 -1
  52. package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +18 -0
  53. package/lib/engine-components/postprocessing/PostProcessingEffect.js +22 -3
  54. package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
  55. package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +11 -3
  56. package/lib/engine-components/postprocessing/PostProcessingHandler.js +117 -117
  57. package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
  58. package/lib/engine-components/postprocessing/Volume.d.ts +2 -1
  59. package/lib/engine-components/postprocessing/Volume.js +50 -27
  60. package/lib/engine-components/postprocessing/Volume.js.map +1 -1
  61. package/lib/engine-components/postprocessing/index.d.ts +1 -0
  62. package/lib/engine-components/postprocessing/index.js +1 -0
  63. package/lib/engine-components/postprocessing/index.js.map +1 -1
  64. package/lib/engine-components/postprocessing/utils.d.ts +43 -0
  65. package/lib/engine-components/postprocessing/utils.js +81 -0
  66. package/lib/engine-components/postprocessing/utils.js.map +1 -1
  67. package/package.json +1 -1
  68. package/plugins/vite/dependency-watcher.js +8 -2
  69. package/src/engine/engine_assetdatabase.ts +3 -1
  70. package/src/engine/engine_context.ts +22 -12
  71. package/src/engine/engine_utils_screenshot.ts +13 -3
  72. package/src/engine-components/ReflectionProbe.ts +5 -1
  73. package/src/engine-components/Renderer.ts +10 -7
  74. package/src/engine-components/postprocessing/Effects/Antialiasing.ts +3 -1
  75. package/src/engine-components/postprocessing/Effects/BloomEffect.ts +4 -2
  76. package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.ts +1 -0
  77. package/src/engine-components/postprocessing/Effects/Sharpening.ts +5 -0
  78. package/src/engine-components/postprocessing/Effects/Tonemapping.ts +9 -7
  79. package/src/engine-components/postprocessing/PostProcessingEffect.ts +23 -3
  80. package/src/engine-components/postprocessing/PostProcessingHandler.ts +135 -123
  81. package/src/engine-components/postprocessing/Volume.ts +56 -33
  82. package/src/engine-components/postprocessing/index.ts +2 -1
  83. package/src/engine-components/postprocessing/utils.ts +101 -2
  84. package/dist/generateMeshBVH.worker-Cdfpaq5W.js +0 -25
  85. package/dist/needle-engine.bundle-CZu9qkBv.min.js +0 -1559
@@ -1,5 +1,5 @@
1
1
  import type { Effect, EffectComposer, Pass, ToneMappingEffect as _TonemappingEffect } from "postprocessing";
2
- import { HalfFloatType, NoToneMapping } from "three";
2
+ import { Camera as Camera3, HalfFloatType, NoToneMapping, Scene } from "three";
3
3
 
4
4
  import { showBalloonWarning } from "../../engine/debug/index.js";
5
5
  // import { internal_SetSharpeningEffectModule } from "./Effects/Sharpening.js";
@@ -9,13 +9,13 @@ import type { Constructor } from "../../engine/engine_types.js";
9
9
  import { DeviceUtilities, getParam } from "../../engine/engine_utils.js";
10
10
  import { Camera } from "../Camera.js";
11
11
  import { PostProcessingEffect, PostProcessingEffectContext } from "./PostProcessingEffect.js";
12
+ import { orderEffects, PostprocessingEffectData } from "./utils.js";
12
13
 
13
14
  declare const NEEDLE_USE_POSTPROCESSING: boolean;
14
15
  globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
15
16
 
16
17
 
17
18
  const debug = getParam("debugpost");
18
- const dontMergePasses = getParam("debugpostpasses");
19
19
 
20
20
  const activeKey = Symbol("needle:postprocessing-handler");
21
21
  const autoclearSetting = Symbol("needle:previous-autoclear-state");
@@ -27,7 +27,7 @@ export class PostProcessingHandler {
27
27
 
28
28
  private _composer: EffectComposer | null = null;
29
29
  private _lastVolumeComponents?: PostProcessingEffect[];
30
- private _effects: Array<Effect | Pass> = [];
30
+ private readonly _effects: Array<PostprocessingEffectData> = [];
31
31
 
32
32
  get isActive() {
33
33
  return this._isActive;
@@ -44,7 +44,7 @@ export class PostProcessingHandler {
44
44
  this.context = context;
45
45
  }
46
46
 
47
- apply(components: PostProcessingEffect[]) : Promise<void> {
47
+ apply(components: PostProcessingEffect[]): Promise<void> {
48
48
  if ("env" in import.meta && import.meta.env.VITE_NEEDLE_USE_POSTPROCESSING === "false") {
49
49
  if (debug) console.warn("Postprocessing is disabled via vite env setting");
50
50
  else console.debug("Postprocessing is disabled via vite env setting");
@@ -60,7 +60,7 @@ export class PostProcessingHandler {
60
60
  return this.onApply(this.context, components);
61
61
  }
62
62
 
63
- unapply() {
63
+ unapply(dispose: boolean = true) {
64
64
  if (debug) console.log("Unapplying postprocessing effects");
65
65
  this._isActive = false;
66
66
  if (this._lastVolumeComponents) {
@@ -73,21 +73,26 @@ export class PostProcessingHandler {
73
73
  const active = context[activeKey] as PostProcessingHandler | null;
74
74
  if (active === this) {
75
75
  delete context[activeKey];
76
+
77
+ // Restore the auto clear setting
78
+ if (typeof context.renderer[autoclearSetting] === "boolean") {
79
+ context.renderer.autoClear = context.renderer[autoclearSetting];
80
+ }
76
81
  }
82
+
83
+ this._composer?.removeAllPasses();
84
+ if (dispose) this._composer?.dispose();
85
+
77
86
  if (context.composer === this._composer) {
78
- context.composer?.dispose();
79
87
  context.composer = null;
80
88
  }
81
- if (typeof context.renderer[autoclearSetting] === "boolean") {
82
- context.renderer.autoClear = context.renderer[autoclearSetting];
83
- }
84
89
  }
85
90
 
86
91
  dispose() {
87
- this.unapply();
92
+ this.unapply(true);
88
93
 
89
94
  for (const effect of this._effects) {
90
- effect.dispose();
95
+ effect.effect.dispose();
91
96
  }
92
97
  this._effects.length = 0;
93
98
  this._composer = null;
@@ -105,6 +110,7 @@ export class PostProcessingHandler {
105
110
  // import("./Effects/Sharpening.effect")
106
111
  ]);
107
112
 
113
+
108
114
  // try {
109
115
  // internal_SetSharpeningEffectModule(modules[2]);
110
116
  // }
@@ -141,10 +147,22 @@ export class PostProcessingHandler {
141
147
  // apply or collect effects
142
148
  const res = component.apply(ctx);
143
149
  if (!res) continue;
150
+
151
+
144
152
  if (Array.isArray(res)) {
145
- this._effects.push(...res);
153
+ for (const effect of res) {
154
+ this._effects.push({
155
+ effect,
156
+ priority: component.priority
157
+ });
158
+ }
159
+ }
160
+ else {
161
+ this._effects.push({
162
+ effect: res,
163
+ priority: component.priority
164
+ });
146
165
  }
147
- else this._effects.push(res);
148
166
  }
149
167
  }
150
168
  else {
@@ -157,20 +175,33 @@ export class PostProcessingHandler {
157
175
  if (this.context.renderer.toneMapping != NoToneMapping) {
158
176
  if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
159
177
  const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
160
- this._effects.push(tonemapping);
178
+ this._effects.push({ effect: tonemapping });
161
179
  }
162
180
  }
163
181
 
164
182
  this.applyEffects(context);
165
183
  }
166
184
 
185
+ private _anyPassHasDepth = false;
186
+ private _anyPassHasNormal = false;
187
+ private _hasSmaaEffect = false;
188
+
189
+ get anyPassHasDepth() { return this._anyPassHasDepth; }
190
+ get anyPassHasNormal() { return this._anyPassHasNormal; }
191
+ get hasSmaaEffect() { return this._hasSmaaEffect; }
192
+
167
193
 
168
194
  /** Build composer passes */
169
195
  private applyEffects(context: Context) {
196
+ // Reset state
197
+ this._anyPassHasDepth = false;
198
+ this._anyPassHasNormal = false;
199
+ this._hasSmaaEffect = false;
170
200
 
171
201
  const effectsOrPasses = this._effects;
172
-
173
- if (effectsOrPasses.length <= 0) return;
202
+ if (effectsOrPasses.length <= 0) {
203
+ return;
204
+ }
174
205
 
175
206
  const camera = context.mainCameraComponent as Camera;
176
207
  const renderer = context.renderer;
@@ -180,6 +211,10 @@ export class PostProcessingHandler {
180
211
  // Store the auto clear setting because the postprocessing composer just disables it
181
212
  // and when we disable postprocessing we want to restore the original setting
182
213
  // https://github.com/pmndrs/postprocessing/blob/271944b74b543a5b743a62803a167b60cc6bb4ee/src/core/EffectComposer.js#L230C12-L230C12
214
+ // First we need to get the previously set autoClear setting, if it exists
215
+ if (typeof renderer[autoclearSetting] === "boolean") {
216
+ renderer.autoClear = renderer[autoclearSetting];
217
+ }
183
218
  renderer[autoclearSetting] = renderer.autoClear;
184
219
 
185
220
  // create composer and set active on context
@@ -194,11 +229,14 @@ export class PostProcessingHandler {
194
229
  if (context.composer && context.composer !== this._composer) {
195
230
  console.warn("There's already an active EffectComposer in your scene: replacing it with a new one. This might cause unexpected behaviour. Make sure to only use one PostprocessingManager/Volume in your scene.");
196
231
  }
232
+
197
233
  context.composer = this._composer;
198
234
  const composer = context.composer;
199
235
  composer.setMainCamera(cam);
200
236
  composer.setRenderer(renderer);
201
237
  composer.setMainScene(scene);
238
+ composer.autoRenderToScreen = true; // Must be enabled because it might be disabled during addPass by the composer itself (depending on the effect's settings or order)
239
+ composer.multisampling = 0;
202
240
 
203
241
  for (const prev of composer.passes)
204
242
  prev.dispose();
@@ -210,64 +248,100 @@ export class PostProcessingHandler {
210
248
  screenpass.mainScene = scene;
211
249
  composer.addPass(screenpass);
212
250
 
213
- const automaticEffectsOrdering = true;
214
- if (automaticEffectsOrdering && !dontMergePasses) {
215
- try {
216
- this.orderEffects();
251
+ try {
252
+ orderEffects(this._effects);
217
253
 
218
- const effects: Array<Effect> = [];
254
+ const effectsToMerge: Array<Effect> = [];
255
+ let hasConvolutionEffectInArray = false;
219
256
 
220
- for (const ef of effectsOrPasses) {
221
- if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect)
222
- effects.push(ef as Effect);
223
- else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass) {
224
- composer.addPass(ef as Pass);
225
- }
226
- else {
227
- // seems some effects are not correctly typed, but three can deal with them,
228
- // so we might need to just pass them through
229
- composer.addPass(ef);
230
- }
231
- }
257
+ for (const entry of effectsOrPasses) {
258
+ const ef = entry.effect;
232
259
 
233
- // create and apply uber pass
234
- if (effects.length > 0) {
235
- const pass = new MODULES.POSTPROCESSING.MODULE.EffectPass(cam, ...effects);
236
- pass.name = effects.map(e => e.name).join(" ");
237
- pass.mainScene = scene;
238
- pass.enabled = true;
239
- composer.addPass(pass);
260
+ if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
261
+ this._hasSmaaEffect = true;
262
+ }
263
+ else if (ef instanceof MODULES.POSTPROCESSING.MODULE.NormalPass) {
264
+ this._anyPassHasNormal = true;
240
265
  }
241
- }
242
- catch (e) {
243
- console.error("Error while applying postprocessing effects", e);
244
- composer.removeAllPasses();
245
- }
246
- }
247
- else {
248
- // we still want to sort passes, but we do not want to merge them for debugging
249
- if (automaticEffectsOrdering)
250
- this.orderEffects();
251
266
 
252
- for (const ef of effectsOrPasses) {
253
267
  if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
254
- const pass = new MODULES.POSTPROCESSING.MODULE.EffectPass(cam, ef as Effect);
255
- pass.name = ef.name;
256
- composer.addPass(pass);
268
+ const attributes = ef.getAttributes();
269
+ const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
270
+ if (attributes & convolution) {
271
+ if (debug) console.log("[PostProcessing] Convolution effect: " + ef.name);
272
+ if (hasConvolutionEffectInArray) {
273
+ if (debug) console.log("[PostProcessing] Merging effects with convolution", effectsToMerge.map(e => e.name).join(", "));
274
+ this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
275
+ }
276
+ hasConvolutionEffectInArray = true;
277
+ }
278
+ // Otherwise we can merge it
279
+ effectsToMerge.push(ef as Effect);
257
280
  }
258
- else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass)
281
+ else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass) {
282
+ hasConvolutionEffectInArray = false;
283
+ this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
284
+ ef.renderToScreen = false;
259
285
  composer.addPass(ef as Pass);
260
- else
286
+ }
287
+ else {
261
288
  // seems some effects are not correctly typed, but three can deal with them,
262
- // so we just pass them through
289
+ // so we might need to just pass them through
290
+ hasConvolutionEffectInArray = false;
291
+ this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
263
292
  composer.addPass(ef);
293
+ }
294
+ }
295
+
296
+ this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
297
+ }
298
+ catch (e) {
299
+ console.error("Error while applying postprocessing effects", e);
300
+ composer.removeAllPasses();
301
+ }
302
+
303
+ // The last pass is the one that renders to the screen, so we need to set the gamma correction for it (and enable it for all others)
304
+ for (let i = 0; i < composer.passes.length; i++) {
305
+ const pass = composer.passes[i];
306
+ const isLast = i === composer.passes.length - 1;
307
+ if ((pass as any)?.configuration !== undefined) {
308
+ (pass as any).configuration.gammaCorrection = isLast;
309
+ }
310
+ else if ("autosetGamma" in pass) {
311
+ // Some effects have a autosetGamma property that we can use to set the gamma correction
312
+ pass.autosetGamma = isLast;
264
313
  }
314
+
315
+ this._anyPassHasDepth ||= pass.needsDepthTexture;
316
+ }
317
+
318
+ // DEBUG LAND BELOW
319
+ if (debug) console.log("[PostProcessing] Passes →", [...composer.passes], "\n---------------------------------\n• " + composer.passes.map(i => i.name).join("\n• ") + "\n");
320
+ if (debug) this._onCreateEffectsDebug(this._composer!, cam);
321
+ }
322
+
323
+
324
+
325
+ /** Should be called before `composer.addPass()` to create an effect pass with all previously collected effects that can be merged up to that point */
326
+ private createPassForMergeableEffects(effects: Array<Effect>, composer: EffectComposer, camera: Camera3, scene: Scene) {
327
+ if (effects.length > 0) {
328
+ const pass = new MODULES.POSTPROCESSING.MODULE.EffectPass(camera, ...effects);
329
+ pass.name = effects.map(e => e.name).join(", ");
330
+ pass.mainScene = scene;
331
+ pass.enabled = true;
332
+ pass.renderToScreen = false;
333
+ composer.addPass(pass);
334
+ effects.length = 0; // Clear effects after adding them to the pass
265
335
  }
336
+ }
337
+
266
338
 
267
- if (debug) {
268
339
 
269
- console.log("[PostProcessing] Passes →", composer.passes);
340
+ private _menuEntry: HTMLSelectElement | null = null;
341
+ private _passIndices: number[] | null = null;
270
342
 
343
+ private _onCreateEffectsDebug(composer: EffectComposer, cam: Camera3) {
344
+ if (debug === "passes") {
271
345
  // DepthEffect for debugging purposes, disabled by default, can be selected in the debug pass select
272
346
  const depthEffect = new MODULES.POSTPROCESSING.MODULE.DepthEffect({
273
347
  blendFunction: MODULES.POSTPROCESSING.MODULE.BlendFunction.NORMAL,
@@ -303,7 +377,7 @@ export class PostProcessingHandler {
303
377
  if (menu && this._passIndices === null) {
304
378
  if (this._menuEntry)
305
379
  this._menuEntry.remove();
306
-
380
+
307
381
  const select = document.createElement("select");
308
382
  select.multiple = true;
309
383
  const defaultOpt = document.createElement("option");
@@ -327,73 +401,11 @@ export class PostProcessingHandler {
327
401
  else {
328
402
  this._passIndices = indices;
329
403
  }
330
-
331
- this.applyEffects(context);
404
+ this.applyEffects(this.context);
332
405
  });
333
406
  }
334
407
  }
335
408
  }
336
409
 
337
- private _menuEntry: HTMLSelectElement | null = null;
338
- private _passIndices: number[] | null = null;
339
-
340
- private orderEffects() {
341
- if (debug) console.log("Before ordering effects", [...this._effects]);
342
-
343
-
344
-
345
- // Order of effects for correct results.
346
- // Aligned with https://github.com/pmndrs/postprocessing/wiki/Effect-Merging#effect-execution-order
347
- // We can not put this into global scope because then the module might not yet be initialized
348
- effectsOrder ??= [
349
- MODULES.POSTPROCESSING.MODULE.NormalPass,
350
- MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass,
351
- MODULES.POSTPROCESSING.MODULE.SMAAEffect,
352
- MODULES.POSTPROCESSING.MODULE.SSAOEffect,
353
- MODULES.POSTPROCESSING_AO.MODULE.N8AOPostPass,
354
- MODULES.POSTPROCESSING.MODULE.TiltShiftEffect,
355
- MODULES.POSTPROCESSING.MODULE.DepthOfFieldEffect,
356
- MODULES.POSTPROCESSING.MODULE.ChromaticAberrationEffect,
357
- MODULES.POSTPROCESSING.MODULE.BloomEffect,
358
- MODULES.POSTPROCESSING.MODULE.SelectiveBloomEffect,
359
- MODULES.POSTPROCESSING.MODULE.VignetteEffect,
360
- MODULES.POSTPROCESSING.MODULE.PixelationEffect,
361
- MODULES.POSTPROCESSING.MODULE.ToneMappingEffect,
362
- MODULES.POSTPROCESSING.MODULE.HueSaturationEffect,
363
- MODULES.POSTPROCESSING.MODULE.BrightnessContrastEffect,
364
- // __SHARPENING_MODULE._SharpeningEffect,
365
- ];
366
-
367
-
368
- // TODO: enforce correct order of effects (e.g. DOF before Bloom)
369
- const effects = this._effects;
370
- effects.sort((a, b) => {
371
- // we use find index here because sometimes constructor names are prefixed with `_`
372
- // TODO: find a more robust solution that isnt name based (not sure if that exists tho... maybe we must give effect TYPES some priority/index)
373
- const aidx = effectsOrder!.findIndex(e => a.constructor.name.endsWith(e.name));
374
- const bidx = effectsOrder!.findIndex(e => b.constructor.name.endsWith(e.name));
375
- // Unknown effects should be rendered first
376
- if (aidx < 0) {
377
- if (debug) console.warn("Unknown effect found: ", a.constructor.name);
378
- return -1;
379
- }
380
- else if (bidx < 0) {
381
- if (debug) console.warn("Unknown effect found: ", b.constructor.name);
382
- return 1;
383
- }
384
- if (aidx < 0) return 1;
385
- if (bidx < 0) return -1;
386
- return aidx - bidx;
387
- });
388
- if (debug) console.log("After ordering effects", [...this._effects]);
389
- for (let i = 0; i < effects.length; i++) {
390
- const effect = effects[i] as any;
391
- if (effect?.configuration?.gammaCorrection !== undefined) {
392
- const isLast = i === effects.length - 1;
393
- effect.configuration.gammaCorrection = isLast;
394
- }
395
- }
396
- }
397
410
  }
398
411
 
399
- let effectsOrder: Array<Constructor<Effect | Pass>> | null = null;
@@ -1,4 +1,4 @@
1
- import type { Effect } from "postprocessing";
1
+ import type { Effect, EffectComposer } from "postprocessing";
2
2
 
3
3
  import { isDevEnvironment, showBalloonMessage } from "../../engine/debug/index.js";
4
4
  import { Context } from "../../engine/engine_context.js";
@@ -56,6 +56,9 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
56
56
  return this._activeEffects;
57
57
  }
58
58
 
59
+ get dirty() { return this._isDirty; }
60
+ set dirty(value: boolean) { this._isDirty = value; }
61
+
59
62
  @serializeable(VolumeProfile)
60
63
  sharedProfile?: VolumeProfile;
61
64
 
@@ -184,40 +187,64 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
184
187
 
185
188
  this.context.composer.setMainScene(this.context.scene);
186
189
 
187
- const composer = this.context.composer;
188
- if (this.multisampling === "auto") {
189
-
190
- const timeSinceLastChange = this.context.time.realtimeSinceStartup - this._multisampleAutoChangeTime;
190
+ const composer = this.context.composer as EffectComposer;
191
191
 
192
- if (this.context.time.realtimeSinceStartup - this._componentEnabledTime > 2
193
- && timeSinceLastChange > .5
194
- ) {
195
- const prev = composer.multisampling;
192
+ if (this.multisampling === "auto") {
196
193
 
197
- if (composer.multisampling > 0 && this.context.time.smoothedFps <= 50) {
198
- this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
199
- this._multisampleAutoDecreaseTime = this.context.time.realtimeSinceStartup;
200
- composer.multisampling *= .5;
201
- composer.multisampling = Math.floor(composer.multisampling);
202
- if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${composer.multisampling}`);
194
+ // If the postprocessing handler is using depth+normals (e.g. with SMAA) we ALWAYS disable multisampling to avoid ugly edges
195
+ if (this._postprocessing && (this._postprocessing.hasSmaaEffect)) {
196
+ if (composer.multisampling !== 0) {
197
+ composer.multisampling = 0;
198
+ if (debug || isDevEnvironment()) {
199
+ console.warn(`[PostProcessing] Disabling multisampling you're using SMAA and have set the 'multisampling: auto' on your PostprocessingManager/Volume component. If you need anti-aliasing, consider adding an Antialiasing component or SMAA effect to the PostprocessingManager.`);
200
+ }
203
201
  }
204
- // if performance is good for a while try increasing multisampling again
205
- else if (timeSinceLastChange > 1
206
- && this.context.time.smoothedFps >= 59
207
- && composer.multisampling < this.context.renderer.capabilities.maxSamples
208
- && this.context.time.realtimeSinceStartup - this._multisampleAutoDecreaseTime > 10
202
+ }
203
+ else {
204
+ const timeSinceLastChange = this.context.time.realtimeSinceStartup - this._multisampleAutoChangeTime;
205
+
206
+ if (this.context.time.realtimeSinceStartup - this._componentEnabledTime > 2
207
+ && timeSinceLastChange > .5
209
208
  ) {
210
- this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
211
- composer.multisampling = composer.multisampling <= 0 ? 1 : composer.multisampling * 2;
212
- composer.multisampling = Math.floor(composer.multisampling);
213
- if (debug) console.debug(`[PostProcessing] Increased multisampling from ${prev} to ${composer.multisampling}`);
209
+ const prev = composer.multisampling;
210
+
211
+ if (composer.multisampling > 0 && this.context.time.smoothedFps <= 50) {
212
+ this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
213
+ this._multisampleAutoDecreaseTime = this.context.time.realtimeSinceStartup;
214
+ let newMultiSample = composer.multisampling * .5;
215
+ newMultiSample = Math.floor(newMultiSample);
216
+ if (newMultiSample != composer.multisampling) {
217
+ composer.multisampling = newMultiSample;
218
+ }
219
+ if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${composer.multisampling}`);
220
+ }
221
+ // if performance is good for a while try increasing multisampling again
222
+ else if (timeSinceLastChange > 1
223
+ && this.context.time.smoothedFps >= 59
224
+ && composer.multisampling < this.context.renderer.capabilities.maxSamples
225
+ && this.context.time.realtimeSinceStartup - this._multisampleAutoDecreaseTime > 10
226
+ ) {
227
+ this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
228
+ let newMultiSample = composer.multisampling <= 0 ? 1 : composer.multisampling * 2;
229
+ newMultiSample = Math.floor(newMultiSample);
230
+ if (newMultiSample !== composer.multisampling) {
231
+ composer.multisampling = newMultiSample;
232
+ }
233
+ if (debug) console.debug(`[PostProcessing] Increased multisampling from ${prev} to ${composer.multisampling}`);
234
+ }
214
235
  }
215
236
  }
216
237
  }
217
238
  else {
218
- composer.multisampling = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
239
+ const newMultiSample = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
240
+ if (newMultiSample !== composer.multisampling)
241
+ composer.multisampling = newMultiSample;
219
242
  }
220
243
 
244
+ // Fix multisampling for the composer, it ONLY needs to be done for the input buffer
245
+ // this is super important for performance a negative visual impact
246
+ composer.outputBuffer.samples = 0;
247
+
221
248
  // only set the main camera if any pass has a different camera
222
249
  // trying to avoid doing this regularly since it involves doing potentially unnecessary work
223
250
  // https://github.com/pmndrs/postprocessing/blob/3d3df0576b6d49aec9e763262d5a1ff7429fd91a/src/core/EffectComposer.js#L406
@@ -244,7 +271,7 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
244
271
  private _isDirty: boolean = false;
245
272
 
246
273
  private apply() {
247
- if (debug) console.log(`Apply PostProcessing "${this.name}"`);
274
+ if (debug) console.log(`Apply PostProcessing "${this.name || "unnamed"}"`);
248
275
 
249
276
  if (isDevEnvironment()) {
250
277
  if (this._lastApplyTime !== undefined && Date.now() - this._lastApplyTime < 100) {
@@ -256,7 +283,6 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
256
283
  }
257
284
 
258
285
  this._isDirty = false;
259
- this.unapply();
260
286
 
261
287
  this._activeEffects.length = 0;
262
288
  // get from profile
@@ -299,16 +325,13 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
299
325
  console.warn(`[PostProcessing] No composer found`);
300
326
  }
301
327
  })
302
-
328
+ }
329
+ else {
330
+ this._postprocessing?.unapply(false);
303
331
  }
304
332
 
305
333
  }
306
334
 
307
- private unapply() {
308
- this._postprocessing?.unapply();
309
- }
310
-
311
-
312
335
  private _applyPostQueue() {
313
336
  if (this._modificationQueue) {
314
337
  for (const entry of this._modificationQueue.values()) this.onEditorModification(entry);
@@ -1,5 +1,6 @@
1
1
  export * from "./PostProcessingEffect.js";
2
2
  export * from "./PostProcessingHandler.js"
3
+ export { PostProcessingEffectPriority } from "./utils.js";
3
4
  export { PostProcessingManager } from "./Volume.js"
4
5
  export * from "./VolumeParameter.js"
5
- export * from "./VolumeProfile.js";
6
+ export * from "./VolumeProfile.js";