@needle-tools/engine 5.1.0-canary.db0c38f → 5.1.0-canary.deec6e4
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/CHANGELOG.md +34 -0
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-YnpzzOPL.min.js → needle-engine.bundle-1s2gOoKZ.min.js} +144 -144
- package/dist/{needle-engine.bundle-B29kieh0.js → needle-engine.bundle-CvtELXh0.js} +6650 -6584
- package/dist/{needle-engine.bundle-Dq0Ly8fW.umd.cjs → needle-engine.bundle-j4nGJXCs.umd.cjs} +138 -138
- package/dist/needle-engine.d.ts +101 -89
- package/dist/needle-engine.js +188 -186
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/api.d.ts +1 -1
- package/lib/engine/debug/debug_spatial_console.d.ts +2 -0
- package/lib/engine/debug/debug_spatial_console.js +10 -7
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +2 -0
- package/lib/engine/engine_addressables.js +6 -3
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_context.d.ts +21 -20
- package/lib/engine/engine_context.js +25 -14
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_init.js +15 -0
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +14 -6
- 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_pmrem.js +2 -2
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +13 -17
- package/lib/engine/engine_scenedata.js +56 -29
- package/lib/engine/engine_scenedata.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +10 -16
- package/lib/engine/engine_serialization_builtin_serializer.js +28 -41
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_ssr.d.ts +2 -0
- package/lib/engine/engine_ssr.js +20 -0
- package/lib/engine/engine_ssr.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/webcomponents/jsx.d.ts +51 -0
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +2 -3
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +2 -0
- package/lib/engine-components/AnimatorController.js +4 -1
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/Light.d.ts +6 -8
- package/lib/engine-components/Light.js +40 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.js +2 -0
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/postprocessing/VolumeParameter.d.ts +2 -0
- package/lib/engine-components/postprocessing/VolumeParameter.js +4 -1
- package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -0
- package/lib/needle-engine.js +2 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +3 -2
- package/plugins/dts-generator/dts.codegen.js +255 -50
- package/plugins/dts-generator/dts.scan.js +37 -9
- package/plugins/dts-generator/dts.writer.js +1 -1
- package/plugins/dts-generator/glb.discovery.js +140 -23
- package/plugins/dts-generator/glb.extractor.js +48 -8
- package/plugins/dts-generator/glb.reader.js +80 -27
- package/plugins/dts-generator/index.js +1 -1
- package/plugins/types/needle-bindings.d.ts +25 -14
- package/plugins/types/userconfig.d.ts +12 -0
- 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 +2 -2
- package/plugins/vite/dts-generator.js +43 -9
- package/plugins/vite/index.d.ts +9 -3
- package/plugins/vite/index.js +23 -10
- 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 +22 -22
- package/src/engine/api.ts +1 -1
- package/src/engine/debug/debug_spatial_console.ts +10 -7
- package/src/engine/engine_addressables.ts +6 -3
- package/src/engine/engine_context.ts +34 -20
- package/src/engine/engine_init.ts +14 -0
- package/src/engine/engine_license.ts +12 -10
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +53 -27
- package/src/engine/engine_serialization_builtin_serializer.ts +32 -43
- package/src/engine/engine_ssr.ts +29 -3
- package/src/engine/engine_types.ts +2 -0
- package/src/engine/webcomponents/jsx.d.ts +51 -0
- package/src/engine/webcomponents/logo-element.ts +1 -0
- package/src/engine/webcomponents/needle menu/needle-menu.ts +2 -1
- package/src/engine/webcomponents/needle-button.ts +1 -0
- package/src/engine/webcomponents/needle-engine.ts +1 -0
- package/src/engine-components/AnimatorController.ts +4 -1
- package/src/engine-components/Light.ts +40 -26
- package/src/engine-components/ReflectionProbe.ts +2 -0
- package/src/engine-components/postprocessing/VolumeParameter.ts +4 -1
- package/src/needle-engine.ts +3 -0
|
@@ -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
|
|
|
@@ -90,7 +87,9 @@ export namespace Telemetry {
|
|
|
90
87
|
});
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
export function init() {
|
|
91
|
+
if(!SSR) onInitialized((ctx => sendPageViewEvent(ctx)), { once: true });
|
|
92
|
+
}
|
|
94
93
|
|
|
95
94
|
function sendPageViewEvent(ctx: IContext): Promise<void> | void {
|
|
96
95
|
if (!isAllowed(ctx)) {
|
|
@@ -243,11 +242,14 @@ export namespace Telemetry {
|
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
}
|
|
251
253
|
|
|
252
254
|
export let runtimeLicenseCheckPromise: Promise<void> | undefined = undefined;
|
|
253
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,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
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { SceneData } from "needle
|
|
1
|
+
import type { SceneData } from "needle-bindings";
|
|
2
|
+
export type { SceneData };
|
|
2
3
|
import type { IContext } from "./engine_types.js";
|
|
3
4
|
import { getComponent } from "./engine_components.js";
|
|
4
5
|
import { TypeStore } from "./engine_typestore.js";
|
|
@@ -6,15 +7,13 @@ import { isDevEnvironment } from "./debug/index.js";
|
|
|
6
7
|
import { ContextRegistry } from "./engine_context_registry.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* safe to import at module level, including in SSR environments
|
|
12
|
-
* (returns a silent error proxy when no context is active).
|
|
10
|
+
* Quick access to the current Needle Engine context from anywhere — no need to pass `ctx` around.
|
|
11
|
+
* Use it in React/Svelte/Vue components, button handlers, or plain JavaScript.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Safe to import at module level, including in SSR environments.
|
|
14
|
+
* For pages with multiple `<needle-engine>` elements, use `ctx` directly instead.
|
|
16
15
|
*
|
|
17
|
-
*
|
|
16
|
+
* @experimental This API may change in future releases.
|
|
18
17
|
*
|
|
19
18
|
* @example
|
|
20
19
|
* import { needle } from "@needle-tools/engine";
|
|
@@ -28,7 +27,7 @@ export const needle: IContext = new Proxy({} as IContext, {
|
|
|
28
27
|
const ctx = ContextRegistry.Current;
|
|
29
28
|
if (!ctx) {
|
|
30
29
|
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
31
|
-
fn(`[needle] needle.${prop} accessed before scene started`);
|
|
30
|
+
fn(`[needle] needle.${prop} was accessed before the scene started. Use "needle" inside event handlers or callbacks, not at module top-level. For setup code use: onStart(ctx => { ... })`);
|
|
32
31
|
return makeErrorProxy(`needle not ready — scene hasn't started yet`);
|
|
33
32
|
}
|
|
34
33
|
const val = (ctx as any)[prop];
|
|
@@ -38,7 +37,7 @@ export const needle: IContext = new Proxy({} as IContext, {
|
|
|
38
37
|
const ctx = ContextRegistry.Current;
|
|
39
38
|
if (!ctx) {
|
|
40
39
|
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
41
|
-
fn(`[needle] needle.${prop} set before scene started`);
|
|
40
|
+
fn(`[needle] needle.${prop} was set before the scene started. Use "needle" inside event handlers or callbacks, not at module top-level. For setup code use: onStart(ctx => { ... })`);
|
|
42
41
|
return true;
|
|
43
42
|
}
|
|
44
43
|
(ctx as any)[prop] = value;
|
|
@@ -70,36 +69,63 @@ function makeErrorProxy(message: string): object {
|
|
|
70
69
|
return new Proxy({}, handler);
|
|
71
70
|
}
|
|
72
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Returns a proxy for a scene node that exposes `$object`, `$components`,
|
|
74
|
+
* and child nodes as nested properties.
|
|
75
|
+
*/
|
|
76
|
+
function makeNodeProxy(ctx: IContext, node: import("three").Object3D): object {
|
|
77
|
+
return new Proxy({}, {
|
|
78
|
+
get(_t, prop: string) {
|
|
79
|
+
if (prop === "$object") return node;
|
|
80
|
+
if (prop === "$components") {
|
|
81
|
+
return new Proxy({}, {
|
|
82
|
+
get(_t2, compName: string) {
|
|
83
|
+
if (compName === "then") return undefined;
|
|
84
|
+
const ctor = TypeStore.get(compName);
|
|
85
|
+
if (!ctor) return makeErrorProxy(`Component type "${compName}" not registered (node "${node.name}")`);
|
|
86
|
+
const comp = getComponent(node, ctor);
|
|
87
|
+
if (!comp) return makeErrorProxy(`Component "${compName}" not found on node "${node.name}"`);
|
|
88
|
+
return comp;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (prop === "then") return undefined; // not a Promise
|
|
93
|
+
// Child node lookup by name
|
|
94
|
+
const child = node.children.find(c => c.name === prop) ?? null;
|
|
95
|
+
if (!child) {
|
|
96
|
+
const fn = isDevEnvironment() ? console.error : console.warn;
|
|
97
|
+
fn(`[SceneData] "${prop}" is not a child of "${node.name}". Use .$object to get the Three.js object or .$components.Name to access a component.`);
|
|
98
|
+
return makeErrorProxy(`"${prop}" not found on node "${node.name}"`);
|
|
99
|
+
}
|
|
100
|
+
return makeNodeProxy(ctx, child);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
73
105
|
/**
|
|
74
106
|
* Returns a lazily-resolved proxy typed as {@link SceneData}.
|
|
75
107
|
* The proxy is cached per context — each context gets exactly one instance.
|
|
76
108
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
109
|
+
* Shape mirrors the generated `needle-bindings.gen.d.ts`:
|
|
110
|
+
* ctx.sceneData.MyGlb.Camera.$components.OrbitControls.autoRotate = true;
|
|
111
|
+
* ctx.sceneData.MyGlb.Camera.$object // → THREE.Camera
|
|
112
|
+
* ctx.sceneData.MyGlb.UI.Button.$components.Button // → Needle Button component
|
|
81
113
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* ctx.sceneData.Camera.OrbitControls.autoRotate = true;
|
|
114
|
+
* GLB name is ignored at runtime (scene is already loaded).
|
|
115
|
+
* Node lookup starts at the scene root.
|
|
87
116
|
*/
|
|
88
117
|
export function getSceneData(ctx: IContext): SceneData {
|
|
89
118
|
let proxy = cache.get(ctx);
|
|
90
119
|
if (!proxy) {
|
|
91
120
|
proxy = new Proxy({} as SceneData, {
|
|
92
|
-
get(_target,
|
|
121
|
+
get(_target, _glbName: string) {
|
|
122
|
+
// GLB name level — ignored at runtime, return node-name proxy
|
|
93
123
|
return new Proxy({}, {
|
|
94
|
-
get(_target,
|
|
124
|
+
get(_target, nodeName: string) {
|
|
125
|
+
if (nodeName === "then") return undefined;
|
|
95
126
|
const node = ctx.scene.getObjectByName(nodeName) ?? null;
|
|
96
127
|
if (!node) return makeErrorProxy(`Node "${nodeName}" not found in scene`);
|
|
97
|
-
|
|
98
|
-
const ctor = TypeStore.get(compName);
|
|
99
|
-
if (!ctor) return makeErrorProxy(`Component type "${compName}" not registered (node "${nodeName}")`);
|
|
100
|
-
const comp = getComponent(node, ctor);
|
|
101
|
-
if (!comp) return makeErrorProxy(`Component "${compName}" not found on node "${nodeName}"`);
|
|
102
|
-
return comp;
|
|
128
|
+
return makeNodeProxy(ctx, node);
|
|
103
129
|
}
|
|
104
130
|
});
|
|
105
131
|
}
|
|
@@ -11,24 +11,6 @@ import { IComponent } from "./engine_types.js";
|
|
|
11
11
|
import { resolveUrl } from "./engine_utils.js";
|
|
12
12
|
import { RGBAColor } from "./js-extensions/index.js";
|
|
13
13
|
|
|
14
|
-
// export class SourcePath {
|
|
15
|
-
// src?:string
|
|
16
|
-
// };
|
|
17
|
-
|
|
18
|
-
// class SourcePathSerializer extends TypeSerializer{
|
|
19
|
-
// constructor(){
|
|
20
|
-
// super(SourcePath);
|
|
21
|
-
// }
|
|
22
|
-
// onDeserialize(data: any, _context: SerializationContext) {
|
|
23
|
-
// if(data.src && typeof data.src === "string"){
|
|
24
|
-
// return data.src;
|
|
25
|
-
// }
|
|
26
|
-
// }
|
|
27
|
-
// onSerialize(_data: any, _context: SerializationContext) {
|
|
28
|
-
|
|
29
|
-
// }
|
|
30
|
-
// }
|
|
31
|
-
// new SourcePathSerializer();
|
|
32
14
|
|
|
33
15
|
class ColorSerializer extends TypeSerializer {
|
|
34
16
|
constructor() {
|
|
@@ -52,7 +34,6 @@ class ColorSerializer extends TypeSerializer {
|
|
|
52
34
|
return { r: data.r, g: data.g, b: data.b }
|
|
53
35
|
}
|
|
54
36
|
}
|
|
55
|
-
export const colorSerializer = new ColorSerializer();
|
|
56
37
|
|
|
57
38
|
class EulerSerializer extends TypeSerializer {
|
|
58
39
|
constructor() {
|
|
@@ -72,7 +53,6 @@ class EulerSerializer extends TypeSerializer {
|
|
|
72
53
|
return { x: data.x, y: data.y, z: data.z, order: data.order };
|
|
73
54
|
}
|
|
74
55
|
}
|
|
75
|
-
export const euler = new EulerSerializer();
|
|
76
56
|
|
|
77
57
|
declare type ObjectData = {
|
|
78
58
|
node?: number;
|
|
@@ -107,16 +87,6 @@ class ObjectSerializer extends TypeSerializer {
|
|
|
107
87
|
console.warn(`Wrong usage of @serializable detected in your script \"${scriptname}\"\n\nIt looks like you used @serializable(Object3D) or @serializable(GameObject) for a prefab or scene reference which is exported to a separate glTF file.\n\nTo fix this please change your code to:\n\n@serializable(AssetReference)\n${context.path}! : AssetReference;\n\0`);
|
|
108
88
|
}
|
|
109
89
|
// ACTUALLY: this is already handled by the extension_utils where we resolve json pointers recursively
|
|
110
|
-
// if(data.startsWith("/nodes/")){
|
|
111
|
-
// const node = parseInt(data.substring("/nodes/".length));
|
|
112
|
-
// if (context.nodeToObject) {
|
|
113
|
-
// const res = context.nodeToObject[node];
|
|
114
|
-
// if (debugExtension)
|
|
115
|
-
// console.log("Deserialized object reference?", data, res, context?.nodeToObject);
|
|
116
|
-
// if (!res) console.warn("Did not find node: " + data, context.nodeToObject, context.object);
|
|
117
|
-
// return res;
|
|
118
|
-
// }
|
|
119
|
-
// }
|
|
120
90
|
return undefined;
|
|
121
91
|
}
|
|
122
92
|
|
|
@@ -166,7 +136,6 @@ class ObjectSerializer extends TypeSerializer {
|
|
|
166
136
|
return undefined;
|
|
167
137
|
}
|
|
168
138
|
}
|
|
169
|
-
export const objectSerializer = new ObjectSerializer();
|
|
170
139
|
|
|
171
140
|
|
|
172
141
|
class ComponentSerializer extends TypeSerializer {
|
|
@@ -241,7 +210,6 @@ class ComponentSerializer extends TypeSerializer {
|
|
|
241
210
|
}
|
|
242
211
|
}
|
|
243
212
|
}
|
|
244
|
-
export const componentSerializer = new ComponentSerializer();
|
|
245
213
|
|
|
246
214
|
|
|
247
215
|
declare class EventListData {
|
|
@@ -396,21 +364,12 @@ class EventListSerializer extends TypeSerializer {
|
|
|
396
364
|
// };
|
|
397
365
|
// }
|
|
398
366
|
}
|
|
399
|
-
export const eventListSerializer = new EventListSerializer();
|
|
400
367
|
|
|
401
368
|
|
|
402
369
|
/** Map<Clone, Original> texture. This is used for compressed textures (or when the GLTFLoader is cloning RenderTextures)
|
|
403
370
|
* It's a weak map so we don't have to worry about memory leaks
|
|
404
371
|
*/
|
|
405
372
|
const cloneOriginalMap = new WeakMap<Texture, Texture>();
|
|
406
|
-
const textureClone = Texture.prototype.clone;
|
|
407
|
-
Texture.prototype.clone = function () {
|
|
408
|
-
const clone = textureClone.call(this);
|
|
409
|
-
if (!cloneOriginalMap.has(clone)) {
|
|
410
|
-
cloneOriginalMap.set(clone, this);
|
|
411
|
-
}
|
|
412
|
-
return clone;
|
|
413
|
-
}
|
|
414
373
|
|
|
415
374
|
export class RenderTextureSerializer extends TypeSerializer {
|
|
416
375
|
constructor() {
|
|
@@ -452,7 +411,6 @@ export class RenderTextureSerializer extends TypeSerializer {
|
|
|
452
411
|
return undefined;
|
|
453
412
|
}
|
|
454
413
|
}
|
|
455
|
-
new RenderTextureSerializer();
|
|
456
414
|
|
|
457
415
|
|
|
458
416
|
export class UriSerializer extends TypeSerializer {
|
|
@@ -471,4 +429,35 @@ export class UriSerializer extends TypeSerializer {
|
|
|
471
429
|
return undefined;
|
|
472
430
|
}
|
|
473
431
|
}
|
|
474
|
-
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
// Module-level references used by EventListSerializer internally
|
|
435
|
+
export let colorSerializer: ColorSerializer;
|
|
436
|
+
export let objectSerializer: ObjectSerializer;
|
|
437
|
+
export let componentSerializer: ComponentSerializer;
|
|
438
|
+
export let eventListSerializer: EventListSerializer;
|
|
439
|
+
|
|
440
|
+
/** Register all builtin serializers and prototype patches.
|
|
441
|
+
* Must be called from {@link initEngine} so the registrations survive tree-shaking
|
|
442
|
+
* when the package declares `sideEffects: false`.
|
|
443
|
+
*/
|
|
444
|
+
export function initBuiltinSerializers() {
|
|
445
|
+
// Prototype patches
|
|
446
|
+
const textureClone = Texture.prototype.clone;
|
|
447
|
+
Texture.prototype.clone = function () {
|
|
448
|
+
const clone = textureClone.call(this);
|
|
449
|
+
if (!cloneOriginalMap.has(clone)) {
|
|
450
|
+
cloneOriginalMap.set(clone, this);
|
|
451
|
+
}
|
|
452
|
+
return clone;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Register all serializers
|
|
456
|
+
colorSerializer = new ColorSerializer();
|
|
457
|
+
new EulerSerializer();
|
|
458
|
+
objectSerializer = new ObjectSerializer();
|
|
459
|
+
componentSerializer = new ComponentSerializer();
|
|
460
|
+
eventListSerializer = new EventListSerializer();
|
|
461
|
+
new RenderTextureSerializer();
|
|
462
|
+
new UriSerializer();
|
|
463
|
+
}
|
package/src/engine/engine_ssr.ts
CHANGED
|
@@ -9,14 +9,40 @@
|
|
|
9
9
|
* to a plain empty class so that `class Foo extends HTMLElementBase` is valid.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
|
|
13
|
+
/** True when running in an SSR/Node environment (no browser globals). */
|
|
14
|
+
export const SSR: boolean = typeof window === "undefined";
|
|
15
|
+
|
|
12
16
|
/** SSR-safe base class for web components. */
|
|
13
17
|
export const HTMLElementBase: typeof HTMLElement =
|
|
14
|
-
typeof HTMLElement !== "undefined" ? HTMLElement : (class {} as unknown as typeof HTMLElement);
|
|
18
|
+
typeof HTMLElement !== "undefined" ? HTMLElement : (class { } as unknown as typeof HTMLElement);
|
|
15
19
|
|
|
16
20
|
/** SSR-safe base class for pointer events. */
|
|
17
21
|
export const PointerEventBase: typeof PointerEvent =
|
|
18
|
-
typeof PointerEvent !== "undefined" ? PointerEvent : (class {} as unknown as typeof PointerEvent);
|
|
22
|
+
typeof PointerEvent !== "undefined" ? PointerEvent : (class { } as unknown as typeof PointerEvent);
|
|
19
23
|
|
|
20
24
|
/** SSR-safe base class for keyboard events. */
|
|
21
25
|
export const KeyboardEventBase: typeof KeyboardEvent =
|
|
22
|
-
typeof KeyboardEvent !== "undefined" ? KeyboardEvent : (class {} as unknown as typeof KeyboardEvent);
|
|
26
|
+
typeof KeyboardEvent !== "undefined" ? KeyboardEvent : (class { } as unknown as typeof KeyboardEvent);
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
// #region minimal polyfills
|
|
32
|
+
// Three.js FileLoader uses ProgressEvent in fetch stream callbacks.
|
|
33
|
+
// It doesn't exist in Node — install a minimal stub so SSR doesn't crash.
|
|
34
|
+
if (typeof globalThis.ProgressEvent === "undefined") {
|
|
35
|
+
(globalThis as any).ProgressEvent = class ProgressEvent {
|
|
36
|
+
readonly type: string;
|
|
37
|
+
readonly lengthComputable: boolean;
|
|
38
|
+
readonly loaded: number;
|
|
39
|
+
readonly total: number;
|
|
40
|
+
constructor(type: string, init?: { lengthComputable?: boolean; loaded?: number; total?: number }) {
|
|
41
|
+
this.type = type;
|
|
42
|
+
this.lengthComputable = init?.lengthComputable ?? false;
|
|
43
|
+
this.loaded = init?.loaded ?? 0;
|
|
44
|
+
this.total = init?.total ?? 0;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// #endregion
|
|
@@ -258,6 +258,8 @@ export declare interface ICameraController {
|
|
|
258
258
|
export declare interface ILight extends IComponent {
|
|
259
259
|
intensity: number;
|
|
260
260
|
color: Color;
|
|
261
|
+
/** The type of light as a lowercase string: `"directional"`, `"point"`, `"spot"` */
|
|
262
|
+
readonly type: "directional" | "point" | "spot";
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
export declare interface ISharedMaterials {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSX IntrinsicElements declarations for Needle Engine web components.
|
|
3
|
+
*
|
|
4
|
+
* Covers both the global JSX namespace (Preact, SolidJS, Svelte, vanilla TS)
|
|
5
|
+
* and React's module-scoped JSX namespace (react-jsx / react-jsxdev transform).
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This file must be a module (has a top-level import/export) so that
|
|
8
|
+
* `declare module "react"` is treated as a *module augmentation* (merging into
|
|
9
|
+
* the existing React types) rather than an *ambient module declaration* (which
|
|
10
|
+
* would shadow/replace @types/react).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { NeedleEngineAttributes } from "./needle-engine.js";
|
|
14
|
+
|
|
15
|
+
interface NeedleButtonJSXAttributes extends Partial<Omit<HTMLElement, "style" | "children">> {
|
|
16
|
+
style?: Partial<CSSStyleDeclaration>;
|
|
17
|
+
ar?: boolean | string;
|
|
18
|
+
vr?: boolean | string;
|
|
19
|
+
quicklook?: boolean | string;
|
|
20
|
+
qrcode?: boolean | string;
|
|
21
|
+
unstyled?: boolean | string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface NeedleMenuJSXAttributes extends Partial<Omit<HTMLElement, "style" | "children">> {
|
|
25
|
+
style?: Partial<CSSStyleDeclaration>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface NeedleLogoElementJSXAttributes extends Partial<Omit<HTMLElement, "style" | "children">> {
|
|
29
|
+
style?: Partial<CSSStyleDeclaration>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface NeedleElements {
|
|
33
|
+
"needle-engine": Partial<NeedleEngineAttributes>;
|
|
34
|
+
"needle-button": NeedleButtonJSXAttributes;
|
|
35
|
+
"needle-menu": NeedleMenuJSXAttributes;
|
|
36
|
+
"needle-logo-element": NeedleLogoElementJSXAttributes;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Global JSX namespace — Preact, SolidJS, Svelte, vanilla TS
|
|
40
|
+
declare global {
|
|
41
|
+
namespace JSX {
|
|
42
|
+
interface IntrinsicElements extends NeedleElements {}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// React module-scoped JSX namespace (module augmentation, not ambient declaration)
|
|
47
|
+
declare module "react" {
|
|
48
|
+
namespace JSX {
|
|
49
|
+
interface IntrinsicElements extends NeedleElements {}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -12,9 +12,10 @@ import { NeedleLogoElement } from "../logo-element.js";
|
|
|
12
12
|
import { getElementPriority, setElementPriority as _setElementPriority } from "./menu-priority.js";
|
|
13
13
|
import { NeedleSpatialMenu } from "./needle-menu-spatial.js";
|
|
14
14
|
|
|
15
|
+
// NOTE: keep JSX attributes in sync with ../jsx.d.ts
|
|
15
16
|
declare global {
|
|
16
17
|
interface HTMLElementTagNameMap {
|
|
17
|
-
"needle-
|
|
18
|
+
"needle-menu": NeedleMenuElement;
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -4,6 +4,7 @@ import { ButtonsFactory } from "./buttons.js";
|
|
|
4
4
|
import { iconFontUrl, loadFont } from "./fonts.js";
|
|
5
5
|
import { WebXRButtonFactory } from "./WebXRButtons.js";
|
|
6
6
|
|
|
7
|
+
// NOTE: keep JSX attributes in sync with ./jsx.d.ts
|
|
7
8
|
declare global {
|
|
8
9
|
interface HTMLElementTagNameMap {
|
|
9
10
|
"needle-button": NeedleButtonElement;
|
|
@@ -17,6 +17,7 @@ import { arContainerClassName, AROverlayHandler } from "./needle-engine.ar-overl
|
|
|
17
17
|
import type { registerObservableAttribute } from "./needle-engine.extras.js";
|
|
18
18
|
import { calculateProgress01, EngineLoadingView, type ILoadingViewHandler } from "./needle-engine.loading.js";
|
|
19
19
|
|
|
20
|
+
// NOTE: keep JSX attributes in sync with ./jsx.d.ts
|
|
20
21
|
declare global {
|
|
21
22
|
interface HTMLElementTagNameMap {
|
|
22
23
|
"needle-engine": NeedleEngineWebComponent;
|
|
@@ -1586,4 +1586,7 @@ class AnimatorControllerSerializator extends TypeSerializer {
|
|
|
1586
1586
|
return undefined;
|
|
1587
1587
|
}
|
|
1588
1588
|
}
|
|
1589
|
-
|
|
1589
|
+
/** @internal */
|
|
1590
|
+
export function initAnimatorControllerSerializer() {
|
|
1591
|
+
new AnimatorControllerSerializator(AnimatorController);
|
|
1592
|
+
}
|
|
@@ -106,11 +106,40 @@ enum LightShadows {
|
|
|
106
106
|
export class Light extends Behaviour implements ILight {
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* The type of light
|
|
110
|
-
* Can not be changed at runtime.
|
|
109
|
+
* The type of light as a lowercase string: `"directional"`, `"point"`, `"spot"`.
|
|
110
|
+
* Implements {@link ILight.type}. Can not be changed at runtime.
|
|
111
111
|
*/
|
|
112
|
+
get type(): ILight["type"] {
|
|
113
|
+
return this._type;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Numeric LightType serialized from Unity/Blender — converts to string on write */
|
|
112
117
|
@serializable()
|
|
113
|
-
|
|
118
|
+
set type(value: LightType | ILight["type"]) {
|
|
119
|
+
if (this.light && this.__didAwake) {
|
|
120
|
+
throw new Error("Changing the light type at runtime is not supported");
|
|
121
|
+
}
|
|
122
|
+
switch (value) {
|
|
123
|
+
case LightType.Directional:
|
|
124
|
+
this._type = "directional";
|
|
125
|
+
break;
|
|
126
|
+
case LightType.Point:
|
|
127
|
+
this._type = "point";
|
|
128
|
+
break;
|
|
129
|
+
case LightType.Spot:
|
|
130
|
+
this._type = "spot";
|
|
131
|
+
break;
|
|
132
|
+
case "directional":
|
|
133
|
+
case "point":
|
|
134
|
+
case "spot":
|
|
135
|
+
this._type = value;
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
throw new Error("Invalid light type: " + value);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
private _type: ILight["type"] = "point";
|
|
142
|
+
|
|
114
143
|
|
|
115
144
|
/**
|
|
116
145
|
* The maximum distance the light affects.
|
|
@@ -345,7 +374,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
345
374
|
*/
|
|
346
375
|
public getWorldPosition(vec: Vector3): Vector3 {
|
|
347
376
|
if (this.light) {
|
|
348
|
-
if (this.type ===
|
|
377
|
+
if (this.type === "directional") {
|
|
349
378
|
return this.light.getWorldPosition(vec).multiplyScalar(1);
|
|
350
379
|
}
|
|
351
380
|
return this.light.getWorldPosition(vec);
|
|
@@ -372,12 +401,13 @@ export class Light extends Behaviour implements ILight {
|
|
|
372
401
|
else if (this.light.parent !== this.gameObject)
|
|
373
402
|
this.gameObject.add(this.light);
|
|
374
403
|
}
|
|
375
|
-
|
|
376
|
-
this.startCoroutine(this.updateMainLightRoutine(), FrameEvent.LateUpdate);
|
|
404
|
+
this.context.lights.push(this);
|
|
377
405
|
}
|
|
378
406
|
|
|
379
407
|
onDisable() {
|
|
380
408
|
if (debug) console.log("DISABLE LIGHT", this.name);
|
|
409
|
+
const index = this.context.lights.indexOf(this);
|
|
410
|
+
if (index !== -1) this.context.lights.splice(index, 1);
|
|
381
411
|
if (this.light) {
|
|
382
412
|
if (this.selfIsLight)
|
|
383
413
|
this.light.intensity = 0;
|
|
@@ -399,14 +429,14 @@ export class Light extends Behaviour implements ILight {
|
|
|
399
429
|
this._intensity = this.light.intensity;
|
|
400
430
|
|
|
401
431
|
switch (this.type) {
|
|
402
|
-
case
|
|
432
|
+
case "directional":
|
|
403
433
|
this.setDirectionalLight(this.light as DirectionalLight);
|
|
404
434
|
break;
|
|
405
435
|
}
|
|
406
436
|
}
|
|
407
437
|
else if (!this.light) {
|
|
408
438
|
switch (this.type) {
|
|
409
|
-
case
|
|
439
|
+
case "directional":
|
|
410
440
|
// console.log(this);
|
|
411
441
|
const dirLight = new DirectionalLight(this.color, this.intensity * Math.PI);
|
|
412
442
|
// directional light target is at 0 0 0 by default
|
|
@@ -425,7 +455,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
425
455
|
}
|
|
426
456
|
break;
|
|
427
457
|
|
|
428
|
-
case
|
|
458
|
+
case "spot":
|
|
429
459
|
const spotLight = new SpotLight(this.color, this.intensity * Math.PI, this.range, toRadians(this.spotAngle / 2), 1 - toRadians(this.innerSpotAngle / 2) / toRadians(this.spotAngle / 2), 2);
|
|
430
460
|
spotLight.position.set(0, 0, 0);
|
|
431
461
|
spotLight.rotation.set(0, 0, 0);
|
|
@@ -437,7 +467,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
437
467
|
spotLightTarget.rotation.set(0, 0, 0);
|
|
438
468
|
break;
|
|
439
469
|
|
|
440
|
-
case
|
|
470
|
+
case "point":
|
|
441
471
|
const pointLight = new PointLight(this.color, this.intensity * Math.PI, this.range);
|
|
442
472
|
this.light = pointLight;
|
|
443
473
|
|
|
@@ -526,22 +556,6 @@ export class Light extends Behaviour implements ILight {
|
|
|
526
556
|
|
|
527
557
|
}
|
|
528
558
|
|
|
529
|
-
/**
|
|
530
|
-
* Coroutine that updates the main light reference in the context
|
|
531
|
-
* if this directional light should be the main light
|
|
532
|
-
*/
|
|
533
|
-
*updateMainLightRoutine() {
|
|
534
|
-
while (true) {
|
|
535
|
-
if (this.type === LightType.Directional) {
|
|
536
|
-
if (!this.context.mainLight || this.intensity > this.context.mainLight.intensity) {
|
|
537
|
-
this.context.mainLight = this;
|
|
538
|
-
}
|
|
539
|
-
yield;
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
559
|
/**
|
|
546
560
|
* Controls whether the renderer's shadow map type can be changed when soft shadows are used
|
|
547
561
|
*/
|
|
@@ -290,6 +290,8 @@ export class ReflectionProbe extends Behaviour {
|
|
|
290
290
|
const current = block.getOverride("envMap")?.value;
|
|
291
291
|
if (current === this.texture) {
|
|
292
292
|
block.removeOveride("envMap");
|
|
293
|
+
block.removeOveride("envMapRotation");
|
|
294
|
+
block.removeOveride("envMapIntensity");
|
|
293
295
|
}
|
|
294
296
|
}
|
|
295
297
|
}
|
package/src/needle-engine.ts
CHANGED