@needle-tools/engine 5.0.3 → 5.1.0-alpha.1

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 (173) hide show
  1. package/CHANGELOG.md +31 -4
  2. package/README.md +6 -7
  3. package/components.needle.json +1 -1
  4. package/dist/{needle-engine.bundle-BXk8jYW3.js → needle-engine.bundle-BGyKqxBH.js} +12394 -11786
  5. package/dist/needle-engine.bundle-CiYtOO2O.min.js +1732 -0
  6. package/dist/needle-engine.bundle-DzVx9Z8D.umd.cjs +1732 -0
  7. package/dist/needle-engine.d.ts +660 -63
  8. package/dist/needle-engine.js +579 -566
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/dist/{vendor-vHLk8sXu.js → vendor-CAcsI0eU.js} +116 -115
  12. package/dist/{vendor-CntUvmJu.umd.cjs → vendor-CEM38hLE.umd.cjs} +2 -2
  13. package/dist/{vendor-DPbfJJ4d.min.js → vendor-HRlxIBga.min.js} +2 -2
  14. package/lib/engine/api.d.ts +2 -0
  15. package/lib/engine/api.js +2 -0
  16. package/lib/engine/api.js.map +1 -1
  17. package/lib/engine/engine_addressables.js +5 -1
  18. package/lib/engine/engine_addressables.js.map +1 -1
  19. package/lib/engine/engine_animation.d.ts +14 -7
  20. package/lib/engine/engine_animation.js +49 -9
  21. package/lib/engine/engine_animation.js.map +1 -1
  22. package/lib/engine/engine_components.js +33 -4
  23. package/lib/engine/engine_components.js.map +1 -1
  24. package/lib/engine/engine_context.d.ts +7 -2
  25. package/lib/engine/engine_context.js +10 -2
  26. package/lib/engine/engine_context.js.map +1 -1
  27. package/lib/engine/engine_gameobject.d.ts +4 -0
  28. package/lib/engine/engine_gameobject.js.map +1 -1
  29. package/lib/engine/engine_init.js +4 -0
  30. package/lib/engine/engine_init.js.map +1 -1
  31. package/lib/engine/engine_input.js +4 -1
  32. package/lib/engine/engine_input.js.map +1 -1
  33. package/lib/engine/engine_materialpropertyblock.js +0 -19
  34. package/lib/engine/engine_materialpropertyblock.js.map +1 -1
  35. package/lib/engine/engine_networking.d.ts +11 -8
  36. package/lib/engine/engine_networking.js +43 -26
  37. package/lib/engine/engine_networking.js.map +1 -1
  38. package/lib/engine/engine_networking_instantiate.d.ts +100 -5
  39. package/lib/engine/engine_networking_instantiate.js +150 -16
  40. package/lib/engine/engine_networking_instantiate.js.map +1 -1
  41. package/lib/engine/engine_networking_prefabs.d.ts +59 -0
  42. package/lib/engine/engine_networking_prefabs.js +67 -0
  43. package/lib/engine/engine_networking_prefabs.js.map +1 -0
  44. package/lib/engine/engine_physics_rapier.d.ts +3 -0
  45. package/lib/engine/engine_physics_rapier.js +13 -9
  46. package/lib/engine/engine_physics_rapier.js.map +1 -1
  47. package/lib/engine/engine_utils.js +2 -2
  48. package/lib/engine/engine_utils.js.map +1 -1
  49. package/lib/engine/postprocessing/api.d.ts +2 -0
  50. package/lib/engine/postprocessing/api.js +2 -0
  51. package/lib/engine/postprocessing/api.js.map +1 -0
  52. package/lib/engine/postprocessing/index.d.ts +2 -0
  53. package/lib/engine/postprocessing/index.js +2 -0
  54. package/lib/engine/postprocessing/index.js.map +1 -0
  55. package/lib/engine/postprocessing/postprocessing.d.ts +83 -0
  56. package/lib/engine/postprocessing/postprocessing.js +280 -0
  57. package/lib/engine/postprocessing/postprocessing.js.map +1 -0
  58. package/lib/engine/postprocessing/types.d.ts +39 -0
  59. package/lib/engine/postprocessing/types.js +2 -0
  60. package/lib/engine/postprocessing/types.js.map +1 -0
  61. package/lib/engine/webcomponents/WebXRButtons.js +17 -3
  62. package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
  63. package/lib/engine/xr/NeedleXRSession.d.ts +2 -0
  64. package/lib/engine/xr/NeedleXRSession.js +47 -14
  65. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  66. package/lib/engine/xr/events.d.ts +30 -3
  67. package/lib/engine/xr/events.js +38 -0
  68. package/lib/engine/xr/events.js.map +1 -1
  69. package/lib/engine/xr/init.d.ts +4 -0
  70. package/lib/engine/xr/init.js +43 -0
  71. package/lib/engine/xr/init.js.map +1 -0
  72. package/lib/engine-components/AnimationUtils.d.ts +4 -1
  73. package/lib/engine-components/AnimationUtils.js +7 -19
  74. package/lib/engine-components/AnimationUtils.js.map +1 -1
  75. package/lib/engine-components/AnimatorController.d.ts +135 -2
  76. package/lib/engine-components/AnimatorController.js +216 -13
  77. package/lib/engine-components/AnimatorController.js.map +1 -1
  78. package/lib/engine-components/SeeThrough.d.ts +0 -2
  79. package/lib/engine-components/SeeThrough.js +0 -89
  80. package/lib/engine-components/SeeThrough.js.map +1 -1
  81. package/lib/engine-components/SyncedRoom.d.ts +4 -0
  82. package/lib/engine-components/SyncedRoom.js +23 -8
  83. package/lib/engine-components/SyncedRoom.js.map +1 -1
  84. package/lib/engine-components/SyncedTransform.js +5 -5
  85. package/lib/engine-components/SyncedTransform.js.map +1 -1
  86. package/lib/engine-components/Voip.d.ts +46 -0
  87. package/lib/engine-components/Voip.js +126 -2
  88. package/lib/engine-components/Voip.js.map +1 -1
  89. package/lib/engine-components/api.d.ts +1 -0
  90. package/lib/engine-components/api.js +1 -0
  91. package/lib/engine-components/api.js.map +1 -1
  92. package/lib/engine-components/codegen/components.d.ts +1 -0
  93. package/lib/engine-components/codegen/components.js +1 -0
  94. package/lib/engine-components/codegen/components.js.map +1 -1
  95. package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +5 -2
  96. package/lib/engine-components/postprocessing/Effects/Tonemapping.js +11 -18
  97. package/lib/engine-components/postprocessing/Effects/Tonemapping.js.map +1 -1
  98. package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +3 -4
  99. package/lib/engine-components/postprocessing/PostProcessingEffect.js +6 -15
  100. package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
  101. package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +2 -1
  102. package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
  103. package/lib/engine-components/postprocessing/Volume.d.ts +18 -11
  104. package/lib/engine-components/postprocessing/Volume.js +61 -140
  105. package/lib/engine-components/postprocessing/Volume.js.map +1 -1
  106. package/lib/engine-components/postprocessing/index.d.ts +1 -0
  107. package/lib/engine-components/postprocessing/index.js +1 -0
  108. package/lib/engine-components/postprocessing/index.js.map +1 -1
  109. package/lib/engine-components/postprocessing/utils.d.ts +2 -0
  110. package/lib/engine-components/postprocessing/utils.js +2 -0
  111. package/lib/engine-components/postprocessing/utils.js.map +1 -1
  112. package/lib/engine-components/ui/Canvas.js +2 -2
  113. package/lib/engine-components/ui/Canvas.js.map +1 -1
  114. package/lib/engine-components/ui/Graphic.d.ts +3 -3
  115. package/lib/engine-components/ui/Graphic.js +6 -2
  116. package/lib/engine-components/ui/Graphic.js.map +1 -1
  117. package/lib/engine-components/ui/Text.d.ts +64 -11
  118. package/lib/engine-components/ui/Text.js +154 -45
  119. package/lib/engine-components/ui/Text.js.map +1 -1
  120. package/lib/engine-components/ui/index.d.ts +1 -0
  121. package/lib/engine-components/ui/index.js +1 -0
  122. package/lib/engine-components/ui/index.js.map +1 -1
  123. package/lib/engine-components-experimental/networking/PlayerSync.d.ts +25 -3
  124. package/lib/engine-components-experimental/networking/PlayerSync.js +60 -11
  125. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
  126. package/package.json +5 -4
  127. package/plugins/common/logger.js +42 -19
  128. package/plugins/vite/ai.d.ts +11 -10
  129. package/plugins/vite/ai.js +305 -31
  130. package/plugins/vite/logger.client.js +4 -3
  131. package/src/engine/api.ts +3 -0
  132. package/src/engine/engine_addressables.ts +4 -1
  133. package/src/engine/engine_animation.ts +47 -9
  134. package/src/engine/engine_components.ts +36 -7
  135. package/src/engine/engine_context.ts +11 -2
  136. package/src/engine/engine_gameobject.ts +5 -0
  137. package/src/engine/engine_init.ts +4 -0
  138. package/src/engine/engine_input.ts +2 -1
  139. package/src/engine/engine_materialpropertyblock.ts +0 -19
  140. package/src/engine/engine_networking.ts +46 -23
  141. package/src/engine/engine_networking_instantiate.ts +160 -18
  142. package/src/engine/engine_networking_prefabs.ts +80 -0
  143. package/src/engine/engine_physics_rapier.ts +14 -9
  144. package/src/engine/engine_utils.ts +2 -2
  145. package/src/engine/postprocessing/api.ts +2 -0
  146. package/src/engine/postprocessing/index.ts +2 -0
  147. package/src/engine/postprocessing/postprocessing.ts +322 -0
  148. package/src/engine/postprocessing/types.ts +43 -0
  149. package/src/engine/webcomponents/WebXRButtons.ts +21 -4
  150. package/src/engine/xr/NeedleXRSession.ts +55 -20
  151. package/src/engine/xr/events.ts +44 -1
  152. package/src/engine/xr/init.ts +49 -0
  153. package/src/engine-components/AnimationUtils.ts +7 -17
  154. package/src/engine-components/AnimatorController.ts +288 -18
  155. package/src/engine-components/SeeThrough.ts +0 -116
  156. package/src/engine-components/SyncedRoom.ts +28 -9
  157. package/src/engine-components/SyncedTransform.ts +5 -5
  158. package/src/engine-components/Voip.ts +129 -2
  159. package/src/engine-components/api.ts +1 -0
  160. package/src/engine-components/codegen/components.ts +1 -0
  161. package/src/engine-components/postprocessing/Effects/Tonemapping.ts +16 -24
  162. package/src/engine-components/postprocessing/PostProcessingEffect.ts +9 -16
  163. package/src/engine-components/postprocessing/PostProcessingHandler.ts +2 -1
  164. package/src/engine-components/postprocessing/Volume.ts +72 -163
  165. package/src/engine-components/postprocessing/index.ts +1 -0
  166. package/src/engine-components/postprocessing/utils.ts +2 -0
  167. package/src/engine-components/ui/Canvas.ts +2 -2
  168. package/src/engine-components/ui/Graphic.ts +7 -3
  169. package/src/engine-components/ui/Text.ts +170 -52
  170. package/src/engine-components/ui/index.ts +2 -1
  171. package/src/engine-components-experimental/networking/PlayerSync.ts +64 -11
  172. package/dist/needle-engine.bundle-CNH61kLA.umd.cjs +0 -1730
  173. package/dist/needle-engine.bundle-Dvh1jROn.min.js +0 -1730
