@needle-tools/engine 4.13.1 → 4.14.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/components.needle.json +1 -1
- package/dist/generateMeshBVH.worker-DiCnZlf3.js +21 -0
- package/dist/gltf-progressive-Bm_6aEi4.js +1528 -0
- package/dist/gltf-progressive-BttGBXw6.umd.cjs +10 -0
- package/dist/gltf-progressive-T5WKTux5.min.js +10 -0
- package/dist/loader.worker-BqODMeeW.js +23 -0
- package/dist/{materialx-B9ddsHcF.min.js → materialx-CJyQZtjt.min.js} +1 -1
- package/dist/{materialx-fkoFuRh3.js → materialx-DMs1E08Z.js} +2 -2
- package/dist/{materialx-BF23AVE8.umd.cjs → materialx-DaKKOoVk.umd.cjs} +1 -1
- package/dist/needle-engine.bundle-BW2VusZV.min.js +1646 -0
- package/dist/{needle-engine.bundle-tjI5Fq2c.umd.cjs → needle-engine.bundle-Cb5bKEqa.umd.cjs} +152 -159
- package/dist/{needle-engine.bundle-DauZUYl7.js → needle-engine.bundle-D9VPvp5o.js} +7798 -7497
- package/dist/needle-engine.d.ts +779 -42
- package/dist/needle-engine.js +416 -415
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-BVNrgYZK.min.js → postprocessing-CctM1XIO.min.js} +1 -1
- package/dist/{postprocessing-DdM-tz1j.js → postprocessing-DGm6qJ-I.js} +2 -2
- package/dist/{postprocessing-CI2TjWpu.umd.cjs → postprocessing-Dbl2PJpd.umd.cjs} +1 -1
- package/dist/{three-BW2s1Yl-.umd.cjs → three-BjYim-vL.umd.cjs} +4 -4
- package/dist/{three-VvRoMeIN.js → three-Bvk2VKbF.js} +4210 -4210
- package/dist/{three-I__hSXzr.min.js → three-IG2qPafA.min.js} +33 -33
- package/dist/{three-examples-Bpfu6ke_.umd.cjs → three-examples-BMmNgNCN.umd.cjs} +1 -1
- package/dist/{three-examples-BhfOE7NG.js → three-examples-CMYCd5nH.js} +1 -1
- package/dist/{three-examples-D8zAE_7t.min.js → three-examples-CQl1fFZp.min.js} +1 -1
- package/dist/{three-mesh-ui-C3QbemOV.min.js → three-mesh-ui-5HVE2RV-.min.js} +1 -1
- package/dist/{three-mesh-ui-CcMp-FQm.js → three-mesh-ui-BlakAItG.js} +1 -1
- package/dist/{three-mesh-ui-BU55xDxJ.umd.cjs → three-mesh-ui-D828VbQp.umd.cjs} +1 -1
- package/dist/{vendor-DW7zqjuT.min.js → vendor-BxK0WKmT.min.js} +1 -1
- package/dist/{vendor-COVQl0b8.umd.cjs → vendor-CIDkyBaO.umd.cjs} +1 -1
- package/dist/{vendor-BiyIZ61v.js → vendor-ixwD-vv2.js} +1 -1
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_context.js +12 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_lightdata.js +8 -6
- package/lib/engine/engine_lightdata.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.d.ts +259 -0
- package/lib/engine/engine_materialpropertyblock.js +682 -0
- package/lib/engine/engine_materialpropertyblock.js.map +1 -0
- package/lib/engine/engine_utils.d.ts +1 -0
- package/lib/engine/engine_utils.js +1 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_screenshot.d.ts +171 -14
- package/lib/engine/engine_utils_screenshot.js +65 -0
- package/lib/engine/engine_utils_screenshot.js.map +1 -1
- package/lib/engine/engine_utils_screenshot.xr.d.ts +1 -1
- package/lib/engine/engine_utils_screenshot.xr.js +1 -1
- package/lib/engine/extensions/NEEDLE_techniques_webgl.js +3 -0
- package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +5 -0
- package/lib/engine/xr/NeedleXRSession.js +5 -0
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/Duplicatable.js.map +1 -1
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/NestedGltf.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +32 -4
- package/lib/engine-components/ReflectionProbe.js +58 -88
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +2 -0
- package/lib/engine-components/Renderer.js +30 -6
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/RendererLightmap.d.ts +13 -9
- package/lib/engine-components/RendererLightmap.js +67 -81
- package/lib/engine-components/RendererLightmap.js.map +1 -1
- package/lib/engine-components/SeeThrough.d.ts +0 -2
- package/lib/engine-components/SeeThrough.js +114 -88
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/SmoothFollow.js.map +1 -1
- package/lib/engine-components/VideoPlayer.js +6 -0
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/ui/Button.js.map +1 -1
- package/lib/engine-components/ui/Raycaster.js.map +1 -1
- package/lib/engine-components/utils/OpenURL.d.ts +1 -0
- package/lib/engine-components/utils/OpenURL.js +1 -0
- package/lib/engine-components/utils/OpenURL.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +1 -0
- package/lib/engine-components/web/CursorFollow.js +1 -0
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +1 -0
- package/lib/engine-components/web/ScrollFollow.js +1 -0
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +2 -2
- package/lib/engine-components/web/ViewBox.js +2 -2
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/lib/engine-components/webxr/WebARCameraBackground.d.ts +9 -0
- package/lib/engine-components/webxr/WebARCameraBackground.js +9 -0
- package/lib/engine-components/webxr/WebARCameraBackground.js.map +1 -1
- package/lib/engine-components/webxr/WebXR.d.ts +1 -0
- package/lib/engine-components/webxr/WebXR.js +1 -0
- package/lib/engine-components/webxr/WebXR.js.map +1 -1
- package/lib/engine-components/webxr/WebXRPlaneTracking.js.map +1 -1
- package/lib/engine-components/webxr/controllers/XRControllerFollow.js.map +1 -1
- package/lib/engine-components/webxr/controllers/XRControllerMovement.js.map +1 -1
- package/package.json +5 -5
- package/plugins/vite/build-pipeline.js +16 -2
- package/src/engine/api.ts +1 -0
- package/src/engine/engine_context.ts +17 -3
- package/src/engine/engine_lightdata.ts +8 -6
- package/src/engine/engine_materialpropertyblock.ts +866 -0
- package/src/engine/engine_utils.ts +1 -0
- package/src/engine/engine_utils_screenshot.ts +241 -17
- package/src/engine/engine_utils_screenshot.xr.ts +1 -1
- package/src/engine/extensions/NEEDLE_techniques_webgl.ts +3 -0
- package/src/engine/xr/NeedleXRSession.ts +5 -0
- package/src/engine-components/Camera.ts +2 -2
- package/src/engine-components/ContactShadows.ts +1 -1
- package/src/engine-components/DropListener.ts +1 -1
- package/src/engine-components/Duplicatable.ts +1 -1
- package/src/engine-components/GroundProjection.ts +3 -0
- package/src/engine-components/NeedleMenu.ts +3 -0
- package/src/engine-components/NestedGltf.ts +1 -1
- package/src/engine-components/ReflectionProbe.ts +64 -105
- package/src/engine-components/Renderer.ts +34 -6
- package/src/engine-components/RendererLightmap.ts +75 -87
- package/src/engine-components/SeeThrough.ts +124 -109
- package/src/engine-components/SmoothFollow.ts +2 -2
- package/src/engine-components/VideoPlayer.ts +6 -0
- package/src/engine-components/ui/Button.ts +1 -1
- package/src/engine-components/ui/Raycaster.ts +1 -1
- package/src/engine-components/utils/OpenURL.ts +1 -0
- package/src/engine-components/web/CursorFollow.ts +1 -0
- package/src/engine-components/web/ScrollFollow.ts +1 -0
- package/src/engine-components/web/ViewBox.ts +9 -2
- package/src/engine-components/webxr/WebARCameraBackground.ts +12 -3
- package/src/engine-components/webxr/WebXR.ts +1 -0
- package/src/engine-components/webxr/WebXRPlaneTracking.ts +3 -3
- package/src/engine-components/webxr/controllers/XRControllerFollow.ts +1 -1
- package/src/engine-components/webxr/controllers/XRControllerMovement.ts +4 -4
- package/dist/generateMeshBVH.worker-iyfPIK6R.js +0 -21
- package/dist/gltf-progressive-BURrJW0U.umd.cjs +0 -8
- package/dist/gltf-progressive-DHLDFNvQ.min.js +0 -8
- package/dist/gltf-progressive-eiJCrjLb.js +0 -1400
- package/dist/loader.worker-C1GG9A7C.js +0 -23
- package/dist/needle-engine.bundle-BNIUpreS.min.js +0 -1653
- 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
|
-
*
|
|
132
|
-
* @
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
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
|
-
*
|
|
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
|
|
148
|
-
*
|
|
149
|
-
* @param
|
|
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
|
|
|
@@ -132,7 +132,7 @@ export class Camera extends Behaviour implements ICamera {
|
|
|
132
132
|
*/
|
|
133
133
|
get nearClipPlane(): number { return this._nearClipPlane; }
|
|
134
134
|
@serializable()
|
|
135
|
-
set nearClipPlane(val) {
|
|
135
|
+
set nearClipPlane(val: number) {
|
|
136
136
|
const changed = this._nearClipPlane != val;
|
|
137
137
|
this._nearClipPlane = val;
|
|
138
138
|
if (this._cam && (changed || this._cam.near != val)) {
|
|
@@ -149,7 +149,7 @@ export class Camera extends Behaviour implements ICamera {
|
|
|
149
149
|
*/
|
|
150
150
|
get farClipPlane(): number { return this._farClipPlane; }
|
|
151
151
|
@serializable()
|
|
152
|
-
set farClipPlane(val) {
|
|
152
|
+
set farClipPlane(val: number) {
|
|
153
153
|
const changed = this._farClipPlane != val;
|
|
154
154
|
this._farClipPlane = val;
|
|
155
155
|
if (this._cam && (changed || this._cam.far != val)) {
|
|
@@ -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
|
|
|
@@ -181,7 +181,7 @@ export class DropListener extends Behaviour {
|
|
|
181
181
|
* Only used when fitIntoVolume is enabled.
|
|
182
182
|
*/
|
|
183
183
|
@serializable(Vector3)
|
|
184
|
-
fitVolumeSize = new Vector3(1, 1, 1);
|
|
184
|
+
fitVolumeSize: Vector3 = new Vector3(1, 1, 1);
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
187
|
* When enabled, dropped objects will be positioned at the point where the cursor hit the scene.
|
|
@@ -6,6 +6,9 @@ import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
|
6
6
|
import { getBoundingBox, getTempVector, getWorldScale, Graphics, setVisibleInCustomShadowRendering, setWorldPosition } from "../engine/engine_three_utils.js";
|
|
7
7
|
import { delayForFrames, getParam, Watch as Watch } from "../engine/engine_utils.js";
|
|
8
8
|
import { Behaviour } from "./Component.js";
|
|
9
|
+
// Type-only imports for TSDoc @see links
|
|
10
|
+
import type { Camera } from "./Camera.js";
|
|
11
|
+
import type { ContactShadows } from "./ContactShadows.js";
|
|
9
12
|
|
|
10
13
|
const debug = getParam("debuggroundprojection");
|
|
11
14
|
|
|
@@ -2,6 +2,9 @@ import type { Context } from '../engine/engine_context.js';
|
|
|
2
2
|
import { serializable } from '../engine/engine_serialization.js';
|
|
3
3
|
import { DeviceUtilities } from '../engine/engine_utils.js';
|
|
4
4
|
import { Behaviour } from './Component.js';
|
|
5
|
+
// Type-only imports for TSDoc @see links
|
|
6
|
+
import type { ScreenCapture } from './ScreenCapture.js';
|
|
7
|
+
import type { Voip } from './Voip.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* [NeedleMenu](https://engine.needle.tools/docs/api/NeedleMenu) provides configuration for the built-in UI menu.
|
|
@@ -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,13 @@ 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";
|
|
11
|
+
import { EventList } from "./EventList.js";
|
|
10
12
|
|
|
11
13
|
export const debug = getParam("debugreflectionprobe");
|
|
12
14
|
const disable = getParam("noreflectionprobe");
|
|
13
15
|
|
|
14
16
|
const $reflectionProbeKey = Symbol("reflectionProbeKey");
|
|
15
|
-
const $originalMaterial = Symbol("original material");
|
|
16
|
-
|
|
17
17
|
/**
|
|
18
18
|
* The [ReflectionProbe](https://engine.needle.tools/docs/api/ReflectionProbe) provides environment reflection data to materials within its defined area.
|
|
19
19
|
* Use for chrome-like materials that need accurate environment reflections.
|
|
@@ -43,9 +43,27 @@ export class ReflectionProbe extends Behaviour {
|
|
|
43
43
|
private static _probes: Map<Context, ReflectionProbe[]> = new Map();
|
|
44
44
|
|
|
45
45
|
static isUsingReflectionProbe(material: Material) {
|
|
46
|
-
return !!(material
|
|
46
|
+
return !!(material as any)[$reflectionProbeKey];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Event invoked when a reflection probe is enabled. Used internally by Renderer components to update probes when they become active. Not recommended to call this directly in most cases.
|
|
52
|
+
*/
|
|
53
|
+
static readonly onEnabled: EventList<ReflectionProbe> = new EventList();
|
|
54
|
+
static readonly onDisabled: EventList<ReflectionProbe> = new EventList();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets the active reflection probe for the given object and context. If `isAnchor` is true, it will only return a probe if the object is the anchor of that probe. Otherwise, it checks if the object is within the probe's influence area.
|
|
58
|
+
*
|
|
59
|
+
* Note: This method is used internally by the Renderer component to determine which reflection probe to apply. It is not recommended to call this method directly in most cases. Instead, assign probes to renderers using the "anchor" property or rely on automatic assignment when supported.
|
|
60
|
+
* Note: Volume-based automatic assignment is not fully supported yet, so explicit assignment is recommended for now.
|
|
61
|
+
*
|
|
62
|
+
* @param object The object to find a reflection probe for
|
|
63
|
+
* @param context The context to search within
|
|
64
|
+
* @param isAnchor If true, only return a probe if the object is the anchor of that probe
|
|
65
|
+
* @param anchor Optional anchor object to match against probes
|
|
66
|
+
*/
|
|
49
67
|
public static get(object: Object3D | null | undefined, context: Context, isAnchor: boolean, anchor?: Object3D): ReflectionProbe | null {
|
|
50
68
|
if (!object || object.isObject3D !== true) return null;
|
|
51
69
|
if (disable) return null;
|
|
@@ -55,7 +73,6 @@ export class ReflectionProbe extends Behaviour {
|
|
|
55
73
|
if (!probe.__didAwake) probe.__internalAwake();
|
|
56
74
|
if (probe.activeAndEnabled) {
|
|
57
75
|
if (anchor) {
|
|
58
|
-
// test if anchor is reflection probe object
|
|
59
76
|
if (probe.gameObject === anchor) {
|
|
60
77
|
return probe;
|
|
61
78
|
}
|
|
@@ -72,13 +89,10 @@ export class ReflectionProbe extends Behaviour {
|
|
|
72
89
|
return null;
|
|
73
90
|
}
|
|
74
91
|
|
|
75
|
-
|
|
76
|
-
|
|
77
92
|
private _texture!: Texture;
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
@serializable(Texture)
|
|
80
95
|
set texture(tex: Texture) {
|
|
81
|
-
|
|
82
96
|
if (this._texture === tex) return;
|
|
83
97
|
this._texture = tex;
|
|
84
98
|
|
|
@@ -86,7 +100,6 @@ export class ReflectionProbe extends Behaviour {
|
|
|
86
100
|
|
|
87
101
|
if (tex) {
|
|
88
102
|
if (tex instanceof CubeTexture) {
|
|
89
|
-
// cube textures use CubeReflectionMapping by default
|
|
90
103
|
}
|
|
91
104
|
else if (tex.mapping !== EquirectangularReflectionMapping) {
|
|
92
105
|
tex.mapping = EquirectangularReflectionMapping;
|
|
@@ -99,11 +112,26 @@ export class ReflectionProbe extends Behaviour {
|
|
|
99
112
|
return this._texture;
|
|
100
113
|
}
|
|
101
114
|
|
|
115
|
+
@serializable()
|
|
116
|
+
intensity: number = 1;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Defines the center and size of the reflection probe's influence area.
|
|
120
|
+
*/
|
|
102
121
|
@serializable(Vector3)
|
|
103
122
|
center?: Vector3;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Defines the size of the reflection probe's influence area. Objects within this box will be affected by the probe's reflections.
|
|
126
|
+
*/
|
|
104
127
|
@serializable(Vector3)
|
|
105
128
|
size?: Vector3;
|
|
106
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Workaround for lightmap. Compensates for the fact that lightmaps are pre-multiplied by intensity, while reflection probes are not. This means that if you use both lightmaps and reflection probes, you may need to adjust this value to get the correct balance between them. The default value of `Math.PI` is a good starting point for most cases, but you may need to tweak it based on your specific lighting setup and artistic needs.
|
|
132
|
+
*/
|
|
133
|
+
__lightmapIntensityScale: boolean = true;
|
|
134
|
+
|
|
107
135
|
private _boxHelper?: BoxHelperComponent;
|
|
108
136
|
|
|
109
137
|
private isInBox(obj: Object3D) {
|
|
@@ -130,6 +158,15 @@ export class ReflectionProbe extends Behaviour {
|
|
|
130
158
|
this._texture.needsUpdate = true;
|
|
131
159
|
}
|
|
132
160
|
}
|
|
161
|
+
|
|
162
|
+
onEnable(): void {
|
|
163
|
+
ReflectionProbe.onEnabled?.invoke(this);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
onDisable(): void {
|
|
167
|
+
ReflectionProbe.onDisabled?.invoke(this);
|
|
168
|
+
}
|
|
169
|
+
|
|
133
170
|
start(): void {
|
|
134
171
|
if (!this._texture) {
|
|
135
172
|
console.warn(`[ReflectionProbe] Missing texture. Please assign a custom cubemap texture. To use reflection probes assign them to your renderer's "anchor" property.`);
|
|
@@ -146,107 +183,29 @@ export class ReflectionProbe extends Behaviour {
|
|
|
146
183
|
}
|
|
147
184
|
}
|
|
148
185
|
|
|
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
|
-
onSet(_rend: IRenderer) {
|
|
186
|
+
apply(object: Object3D) {
|
|
157
187
|
if (disable) return;
|
|
158
188
|
if (!this.enabled) return;
|
|
159
|
-
if (_rend.sharedMaterials?.length <= 0) return;
|
|
160
189
|
if (!this.texture) return;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
for (let i = 0; i < _rend.sharedMaterials.length; i++) {
|
|
173
|
-
const material = _rend.sharedMaterials[i];
|
|
174
|
-
if (!material) {
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
if (material["envMap"] === undefined) {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
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;
|
|
231
|
-
|
|
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
|
-
}
|
|
238
|
-
|
|
239
|
-
_rend.sharedMaterials[i] = copy;
|
|
190
|
+
const propertyBlock = MaterialPropertyBlock.get(object);
|
|
191
|
+
propertyBlock.setOverride("envMap", this.texture);
|
|
192
|
+
propertyBlock.setOverride("envMapRotation", this.gameObject.rotation);
|
|
193
|
+
|
|
194
|
+
let intensity = this.intensity;
|
|
195
|
+
if (this.__lightmapIntensityScale && propertyBlock.getOverride("lightMap")) {
|
|
196
|
+
// @TODO: Remove this here and in Renderer https://linear.app/needle/issue/NE-6922
|
|
197
|
+
intensity /= Math.PI;
|
|
240
198
|
}
|
|
199
|
+
propertyBlock.setOverride("envMapIntensity", intensity);
|
|
241
200
|
}
|
|
242
201
|
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
202
|
+
unapply(obj: Object3D) {
|
|
203
|
+
const block = MaterialPropertyBlock.get(obj);
|
|
204
|
+
if (block) {
|
|
205
|
+
const current = block.getOverride("envMap")?.value;
|
|
206
|
+
if (current === this.texture) {
|
|
207
|
+
block.removeOveride("envMap");
|
|
249
208
|
}
|
|
250
209
|
}
|
|
251
210
|
}
|
|
252
|
-
}
|
|
211
|
+
}
|