@needle-tools/engine 5.1.0-alpha → 5.1.0-canary.30cc545
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 -1
- package/SKILL.md +39 -21
- 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-wM-BWPX9.umd.cjs → needle-engine.bundle-BMlLSACE.umd.cjs} +250 -174
- package/dist/{needle-engine.bundle-qDahLTqW.min.js → needle-engine.bundle-BXPPQRer.min.js} +242 -166
- package/dist/{needle-engine.bundle-CwhCzjep.js → needle-engine.bundle-d_9mSxN4.js} +12930 -12465
- package/dist/needle-engine.d.ts +267 -16
- package/dist/needle-engine.js +569 -563
- 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/{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_components.js +5 -1
- package/lib/engine/engine_components.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 +25 -0
- package/lib/engine/engine_context.js +27 -0
- 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 +2 -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.js +11 -9
- package/lib/engine/engine_license.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.d.ts +3 -0
- package/lib/engine/engine_physics_rapier.js +13 -10
- 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 +36 -0
- package/lib/engine/engine_scenedata.js +111 -0
- package/lib/engine/engine_scenedata.js.map +1 -0
- package/lib/engine/engine_ssr.d.ts +16 -0
- package/lib/engine/engine_ssr.js +38 -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_utils.js +4 -2
- 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 +2 -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 +2 -1
- 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 +2 -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 +2 -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.d.ts +1 -0
- package/lib/engine/xr/NeedleXRSession.js +5 -5
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/events.d.ts +30 -3
- package/lib/engine/xr/events.js +38 -0
- package/lib/engine/xr/events.js.map +1 -1
- package/lib/engine/xr/init.js +1 -7
- package/lib/engine/xr/init.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +135 -2
- package/lib/engine-components/AnimatorController.js +218 -2
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/GroundProjection.d.ts +1 -0
- package/lib/engine-components/GroundProjection.js +184 -48
- package/lib/engine-components/GroundProjection.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/api.d.ts +1 -0
- package/lib/engine-components/api.js +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.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/package.json +6 -5
- package/plugins/common/logger.js +42 -19
- package/plugins/dts-generator/dts.codegen.js +129 -0
- package/plugins/dts-generator/dts.scan.js +71 -0
- package/plugins/dts-generator/dts.writer.js +59 -0
- package/plugins/dts-generator/glb.discovery.js +162 -0
- package/plugins/dts-generator/glb.extractor.js +175 -0
- package/plugins/dts-generator/glb.reader.js +114 -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 +19 -0
- package/plugins/types/userconfig.d.ts +9 -2
- package/plugins/vite/dts-generator.d.ts +7 -0
- package/plugins/vite/dts-generator.js +157 -0
- package/plugins/vite/index.d.ts +1 -0
- package/plugins/vite/index.js +4 -0
- package/plugins/vite/logger.client.js +4 -3
- package/plugins/vite/logging.js +2 -2
- package/plugins/vite/reload.js +2 -1
- 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_components.ts +7 -4
- package/src/engine/engine_constants.ts +11 -6
- package/src/engine/engine_context.ts +29 -0
- package/src/engine/engine_context_registry.ts +1 -1
- package/src/engine/engine_init.ts +2 -0
- package/src/engine/engine_input.ts +3 -2
- package/src/engine/engine_license.ts +11 -9
- package/src/engine/engine_networking_blob.ts +5 -11
- package/src/engine/engine_physics_rapier.ts +14 -12
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +110 -0
- package/src/engine/engine_ssr.ts +45 -0
- package/src/engine/engine_three_utils.ts +15 -7
- package/src/engine/engine_utils.ts +3 -2
- package/src/engine/engine_utils_hash.ts +65 -0
- package/src/engine/webcomponents/logo-element.ts +2 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +2 -1
- package/src/engine/webcomponents/needle-button.ts +2 -1
- package/src/engine/webcomponents/needle-engine.ts +2 -1
- package/src/engine/xr/NeedleXRSession.ts +6 -6
- package/src/engine/xr/events.ts +44 -1
- package/src/engine/xr/init.ts +0 -7
- package/src/engine-components/AnimatorController.ts +286 -4
- package/src/engine-components/GroundProjection.ts +226 -52
- package/src/engine-components/RigidBody.ts +3 -3
- package/src/engine-components/SceneSwitcher.ts +1 -0
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/codegen/components.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/vite-env.d.ts +16 -0
- package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
- package/dist/vendor-HRlxIBga.min.js +0 -1116
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Type tables and components.needle.json manifest loader.
|
|
4
|
+
*
|
|
5
|
+
* Knows about primitive TS types, known Three.js types, known Needle Engine
|
|
6
|
+
* types, and how to resolve field types from the manifest.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
/** Primitive TS type strings that can safely appear in an ambient declaration. */
|
|
17
|
+
export const PRIMITIVE_TYPES = new Set(["number", "string", "boolean"]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Known Three.js types → import("three").TypeName
|
|
21
|
+
* @type {Record<string, string>}
|
|
22
|
+
*/
|
|
23
|
+
export const THREE_TYPES = {
|
|
24
|
+
Color: `import("three").Color`,
|
|
25
|
+
ColorRepresentation: `import("three").ColorRepresentation`,
|
|
26
|
+
Euler: `import("three").Euler`,
|
|
27
|
+
Texture: `import("three").Texture`,
|
|
28
|
+
// Materials
|
|
29
|
+
Material: `import("three").Material`,
|
|
30
|
+
MeshStandardMaterial: `import("three").MeshStandardMaterial`,
|
|
31
|
+
// Objects
|
|
32
|
+
Object3D: `import("three").Object3D`,
|
|
33
|
+
Mesh: `import("three").Mesh`,
|
|
34
|
+
SkinnedMesh: `import("three").SkinnedMesh`,
|
|
35
|
+
// Other
|
|
36
|
+
Vector2: `import("three").Vector2`,
|
|
37
|
+
Vector3: `import("three").Vector3`,
|
|
38
|
+
Vector4: `import("three").Vector4`,
|
|
39
|
+
Matrix3: `import("three").Matrix3`,
|
|
40
|
+
Matrix4: `import("three").Matrix4`,
|
|
41
|
+
Quaternion: `import("three").Quaternion`,
|
|
42
|
+
// Animation
|
|
43
|
+
AnimationClip: `import("three").AnimationClip`,
|
|
44
|
+
AnimationMixer: `import("three").AnimationMixer`,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Known Needle Engine types → import("@needle-tools/engine").TypeName
|
|
49
|
+
* @type {Record<string, string>}
|
|
50
|
+
*/
|
|
51
|
+
export const NEEDLE_TYPES = {
|
|
52
|
+
AssetReference: `import("@needle-tools/engine").AssetReference`,
|
|
53
|
+
EventList: `import("@needle-tools/engine").EventList`,
|
|
54
|
+
GameObject: `import("@needle-tools/engine").GameObject`,
|
|
55
|
+
LookAtConstraint: `import("@needle-tools/engine").LookAtConstraint`,
|
|
56
|
+
RGBAColor: `import("@needle-tools/engine").RGBAColor`,
|
|
57
|
+
RenderTexture: `import("@needle-tools/engine").RenderTexture`,
|
|
58
|
+
Renderer: `import("@needle-tools/engine").Renderer`,
|
|
59
|
+
Rigidbody: `import("@needle-tools/engine").Rigidbody`,
|
|
60
|
+
Sprite: `import("@needle-tools/engine").Sprite`,
|
|
61
|
+
Vec2: `import("@needle-tools/engine").Vec2`,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Map a single non-array, non-primitive type token to its TS representation.
|
|
66
|
+
* Returns null if unknown.
|
|
67
|
+
* @param {string} token
|
|
68
|
+
* @returns {string | null}
|
|
69
|
+
*/
|
|
70
|
+
function mapKnownType(token) {
|
|
71
|
+
if (token in THREE_TYPES) return THREE_TYPES[token];
|
|
72
|
+
if (token in NEEDLE_TYPES) return NEEDLE_TYPES[token];
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Convert a manifest type string to a safe ambient TS type.
|
|
78
|
+
* Primitives and known Three.js/Needle types are resolved precisely.
|
|
79
|
+
* For unknown types on a manifest component, falls back to
|
|
80
|
+
* `import("@needle-tools/engine").ComponentName["fieldName"]`.
|
|
81
|
+
* Truly unresolvable types → "unknown".
|
|
82
|
+
*
|
|
83
|
+
* @param {string} typeStr
|
|
84
|
+
* @param {string} [componentName] The manifest component class name (enables indexed-access fallback)
|
|
85
|
+
* @param {string} [fieldName] The field name on that component
|
|
86
|
+
* @returns {string}
|
|
87
|
+
*/
|
|
88
|
+
export function manifestTypeToTs(typeStr, componentName, fieldName) {
|
|
89
|
+
const parts = typeStr.split(" | ").map(p => p.trim());
|
|
90
|
+
const safeParts = parts.map(p => {
|
|
91
|
+
if (p === "undefined" || p === "null") return p;
|
|
92
|
+
const arrayMatch = p.match(/^(number|string|boolean)\[\]$/);
|
|
93
|
+
if (arrayMatch) return p;
|
|
94
|
+
if (PRIMITIVE_TYPES.has(p)) return p;
|
|
95
|
+
const arrayTypeMatch = p.match(/^(\w+)\[\]$/);
|
|
96
|
+
if (arrayTypeMatch) {
|
|
97
|
+
const base = arrayTypeMatch[1];
|
|
98
|
+
const mapped = mapKnownType(base);
|
|
99
|
+
if (mapped) return `${mapped}[]`;
|
|
100
|
+
}
|
|
101
|
+
const known = mapKnownType(p);
|
|
102
|
+
if (known) return known;
|
|
103
|
+
return null;
|
|
104
|
+
});
|
|
105
|
+
if (safeParts.every(p => p !== null)) {
|
|
106
|
+
return /** @type {string[]} */ (safeParts).join(" | ");
|
|
107
|
+
}
|
|
108
|
+
if (componentName && fieldName) {
|
|
109
|
+
return `import("@needle-tools/engine").${componentName}["${fieldName}"]`;
|
|
110
|
+
}
|
|
111
|
+
return "unknown";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Load components.needle.json and build a lookup:
|
|
116
|
+
* componentName → Map<fieldName, tsType>
|
|
117
|
+
* Inherited fields are flattened (inheritedFrom chain is resolved).
|
|
118
|
+
*
|
|
119
|
+
* @returns {Map<string, Map<string, string>>}
|
|
120
|
+
*/
|
|
121
|
+
export function loadComponentsManifest() {
|
|
122
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
123
|
+
const manifest = new Map();
|
|
124
|
+
const manifestPath = join(__dirname, "../../components.needle.json");
|
|
125
|
+
if (!existsSync(manifestPath)) return manifest;
|
|
126
|
+
try {
|
|
127
|
+
/** @type {Array<{name: string, inheritedFrom?: string, children?: Array<{name: string, kind: string, type: string}>}>} */
|
|
128
|
+
const entries = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
129
|
+
|
|
130
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
131
|
+
const ownFields = new Map();
|
|
132
|
+
/** @type {Map<string, string>} */
|
|
133
|
+
const inheritedFrom = new Map();
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
if (!entry.name) continue;
|
|
136
|
+
inheritedFrom.set(entry.name, entry.inheritedFrom || "");
|
|
137
|
+
/** @type {Map<string, string>} */
|
|
138
|
+
const fields = new Map();
|
|
139
|
+
if (Array.isArray(entry.children)) {
|
|
140
|
+
for (const child of entry.children) {
|
|
141
|
+
if (child.kind === "property" && child.name && child.type) {
|
|
142
|
+
fields.set(child.name, manifestTypeToTs(child.type, entry.name, child.name));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
ownFields.set(entry.name, fields);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @param {string} name @returns {Map<string, string>} */
|
|
150
|
+
function resolveFields(name) {
|
|
151
|
+
if (manifest.has(name)) return /** @type {Map<string, string>} */ (manifest.get(name));
|
|
152
|
+
const own = ownFields.get(name) ?? new Map();
|
|
153
|
+
const parent = inheritedFrom.get(name);
|
|
154
|
+
if (parent && ownFields.has(parent)) {
|
|
155
|
+
const parentFields = resolveFields(parent);
|
|
156
|
+
const merged = new Map([...parentFields, ...own]);
|
|
157
|
+
manifest.set(name, merged);
|
|
158
|
+
return merged;
|
|
159
|
+
}
|
|
160
|
+
manifest.set(name, own);
|
|
161
|
+
return own;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const name of ownFields.keys()) {
|
|
165
|
+
resolveFields(name);
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
console.warn("[needle:dts-generator] Failed to load components.needle.json:", (/** @type {any} */ (e))?.message ?? e);
|
|
169
|
+
}
|
|
170
|
+
return manifest;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
174
|
+
export const componentsManifest = loadComponentsManifest();
|
package/plugins/types/index.d.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient module declaration for `needle:bindings`.
|
|
3
|
+
*
|
|
4
|
+
* The `SceneData` interface is empty by default.
|
|
5
|
+
* When the `needle:dts-generator` Vite plugin is active it writes
|
|
6
|
+
* `src/generated/needle-bindings.d.ts` into the user's project, which
|
|
7
|
+
* augments this interface with the actual component bindings extracted
|
|
8
|
+
* from the project's GLB files at build time.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import type { SceneData } from "@needle-tools/engine";
|
|
12
|
+
* type MyBallData = SceneData["RedBall"]["MyBall"];
|
|
13
|
+
*/
|
|
14
|
+
declare module "needle:bindings" {
|
|
15
|
+
interface SceneData {
|
|
16
|
+
/** Fallback for nodes not present in the generated bindings — no type info, but no crash. */
|
|
17
|
+
[nodeName: string]: Record<string, unknown> | undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -26,10 +26,17 @@ export type userSettings = {
|
|
|
26
26
|
noCopy?: boolean;
|
|
27
27
|
/** When enabled the needle-engine include directory will be copied */
|
|
28
28
|
copyIncludesFromEngine?: boolean;
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/** Set to `false` to prevent the Rapier physics engine from loading.
|
|
32
|
+
* NOTE: by default Needle Engine uses dynamic loading of the Rapier physics engine. This means that the Rapier code is only loaded when a physics component is used in the scene.
|
|
33
|
+
*/
|
|
30
34
|
useRapier?: boolean;
|
|
31
|
-
/**
|
|
35
|
+
/** Set to `false` to prevent postprocessing effects from loading.
|
|
36
|
+
* NOTE: by default Needle Engine uses dynamic loading of postprocessing modules. This means that the postprocessing code is only loaded when a postprocessing effect is used in the scene.
|
|
37
|
+
*/
|
|
32
38
|
usePostprocessing?: boolean;
|
|
39
|
+
|
|
33
40
|
noDependencyWatcher?: boolean;
|
|
34
41
|
/** set to false to suppress editor-sync package installation and connection */
|
|
35
42
|
dontInstallEditor?: boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
|
|
3
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} _config
|
|
4
|
+
* @param {import('../types').userSettings} [_userSettings]
|
|
5
|
+
* @returns {import('vite').Plugin}
|
|
6
|
+
*/
|
|
7
|
+
export function needleDtsGenerator(_command: "build" | "serve", _config: import("../types/needleConfig").needleMeta | null | undefined, _userSettings?: import("../types").userSettings): import("vite").Plugin;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugin: needle:dts-generator
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper around plugins/dts-generator/index.js.
|
|
6
|
+
* Regenerates `needle-bindings.d.ts` on startup and whenever a .glb / .gltf
|
|
7
|
+
* file changes in the assets directory.
|
|
8
|
+
*
|
|
9
|
+
* The generated file is written to `{codegenDirectory}/needle-bindings.d.ts`
|
|
10
|
+
* (falls back to `src/generated/needle-bindings.d.ts`).
|
|
11
|
+
*
|
|
12
|
+
* Usage — the plugin is already wired into needlePlugins(). To use standalone:
|
|
13
|
+
*
|
|
14
|
+
* import { needleDtsGenerator } from "@needle-tools/engine/plugins/vite/dts-generator.js";
|
|
15
|
+
* // in vite.config.js plugins array:
|
|
16
|
+
* needleDtsGenerator(command, needleConfig, userSettings)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { join, resolve } from 'path';
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
21
|
+
import { tryLoadProjectConfig } from './config.js';
|
|
22
|
+
import { generateBindingsDts } from '../dts-generator/index.js';
|
|
23
|
+
import { needleLog } from './logging.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Ensure `.vscode/settings.json` references the generated `needle-html-data.json`
|
|
27
|
+
* so VS Code provides `data-bind-needle` completions in HTML files automatically.
|
|
28
|
+
* Only adds the entry if it isn't already present — never overwrites other settings.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} projectRoot
|
|
31
|
+
* @param {string} htmlDataPath Absolute path to the generated needle-html-data.json
|
|
32
|
+
*/
|
|
33
|
+
function ensureVscodeHtmlCustomData(projectRoot, htmlDataPath) {
|
|
34
|
+
const vscodeDir = join(projectRoot, ".vscode");
|
|
35
|
+
const settingsPath = join(vscodeDir, "settings.json");
|
|
36
|
+
|
|
37
|
+
// Relative path from project root for portability
|
|
38
|
+
const relPath = htmlDataPath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
|
|
39
|
+
|
|
40
|
+
/** @type {Record<string, unknown>} */
|
|
41
|
+
let settings = {};
|
|
42
|
+
if (existsSync(settingsPath)) {
|
|
43
|
+
try {
|
|
44
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
45
|
+
} catch (_e) { /* malformed JSON — leave settings empty, will add key */ }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const key = "html.customData";
|
|
49
|
+
const existing = Array.isArray(settings[key]) ? /** @type {string[]} */ (settings[key]) : [];
|
|
50
|
+
if (existing.includes(relPath)) return; // already registered
|
|
51
|
+
|
|
52
|
+
settings[key] = [...existing, relPath];
|
|
53
|
+
|
|
54
|
+
mkdirSync(vscodeDir, { recursive: true });
|
|
55
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
56
|
+
needleLog("needle:dts-generator", `registered HTML completions in .vscode/settings.json`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
|
|
61
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} _config
|
|
62
|
+
* @param {import('../types').userSettings} [_userSettings]
|
|
63
|
+
* @returns {import('vite').Plugin}
|
|
64
|
+
*/
|
|
65
|
+
export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
66
|
+
let projectRoot = process.cwd();
|
|
67
|
+
|
|
68
|
+
function resolveCodegenDir() {
|
|
69
|
+
const projectConfig = tryLoadProjectConfig();
|
|
70
|
+
return projectConfig?.codegenDirectory
|
|
71
|
+
? resolve(projectRoot, projectConfig.codegenDirectory)
|
|
72
|
+
: join(projectRoot, "src", "generated");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveOutputPath() {
|
|
76
|
+
return join(resolveCodegenDir(), "needle-bindings.d.ts");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveAssetsDir() {
|
|
80
|
+
const projectConfig = tryLoadProjectConfig();
|
|
81
|
+
return projectConfig?.assetsDirectory
|
|
82
|
+
? resolve(projectRoot, projectConfig.assetsDirectory)
|
|
83
|
+
: join(projectRoot, "assets");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @type {import('vite').ViteDevServer | undefined} */
|
|
87
|
+
let devServer;
|
|
88
|
+
|
|
89
|
+
async function run() {
|
|
90
|
+
try {
|
|
91
|
+
const assetsDir = resolveAssetsDir();
|
|
92
|
+
const outputPath = resolveOutputPath();
|
|
93
|
+
const codegenDir = resolveCodegenDir();
|
|
94
|
+
const count = await generateBindingsDts({ assetsDir, outputPath, projectRoot, codegenDir });
|
|
95
|
+
// Always register HTML custom data — the file is written on every run
|
|
96
|
+
const htmlDataPath = join(codegenDir, "needle-html-data.json");
|
|
97
|
+
ensureVscodeHtmlCustomData(projectRoot, htmlDataPath);
|
|
98
|
+
if (count !== false) {
|
|
99
|
+
needleLog("needle:dts-generator", `${count} binding(s) → ${outputPath.replace(process.cwd(), ".")}`);
|
|
100
|
+
if (devServer) {
|
|
101
|
+
const hot = devServer.hot ?? devServer.ws;
|
|
102
|
+
hot.send({ type: "full-reload", path: "*" });
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
needleLog("needle:dts-generator", `up-to-date`);
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
needleLog("needle:dts-generator", "Failed: " + (/** @type {any} */ (err)?.message ?? err));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
name: "needle:dts-generator",
|
|
114
|
+
|
|
115
|
+
/** @param {import('vite').ResolvedConfig} config */
|
|
116
|
+
configResolved(config) {
|
|
117
|
+
projectRoot = config.root ?? process.cwd();
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
buildStart() {
|
|
121
|
+
return run();
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/** @param {import('vite').ViteDevServer} server */
|
|
125
|
+
configureServer(server) {
|
|
126
|
+
devServer = server;
|
|
127
|
+
|
|
128
|
+
// Watch assets directory for GLB/glTF changes and regenerate
|
|
129
|
+
const assetsDir = resolveAssetsDir();
|
|
130
|
+
server.watcher.add(assetsDir);
|
|
131
|
+
|
|
132
|
+
// Also watch files that determine which GLBs are entrypoints
|
|
133
|
+
const indexHtmlPath = join(projectRoot, "index.html");
|
|
134
|
+
const genJsPath = join(resolveCodegenDir(), "gen.js");
|
|
135
|
+
server.watcher.add(indexHtmlPath);
|
|
136
|
+
server.watcher.add(genJsPath);
|
|
137
|
+
|
|
138
|
+
server.watcher.on("change", (file) => {
|
|
139
|
+
if (
|
|
140
|
+
(/\.(glb|gltf)$/i.test(file) && file.startsWith(assetsDir)) ||
|
|
141
|
+
file === indexHtmlPath ||
|
|
142
|
+
file === genJsPath
|
|
143
|
+
) {
|
|
144
|
+
run();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
server.watcher.on("add", (file) => {
|
|
148
|
+
if (/\.(glb|gltf)$/i.test(file) && file.startsWith(assetsDir)) {
|
|
149
|
+
run();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Return post-hook so Vite awaits the initial run before printing "ready"
|
|
154
|
+
return () => run();
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
package/plugins/vite/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
export function needlePlugins(command: "build" | "serve", config?: import("../types/needleConfig").needleMeta | null | undefined, userSettings?: import("../types/index.js").userSettings): Promise<import("vite").Plugin[]>;
|
|
28
28
|
export { needleAI } from "./ai.js";
|
|
29
|
+
export { needleDtsGenerator } from "./dts-generator.js";
|
|
29
30
|
export { needleAsap } from "./asap.js";
|
|
30
31
|
export { needleDefines } from "./defines.js";
|
|
31
32
|
export { needleBuildPipeline } from "./build-pipeline.js";
|
package/plugins/vite/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { needleAI } from "./ai.js";
|
|
2
2
|
export { needleAI } from "./ai.js";
|
|
3
3
|
|
|
4
|
+
import { needleDtsGenerator } from "./dts-generator.js";
|
|
5
|
+
export { needleDtsGenerator } from "./dts-generator.js";
|
|
6
|
+
|
|
4
7
|
import { needleAsap } from "./asap.js";
|
|
5
8
|
export { needleAsap } from "./asap.js";
|
|
6
9
|
|
|
@@ -126,6 +129,7 @@ export async function needlePlugins(command, config = undefined, userSettings =
|
|
|
126
129
|
userSettings = { ...defaultUserSettings, ...userSettings };
|
|
127
130
|
|
|
128
131
|
const array = [
|
|
132
|
+
needleDtsGenerator(command, config, userSettings),
|
|
129
133
|
needleAI(command, config, userSettings),
|
|
130
134
|
needleLogger(command, config, userSettings),
|
|
131
135
|
needleDefines(command, config, userSettings),
|
|
@@ -99,8 +99,9 @@ if (import.meta && "hot" in import.meta) {
|
|
|
99
99
|
// unpatch();
|
|
100
100
|
// }, 10_000);
|
|
101
101
|
|
|
102
|
+
const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
102
103
|
const threshold = 100;
|
|
103
|
-
const devToolsArePotentiallyOpen = window.outerHeight - window.innerHeight > threshold || window.outerWidth - window.innerWidth > threshold;
|
|
104
|
+
const devToolsArePotentiallyOpen = !isMobile && (window.outerHeight - window.innerHeight > threshold || window.outerWidth - window.innerWidth > threshold);
|
|
104
105
|
if (devToolsArePotentiallyOpen) {
|
|
105
106
|
sendLogToServer("internal", "Console logging is disabled (devtools are open)");
|
|
106
107
|
}
|
|
@@ -240,8 +241,8 @@ function stringifyLog(log, seen = /** @type {Set<unknown>} */ (new Set()), depth
|
|
|
240
241
|
const isServer = typeof window === "undefined";
|
|
241
242
|
const stringify_limits = {
|
|
242
243
|
string: isServer ? 100_000 : 1_000,
|
|
243
|
-
object_keys: isServer ?
|
|
244
|
-
object_depth: isServer ?
|
|
244
|
+
object_keys: isServer ? 10 : 10,
|
|
245
|
+
object_depth: isServer ? 3 : 3,
|
|
245
246
|
array_items: isServer ? 2_000 : 100,
|
|
246
247
|
}
|
|
247
248
|
|
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/reload.js
CHANGED
|
@@ -172,8 +172,9 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
172
172
|
// Check if codegen files actually changed their content
|
|
173
173
|
// this will return false if its the first update
|
|
174
174
|
// meaning if its the first export after the server starts those will not trigger a reload
|
|
175
|
+
// Ignore d.ts
|
|
175
176
|
const shouldCheckIfContentChanged = file.includes("/codegen/") || file.includes("/generated/") || file.endsWith("gen.js");// || file.endsWith(".glb") || file.endsWith(".gltf") || file.endsWith(".bin");
|
|
176
|
-
if (shouldCheckIfContentChanged) {
|
|
177
|
+
if (shouldCheckIfContentChanged && !file.endsWith(".d.ts") && !file.endsWith("needle-html-data.json")) {
|
|
177
178
|
if (reloadIsScheduled) {
|
|
178
179
|
return [];
|
|
179
180
|
}
|
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
|
|
@@ -10,6 +10,7 @@ import type { ComponentInit, Constructor, ConstructorConcrete, IComponent, IGame
|
|
|
10
10
|
import { $componentName } from "./engine_types.js";
|
|
11
11
|
import { getParam } from "./engine_utils.js";
|
|
12
12
|
import { apply } from "./js-extensions/index.js";
|
|
13
|
+
import { TypeStore } from "./engine_typestore.js";
|
|
13
14
|
|
|
14
15
|
const COMPONENT_GUID_NAMESPACE = 'eff8ba80-635d-11ec-90d6-0242ac120003';
|
|
15
16
|
|
|
@@ -137,7 +138,7 @@ export function addComponent<T extends IComponent>(obj: Object3D, componentInsta
|
|
|
137
138
|
if (componentInstance.guid === undefined || componentInstance.guid === "invalid") {
|
|
138
139
|
componentInstance.guid = generateDeterministicComponentGuid(obj, componentInstance);
|
|
139
140
|
}
|
|
140
|
-
if(init) componentInstance._internalInit(init);
|
|
141
|
+
if (init) componentInstance._internalInit(init);
|
|
141
142
|
// Register the component - make sure to provide the component instance context (if assigned)
|
|
142
143
|
registerComponent(componentInstance, componentInstance.context);
|
|
143
144
|
return componentInstance;
|
|
@@ -170,10 +171,12 @@ function onGetComponent<T>(obj: Object3D | null | undefined, componentType: Cons
|
|
|
170
171
|
}
|
|
171
172
|
if (!(obj?.userData?.components)) return null;
|
|
172
173
|
if (typeof componentType === "string") {
|
|
173
|
-
|
|
174
|
+
const type = TypeStore.get(componentType);
|
|
175
|
+
if (!didWarnAboutComponentAccess && !type) {
|
|
174
176
|
didWarnAboutComponentAccess = true;
|
|
175
177
|
console.warn(`Accessing components by name is not supported.\nPlease use the component type instead. This may keep working in local development but it will fail when bundling your application.\n\nYou can import other modules your main module to get access to types\nor if you use npmdefs you can make types available globally using globalThis:\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis`, componentType);
|
|
176
178
|
}
|
|
179
|
+
if (type) componentType = type as Constructor<T>;
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
if (debugEnabled())
|
|
@@ -248,7 +251,7 @@ export function getComponents<T extends IComponent>(obj: Object3D, componentType
|
|
|
248
251
|
* ```
|
|
249
252
|
*/
|
|
250
253
|
export function getComponentInChildren<T extends IComponent>(obj: Object3D, componentType: Constructor<T>, includeInactive: boolean = false): T | null {
|
|
251
|
-
if (includeInactive === false && obj[activeInHierarchyFieldName] === false) return null;
|
|
254
|
+
if (includeInactive === false && obj[activeInHierarchyFieldName] === false) return null;
|
|
252
255
|
const res = getComponent(obj, componentType) as IComponent | null;
|
|
253
256
|
if (includeInactive === false && (res?.enabled === false || res?.activeAndEnabled === false)) return null;
|
|
254
257
|
if (res) return res as T;
|
|
@@ -361,7 +364,7 @@ export function findObjectOfType<T extends IComponent>(type: Constructor<T>, con
|
|
|
361
364
|
if (!scene) return null;
|
|
362
365
|
|
|
363
366
|
const res = getComponentInChildren(scene, type, includeInactive);
|
|
364
|
-
if(res) return res;
|
|
367
|
+
if (res) return res;
|
|
365
368
|
return null;
|
|
366
369
|
}
|
|
367
370
|
|
|
@@ -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;
|
|
@@ -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
|
*/
|