@needle-tools/engine 5.1.0-alpha.1 → 5.1.0-canary.0d9f44e
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/.needle/generated/needle-bindings.gen.d.ts +5 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-DJBMx-zB.umd.cjs → gltf-progressive-BmblPzFj.umd.cjs} +4 -4
- package/dist/{gltf-progressive-BryRjllq.min.js → gltf-progressive-CN_mbb66.min.js} +2 -2
- package/dist/{gltf-progressive-Cl167Vjx.js → gltf-progressive-DUlhxdv4.js} +5 -2
- package/dist/{needle-engine.bundle-BGyKqxBH.js → needle-engine.bundle-B35n_IHX.js} +8736 -8500
- package/dist/{needle-engine.bundle-DzVx9Z8D.umd.cjs → needle-engine.bundle-CDj15wRB.umd.cjs} +164 -164
- package/dist/{needle-engine.bundle-CiYtOO2O.min.js → needle-engine.bundle-D5zzggEG.min.js} +164 -164
- package/dist/needle-engine.d.ts +140 -22
- package/dist/needle-engine.js +172 -170
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-B_9sKVU7.min.js → postprocessing-B571qGWR.min.js} +34 -34
- package/dist/{postprocessing-WDc9WwI3.js → postprocessing-CfrLAbLX.js} +0 -1
- package/dist/{postprocessing-B2wb6pzI.umd.cjs → postprocessing-CiGkAeM9.umd.cjs} +17 -17
- package/dist/three-examples.js +4289 -3778
- package/dist/three-examples.min.js +301 -14
- package/dist/three-examples.umd.cjs +301 -14
- package/dist/{vendor-CAcsI0eU.js → vendor-BFrMaK9q.js} +8983 -9136
- package/dist/vendor-CJmyOrCq.min.js +1116 -0
- package/dist/vendor-DkMW3WY4.umd.cjs +1116 -0
- package/lib/engine/api.d.ts +12 -0
- package/lib/engine/api.js +2 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug_environment.js +1 -1
- package/lib/engine/debug/debug_environment.js.map +1 -1
- package/lib/engine/engine_application.js +8 -6
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_constants.js +6 -0
- package/lib/engine/engine_constants.js.map +1 -1
- package/lib/engine/engine_context.d.ts +31 -2
- package/lib/engine/engine_context.js +43 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_registry.js +1 -1
- package/lib/engine/engine_context_registry.js.map +1 -1
- package/lib/engine/engine_init.js +5 -0
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.d.ts +3 -2
- package/lib/engine/engine_input.js +3 -2
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +25 -15
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_networking_blob.d.ts +1 -1
- package/lib/engine/engine_networking_blob.js +5 -11
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +0 -1
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_pmrem.js +2 -2
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +34 -0
- package/lib/engine/engine_scenedata.js +135 -0
- package/lib/engine/engine_scenedata.js.map +1 -0
- package/lib/engine/engine_ssr.d.ts +18 -0
- package/lib/engine/engine_ssr.js +40 -0
- package/lib/engine/engine_ssr.js.map +1 -0
- package/lib/engine/engine_three_utils.d.ts +14 -7
- package/lib/engine/engine_three_utils.js +14 -7
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.js +2 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_hash.d.ts +9 -0
- package/lib/engine/engine_utils_hash.js +112 -0
- package/lib/engine/engine_utils_hash.js.map +1 -0
- package/lib/engine/webcomponents/logo-element.d.ts +10 -1
- package/lib/engine/webcomponents/logo-element.js +2 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +12 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js +2 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +15 -1
- package/lib/engine/webcomponents/needle-button.js +2 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +7 -1
- package/lib/engine/webcomponents/needle-engine.js +2 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.js +1 -1
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine-components/Light.d.ts +25 -8
- package/lib/engine-components/Light.js +132 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/RigidBody.js +3 -3
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +2 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
- package/lib/engine-components/postprocessing/Effects/Sharpening.js +1 -2
- package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +5 -6
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +0 -1
- package/lib/engine-components/web/ScrollFollow.js +3 -2
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/needle-engine.d.ts +1 -0
- package/lib/needle-engine.js +1 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +7 -5
- package/plugins/dts-generator/dts.codegen.js +334 -0
- package/plugins/dts-generator/dts.scan.js +99 -0
- package/plugins/dts-generator/dts.writer.js +59 -0
- package/plugins/dts-generator/glb.discovery.js +279 -0
- package/plugins/dts-generator/glb.extractor.js +215 -0
- package/plugins/dts-generator/glb.reader.js +167 -0
- package/plugins/dts-generator/index.js +36 -0
- package/plugins/dts-generator/manifest.types.js +174 -0
- package/plugins/types/index.d.ts +2 -1
- package/plugins/types/needle-bindings.d.ts +30 -0
- package/plugins/types/userconfig.d.ts +21 -2
- package/plugins/vite/asap.js +1 -1
- package/plugins/vite/dependency-watcher.d.ts +2 -2
- package/plugins/vite/dependency-watcher.js +3 -4
- package/plugins/vite/drop.d.ts +2 -2
- package/plugins/vite/drop.js +3 -4
- package/plugins/vite/dts-generator.d.ts +7 -0
- package/plugins/vite/dts-generator.js +191 -0
- package/plugins/vite/index.d.ts +10 -3
- package/plugins/vite/index.js +27 -10
- package/plugins/vite/logging.js +2 -2
- package/plugins/vite/meta.js +4 -2
- package/plugins/vite/poster.d.ts +2 -2
- package/plugins/vite/poster.js +3 -5
- package/plugins/vite/reload.d.ts +2 -2
- package/plugins/vite/reload.js +5 -5
- package/src/engine/api.ts +15 -1
- package/src/engine/debug/debug_environment.ts +1 -1
- package/src/engine/engine_application.ts +8 -6
- package/src/engine/engine_constants.ts +11 -6
- package/src/engine/engine_context.ts +47 -2
- package/src/engine/engine_context_registry.ts +1 -1
- package/src/engine/engine_init.ts +4 -0
- package/src/engine/engine_input.ts +3 -2
- package/src/engine/engine_license.ts +23 -19
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_networking_blob.ts +5 -11
- package/src/engine/engine_physics_rapier.ts +0 -3
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +133 -0
- package/src/engine/engine_ssr.ts +48 -0
- package/src/engine/engine_three_utils.ts +15 -7
- package/src/engine/engine_types.ts +2 -0
- package/src/engine/engine_utils.ts +1 -0
- package/src/engine/engine_utils_hash.ts +65 -0
- package/src/engine/webcomponents/logo-element.ts +10 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +11 -2
- package/src/engine/webcomponents/needle-button.ts +15 -1
- package/src/engine/webcomponents/needle-engine.ts +8 -1
- package/src/engine/xr/NeedleXRSession.ts +1 -1
- package/src/engine-components/Light.ts +132 -27
- package/src/engine-components/RigidBody.ts +3 -3
- package/src/engine-components/SceneSwitcher.ts +1 -0
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +1 -1
- package/src/engine-components/postprocessing/Effects/Sharpening.ts +1 -2
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +4 -8
- package/src/engine-components/web/ScrollFollow.ts +2 -2
- package/src/needle-engine.ts +2 -0
- package/src/vite-env.d.ts +16 -0
- package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
- package/dist/vendor-HRlxIBga.min.js +0 -1116
package/plugins/vite/logging.js
CHANGED
|
@@ -97,8 +97,8 @@ export function needleLog(pluginName, message, level = 'log', options = undefine
|
|
|
97
97
|
? (dimBody ? leveledBody.split('\n').map(l => colors.dim(l)).join('\n') : leveledBody)
|
|
98
98
|
: "";
|
|
99
99
|
const payloadCore = emitHeader
|
|
100
|
-
? (body.length > 0 ? `${header}\n${body}
|
|
101
|
-
: (body.length > 0 ? `${body}
|
|
100
|
+
? (body.length > 0 ? `${header}\n${body}` : `${header}`)
|
|
101
|
+
: (body.length > 0 ? `${body}` : "");
|
|
102
102
|
const payload = leadingNewline ? `\n${payloadCore}` : payloadCore;
|
|
103
103
|
log(payload);
|
|
104
104
|
return;
|
package/plugins/vite/meta.js
CHANGED
|
@@ -13,8 +13,7 @@ import { needleGreenBold, needleLog } from './logging.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export function needleMeta(command, config, userSettings) {
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
const isBuild = command === 'build';
|
|
16
|
+
let isBuild = command === 'build';
|
|
18
17
|
|
|
19
18
|
async function updateConfig() {
|
|
20
19
|
config = await loadConfig();
|
|
@@ -25,6 +24,9 @@ export function needleMeta(command, config, userSettings) {
|
|
|
25
24
|
return {
|
|
26
25
|
// replace meta tags
|
|
27
26
|
name: 'needle:meta',
|
|
27
|
+
configResolved(resolvedConfig) {
|
|
28
|
+
isBuild = resolvedConfig.command === 'build';
|
|
29
|
+
},
|
|
28
30
|
transformIndexHtml: {
|
|
29
31
|
order: 'pre',
|
|
30
32
|
handler(/** @type {string} */ html, /** @type {unknown} */ _ctx) {
|
package/plugins/vite/poster.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export function getPosterPath(): string;
|
|
2
2
|
/**
|
|
3
|
-
* @param {"build" | "serve"}
|
|
3
|
+
* @param {"build" | "serve" | undefined} _command
|
|
4
4
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
5
5
|
* @param {import('../types').userSettings} userSettings
|
|
6
6
|
* @returns {import('vite').Plugin | undefined}
|
|
7
7
|
*/
|
|
8
|
-
export function needlePoster(
|
|
8
|
+
export function needlePoster(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null | undefined, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
|
package/plugins/vite/poster.js
CHANGED
|
@@ -10,19 +10,17 @@ export function getPosterPath() {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @param {"build" | "serve"}
|
|
13
|
+
* @param {"build" | "serve" | undefined} _command
|
|
14
14
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
15
15
|
* @param {import('../types').userSettings} userSettings
|
|
16
16
|
* @returns {import('vite').Plugin | undefined}
|
|
17
17
|
*/
|
|
18
|
-
export function needlePoster(
|
|
19
|
-
// only relevant for local development
|
|
20
|
-
if (command === 'build') return [];
|
|
21
|
-
|
|
18
|
+
export function needlePoster(_command, config, userSettings) {
|
|
22
19
|
if (userSettings.noPoster) return;
|
|
23
20
|
|
|
24
21
|
return {
|
|
25
22
|
name: 'needle:poster',
|
|
23
|
+
apply: 'serve',
|
|
26
24
|
configureServer(server) {
|
|
27
25
|
const hot = server.hot ?? server.ws;
|
|
28
26
|
hot.on('needle:screenshot', async (data, client) => {
|
package/plugins/vite/reload.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {"build" | "serve"}
|
|
2
|
+
* @param {"build" | "serve" | undefined} _command
|
|
3
3
|
* @param {import('../types/needleConfig').needleMeta | null} config
|
|
4
4
|
* @param {import('../types').userSettings} userSettings
|
|
5
5
|
* @returns {import('vite').Plugin | undefined}
|
|
6
6
|
*/
|
|
7
|
-
export function needleReload(
|
|
7
|
+
export function needleReload(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
|
package/plugins/vite/reload.js
CHANGED
|
@@ -15,14 +15,12 @@ const filesUsingHotReload = new Set();
|
|
|
15
15
|
let assetsDirectory = "";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* @param {"build" | "serve"}
|
|
18
|
+
* @param {"build" | "serve" | undefined} _command
|
|
19
19
|
* @param {import('../types/needleConfig').needleMeta | null} config
|
|
20
20
|
* @param {import('../types').userSettings} userSettings
|
|
21
21
|
* @returns {import('vite').Plugin | undefined}
|
|
22
22
|
*/
|
|
23
|
-
export function needleReload(
|
|
24
|
-
if (command === "build") return undefined;
|
|
25
|
-
|
|
23
|
+
export function needleReload(_command, config, userSettings) {
|
|
26
24
|
if (userSettings?.noReload === true) return undefined;
|
|
27
25
|
|
|
28
26
|
|
|
@@ -51,6 +49,7 @@ export function needleReload(command, config, userSettings) {
|
|
|
51
49
|
|
|
52
50
|
return {
|
|
53
51
|
name: 'needle:reload',
|
|
52
|
+
apply: 'serve',
|
|
54
53
|
/** @param {import('vite').UserConfig} config */
|
|
55
54
|
config(config) {
|
|
56
55
|
if (!config.server) config.server = { watch: { ignored: [] } };
|
|
@@ -172,8 +171,9 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
172
171
|
// Check if codegen files actually changed their content
|
|
173
172
|
// this will return false if its the first update
|
|
174
173
|
// meaning if its the first export after the server starts those will not trigger a reload
|
|
174
|
+
// Ignore d.ts
|
|
175
175
|
const shouldCheckIfContentChanged = file.includes("/codegen/") || file.includes("/generated/") || file.endsWith("gen.js");// || file.endsWith(".glb") || file.endsWith(".gltf") || file.endsWith(".bin");
|
|
176
|
-
if (shouldCheckIfContentChanged) {
|
|
176
|
+
if (!file.includes("/.svelte-kit/") && !file.endsWith(".d.ts") && !file.endsWith("needle-html-data.json") && shouldCheckIfContentChanged) {
|
|
177
177
|
if (reloadIsScheduled) {
|
|
178
178
|
return [];
|
|
179
179
|
}
|
package/src/engine/api.ts
CHANGED
|
@@ -336,6 +336,9 @@ export { loadPMREM } from "./engine_pmrem.js";
|
|
|
336
336
|
/** Scene lighting data and environment configuration */
|
|
337
337
|
export * from "./engine_scenelighting.js";
|
|
338
338
|
|
|
339
|
+
/** Typed scene data proxy — maps SceneData interface to live component instances */
|
|
340
|
+
export { getSceneData, needle } from "./engine_scenedata.js";
|
|
341
|
+
|
|
339
342
|
|
|
340
343
|
// ============================================================================
|
|
341
344
|
// SERIALIZATION
|
|
@@ -436,4 +439,15 @@ export * from "./webcomponents/needle-engine.loading.js";
|
|
|
436
439
|
* XR API: NeedleXRSession, NeedleXRController, XRRig.
|
|
437
440
|
* @see {@link https://engine.needle.tools/docs/xr.html | XR Documentation}
|
|
438
441
|
*/
|
|
439
|
-
export * from "./xr/api.js"
|
|
442
|
+
export * from "./xr/api.js"
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* HTML ↔ 3D component binding types, similar to Svelte's `PageData`.
|
|
446
|
+
* `SceneData` is a nested interface augmented per-project by the `needle:dts-generator`
|
|
447
|
+
* Vite plugin — it maps scene node names to their components and typed fields.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* import type { SceneData } from "@needle-tools/engine";
|
|
451
|
+
* type OrbitSettings = SceneData["Camera"]["OrbitControls"];
|
|
452
|
+
*/
|
|
453
|
+
export type { SceneData } from "needle-bindings";
|
|
@@ -15,7 +15,7 @@ export function isDevEnvironment(): boolean {
|
|
|
15
15
|
let res = isLocalNetwork();
|
|
16
16
|
if (!res) {
|
|
17
17
|
// is stackblitz?
|
|
18
|
-
res = window.location.hostname.endsWith(".local-credentialless.webcontainer.io");
|
|
18
|
+
res = typeof window !== "undefined" && window.location.hostname.endsWith(".local-credentialless.webcontainer.io");
|
|
19
19
|
}
|
|
20
20
|
_cachedDevEnvironment = res;
|
|
21
21
|
return res;
|
|
@@ -31,12 +31,14 @@ export function internalOnUserInputRegistered() {
|
|
|
31
31
|
userInteractionCallbacks.length = 0;
|
|
32
32
|
copy.forEach(cb => cb());
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
document.addEventListener('
|
|
36
|
-
document.addEventListener('
|
|
37
|
-
document.addEventListener('
|
|
38
|
-
document.addEventListener('
|
|
39
|
-
document.addEventListener('
|
|
34
|
+
if (typeof document !== "undefined") {
|
|
35
|
+
document.addEventListener('mousedown', internalOnUserInputRegistered);
|
|
36
|
+
document.addEventListener('pointerup', internalOnUserInputRegistered);
|
|
37
|
+
document.addEventListener('click', internalOnUserInputRegistered);
|
|
38
|
+
document.addEventListener('dragstart', internalOnUserInputRegistered);
|
|
39
|
+
document.addEventListener('touchend', internalOnUserInputRegistered);
|
|
40
|
+
document.addEventListener('keydown', internalOnUserInputRegistered);
|
|
41
|
+
}
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
// User Activation should be available across browsers since November 2023 https://developer.mozilla.org/en-US/docs/Web/API/UserActivation
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getParam } from "../engine/engine_utils.js";
|
|
2
2
|
const debug = getParam("debugdefines");
|
|
3
3
|
|
|
4
|
+
// #region global defines
|
|
5
|
+
|
|
4
6
|
// We jump through hoops like this to support 3 cases:
|
|
5
7
|
// 1) Vanilla js or angular js where global defines are not guaranteed to be made
|
|
6
8
|
// 2) Vite where global defines are made, vite defines are also automatically set to globalThis
|
|
@@ -11,11 +13,6 @@ tryEval(`if(!globalThis["NEEDLE_ENGINE_GENERATOR"]) globalThis["NEEDLE_ENGINE_GE
|
|
|
11
13
|
tryEval(`if(!globalThis["NEEDLE_PROJECT_BUILD_TIME"]) globalThis["NEEDLE_PROJECT_BUILD_TIME"] = "unknown";`);
|
|
12
14
|
tryEval(`if(!globalThis["NEEDLE_PUBLIC_KEY"]) globalThis["NEEDLE_PUBLIC_KEY"] = "unknown";`);
|
|
13
15
|
|
|
14
|
-
declare const NEEDLE_ENGINE_VERSION: string
|
|
15
|
-
declare const NEEDLE_ENGINE_GENERATOR: string;
|
|
16
|
-
declare const NEEDLE_PROJECT_BUILD_TIME: string;
|
|
17
|
-
declare const NEEDLE_PUBLIC_KEY: string;
|
|
18
|
-
|
|
19
16
|
// Make sure to wrap the new global this define in underscores to prevent the bundler from replacing it with the actual value
|
|
20
17
|
tryEval(`globalThis["__NEEDLE_ENGINE_VERSION__"] = "` + NEEDLE_ENGINE_VERSION + `";`);
|
|
21
18
|
tryEval(`globalThis["__NEEDLE_ENGINE_GENERATOR__"] = "` + NEEDLE_ENGINE_GENERATOR + `";`);
|
|
@@ -50,4 +47,12 @@ function tryEval(str: string) {
|
|
|
50
47
|
if (debug)
|
|
51
48
|
console.error(err);
|
|
52
49
|
}
|
|
53
|
-
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// #region treeshake flags
|
|
55
|
+
// globalThis fallbacks for vanilla JS environments (no bundler define)
|
|
56
|
+
globalThis["NEEDLE_USE_RAPIER"] = globalThis["NEEDLE_USE_RAPIER"] !== undefined ? globalThis["NEEDLE_USE_RAPIER"] : true;
|
|
57
|
+
globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
|
|
58
|
+
// #endregion treeshake flags
|
|
@@ -37,6 +37,8 @@ import { NetworkConnection } from './engine_networking.js';
|
|
|
37
37
|
import { Physics } from './engine_physics.js';
|
|
38
38
|
import { PlayerViewManager } from './engine_playerview.js';
|
|
39
39
|
import { RendererData as SceneLighting } from './engine_scenelighting.js';
|
|
40
|
+
import { getSceneData } from './engine_scenedata.js';
|
|
41
|
+
import type { SceneData } from 'needle-bindings';
|
|
40
42
|
import { getTempColor, logHierarchy } from './engine_three_utils.js';
|
|
41
43
|
import { Time } from './engine_time.js';
|
|
42
44
|
import { patchTonemapping } from './engine_tonemapping.js';
|
|
@@ -475,6 +477,33 @@ export class Context implements IContext {
|
|
|
475
477
|
* The main camera of the scene - this camera is used for rendering
|
|
476
478
|
* Use `setCurrentCamera` for updating the main camera.
|
|
477
479
|
*/
|
|
480
|
+
/**
|
|
481
|
+
* Typed proxy providing direct access to scene components by node and component name.
|
|
482
|
+
* Types are auto-generated from your GLB assets by the `needle:dts-generator` Vite plugin
|
|
483
|
+
* and written to `src/generated/needle-bindings.d.ts` on every dev-server start and GLB change.
|
|
484
|
+
*
|
|
485
|
+
* Each property access traverses the live scene graph on demand — no caching, always fresh.
|
|
486
|
+
* This is a convenience shorthand for `getComponent` lookups; the same result can be
|
|
487
|
+
* achieved manually via `scene.getObjectByName(name)` + `getComponent(node, Type)`.
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* // Toggle auto-rotate on the orbit camera
|
|
491
|
+
* ctx.sceneData.Camera.OrbitControls.autoRotate = true;
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* // Change the background color
|
|
495
|
+
* ctx.sceneData.Camera.Camera.backgroundColor = new RGBAColor(1, 0, 0, 1);
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* // Equivalent manual approach (without sceneData)
|
|
499
|
+
* const node = ctx.scene.getObjectByName("Camera");
|
|
500
|
+
* const orbit = getComponent(node, OrbitControls);
|
|
501
|
+
* if (orbit) orbit.autoRotate = true;
|
|
502
|
+
*/
|
|
503
|
+
get sceneData(): SceneData {
|
|
504
|
+
return getSceneData(this);
|
|
505
|
+
}
|
|
506
|
+
|
|
478
507
|
get mainCamera(): Camera {
|
|
479
508
|
if (this._mainCamera) {
|
|
480
509
|
return this._mainCamera;
|
|
@@ -513,8 +542,22 @@ export class Context implements IContext {
|
|
|
513
542
|
connection: NetworkConnection;
|
|
514
543
|
/** @deprecated AssetDatabase is deprecated */
|
|
515
544
|
assets: AssetDatabase;
|
|
516
|
-
|
|
517
|
-
|
|
545
|
+
|
|
546
|
+
/** All registered lights in the scene, maintained by the Light component */
|
|
547
|
+
readonly lights: Set<ILight> = new Set();
|
|
548
|
+
|
|
549
|
+
/** The brightest registered directional light, or null if none are registered
|
|
550
|
+
* @see lights
|
|
551
|
+
*/
|
|
552
|
+
get mainLight(): ILight | null {
|
|
553
|
+
let best: ILight | null = null;
|
|
554
|
+
for (const light of this.lights) {
|
|
555
|
+
if (light.type !== "directional") continue;
|
|
556
|
+
if (!best || light.intensity > best.intensity) best = light;
|
|
557
|
+
}
|
|
558
|
+
return best;
|
|
559
|
+
}
|
|
560
|
+
|
|
518
561
|
/** @deprecated Use sceneLighting */
|
|
519
562
|
get rendererData() { return this.sceneLighting }
|
|
520
563
|
/** Access the scene lighting manager to control lighting settings in the context */
|
|
@@ -829,6 +872,8 @@ export class Context implements IContext {
|
|
|
829
872
|
this._onBeforeRenderListeners.clear();
|
|
830
873
|
this._onAfterRenderListeners.clear();
|
|
831
874
|
|
|
875
|
+
this.lights.clear();
|
|
876
|
+
|
|
832
877
|
if (!this.isManagedExternally) {
|
|
833
878
|
if (this.renderer) {
|
|
834
879
|
this.renderer.renderLists.dispose();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type IComponent, type IContext, type LoadedModel } from "./engine_types.js";
|
|
2
2
|
|
|
3
|
-
const debug = typeof window !== undefined ? window.location.search.includes("debugcontext") : false;
|
|
3
|
+
const debug = typeof window !== "undefined" ? window.location.search.includes("debugcontext") : false;
|
|
4
4
|
|
|
5
5
|
/** The various events that can be dispatched by a Needle Engine {@link IContext} instance
|
|
6
6
|
*/
|
|
@@ -11,6 +11,8 @@ import { initCameraExtensions } from "./js-extensions/Camera.js";
|
|
|
11
11
|
import { patchLayers } from "./js-extensions/Layers.js";
|
|
12
12
|
import { initObject3DExtensions } from "./js-extensions/Object3D.js";
|
|
13
13
|
import { initVectorExtensions } from "./js-extensions/Vector.js";
|
|
14
|
+
import { SSR } from "./engine_ssr.js";
|
|
15
|
+
import { initLicense } from "./engine_license.js";
|
|
14
16
|
import { initWebComponents } from "./webcomponents/init.js";
|
|
15
17
|
import { initPhysics } from "./engine_physics_rapier.js";
|
|
16
18
|
import { initXR } from "./xr/init.js";
|
|
@@ -28,6 +30,7 @@ let initialized = false;
|
|
|
28
30
|
*/
|
|
29
31
|
export function initEngine() {
|
|
30
32
|
if (initialized) return;
|
|
33
|
+
if (SSR) return;
|
|
31
34
|
initialized = true;
|
|
32
35
|
|
|
33
36
|
initWebComponents();
|
|
@@ -46,4 +49,5 @@ export function initEngine() {
|
|
|
46
49
|
initSceneSwitcherAttributes();
|
|
47
50
|
initPhysics();
|
|
48
51
|
initXR();
|
|
52
|
+
initLicense();
|
|
49
53
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Intersection, Matrix4, Object3D, Ray, Vector2, Vector3 } from 'three';
|
|
2
2
|
|
|
3
3
|
import { showBalloonMessage, showBalloonWarning } from './debug/debug.js';
|
|
4
|
+
import { PointerEventBase, KeyboardEventBase } from './engine_ssr.js';
|
|
4
5
|
import { Context } from './engine_setup.js';
|
|
5
6
|
import { getTempVector, getWorldQuaternion } from './engine_three_utils.js';
|
|
6
7
|
import type { ButtonName, CursorTypeName, IGameObject, IInput, MouseButtonName, Vec2 } from './engine_types.js';
|
|
@@ -116,7 +117,7 @@ export declare type NEPointerEventIntersection = Intersection & { event?: NEPoin
|
|
|
116
117
|
* @see {@link Input} for the input management system
|
|
117
118
|
* @see {@link PointerType} for available pointer types
|
|
118
119
|
*/
|
|
119
|
-
export class NEPointerEvent extends
|
|
120
|
+
export class NEPointerEvent extends PointerEventBase {
|
|
120
121
|
|
|
121
122
|
/**
|
|
122
123
|
* Spatial input data
|
|
@@ -242,7 +243,7 @@ export class NEPointerEvent extends PointerEvent {
|
|
|
242
243
|
if (debug) console.warn("Stop propagation...", this.pointerId, this.pointerType)
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
|
-
export class NEKeyboardEvent extends
|
|
246
|
+
export class NEKeyboardEvent extends KeyboardEventBase {
|
|
246
247
|
source?: Event
|
|
247
248
|
constructor(type: InputEvents, source: Event, init: KeyboardEventInit) {
|
|
248
249
|
super(type, init)
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { dof } from "three/src/nodes/TSL.js";
|
|
2
|
-
|
|
3
|
-
import { isDevEnvironment } from "./debug/index.js";
|
|
4
1
|
import { BUILD_TIME, GENERATOR, PUBLIC_KEY, VERSION } from "./engine_constants.js";
|
|
5
2
|
import { ContextEvent, ContextRegistry } from "./engine_context_registry.js";
|
|
6
3
|
import { onInitialized } from "./engine_lifecycle_api.js";
|
|
@@ -8,7 +5,7 @@ import { isLocalNetwork } from "./engine_networking_utils.js";
|
|
|
8
5
|
import { Context } from "./engine_setup.js";
|
|
9
6
|
import type { IContext } from "./engine_types.js";
|
|
10
7
|
import { getParam } from "./engine_utils.js";
|
|
11
|
-
import {
|
|
8
|
+
import { SSR } from "./engine_ssr.js";
|
|
12
9
|
|
|
13
10
|
const debug = getParam("debuglicense");
|
|
14
11
|
|
|
@@ -77,18 +74,22 @@ function invokeLicenseCheckResultChanged(result: boolean) {
|
|
|
77
74
|
// #region Telemetry
|
|
78
75
|
export namespace Telemetry {
|
|
79
76
|
|
|
80
|
-
window
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
|
|
84
|
-
sendError(Context.Current, "unhandled_promise_rejection", {
|
|
85
|
-
message: event.reason?.message,
|
|
86
|
-
stack: event.reason?.stack,
|
|
87
|
-
timestamp: Date.now(),
|
|
77
|
+
if (typeof window !== "undefined") {
|
|
78
|
+
window.addEventListener("error", (event: ErrorEvent) => {
|
|
79
|
+
sendError(Context.Current, "unhandled_error", event);
|
|
88
80
|
});
|
|
89
|
-
|
|
81
|
+
window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
|
|
82
|
+
sendError(Context.Current, "unhandled_promise_rejection", {
|
|
83
|
+
message: event.reason?.message,
|
|
84
|
+
stack: event.reason?.stack,
|
|
85
|
+
timestamp: Date.now(),
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
90
89
|
|
|
91
|
-
|
|
90
|
+
export function init() {
|
|
91
|
+
if(!SSR) onInitialized((ctx => sendPageViewEvent(ctx)), { once: true });
|
|
92
|
+
}
|
|
92
93
|
|
|
93
94
|
function sendPageViewEvent(ctx: IContext): Promise<void> | void {
|
|
94
95
|
if (!isAllowed(ctx)) {
|
|
@@ -241,11 +242,14 @@ export namespace Telemetry {
|
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
export function initLicense() {
|
|
246
|
+
Telemetry.init();
|
|
247
|
+
ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
|
|
248
|
+
showLicenseInfo(evt.context);
|
|
249
|
+
handleForbidden(evt.context);
|
|
250
|
+
setTimeout(() => sendUsageMessageToAnalyticsBackend(evt.context), 2000);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
249
253
|
|
|
250
254
|
export let runtimeLicenseCheckPromise: Promise<void> | undefined = undefined;
|
|
251
255
|
let applicationIsForbidden = false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Context, FrameEvent } from "./engine_context.js";
|
|
2
2
|
import { ContextEvent } from "./engine_context_registry.js";
|
|
3
|
+
import { SSR } from "./engine_ssr.js";
|
|
3
4
|
|
|
4
5
|
export declare type Event = ContextEvent | FrameEvent;
|
|
5
6
|
|
|
@@ -38,6 +39,12 @@ let methodsWarningCounter = 0;
|
|
|
38
39
|
* @param evt the event to call the function at
|
|
39
40
|
*/
|
|
40
41
|
export function registerFrameEventCallback(cb: LifecycleMethod, evt: Event, opts?: LifecycleMethodOptions) {
|
|
42
|
+
|
|
43
|
+
// Don't register the callback if we are in SSR, because it will never be called and might cause memory leaks
|
|
44
|
+
if(SSR) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
if (!newMethods.has(evt)) {
|
|
42
49
|
newMethods.set(evt, new Array());
|
|
43
50
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import * as _md5 from "md5";
|
|
2
|
-
// CJS interop: md5 may appear as { default: fn } or fn depending on bundler
|
|
3
|
-
const md5 = typeof _md5 === "function" ? _md5 : (_md5 as any).default;
|
|
4
1
|
import { FileLoader } from "three";
|
|
5
2
|
|
|
6
3
|
import { showBalloonWarning } from "./debug/index.js";
|
|
7
4
|
import { hasCommercialLicense } from "./engine_license.js";
|
|
8
5
|
import { delay } from "./engine_utils.js";
|
|
6
|
+
import { md5Hex, md5AsBytes, sha256Base64 } from "./engine_utils_hash.js";
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
export namespace BlobStorage {
|
|
@@ -22,21 +20,17 @@ export namespace BlobStorage {
|
|
|
22
20
|
/**
|
|
23
21
|
* Generates an md5 hash from a given buffer
|
|
24
22
|
* @param buffer The buffer to hash
|
|
25
|
-
* @returns The md5 hash
|
|
23
|
+
* @returns The md5 hash as a hex string
|
|
26
24
|
*/
|
|
27
25
|
export function hashMD5(buffer: ArrayBuffer): string {
|
|
28
|
-
return
|
|
26
|
+
return md5Hex(new Uint8Array(buffer));
|
|
29
27
|
}
|
|
30
28
|
export function hashMD5_Base64(buffer: ArrayBuffer): string {
|
|
31
|
-
const bytes =
|
|
29
|
+
const bytes = md5AsBytes(new Uint8Array(buffer));
|
|
32
30
|
return btoa(String.fromCharCode(...bytes));
|
|
33
31
|
}
|
|
34
32
|
export function hashSha256(buffer: ArrayBuffer): Promise<string> {
|
|
35
|
-
|
|
36
|
-
const hash = crypto.subtle.digest('SHA-256', bytes).then(res => {
|
|
37
|
-
return btoa(String.fromCharCode(...new Uint8Array(res)));
|
|
38
|
-
})
|
|
39
|
-
return hash;
|
|
33
|
+
return sha256Base64(buffer);
|
|
40
34
|
}
|
|
41
35
|
|
|
42
36
|
export type Upload_Result = {
|
|
@@ -44,9 +44,6 @@ const $bodyKey = Symbol("physics body");
|
|
|
44
44
|
const $colliderRigidbody = Symbol("rigidbody");
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
declare const NEEDLE_USE_RAPIER: boolean;
|
|
48
|
-
globalThis["NEEDLE_USE_RAPIER"] = globalThis["NEEDLE_USE_RAPIER"] !== undefined ? globalThis["NEEDLE_USE_RAPIER"] : true;
|
|
49
|
-
|
|
50
47
|
/** Register the Rapier physics backend. Called from {@link initEngine}
|
|
51
48
|
* to ensure it runs regardless of tree-shaking. */
|
|
52
49
|
export function initPhysics() {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createLoaders } from "@needle-tools/gltf-progressive";
|
|
2
2
|
import { CubeUVReflectionMapping, EquirectangularRefractionMapping, SRGBColorSpace, Texture, TextureLoader, WebGLRenderer } from "three";
|
|
3
|
-
import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader";
|
|
4
|
-
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
|
|
5
|
-
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
|
|
3
|
+
import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader.js";
|
|
4
|
+
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
5
|
+
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
|
|
6
6
|
|
|
7
7
|
const running: Map<string, Promise<Texture | null>> = new Map();
|
|
8
8
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { SceneData } from "needle-bindings";
|
|
2
|
+
export type { SceneData };
|
|
3
|
+
import type { IContext } from "./engine_types.js";
|
|
4
|
+
import { getComponent } from "./engine_components.js";
|
|
5
|
+
import { TypeStore } from "./engine_typestore.js";
|
|
6
|
+
import { isDevEnvironment } from "./debug/index.js";
|
|
7
|
+
import { ContextRegistry } from "./engine_context_registry.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Global proxy for the primary Needle Engine context.
|
|
11
|
+
* Resolves lazily on property access via `ContextRegistry.Current` —
|
|
12
|
+
* safe to import at module level, including in SSR environments
|
|
13
|
+
* (returns a silent error proxy when no context is active).
|
|
14
|
+
*
|
|
15
|
+
* Use this outside of Needle component lifecycle (e.g. in Svelte/React/Vue
|
|
16
|
+
* components, button handlers, or vanilla JS) instead of threading `ctx` around.
|
|
17
|
+
*
|
|
18
|
+
* For multiple `<needle-engine>` elements on the same page, use `ctx` directly.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* import { needle } from "@needle-tools/engine";
|
|
22
|
+
* button.onclick = () => {
|
|
23
|
+
* needle.sceneData.Camera.OrbitControls.autoRotate = true;
|
|
24
|
+
* };
|
|
25
|
+
*/
|
|
26
|
+
export const needle: IContext = new Proxy({} as IContext, {
|
|
27
|
+
get(_target, prop: string) {
|
|
28
|
+
if (prop === "then") return undefined; // not a Promise
|
|
29
|
+
const ctx = ContextRegistry.Current;
|
|
30
|
+
if (!ctx) {
|
|
31
|
+
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
32
|
+
fn(`[needle] needle.${prop} accessed before scene started`);
|
|
33
|
+
return makeErrorProxy(`needle not ready — scene hasn't started yet`);
|
|
34
|
+
}
|
|
35
|
+
const val = (ctx as any)[prop];
|
|
36
|
+
return typeof val === "function" ? val.bind(ctx) : val;
|
|
37
|
+
},
|
|
38
|
+
set(_target, prop: string, value: unknown) {
|
|
39
|
+
const ctx = ContextRegistry.Current;
|
|
40
|
+
if (!ctx) {
|
|
41
|
+
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
42
|
+
fn(`[needle] needle.${prop} set before scene started`);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
(ctx as any)[prop] = value;
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const cache = new WeakMap<IContext, SceneData>();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns a proxy that silently absorbs any property get/set and logs a
|
|
54
|
+
* developer-friendly warning. Used when a node or component lookup fails so
|
|
55
|
+
* that chained access like `ctx.sceneData.Foo.Bar.baz = 1` never throws.
|
|
56
|
+
*/
|
|
57
|
+
function makeErrorProxy(message: string): object {
|
|
58
|
+
const handler: ProxyHandler<object> = {
|
|
59
|
+
get(_t, prop: string) {
|
|
60
|
+
if (prop === "then") return undefined; // not a Promise
|
|
61
|
+
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
62
|
+
fn(`[SceneData] ${message}`);
|
|
63
|
+
return makeErrorProxy(message);
|
|
64
|
+
},
|
|
65
|
+
set(_t, prop: string) {
|
|
66
|
+
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
67
|
+
fn(`[SceneData] ${message} (tried to set "${prop}")`);
|
|
68
|
+
return true; // suppress TypeError
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return new Proxy({}, handler);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Returns a proxy for a scene node that exposes `$object`, `$components`,
|
|
76
|
+
* and child nodes as nested properties.
|
|
77
|
+
*/
|
|
78
|
+
function makeNodeProxy(ctx: IContext, node: import("three").Object3D): object {
|
|
79
|
+
return new Proxy({}, {
|
|
80
|
+
get(_t, prop: string) {
|
|
81
|
+
if (prop === "$object") return node;
|
|
82
|
+
if (prop === "$components") {
|
|
83
|
+
return new Proxy({}, {
|
|
84
|
+
get(_t2, compName: string) {
|
|
85
|
+
if (compName === "then") return undefined;
|
|
86
|
+
const ctor = TypeStore.get(compName);
|
|
87
|
+
if (!ctor) return makeErrorProxy(`Component type "${compName}" not registered (node "${node.name}")`);
|
|
88
|
+
const comp = getComponent(node, ctor);
|
|
89
|
+
if (!comp) return makeErrorProxy(`Component "${compName}" not found on node "${node.name}"`);
|
|
90
|
+
return comp;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Child node lookup by name
|
|
95
|
+
const child = node.children.find(c => c.name === prop) ?? null;
|
|
96
|
+
if (!child) return makeErrorProxy(`Child "${prop}" not found on node "${node.name}"`);
|
|
97
|
+
return makeNodeProxy(ctx, child);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Returns a lazily-resolved proxy typed as {@link SceneData}.
|
|
104
|
+
* The proxy is cached per context — each context gets exactly one instance.
|
|
105
|
+
*
|
|
106
|
+
* Shape mirrors the generated `needle-bindings.gen.d.ts`:
|
|
107
|
+
* ctx.sceneData.MyGlb.Camera.$components.OrbitControls.autoRotate = true;
|
|
108
|
+
* ctx.sceneData.MyGlb.Camera.$object // → THREE.Camera
|
|
109
|
+
* ctx.sceneData.MyGlb.UI.Button.$components.Button // → Needle Button component
|
|
110
|
+
*
|
|
111
|
+
* GLB name is ignored at runtime (scene is already loaded).
|
|
112
|
+
* Node lookup starts at the scene root.
|
|
113
|
+
*/
|
|
114
|
+
export function getSceneData(ctx: IContext): SceneData {
|
|
115
|
+
let proxy = cache.get(ctx);
|
|
116
|
+
if (!proxy) {
|
|
117
|
+
proxy = new Proxy({} as SceneData, {
|
|
118
|
+
get(_target, _glbName: string) {
|
|
119
|
+
// GLB name level — ignored at runtime, return node-name proxy
|
|
120
|
+
return new Proxy({}, {
|
|
121
|
+
get(_target, nodeName: string) {
|
|
122
|
+
if (nodeName === "then") return undefined;
|
|
123
|
+
const node = ctx.scene.getObjectByName(nodeName) ?? null;
|
|
124
|
+
if (!node) return makeErrorProxy(`Node "${nodeName}" not found in scene`);
|
|
125
|
+
return makeNodeProxy(ctx, node);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
cache.set(ctx, proxy);
|
|
131
|
+
}
|
|
132
|
+
return proxy;
|
|
133
|
+
}
|