@@ -3,10 +3,10 @@ import { Application } from "../engine/engine_application.js";
3
3
  import { RoomEvents } from "../engine/engine_networking.js";
4
4
  import { disposeStream, NetworkedStreamEvents, NetworkedStreams, StreamEndedEvent, StreamReceivedEvent } from "../engine/engine_networking_streams.js"
5
5
  import { serializable } from "../engine/engine_serialization_decorator.js";
6
- import { DeviceUtilities, getParam } from "../engine/engine_utils.js";
7
- import { delay } from "../engine/engine_utils.js";
6
+ import { delay, DeviceUtilities, getParam } from "../engine/engine_utils.js";
8
7
  import { getIconElement } from "../engine/webcomponents/icons.js";
9
8
  import { Behaviour } from "./Component.js";
9
+ import { EventList } from "./EventList.js";
10
10
 
11
11
  export const noVoip = "noVoip";
12
12
  const debugParam = getParam("debugvoip");
@@ -75,6 +75,66 @@ export class Voip extends Behaviour {
75
75
  */
76
76
  debug: boolean = false;
77
77
 
78
+ private _volume: number = 1;
79
+
80
+ /**
81
+ * Volume for incoming audio streams (0 = silent, 1 = full volume).
82
+ * Changes apply immediately to all active incoming streams.
83
+ */
84
+ @serializable()
85
+ get volume(): number { return this._volume; }
86
+ set volume(val: number) {
87
+ this._volume = val;
88
+ for (const audio of this._incomingStreams.values()) {
89
+ audio.volume = val;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Get the incoming audio element for a specific user.
95
+ * Use this to route audio through the Web Audio API for spatial audio, effects, or analysis.
96
+ * @param userId The connection ID of the remote user
97
+ * @returns The HTMLAudioElement for this user's stream, or undefined if not connected
98
+ * @example
99
+ * ```ts
100
+ * const audioEl = voip.getAudioElement(userId);
101
+ * if (audioEl) {
102
+ * const audioCtx = new AudioContext();
103
+ * const source = audioCtx.createMediaElementSource(audioEl);
104
+ * const panner = audioCtx.createPanner();
105
+ * source.connect(panner).connect(audioCtx.destination);
106
+ * }
107
+ * ```
108
+ */
109
+ getAudioElement(userId: string): HTMLAudioElement | undefined {
110
+ return this._incomingStreams.get(userId);
111
+ }
112
+
113
+ /**
114
+ * Get all active incoming audio streams as a Map of userId → HTMLAudioElement.
115
+ * Useful for iterating over all connected voice users.
116
+ */
117
+ get incomingStreams(): ReadonlyMap<string, HTMLAudioElement> {
118
+ return this._incomingStreams;
119
+ }
120
+
121
+ /**
122
+ * Threshold for speaking detection (0–255). When a user's audio amplitude exceeds this,
123
+ * they are considered "speaking". Default is 30.
124
+ */
125
+ @serializable()
126
+ speakingThreshold: number = 30;
127
+
128
+ /**
129
+ * Event fired when a user's speaking state changes.
130
+ * Passes `{ userId: string, isSpeaking: boolean, volume: number }`.
131
+ */
132
+ @serializable(EventList)
133
+ onSpeakingChanged: EventList = new EventList();
134
+
135
+ private _speakingStates = new Map<string, boolean>();
136
+ private _analysers = new Map<string, { analyser: AnalyserNode, data: Uint8Array, context: AudioContext }>();
137
+
78
138
  private _net?: NetworkedStreams;
79
139
  private _menubutton?: HTMLElement;
80
140
 
@@ -141,12 +201,25 @@ export class Voip extends Behaviour {
141
201
  this.onEnabledChanged();
142
202
  this.updateButton();
143
203
  window.removeEventListener("visibilitychange", this.onVisibilityChanged);
204
+ // Clean up analysers
205
+ for (const userId of [...this._analysers.keys()]) {
206
+ this.cleanupAnalyser(userId);
207
+ }
144
208
  }
145
209
 
146
210
  /** @internal */
147
211
  onDestroy(): void {
148
212
  this._menubutton?.remove();
149
213
  this._menubutton = undefined;
214
+ // Clean up all streams and analysers
215
+ for (const userId of [...this._analysers.keys()]) {
216
+ this.cleanupAnalyser(userId);
217
+ }
218
+ for (const incoming of this._incomingStreams.values()) {
219
+ disposeStream(incoming.srcObject as MediaStream);
220
+ }
221
+ this._incomingStreams.clear();
222
+ this._speakingStates.clear();
150
223
  }
151
224
 
152
225
  /** Set via the mic button (e.g. when the websocket connection closes and rejoins but the user was muted before we don't want to enable VOIP again automatically) */
@@ -347,10 +420,61 @@ export class Voip extends Behaviour {
347
420
  disposeStream(incoming.srcObject as MediaStream);
348
421
  }
349
422
  this._incomingStreams.clear();
423
+ for (const userId of this._analysers.keys()) {
424
+ this.cleanupAnalyser(userId);
425
+ }
350
426
  }
351
427
 
352
428
  private _incomingStreams: Map<string, HTMLAudioElement> = new Map();
353
429
 
430
+ /** @internal */
431
+ update() {
432
+ // Only run speaking detection if someone is listening
433
+ if (!this.onSpeakingChanged || this.onSpeakingChanged.listenerCount <= 0) return;
434
+
435
+ for (const [userId, info] of this._analysers) {
436
+ info.analyser.getByteFrequencyData(info.data as Uint8Array<ArrayBuffer>);
437
+ // Average amplitude
438
+ let sum = 0;
439
+ for (let i = 0; i < info.data.length; i++) sum += info.data[i];
440
+ const avg = sum / info.data.length;
441
+
442
+ const wasSpeaking = this._speakingStates.get(userId) ?? false;
443
+ const isSpeaking = avg > this.speakingThreshold;
444
+
445
+ if (isSpeaking !== wasSpeaking) {
446
+ this._speakingStates.set(userId, isSpeaking);
447
+ this.onSpeakingChanged.invoke({ userId, isSpeaking, volume: avg / 255 });
448
+ }
449
+ }
450
+ }
451
+
452
+ private setupAnalyser(userId: string, _audioElement: HTMLAudioElement, stream: MediaStream) {
453
+ // Only set up if someone is listening or might listen
454
+ if (this._analysers.has(userId)) return;
455
+ try {
456
+ const audioCtx = new AudioContext();
457
+ const source = audioCtx.createMediaStreamSource(stream);
458
+ const analyser = audioCtx.createAnalyser();
459
+ analyser.fftSize = 256;
460
+ source.connect(analyser);
461
+ const data = new Uint8Array(analyser.frequencyBinCount);
462
+ this._analysers.set(userId, { analyser, data, context: audioCtx });
463
+ }
464
+ catch (err) {
465
+ if (this.debug) console.warn("VOIP: Failed to create analyser for", userId, err);
466
+ }
467
+ }
468
+
469
+ private cleanupAnalyser(userId: string) {
470
+ const info = this._analysers.get(userId);
471
+ if (info) {
472
+ info.context.close();
473
+ this._analysers.delete(userId);
474
+ }
475
+ this._speakingStates.delete(userId);
476
+ }
477
+
354
478
  private onReceiveStream = (evt: StreamReceivedEvent) => {
355
479
  const userId = evt.target.userId;
356
480
  const stream = evt.stream;
@@ -361,7 +485,9 @@ export class Voip extends Behaviour {
361
485
  this._incomingStreams.set(userId, audioElement);
362
486
  }
363
487
  audioElement.srcObject = stream;
488
+ audioElement.volume = this._volume;
364
489
  audioElement.setAttribute("autoplay", "true");
490
+ this.setupAnalyser(userId, audioElement, stream);
365
491
  // for mobile we need to wait for user interaction to play audio. Auto play doesnt work on android when the page is refreshed
366
492
  Application.registerWaitForInteraction(() => {
367
493
  audioElement?.play().catch((err) => {
@@ -374,6 +500,7 @@ export class Voip extends Behaviour {
374
500
  const existing = this._incomingStreams.get(evt.userId);
375
501
  disposeStream(existing?.srcObject as MediaStream);
376
502
  this._incomingStreams.delete(evt.userId);
503
+ this.cleanupAnalyser(evt.userId);
377
504
  }
378
505
 
379
506
  private onEnabledChanged = () => {
@@ -35,6 +35,7 @@
35
35
  */
36
36
 
37
37
  export * from "./codegen/components.js";
38
+ export { AnimatorControllerBuilder, type ConditionMode, type StateOptions, type TransitionOptions } from "./AnimatorController.js";
38
39
  export { Collider } from "./Collider.js"; // export abstract type
39
40
  export { Behaviour, Component, GameObject } from "./Component.js";
40
41
 
@@ -6,6 +6,7 @@ export { Animation } from "../Animation.js";
6
6
  export { Keyframe } from "../AnimationCurve.js";
7
7
  export { AnimationCurve } from "../AnimationCurve.js";
8
8
  export { Animator } from "../Animator.js";
9
+ export { AnimatorControllerBuilder } from "../AnimatorController.js";
9
10
  export { AnimatorController } from "../AnimatorController.js";
10
11
  export { AudioListener } from "../AudioListener.js";
11
12
  export { AudioSource } from "../AudioSource.js";
@@ -1,11 +1,11 @@
1
1
  import type { ToneMappingEffect as _TonemappingEffect, ToneMappingMode } from "postprocessing";
2
+ import type { ToneMapping } from "three";
2
3
 
3
4
  import { MODULES } from "../../../engine/engine_modules.js";
4
5
  import { serializable } from "../../../engine/engine_serialization.js";
5
6
  import { nameToThreeTonemapping } from "../../../engine/engine_tonemapping.js";
6
7
  import { getParam } from "../../../engine/engine_utils.js";
7
8
  import { EffectProviderResult, PostProcessingEffect } from "../PostProcessingEffect.js";
8
- import { findPostProcessingManager } from "../utils.js";
9
9
  import { VolumeParameter } from "../VolumeParameter.js";
10
10
  import { registerCustomEffectType } from "../VolumeProfile.js";
11
11
  import { NEToneMappingMode, NEToneMappingModeNames, threeToNeedleToneMapping, threeToneMappingToEffectMode, toThreeToneMapping } from "./Tonemapping.utils.js";
@@ -14,8 +14,8 @@ const debug = getParam("debugpost");
14
14
 
15
15
 
16
16
  /**
17
- * [ToneMappingEffect](https://engine.needle.tools/docs/api/ToneMappingEffect) adjusts the brightness and contrast of the rendered scene to map high dynamic range (HDR) colors to a displayable range.
18
- * This effect is essential for achieving realistic lighting and color representation in 3D scenes, as it helps to preserve details in both bright and dark areas.
17
+ * [ToneMappingEffect](https://engine.needle.tools/docs/api/ToneMappingEffect) adjusts the brightness and contrast of the rendered scene to map high dynamic range (HDR) colors to a displayable range.
18
+ * This effect is essential for achieving realistic lighting and color representation in 3D scenes, as it helps to preserve details in both bright and dark areas.
19
19
  * Various tonemapping algorithms can be applied to achieve different visual styles and effects.
20
20
  * @summary Tonemapping Post-Processing Effect
21
21
  * @category Effects
@@ -46,18 +46,23 @@ export class ToneMappingEffect extends PostProcessingEffect {
46
46
 
47
47
  get isToneMapping() { return true; }
48
48
 
49
- onEffectEnabled(): void {
50
- // Tonemapping works with and without a postprocessing manager.
51
- // If there's no manager already in the scene we don't need to create one because tonemapping can also be applied without a postprocessing pass
52
- const ppmanager = findPostProcessingManager(this);
53
- if (!ppmanager) return;
54
- super.onEffectEnabled(ppmanager);
49
+ /** The three.js ToneMapping enum value resolved from the current mode */
50
+ get threeToneMapping(): ToneMapping {
51
+ if (this.mode.isInitialized && this.mode.overrideState)
52
+ return toThreeToneMapping(this.mode.value);
53
+ return this.context.renderer.toneMapping as ToneMapping;
54
+ }
55
+
56
+ /** The exposure value to apply */
57
+ get toneMappingExposure(): number {
58
+ if (this.exposure.overrideState && this.exposure.value !== undefined)
59
+ return Math.max(0.0, this.exposure.value);
60
+ return this.context.renderer.toneMappingExposure;
55
61
  }
56
62
 
57
63
  private _tonemappingEffect: _TonemappingEffect | null = null;
58
64
  onCreateEffect(): EffectProviderResult | undefined {
59
65
 
60
-
61
66
  // ensure the effect tonemapping value is initialized
62
67
  if (this.mode.isInitialized == false) {
63
68
  const mode = threeToNeedleToneMapping(this.context.renderer.toneMapping);
@@ -90,19 +95,6 @@ export class ToneMappingEffect extends PostProcessingEffect {
90
95
  return tonemapping;
91
96
  }
92
97
 
93
-
94
- onBeforeRender(): void {
95
- if (this._tonemappingEffect && this.postprocessingContext?.handler.getEffectIsActive(this._tonemappingEffect)) {
96
- if (this.mode.overrideState)
97
- this.context.renderer.toneMapping = toThreeToneMapping(this.mode.value);
98
- if (this.exposure.overrideState && this.exposure.value !== undefined) {
99
- const newValue = Math.max(0.0, this.exposure.value);
100
- this.context.renderer.toneMappingExposure = newValue;
101
- }
102
- }
103
- }
104
-
105
-
106
98
  }
107
99
 
108
- registerCustomEffectType("Tonemapping", ToneMappingEffect);
100
+ registerCustomEffectType("Tonemapping", ToneMappingEffect);
@@ -3,9 +3,10 @@ import type { Effect, Pass } from "postprocessing";
3
3
  import type { EditorModification, IEditorModification } from "../../engine/engine_editor-sync.js";
4
4
  import { serializable } from "../../engine/engine_serialization.js";
5
5
  import { getParam } from "../../engine/engine_utils.js";
6
+ import type { PostProcessing } from "../../engine/postprocessing/index.js";
7
+ import type { IPostProcessingEffect } from "../../engine/postprocessing/types.js";
6
8
  import { Component } from "../Component.js";
7
9
  import type { PostProcessingHandler } from "./PostProcessingHandler.js";
8
- import { getPostProcessingManager, IPostProcessingManager } from "./utils.js";
9
10
  import { VolumeParameter } from "./VolumeParameter.js";
10
11
 
11
12
  const debug = getParam("debugpost");
@@ -50,8 +51,9 @@ export interface IEffectProvider {
50
51
  *
51
52
  * @category Effects
52
53
  * @group Components
54
+ * @see {@link PostProcessing} for core Needle Engine postprocessing control, also accessible via `context.postprocessing`
53
55
  */
54
- export abstract class PostProcessingEffect extends Component implements IEffectProvider, IEditorModification {
56
+ export abstract class PostProcessingEffect extends Component implements IPostProcessingEffect, IEffectProvider, IEditorModification {
55
57
 
56
58
  get isPostProcessingEffect() { return true; }
57
59
 
@@ -101,31 +103,22 @@ export abstract class PostProcessingEffect extends Component implements IEffectP
101
103
  @serializable()
102
104
  active: boolean = true;
103
105
 
104
- private _manager: IPostProcessingManager | null = null;
105
-
106
106
  onEnable(): void {
107
107
  super.onEnable();
108
108
  if (debug) console.warn("Enable", this.constructor.name + (!this.__internalDidAwakeAndStart ? " (awake)" : ""));
109
- // Dont override the serialized value by enabling (we could also just disable this component / map enabled to active)
110
- if (this.__internalDidAwakeAndStart)
111
- this.active = true;
112
- this.onEffectEnabled();
109
+ // Ensure active is synced with the component's enabled state
110
+ this.active = true;
111
+ // Register directly with the core postprocessing stack
112
+ this.context.postprocessing.addEffect(this);
113
113
  }
114
114
 
115
115
  onDisable(): void {
116
116
  super.onDisable();
117
117
  if (debug) console.warn("Disable", this.constructor.name);
118
- this._manager?.removeEffect(this);
118
+ this.context.postprocessing.removeEffect(this);
119
119
  this.active = false;
120
120
  }
121
121
 
122
- protected onEffectEnabled(manager?: IPostProcessingManager) {
123
- if (manager && manager.isPostProcessingManager === true) this._manager = manager;
124
- else if (!this._manager) this._manager = getPostProcessingManager(this);
125
- this._manager!.addEffect(this);
126
- this._manager!.dirty = true;
127
- }
128
-
129
122
  /** override to initialize bindings on parameters */
130
123
  init() { }
131
124
 
@@ -8,6 +8,7 @@ import { Context } from "../../engine/engine_setup.js";
8
8
  import { Graphics } from "../../engine/engine_three_utils.js";
9
9
  import { Constructor } from "../../engine/engine_types.js";
10
10
  import { getParam } from "../../engine/engine_utils.js";
11
+ import type { IPostProcessingHandler } from "../../engine/postprocessing/types.js";
11
12
  import { Camera } from "../Camera.js";
12
13
  import { threeToneMappingToEffectMode } from "./Effects/Tonemapping.utils.js";
13
14
  import { PostProcessingEffect, PostProcessingEffectContext } from "./PostProcessingEffect.js";
@@ -26,7 +27,7 @@ const previousToneMapping = Symbol("needle:previous-tone-mapping");
26
27
  /**
27
28
  * [PostProcessingHandler](https://engine.needle.tools/docs/api/PostProcessingHandler) Is responsible for applying post processing effects to the scene. It is internally used by the {@link Volume} component
28
29
  */
29
- export class PostProcessingHandler {
30
+ export class PostProcessingHandler implements IPostProcessingHandler {
30
31
 
31
32
  private _composer: EffectComposer | null = null;
32
33
  private _lastVolumeComponents?: PostProcessingEffect[];