@needle-tools/engine 4.13.1 → 4.14.0-next.31f837e

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 (106) hide show
  1. package/components.needle.json +1 -1
  2. package/dist/generateMeshBVH.worker-DFcS3P04.js +21 -0
  3. package/dist/gltf-progressive-8voIgNp_.js +1528 -0
  4. package/dist/gltf-progressive-BRRBj-nY.umd.cjs +10 -0
  5. package/dist/gltf-progressive-Dkh3tG4-.min.js +10 -0
  6. package/dist/loader.worker-C6cXDgR1.js +23 -0
  7. package/dist/{materialx-BF23AVE8.umd.cjs → materialx-CxlgposR.umd.cjs} +1 -1
  8. package/dist/{materialx-fkoFuRh3.js → materialx-D66rYPqe.js} +2 -2
  9. package/dist/{materialx-B9ddsHcF.min.js → materialx-Dx8st96L.min.js} +1 -1
  10. package/dist/{needle-engine.bundle-tjI5Fq2c.umd.cjs → needle-engine.bundle-BQXG5qbQ.umd.cjs} +138 -145
  11. package/dist/{needle-engine.bundle-DauZUYl7.js → needle-engine.bundle-Byl5i6zJ.js} +6403 -6164
  12. package/dist/needle-engine.bundle-D7w0XD7M.min.js +1646 -0
  13. package/dist/needle-engine.d.ts +251 -23
  14. package/dist/needle-engine.js +416 -415
  15. package/dist/needle-engine.min.js +1 -1
  16. package/dist/needle-engine.umd.cjs +1 -1
  17. package/dist/{postprocessing-DdM-tz1j.js → postprocessing-BkSpxpYB.js} +2 -2
  18. package/dist/{postprocessing-BVNrgYZK.min.js → postprocessing-Ce5-UWiA.min.js} +1 -1
  19. package/dist/{postprocessing-CI2TjWpu.umd.cjs → postprocessing-DFVElmAh.umd.cjs} +1 -1
  20. package/dist/{three-BW2s1Yl-.umd.cjs → three-Bad8p1pf.umd.cjs} +46 -46
  21. package/dist/{three-I__hSXzr.min.js → three-CWn13_u1.min.js} +33 -33
  22. package/dist/{three-VvRoMeIN.js → three-DFV1-P9z.js} +4209 -4209
  23. package/dist/{three-examples-BhfOE7NG.js → three-examples-43yqn3mL.js} +1 -1
  24. package/dist/{three-examples-Bpfu6ke_.umd.cjs → three-examples-CO-tx3Sp.umd.cjs} +1 -1
  25. package/dist/{three-examples-D8zAE_7t.min.js → three-examples-DKuJVGT4.min.js} +1 -1
  26. package/dist/{three-mesh-ui-BU55xDxJ.umd.cjs → three-mesh-ui-ChzVOraf.umd.cjs} +1 -1
  27. package/dist/{three-mesh-ui-C3QbemOV.min.js → three-mesh-ui-DyEA5HQF.min.js} +1 -1
  28. package/dist/{three-mesh-ui-CcMp-FQm.js → three-mesh-ui-fSAQJxdI.js} +1 -1
  29. package/dist/{vendor-COVQl0b8.umd.cjs → vendor-B51YffMU.umd.cjs} +1 -1
  30. package/dist/{vendor-BiyIZ61v.js → vendor-CgpZ5ivC.js} +1 -1
  31. package/dist/{vendor-DW7zqjuT.min.js → vendor-pe19S9r5.min.js} +1 -1
  32. package/lib/engine/api.d.ts +1 -0
  33. package/lib/engine/api.js +1 -0
  34. package/lib/engine/api.js.map +1 -1
  35. package/lib/engine/engine_context.js +12 -2
  36. package/lib/engine/engine_context.js.map +1 -1
  37. package/lib/engine/engine_lightdata.js +8 -6
  38. package/lib/engine/engine_lightdata.js.map +1 -1
  39. package/lib/engine/engine_materialpropertyblock.d.ts +47 -0
  40. package/lib/engine/engine_materialpropertyblock.js +412 -0
  41. package/lib/engine/engine_materialpropertyblock.js.map +1 -0
  42. package/lib/engine/engine_utils.d.ts +1 -0
  43. package/lib/engine/engine_utils.js +1 -0
  44. package/lib/engine/engine_utils.js.map +1 -1
  45. package/lib/engine/engine_utils_screenshot.d.ts +171 -14
  46. package/lib/engine/engine_utils_screenshot.js +65 -0
  47. package/lib/engine/engine_utils_screenshot.js.map +1 -1
  48. package/lib/engine/engine_utils_screenshot.xr.d.ts +1 -1
  49. package/lib/engine/engine_utils_screenshot.xr.js +1 -1
  50. package/lib/engine/extensions/NEEDLE_techniques_webgl.js +3 -0
  51. package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
  52. package/lib/engine/xr/NeedleXRSession.d.ts +5 -0
  53. package/lib/engine/xr/NeedleXRSession.js +5 -0
  54. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  55. package/lib/engine-components/ReflectionProbe.d.ts +0 -1
  56. package/lib/engine-components/ReflectionProbe.js +15 -76
  57. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  58. package/lib/engine-components/RendererLightmap.d.ts +13 -9
  59. package/lib/engine-components/RendererLightmap.js +68 -81
  60. package/lib/engine-components/RendererLightmap.js.map +1 -1
  61. package/lib/engine-components/SeeThrough.js.map +1 -1
  62. package/lib/engine-components/VideoPlayer.js +6 -0
  63. package/lib/engine-components/VideoPlayer.js.map +1 -1
  64. package/lib/engine-components/utils/OpenURL.d.ts +1 -0
  65. package/lib/engine-components/utils/OpenURL.js +1 -0
  66. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  67. package/lib/engine-components/web/CursorFollow.d.ts +1 -0
  68. package/lib/engine-components/web/CursorFollow.js +1 -0
  69. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  70. package/lib/engine-components/web/ScrollFollow.d.ts +1 -0
  71. package/lib/engine-components/web/ScrollFollow.js +1 -0
  72. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  73. package/lib/engine-components/webxr/WebARCameraBackground.d.ts +9 -0
  74. package/lib/engine-components/webxr/WebARCameraBackground.js +9 -0
  75. package/lib/engine-components/webxr/WebARCameraBackground.js.map +1 -1
  76. package/lib/engine-components/webxr/WebXR.d.ts +1 -0
  77. package/lib/engine-components/webxr/WebXR.js +1 -0
  78. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  79. package/package.json +4 -4
  80. package/plugins/vite/build-pipeline.js +16 -2
  81. package/src/engine/api.ts +1 -0
  82. package/src/engine/engine_context.ts +17 -3
  83. package/src/engine/engine_lightdata.ts +8 -6
  84. package/src/engine/engine_materialpropertyblock.ts +500 -0
  85. package/src/engine/engine_utils.ts +1 -0
  86. package/src/engine/engine_utils_screenshot.ts +241 -17
  87. package/src/engine/engine_utils_screenshot.xr.ts +1 -1
  88. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +3 -0
  89. package/src/engine/xr/NeedleXRSession.ts +5 -0
  90. package/src/engine-components/ContactShadows.ts +1 -1
  91. package/src/engine-components/ReflectionProbe.ts +17 -89
  92. package/src/engine-components/RendererLightmap.ts +76 -87
  93. package/src/engine-components/SeeThrough.ts +2 -2
  94. package/src/engine-components/VideoPlayer.ts +6 -0
  95. package/src/engine-components/utils/OpenURL.ts +1 -0
  96. package/src/engine-components/web/CursorFollow.ts +1 -0
  97. package/src/engine-components/web/ScrollFollow.ts +1 -0
  98. package/src/engine-components/webxr/WebARCameraBackground.ts +12 -3
  99. package/src/engine-components/webxr/WebXR.ts +1 -0
  100. package/dist/generateMeshBVH.worker-iyfPIK6R.js +0 -21
  101. package/dist/gltf-progressive-BURrJW0U.umd.cjs +0 -8
  102. package/dist/gltf-progressive-DHLDFNvQ.min.js +0 -8
  103. package/dist/gltf-progressive-eiJCrjLb.js +0 -1400
  104. package/dist/loader.worker-C1GG9A7C.js +0 -23
  105. package/dist/needle-engine.bundle-BNIUpreS.min.js +0 -1653
  106. package/src/include/three/DragControls.js +0 -232
@@ -32,6 +32,7 @@ declare type ScreenshotImageMimeType = "image/webp" | "image/png" | "image/jpeg"
32
32
  * const dataUrl = screenshot();
33
33
  * saveImage(dataUrl, "screenshot.png");
34
34
  * ```
35
+ *
35
36
  */
36
37
  export function screenshot(context?: Context, width?: number, height?: number, mimeType: ScreenshotImageMimeType = "image/webp", camera?: Camera | null): string | null {
37
38
  return screenshot2({ context, width, height, mimeType, camera });
@@ -106,12 +107,30 @@ export declare type ScreenshotOptionsBlob = ScreenshotOptions & {
106
107
  }
107
108
 
108
109
  export declare type ScreenshotOptionsShare = ScreenshotOptions & {
110
+ /**
111
+ * Set `{ type: "share" }` to share the screenshot using the Web Share API. The promise will resolve with the blob of the screenshot and whether it was shared successfully or not. Note that the Web Share API is only available in secure contexts (HTTPS) and on some platforms.
112
+ */
109
113
  type: "share",
114
+ /**
115
+ * The filename to use when sharing the screenshot. If not provided, a default filename will be used.
116
+ */
110
117
  filename?: string,
118
+ /**
119
+ * The mime type of the shared file. If not provided, the mime type will be inferred from the screenshot options or default to "image/png".
120
+ */
111
121
  file_type?: ScreenshotImageMimeType,
112
122
 
123
+ /**
124
+ * The title to use when sharing the screenshot. This is optional and may not be supported by all platforms.
125
+ */
113
126
  title?: string,
127
+ /**
128
+ * The text to use when sharing the screenshot. This is optional and may not be supported by all platforms.
129
+ */
114
130
  text?: string,
131
+ /**
132
+ * The URL to use when sharing the screenshot. This is optional and may not be supported by all platforms.
133
+ */
115
134
  url?: string,
116
135
  }
117
136
 
@@ -120,36 +139,241 @@ declare type ScreenshotOptionsShareReturnType = {
120
139
  shared: boolean,
121
140
  }
122
141
 
123
- /**
124
- * Take a screenshot from the current scene and return a {@link Texture}. This can applied to a surface in 3D space.
125
- * @param opts Provide `{ type: "texture" }` to get a texture instead of a data url.
142
+ /**
143
+ * Take a screenshot from the current scene and return a {@link Texture}. This can be applied to a surface in 3D space.
144
+ * @param opts Provide `{ type: "texture" }` to get a texture instead of a data url.
126
145
  * @returns The texture of the screenshot. Returns null if the screenshot could not be taken.
146
+ * @category Utilities
147
+ * @example
148
+ * ```ts
149
+ * // Create a texture from the current view
150
+ * const screenshotTexture = screenshot2({ type: "texture", width: 512, height: 512 });
151
+ * if (screenshotTexture) {
152
+ * myMaterial.map = screenshotTexture;
153
+ * myMaterial.needsUpdate = true;
154
+ * }
155
+ *
156
+ * // Update an existing texture
157
+ * const existingTexture = new Texture();
158
+ * screenshot2({ type: "texture", target: existingTexture, transparent: true });
159
+ * ```
127
160
  */
128
161
  export function screenshot2(opts: ScreenshotOptionsTexture): Texture | null;
162
+
129
163
  /**
130
- * Take a screenshot from the current scene.
131
- * @param opts
132
- * @returns The data url of the screenshot. Returns null if the screenshot could not be taken.
133
- * ```ts
134
- * const res = screenshot2({
135
- * width: 1024,
164
+ * Take a screenshot from the current scene and return a data URL string.
165
+ *
166
+ * @param opts Screenshot options. All properties are optional.
167
+ * @returns The data URL of the screenshot (e.g., "data:image/png;base64,..."). Returns null if the screenshot could not be taken.
168
+ * @category Utilities
169
+ *
170
+ * @example Basic screenshot
171
+ * ```ts
172
+ * // Take a simple screenshot with default settings
173
+ * const dataUrl = screenshot2({});
174
+ * console.log(dataUrl); // "data:image/webp;base64,..."
175
+ * ```
176
+ *
177
+ * @example High-resolution screenshot with transparent background
178
+ * ```ts
179
+ * const dataUrl = screenshot2({
180
+ * width: 2048,
181
+ * height: 2048,
182
+ * mimeType: "image/png",
183
+ * transparent: true,
184
+ * trim: true, // Remove transparent edges
185
+ * });
186
+ * ```
187
+ *
188
+ * @example Screenshot with custom background color
189
+ * ```ts
190
+ * import { Color } from "three";
191
+ *
192
+ * const dataUrl = screenshot2({
193
+ * width: 1024,
194
+ * height: 1024,
195
+ * background: new Color(0x00ff00), // Green background
196
+ * });
197
+ * ```
198
+ *
199
+ * @example Download screenshot automatically
200
+ * ```ts
201
+ * screenshot2({
202
+ * width: 1920,
203
+ * height: 1080,
204
+ * mimeType: "image/jpeg",
205
+ * download_filename: "my-scene.jpg",
206
+ * });
207
+ * ```
208
+ *
209
+ * @example Manual download using saveImage
210
+ * ```ts
211
+ * const dataUrl = screenshot2({
212
+ * width: 1024,
213
+ * height: 1024,
214
+ * mimeType: "image/webp",
215
+ * transparent: true,
216
+ * });
217
+ * if (dataUrl) {
218
+ * saveImage(dataUrl, "screenshot.webp");
219
+ * }
220
+ * ```
221
+ *
222
+ * @example Screenshot from specific camera
223
+ * ```ts
224
+ * const myCamera = this.gameObject.getComponent(Camera);
225
+ * const dataUrl = screenshot2({
226
+ * camera: myCamera,
227
+ * width: 1024,
136
228
  * height: 1024,
137
- * mimeType: "image/webp",
138
- * transparent: true,
139
- * })
140
- * // use saveImage to download the image
141
- * saveImage(res, "screenshot.webp");
229
+ * });
142
230
  * ```
143
231
  */
144
232
  export function screenshot2(opts: ScreenshotOptionsDataUrl): string | null;
145
233
 
146
234
  /**
147
- * Take a screenshot asynchronously from the current scene.
148
- * @returns A promise that resolves with the blob of the screenshot. Returns null if the screenshot could not be taken.
149
- * @param {ScreenshotOptionsBlob} opts Set `{ type: "blob" }` to get a blob instead of a data url.
235
+ * Take a screenshot asynchronously and return a Blob. This is useful when you need to process or upload the image data.
236
+ *
237
+ * @param opts Set `{ type: "blob" }` to get a blob instead of a data url. All other {@link ScreenshotOptions} are also available.
238
+ * @returns A Promise that resolves with the Blob of the screenshot. Returns null if the screenshot could not be taken.
239
+ * @category Utilities
240
+ *
241
+ * @example Upload screenshot to server
242
+ * ```ts
243
+ * const blob = await screenshot2({ type: "blob", mimeType: "image/png" });
244
+ * if (blob) {
245
+ * const formData = new FormData();
246
+ * formData.append("screenshot", blob, "screenshot.png");
247
+ * await fetch("/api/upload", { method: "POST", body: formData });
248
+ * }
249
+ * ```
250
+ *
251
+ * @example Save blob to file (browser download)
252
+ * ```ts
253
+ * const blob = await screenshot2({
254
+ * type: "blob",
255
+ * width: 1920,
256
+ * height: 1080,
257
+ * transparent: true
258
+ * });
259
+ * if (blob) {
260
+ * const url = URL.createObjectURL(blob);
261
+ * saveImage(url, "screenshot.png");
262
+ * URL.revokeObjectURL(url); // Clean up
263
+ * }
264
+ * ```
150
265
  */
151
266
  export function screenshot2(opts: ScreenshotOptionsBlob): Promise<Blob | null>;
267
+
268
+ /**
269
+ * Take a screenshot and share it using the Web Share API (mobile-friendly).
270
+ *
271
+ * **Note**: The Web Share API is only available in secure contexts (HTTPS) and may not be supported on all platforms/browsers.
272
+ *
273
+ * @param opts Set `{ type: "share" }` to share the screenshot. Additional options like `filename`, `title`, `text`, and `url` can be provided.
274
+ * @returns A Promise that resolves with an object containing the blob and whether it was successfully shared.
275
+ * @category Utilities
276
+ *
277
+ * @example Share screenshot on mobile
278
+ * ```ts
279
+ * const result = await screenshot2({
280
+ * type: "share",
281
+ * filename: "my-creation.png",
282
+ * title: "Check out my 3D scene!",
283
+ * text: "I created this with Needle Engine",
284
+ * url: "https://engine.needle.tools",
285
+ * mimeType: "image/png",
286
+ * });
287
+ *
288
+ * if (result.shared) {
289
+ * console.log("Screenshot shared successfully!");
290
+ * } else {
291
+ * console.log("User cancelled or sharing not supported");
292
+ * }
293
+ * ```
294
+ *
295
+ * @example Share with fallback
296
+ * ```ts
297
+ * const result = await screenshot2({
298
+ * type: "share",
299
+ * filename: "screenshot.webp",
300
+ * file_type: "image/webp",
301
+ * });
302
+ *
303
+ * if (!result.shared && result.blob) {
304
+ * // Fallback: download the image instead
305
+ * const url = URL.createObjectURL(result.blob);
306
+ * saveImage(url, "screenshot.webp");
307
+ * URL.revokeObjectURL(url);
308
+ * }
309
+ * ```
310
+ */
152
311
  export function screenshot2(opts: ScreenshotOptionsShare): Promise<ScreenshotOptionsShareReturnType>;
312
+
313
+ /**
314
+ * Take a screenshot from the current scene with advanced options.
315
+ *
316
+ * This is the main screenshot function in Needle Engine with support for multiple output formats:
317
+ * - **Data URL** (default): Returns a base64-encoded string suitable for img src attributes
318
+ * - **Texture**: Returns a Three.js Texture that can be applied to materials
319
+ * - **Blob**: Returns a Blob for uploading or processing (async)
320
+ * - **Share**: Uses the Web Share API to share the screenshot (async, mobile-friendly)
321
+ *
322
+ * @param opts Screenshot options. Use the `type` property to specify output format. See {@link ScreenshotOptions} for all available options.
323
+ * @returns Depending on the `type` option:
324
+ * - Data URL (string) when `type` is undefined or not specified
325
+ * - Texture when `type: "texture"`
326
+ * - Promise<Blob | null> when `type: "blob"`
327
+ * - Promise<{blob, shared}> when `type: "share"`
328
+ *
329
+ * Returns null (or Promise resolving to null) if the screenshot could not be taken.
330
+ *
331
+ * @category Utilities
332
+ *
333
+ * @example WebXR / AR Screenshots
334
+ * ```ts
335
+ * // Screenshots work automatically in WebXR sessions
336
+ * // The camera feed will be composited with your 3D content
337
+ * const dataUrl = screenshot2({
338
+ * width: 1920,
339
+ * height: 1080
340
+ * });
341
+ * ```
342
+ *
343
+ * **Note for AR Screenshots**: To include the device camera feed in AR screenshots, you need to request camera access.
344
+ * This is done automatically when you use {@link WebARCameraBackground} component in your scene.
345
+ * If you're not using WebARCameraBackground, you can request camera access manually:
346
+ * ```ts
347
+ * export class MyComponent extends Behaviour {
348
+ * onBeforeXR(mode: XRSessionMode, args: XRSessionInit): void {
349
+ * if (mode === "immersive-ar") {
350
+ * args.optionalFeatures = args.optionalFeatures || [];
351
+ * args.optionalFeatures.push('camera-access');
352
+ * }
353
+ * }
354
+ * }
355
+ * ```
356
+ * Without camera access, AR screenshots will only show your 3D content without the real-world background.
357
+ *
358
+ * @example Combining multiple options
359
+ * ```ts
360
+ * // High-res transparent screenshot with custom camera
361
+ * const myCamera = this.gameObject.getComponent(Camera);
362
+ * const texture = screenshot2({
363
+ * type: "texture",
364
+ * camera: myCamera,
365
+ * width: 2048,
366
+ * height: 2048,
367
+ * transparent: true,
368
+ * trim: true,
369
+ * render_events: true, // Ensure reflection probes are updated
370
+ * });
371
+ * ```
372
+ *
373
+ * @see {@link screenshot} for a simpler alternative with fewer options
374
+ * @see {@link saveImage} for downloading data URLs
375
+ * @see {@link ScreenshotOptions} for all available options
376
+ */
153
377
  export function screenshot2(opts: ScreenshotOptionsDataUrl | ScreenshotOptionsTexture | ScreenshotOptionsBlob | ScreenshotOptionsShare)
154
378
  : Texture | string | null | Promise<Blob | null> | Promise<ScreenshotOptionsShareReturnType> {
155
379
 
@@ -5,7 +5,7 @@ import { isDevEnvironment, showBalloonError } from "./debug/index.js";
5
5
  // Adapted from WebARCameraBackground
6
6
 
7
7
  /**
8
- * Assigns the camera feed to a texture - this must be called during the render loop
8
+ * Assigns the camera feed to a texture - this must be called during the render loop.
9
9
  */
10
10
  export function updateTextureFromXRFrame(renderer: WebGLRenderer, target: Texture): boolean {
11
11
 
@@ -621,6 +621,9 @@ function createUniformProperties(material: CustomShader) {
621
621
  case "_Color":
622
622
  defineProperty("color", key);
623
623
  break;
624
+ case "_map":
625
+ defineProperty("map", key);
626
+ break;
624
627
  // case "_Metallic":
625
628
  // defineProperty("metalness", key);
626
629
  // break;
@@ -279,7 +279,12 @@ const $initialFov = Symbol("initial-fov");
279
279
  * The XRRig can be accessed via the `rig` property
280
280
  * Set a custom XRRig via `NeedleXRSession.addRig(...)` or `NeedleXRSession.removeRig(...)`
281
281
  * By default the active XRRig with the highest priority in the scene is used
282
+ *
283
+ * ### Screenshots in XR
284
+ * Screenshots work automatically during XR sessions, including AR camera feed compositing. See {@link screenshot2} for more information.
285
+ *
282
286
  * @category XR
287
+ * @see {@link screenshot2} for taking screenshots in XR sessions
283
288
  */
284
289
  export class NeedleXRSession implements INeedleXRSession {
285
290
 
@@ -13,9 +13,9 @@ import { HideFlags, IGameObject, Vec3 } from "../engine/engine_types.js";
13
13
  import { getParam } from "../engine/engine_utils.js"
14
14
  import { setCustomVisibility } from "../engine/js-extensions/Layers.js";
15
15
  import { Behaviour, GameObject } from "./Component.js";
16
- import type { ShadowCatcher } from "./ShadowCatcher.js";
17
16
  import type { Light } from "./Light.js";
18
17
  import type { Renderer } from "./Renderer.js";
18
+ import type { ShadowCatcher } from "./ShadowCatcher.js";
19
19
 
20
20
  const debug = getParam("debugcontactshadows");
21
21
 
@@ -1,4 +1,4 @@
1
- import { CubeReflectionMapping, CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, MeshBasicMaterial, Object3D, SRGBColorSpace, Texture, Vector3 } from "three";
1
+ import { Color, CubeReflectionMapping, CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, MeshBasicMaterial, MeshStandardMaterial, Object3D, SRGBColorSpace, Texture, Vector3 } from "three";
2
2
 
3
3
  import { isDevEnvironment, showBalloonWarning } from "../engine/debug/index.js";
4
4
  import { serializable } from "../engine/engine_serialization.js";
@@ -7,13 +7,12 @@ import type { IRenderer } from "../engine/engine_types.js";
7
7
  import { getParam } from "../engine/engine_utils.js";
8
8
  import { BoxHelperComponent } from "./BoxHelperComponent.js";
9
9
  import { Behaviour } from "./Component.js";
10
+ import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
10
11
 
11
12
  export const debug = getParam("debugreflectionprobe");
12
13
  const disable = getParam("noreflectionprobe");
13
14
 
14
15
  const $reflectionProbeKey = Symbol("reflectionProbeKey");
15
- const $originalMaterial = Symbol("original material");
16
-
17
16
  /**
18
17
  * The [ReflectionProbe](https://engine.needle.tools/docs/api/ReflectionProbe) provides environment reflection data to materials within its defined area.
19
18
  * Use for chrome-like materials that need accurate environment reflections.
@@ -38,12 +37,13 @@ const $originalMaterial = Symbol("original material");
38
37
  * @see {@link Renderer} for material and rendering control
39
38
  * @see {@link Light} for scene lighting
40
39
  */
40
+
41
41
  export class ReflectionProbe extends Behaviour {
42
42
 
43
43
  private static _probes: Map<Context, ReflectionProbe[]> = new Map();
44
44
 
45
45
  static isUsingReflectionProbe(material: Material) {
46
- return !!(material[$reflectionProbeKey] || material[$originalMaterial]?.[$reflectionProbeKey]);
46
+ return !!(material as any)[$reflectionProbeKey];
47
47
  }
48
48
 
49
49
  public static get(object: Object3D | null | undefined, context: Context, isAnchor: boolean, anchor?: Object3D): ReflectionProbe | null {
@@ -55,7 +55,6 @@ export class ReflectionProbe extends Behaviour {
55
55
  if (!probe.__didAwake) probe.__internalAwake();
56
56
  if (probe.activeAndEnabled) {
57
57
  if (anchor) {
58
- // test if anchor is reflection probe object
59
58
  if (probe.gameObject === anchor) {
60
59
  return probe;
61
60
  }
@@ -72,13 +71,9 @@ export class ReflectionProbe extends Behaviour {
72
71
  return null;
73
72
  }
74
73
 
75
-
76
-
77
74
  private _texture!: Texture;
78
75
 
79
- // @serializable(Texture)
80
76
  set texture(tex: Texture) {
81
-
82
77
  if (this._texture === tex) return;
83
78
  this._texture = tex;
84
79
 
@@ -86,7 +81,6 @@ export class ReflectionProbe extends Behaviour {
86
81
 
87
82
  if (tex) {
88
83
  if (tex instanceof CubeTexture) {
89
- // cube textures use CubeReflectionMapping by default
90
84
  }
91
85
  else if (tex.mapping !== EquirectangularReflectionMapping) {
92
86
  tex.mapping = EquirectangularReflectionMapping;
@@ -146,29 +140,14 @@ export class ReflectionProbe extends Behaviour {
146
140
  }
147
141
  }
148
142
 
149
-
150
- // when objects are rendered and they share material
151
- // and some need reflection probe and some don't
152
- // we need to make sure we don't override the material but use a copy
153
-
154
- private static _rendererMaterialsCache: Map<IRenderer, Array<{ material: Material, copy: Material, originalVersion: number }>> = new Map();
155
-
156
143
  onSet(_rend: IRenderer) {
157
144
  if (disable) return;
158
145
  if (!this.enabled) return;
159
146
  if (_rend.sharedMaterials?.length <= 0) return;
160
147
  if (!this.texture) return;
161
148
 
162
- let rendererCache = ReflectionProbe._rendererMaterialsCache.get(_rend);
163
- if (!rendererCache) {
164
- rendererCache = [];
165
- ReflectionProbe._rendererMaterialsCache.set(_rend, rendererCache);
166
- }
167
-
168
- // TODO: dont clone material for every renderer that uses reflection probes, we can do it once per material when they use the same reflection texture
149
+ const object = _rend.gameObject as unknown as Object3D;
169
150
 
170
- // need to make sure materials are not shared when using reflection probes
171
- // otherwise some renderers outside of the probe will be affected or vice versa
172
151
  for (let i = 0; i < _rend.sharedMaterials.length; i++) {
173
152
  const material = _rend.sharedMaterials[i];
174
153
  if (!material) {
@@ -178,75 +157,24 @@ export class ReflectionProbe extends Behaviour {
178
157
  continue;
179
158
  }
180
159
 
181
- if (material instanceof MeshBasicMaterial) {
182
- continue;
183
- }
184
-
185
- let cached = rendererCache[i];
186
-
187
- // make sure we have the currently assigned material cached (and an up to date clone of that)
188
- // Compare against the stored original version, not the clone's version (which gets modified by needsUpdate)
189
- // This prevents cloning materials every frame when onUnset restores the original material
190
- const isCachedInstance = material === cached?.copy;
191
- const hasChanged = !cached || cached.material.uuid !== material.uuid || cached.originalVersion !== material.version;
192
- if (!isCachedInstance && hasChanged) {
193
- if (debug) {
194
- let reason = "";
195
- if (!cached) reason = "not cached";
196
- else if (cached.material !== material) reason = "reference changed; cached instance?: " + isCachedInstance;
197
- else if (cached.originalVersion !== material.version) reason = "version changed";
198
- console.warn("Cloning material", material.name, material.version, "Reason:", reason, "\n", material.uuid, "\n", cached?.copy.uuid, "\n", _rend.name);
199
- }
200
-
201
- const clone = material.clone();
202
- clone.version = material.version;
203
-
204
- if (cached) {
205
- cached.copy = clone;
206
- cached.material = material;
207
- cached.originalVersion = material.version;
208
- }
209
- else {
210
- cached = {
211
- material: material,
212
- copy: clone,
213
- originalVersion: material.version
214
- };
215
- rendererCache.push(cached);
216
- }
217
-
218
- clone[$reflectionProbeKey] = this;
219
- clone[$originalMaterial] = material;
220
-
221
- if (debug) console.log("Set reflection", _rend.name, _rend.guid);
222
- }
223
-
224
- // See NE-4771 and NE-4856
225
- if (cached && cached.copy) {
226
- cached.copy.onBeforeCompile = material.onBeforeCompile;
227
- }
228
-
229
- /** this is the material that we copied and that has the reflection probe */
230
- const copy = cached?.copy;
160
+ if (debug) console.log("Setting reflection probe on material", material.name, "for renderer", _rend.name);
231
161
 
232
- if ("envMap" in copy) {
233
- if (copy.envMap !== this.texture) { // Only update if changed
234
- copy.envMap = this.texture;
235
- copy.needsUpdate = true;
236
- }
237
- }
162
+ const propertyBlock = MaterialPropertyBlock.get(object);
163
+ propertyBlock.setOverride("envMap", this.texture);
164
+ // propertyBlock.setOverride("color", new Color(0xff0000));
238
165
 
239
- _rend.sharedMaterials[i] = copy;
166
+ (material as any)[$reflectionProbeKey] = true;
240
167
  }
241
168
  }
242
169
 
243
170
  onUnset(_rend: IRenderer) {
244
- const rendererCache = ReflectionProbe._rendererMaterialsCache.get(_rend);
245
- if (rendererCache) {
246
- for (let i = 0; i < rendererCache.length; i++) {
247
- const cached = rendererCache[i];
248
- _rend.sharedMaterials[i] = cached.material;
171
+ // const object = _rend.gameObject as unknown as Object3D;
172
+
173
+ for (let i = 0; i < _rend.sharedMaterials.length; i++) {
174
+ const material = _rend.sharedMaterials[i];
175
+ if (material) {
176
+ delete (material as any)[$reflectionProbeKey];
249
177
  }
250
178
  }
251
179
  }
252
- }
180
+ }