@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
|
@@ -16,12 +16,24 @@
|
|
|
16
16
|
* needleDtsGenerator(command, needleConfig, userSettings)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { join, resolve } from 'path';
|
|
20
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
19
|
+
import { join, resolve, dirname } from 'path';
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, realpathSync } from 'fs';
|
|
21
21
|
import { tryLoadProjectConfig } from './config.js';
|
|
22
22
|
import { generateBindingsDts } from '../dts-generator/index.js';
|
|
23
23
|
import { needleLog } from './logging.js';
|
|
24
24
|
|
|
25
|
+
// Two dirs up from plugins/vite/ → package root.
|
|
26
|
+
// Use fileURLToPath so %7E and other URL-encoded characters in the path are decoded correctly.
|
|
27
|
+
// realpathSync follows symlinks so the path is stable even in npm link / monorepo setups.
|
|
28
|
+
import { fileURLToPath } from 'url';
|
|
29
|
+
let _packageRoot = /** @type {string | null} */ (null);
|
|
30
|
+
try {
|
|
31
|
+
_packageRoot = realpathSync(join(dirname(fileURLToPath(import.meta.url)), '..', '..'));
|
|
32
|
+
} catch (_e) {
|
|
33
|
+
// If we can't resolve the package root (e.g. unusual install layout), the plugin
|
|
34
|
+
// will silently skip generation rather than crashing the dev server.
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
/**
|
|
26
38
|
* Ensure `.vscode/settings.json` references the generated `needle-html-data.json`
|
|
27
39
|
* so VS Code provides `data-bind-needle` completions in HTML files automatically.
|
|
@@ -60,9 +72,14 @@ function ensureVscodeHtmlCustomData(projectRoot, htmlDataPath) {
|
|
|
60
72
|
* @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
|
|
61
73
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} _config
|
|
62
74
|
* @param {import('../types').userSettings} [_userSettings]
|
|
63
|
-
* @returns {import('vite').Plugin}
|
|
75
|
+
* @returns {import('vite').Plugin | null}
|
|
64
76
|
*/
|
|
65
77
|
export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
78
|
+
|
|
79
|
+
if(_userSettings?.dts?.enabled === false) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
let projectRoot = process.cwd();
|
|
67
84
|
|
|
68
85
|
function resolveCodegenDir() {
|
|
@@ -73,7 +90,11 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
73
90
|
}
|
|
74
91
|
|
|
75
92
|
function resolveOutputPath() {
|
|
76
|
-
|
|
93
|
+
// Write to a .needle dotfolder at the package root.
|
|
94
|
+
// Not in the `files` allowlist → never npm-published.
|
|
95
|
+
// ../../.needle/ from plugins/types/ resolves correctly for both
|
|
96
|
+
// symlinked (js/package~/) and published (node_modules/@needle-tools/engine/) layouts.
|
|
97
|
+
return join(/** @type {string} */ (_packageRoot), ".needle", "generated", "needle-bindings.gen.d.ts");
|
|
77
98
|
}
|
|
78
99
|
|
|
79
100
|
function resolveAssetsDir() {
|
|
@@ -86,14 +107,24 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
86
107
|
/** @type {import('vite').ViteDevServer | undefined} */
|
|
87
108
|
let devServer;
|
|
88
109
|
|
|
89
|
-
|
|
110
|
+
/** @type {Promise<void> | null} */
|
|
111
|
+
let _runInFlight = null;
|
|
112
|
+
|
|
113
|
+
function run() {
|
|
114
|
+
if (_runInFlight) return _runInFlight;
|
|
115
|
+
_runInFlight = _doRun().finally(() => { _runInFlight = null; });
|
|
116
|
+
return _runInFlight;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function _doRun() {
|
|
90
120
|
try {
|
|
121
|
+
if (!_packageRoot) return;
|
|
91
122
|
const assetsDir = resolveAssetsDir();
|
|
92
123
|
const outputPath = resolveOutputPath();
|
|
93
124
|
const codegenDir = resolveCodegenDir();
|
|
94
125
|
const count = await generateBindingsDts({ assetsDir, outputPath, projectRoot, codegenDir });
|
|
95
|
-
//
|
|
96
|
-
const htmlDataPath = join(
|
|
126
|
+
// HTML custom data sits next to the generated dts in .needle/generated/
|
|
127
|
+
const htmlDataPath = join(/** @type {string} */ (_packageRoot), ".needle", "generated", "needle-html-data.json");
|
|
97
128
|
ensureVscodeHtmlCustomData(projectRoot, htmlDataPath);
|
|
98
129
|
if (count !== false) {
|
|
99
130
|
needleLog("needle:dts-generator", `${count} binding(s) → ${outputPath.replace(process.cwd(), ".")}`);
|
|
@@ -102,7 +133,7 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
102
133
|
hot.send({ type: "full-reload", path: "*" });
|
|
103
134
|
}
|
|
104
135
|
} else {
|
|
105
|
-
needleLog("needle:dts-generator", `up-to-date`);
|
|
136
|
+
needleLog("needle:dts-generator", `up-to-date → ${outputPath.replace(process.cwd(), ".")}`);
|
|
106
137
|
}
|
|
107
138
|
} catch (err) {
|
|
108
139
|
needleLog("needle:dts-generator", "Failed: " + (/** @type {any} */ (err)?.message ?? err));
|
|
@@ -118,7 +149,10 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
118
149
|
},
|
|
119
150
|
|
|
120
151
|
buildStart() {
|
|
121
|
-
|
|
152
|
+
// In serve mode, the configureServer post-hook runs instead.
|
|
153
|
+
// Only run here for actual builds (no devServer).
|
|
154
|
+
if (!devServer) return run();
|
|
155
|
+
return undefined;
|
|
122
156
|
},
|
|
123
157
|
|
|
124
158
|
/** @param {import('vite').ViteDevServer} server */
|
package/plugins/vite/index.d.ts
CHANGED
|
@@ -19,12 +19,18 @@
|
|
|
19
19
|
* ],
|
|
20
20
|
* }
|
|
21
21
|
* ```
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* When used without arguments (e.g. in a SvelteKit `defineConfig({})` plain object),
|
|
23
|
+
* the command is resolved automatically from Vite's `configResolved` hook.
|
|
24
|
+
* ```js
|
|
25
|
+
* // SvelteKit — no command needed:
|
|
26
|
+
* export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
|
|
27
|
+
* ```
|
|
28
|
+
* @param {"build" | "serve" | undefined} [command]
|
|
29
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} [config]
|
|
24
30
|
* @param {import('../types/index.js').userSettings} [userSettings]
|
|
25
31
|
* @returns {Promise<import('vite').Plugin[]>}
|
|
26
32
|
*/
|
|
27
|
-
export function needlePlugins(command
|
|
33
|
+
export function needlePlugins(command?: "build" | "serve" | undefined, config?: import("../types/needleConfig").needleMeta | null | undefined, userSettings?: import("../types/index.js").userSettings): Promise<import("vite").Plugin[]>;
|
|
28
34
|
export { needleAI } from "./ai.js";
|
|
29
35
|
export { needleDtsGenerator } from "./dts-generator.js";
|
|
30
36
|
export { needleAsap } from "./asap.js";
|
package/plugins/vite/index.js
CHANGED
|
@@ -116,12 +116,18 @@ const defaultUserSettings = {
|
|
|
116
116
|
* ],
|
|
117
117
|
* }
|
|
118
118
|
* ```
|
|
119
|
-
*
|
|
120
|
-
*
|
|
119
|
+
* When used without arguments (e.g. in a SvelteKit `defineConfig({})` plain object),
|
|
120
|
+
* the command is resolved automatically from Vite's `configResolved` hook.
|
|
121
|
+
* ```js
|
|
122
|
+
* // SvelteKit — no command needed:
|
|
123
|
+
* export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
|
|
124
|
+
* ```
|
|
125
|
+
* @param {"build" | "serve" | undefined} [command]
|
|
126
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} [config]
|
|
121
127
|
* @param {import('../types/index.js').userSettings} [userSettings]
|
|
122
128
|
* @returns {Promise<import('vite').Plugin[]>}
|
|
123
129
|
*/
|
|
124
|
-
export async function needlePlugins(command, config = undefined, userSettings = {}) {
|
|
130
|
+
export async function needlePlugins(command = undefined, config = undefined, userSettings = {}) {
|
|
125
131
|
|
|
126
132
|
if (!config) config = {}
|
|
127
133
|
|
|
@@ -168,14 +174,21 @@ export async function needlePlugins(command, config = undefined, userSettings =
|
|
|
168
174
|
array.push(await editorConnection(command, config, userSettings, array));
|
|
169
175
|
array.push(needleDependencyWatcher(command, config, userSettings));
|
|
170
176
|
|
|
171
|
-
// Ensure the process exits on SIGINT (Ctrl+C) since plugin timers/sockets can keep the event loop alive
|
|
172
|
-
|
|
177
|
+
// Ensure the process exits on SIGINT (Ctrl+C) since plugin timers/sockets can keep the event loop alive.
|
|
178
|
+
// If command is unknown at call time (e.g. SvelteKit plain defineConfig), defer to configResolved.
|
|
179
|
+
const registerSigint = () => {
|
|
173
180
|
process.on('SIGINT', () => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
setTimeout(() => { console.debug('\nGoodbye!'); process.exit(); }, 1000);
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
if (command === "serve") {
|
|
185
|
+
registerSigint();
|
|
186
|
+
} else if (command === undefined) {
|
|
187
|
+
array.push({
|
|
188
|
+
name: "needle:sigint",
|
|
189
|
+
configResolved(config) {
|
|
190
|
+
if (config.command === "serve") registerSigint();
|
|
191
|
+
}
|
|
179
192
|
});
|
|
180
193
|
}
|
|
181
194
|
|
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
|
@@ -7,6 +7,7 @@ import { existsSync, readFileSync, statSync } from 'fs';
|
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { needleLog } from './logging.js';
|
|
9
9
|
|
|
10
|
+
const pluginName = "needle-reload";
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
12
|
const __dirname = path.dirname(__filename);
|
|
12
13
|
|
|
@@ -15,14 +16,12 @@ const filesUsingHotReload = new Set();
|
|
|
15
16
|
let assetsDirectory = "";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* @param {"build" | "serve"}
|
|
19
|
+
* @param {"build" | "serve" | undefined} _command
|
|
19
20
|
* @param {import('../types/needleConfig').needleMeta | null} config
|
|
20
21
|
* @param {import('../types').userSettings} userSettings
|
|
21
22
|
* @returns {import('vite').Plugin | undefined}
|
|
22
23
|
*/
|
|
23
|
-
export function needleReload(
|
|
24
|
-
if (command === "build") return undefined;
|
|
25
|
-
|
|
24
|
+
export function needleReload(_command, config, userSettings) {
|
|
26
25
|
if (userSettings?.noReload === true) return undefined;
|
|
27
26
|
|
|
28
27
|
|
|
@@ -41,7 +40,7 @@ export function needleReload(command, config, userSettings) {
|
|
|
41
40
|
const buildDirectory = projectConfig?.buildDirectory?.length ? process.cwd().replaceAll("\\", "/") + "/" + projectConfig?.buildDirectory : "";
|
|
42
41
|
if (buildDirectory?.length) {
|
|
43
42
|
const relativeBuildDirectory = path.relative(process.cwd(), buildDirectory).replaceAll("\\", "/") || ".";
|
|
44
|
-
setTimeout(() => needleLog(
|
|
43
|
+
setTimeout(() => needleLog(pluginName, "Build directory: " + relativeBuildDirectory), 100);
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
// These ignore patterns will be injected into user config to better control vite reloading
|
|
@@ -51,6 +50,7 @@ export function needleReload(command, config, userSettings) {
|
|
|
51
50
|
|
|
52
51
|
return {
|
|
53
52
|
name: 'needle:reload',
|
|
53
|
+
apply: 'serve',
|
|
54
54
|
/** @param {import('vite').UserConfig} config */
|
|
55
55
|
config(config) {
|
|
56
56
|
if (!config.server) config.server = { watch: { ignored: [] } };
|
|
@@ -60,7 +60,7 @@ export function needleReload(command, config, userSettings) {
|
|
|
60
60
|
// @ts-ignore - watch.ignored is guaranteed to be string[] by the guards above
|
|
61
61
|
config.server.watch.ignored.push(pattern);
|
|
62
62
|
if (userSettings?.debug === true)
|
|
63
|
-
setTimeout(() => needleLog(
|
|
63
|
+
setTimeout(() => needleLog(pluginName, "Updated server ignore patterns: " + JSON.stringify(config.server?.watch?.ignored)), 100);
|
|
64
64
|
},
|
|
65
65
|
/** @param {import('vite').HmrContext & {buildDirectory?: string}} args */
|
|
66
66
|
handleHotUpdate(args) {
|
|
@@ -119,7 +119,7 @@ function getHot(server) {
|
|
|
119
119
|
|
|
120
120
|
/** @param {import('vite').ViteDevServer} server @param {string} [file] */
|
|
121
121
|
function notifyClientWillReload(server, file) {
|
|
122
|
-
|
|
122
|
+
needleLog(pluginName, "Send reload notification");
|
|
123
123
|
getHot(server).send('needle:reload', { type: 'will-reload', file: file });
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -135,7 +135,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
135
135
|
|
|
136
136
|
// Dont reload the whole server when a file that is using hot reload changes
|
|
137
137
|
if (filesUsingHotReload.has(file)) {
|
|
138
|
-
|
|
138
|
+
needleLog(pluginName, "File is using hot reload: " + file);
|
|
139
139
|
return null;
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -154,7 +154,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
154
154
|
// instead of relying on the vite server watch ignore array
|
|
155
155
|
// we could here also match paths that we know we dont want to track
|
|
156
156
|
if (ignorePatterns.length > 0 && ignoreRegex.test(file)) {
|
|
157
|
-
|
|
157
|
+
needleLog(pluginName, "Ignore change in file: " + getFileNameLog(file));
|
|
158
158
|
return [];
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -164,7 +164,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
164
164
|
if (buildDirectory?.length) {
|
|
165
165
|
const dir = path.dirname(file).replaceAll("\\", "/");
|
|
166
166
|
if (dir.startsWith(buildDirectory)) {
|
|
167
|
-
|
|
167
|
+
needleLog(pluginName, "Ignore change in build directory: " + getFileNameLog(file));
|
|
168
168
|
return [];
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -174,18 +174,18 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
174
174
|
// meaning if its the first export after the server starts those will not trigger a reload
|
|
175
175
|
// Ignore d.ts
|
|
176
176
|
const shouldCheckIfContentChanged = file.includes("/codegen/") || file.includes("/generated/") || file.endsWith("gen.js");// || file.endsWith(".glb") || file.endsWith(".gltf") || file.endsWith(".bin");
|
|
177
|
-
if (
|
|
177
|
+
if (!file.includes("/.svelte-kit/") && !file.endsWith(".d.ts") && !file.endsWith("needle-html-data.json") && shouldCheckIfContentChanged) {
|
|
178
178
|
if (reloadIsScheduled) {
|
|
179
179
|
return [];
|
|
180
180
|
}
|
|
181
181
|
if (await testIfFileContentChanged(file, read) === false) {
|
|
182
|
-
|
|
182
|
+
needleLog(pluginName, "File content didnt change: " + getFileNameLog(file));
|
|
183
183
|
return [];
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// these are known file types we export from integrations
|
|
188
|
-
const knownExportFileTypes = [
|
|
188
|
+
const knownExportFileTypes = [".glb", ".gltf", ".bin", "exr", ".ktx2", ".mp3", ".ogg", ".mp4", ".webm"];
|
|
189
189
|
if (!knownExportFileTypes.some((type) => file.endsWith(type)))
|
|
190
190
|
return null;
|
|
191
191
|
|
|
@@ -209,7 +209,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
needleLog(pluginName, "> Detected file change: " + getFileNameLog(file) + " (" + ((fileSize / (1024 * 1024)).toFixed(1)) + " MB)");
|
|
213
213
|
notifyClientWillReload(server);
|
|
214
214
|
scheduleReload(server);
|
|
215
215
|
return [];
|
|
@@ -224,7 +224,7 @@ async function scheduleReload(server, level = 0) {
|
|
|
224
224
|
const lockFile = path.join(process.cwd(), lockFileName);
|
|
225
225
|
if (existsSync(lockFile)) {
|
|
226
226
|
if (level === 0)
|
|
227
|
-
|
|
227
|
+
needleLog(pluginName, "Lock file exists, waiting for export to finish...");
|
|
228
228
|
setTimeout(() => scheduleReload(server, level += 1), 300);
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
@@ -235,13 +235,13 @@ async function scheduleReload(server, level = 0) {
|
|
|
235
235
|
if (timeDiff < 1000) {
|
|
236
236
|
// Sometimes file changes happen immediately after triggering a reload
|
|
237
237
|
// we dont want to reload again in that case
|
|
238
|
-
|
|
238
|
+
needleLog(pluginName, "Ignoring reload, last reload was too recent" + timeDiff + "ms ago");
|
|
239
239
|
return;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
lastReloadTime = Date.now();
|
|
243
243
|
const readableTime = new Date(lastReloadTime).toLocaleTimeString();
|
|
244
|
-
|
|
244
|
+
needleLog(pluginName, "< Reloading... " + readableTime);
|
|
245
245
|
getHot(server).send({
|
|
246
246
|
type: 'full-reload',
|
|
247
247
|
path: '*'
|
|
@@ -300,10 +300,10 @@ function removeVersionQueryArgument(content) {
|
|
|
300
300
|
function insertScriptRegisterHotReloadCode(src, filePath) {
|
|
301
301
|
|
|
302
302
|
// We only want to inject the hot reload code in the needle-engine root file
|
|
303
|
-
if(!filePath.includes("/src/needle-engine.ts")) {
|
|
303
|
+
if (!filePath.includes("/src/needle-engine.ts")) {
|
|
304
304
|
return src;
|
|
305
305
|
}
|
|
306
|
-
|
|
306
|
+
needleLog(pluginName, "[Needle HMR] Hot reload is enabled");
|
|
307
307
|
// this code let's the engine know that we are in hot reload mode
|
|
308
308
|
const code = `
|
|
309
309
|
globalThis.NEEDLE_HOT_RELOAD_ENABLED = true;
|
|
@@ -339,12 +339,12 @@ function insertScriptHotReloadCode(src, filePath) {
|
|
|
339
339
|
return undefined;
|
|
340
340
|
// make import path from engine package
|
|
341
341
|
const fullPathToHotReload = process.cwd() + "/node_modules/@needle-tools/engine/src/engine/engine_hot_reload.ts";
|
|
342
|
-
//
|
|
342
|
+
// needleLog(pluginName, fullPathToHotReload);
|
|
343
343
|
const fileDirectory = path.dirname(filePath);
|
|
344
|
-
//
|
|
344
|
+
// needleLog(pluginName, "DIR", fileDirectory)
|
|
345
345
|
const relativePath = path.relative(fileDirectory, fullPathToHotReload);
|
|
346
346
|
importPath = relativePath.replace(/\\/g, "/");
|
|
347
|
-
//
|
|
347
|
+
// needleLog(pluginName, "importPath: ", importPath);
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
// console.log(importPath, ">", filePath);
|
package/src/engine/api.ts
CHANGED
|
@@ -450,4 +450,4 @@ export * from "./xr/api.js"
|
|
|
450
450
|
* import type { SceneData } from "@needle-tools/engine";
|
|
451
451
|
* type OrbitSettings = SceneData["Camera"]["OrbitControls"];
|
|
452
452
|
*/
|
|
453
|
-
export type { SceneData } from "needle
|
|
453
|
+
export type { SceneData } from "needle-bindings";
|
|
@@ -12,13 +12,16 @@ import { onError } from "./debug_overlay.js";
|
|
|
12
12
|
|
|
13
13
|
let _isActive = false;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
/** @internal */
|
|
16
|
+
export function initSpatialConsole() {
|
|
17
|
+
// enable the spatial console if we receive an error while in dev session and in XR
|
|
18
|
+
onError((...args: any[]) => {
|
|
19
|
+
if (isDevEnvironment() && ContextRegistry.Current?.isInXR) {
|
|
20
|
+
enableSpatialConsole(true);
|
|
21
|
+
onLog("error", ...args);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
/** Enable a spatial debug console that follows the camera */
|
|
@@ -567,7 +567,6 @@ class AddressableSerializer extends TypeSerializer {
|
|
|
567
567
|
}
|
|
568
568
|
|
|
569
569
|
}
|
|
570
|
-
new AddressableSerializer();
|
|
571
570
|
|
|
572
571
|
|
|
573
572
|
|
|
@@ -703,7 +702,6 @@ export class ImageReferenceSerializer extends TypeSerializer {
|
|
|
703
702
|
return undefined;
|
|
704
703
|
}
|
|
705
704
|
}
|
|
706
|
-
new ImageReferenceSerializer();
|
|
707
705
|
|
|
708
706
|
|
|
709
707
|
|
|
@@ -772,4 +770,9 @@ export class FileReferenceSerializer extends TypeSerializer {
|
|
|
772
770
|
return undefined;
|
|
773
771
|
}
|
|
774
772
|
}
|
|
775
|
-
|
|
773
|
+
/** @internal */
|
|
774
|
+
export function initAddressableSerializers() {
|
|
775
|
+
new AddressableSerializer();
|
|
776
|
+
new ImageReferenceSerializer();
|
|
777
|
+
new FileReferenceSerializer();
|
|
778
|
+
}
|
|
@@ -38,7 +38,7 @@ 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
40
|
import { getSceneData } from './engine_scenedata.js';
|
|
41
|
-
import type { SceneData } from 'needle
|
|
41
|
+
import type { SceneData } from 'needle-bindings';
|
|
42
42
|
import { getTempColor, logHierarchy } from './engine_three_utils.js';
|
|
43
43
|
import { Time } from './engine_time.js';
|
|
44
44
|
import { patchTonemapping } from './engine_tonemapping.js';
|
|
@@ -478,13 +478,14 @@ export class Context implements IContext {
|
|
|
478
478
|
* Use `setCurrentCamera` for updating the main camera.
|
|
479
479
|
*/
|
|
480
480
|
/**
|
|
481
|
-
*
|
|
482
|
-
* Types are
|
|
483
|
-
* and written to `src/generated/needle-bindings.d.ts` on every dev-server start and GLB change.
|
|
481
|
+
* Access your scene's full hierarchy, objects, and components directly by name — with full autocomplete.
|
|
482
|
+
* Types are generated automatically from your GLB files when the dev server starts.
|
|
484
483
|
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
484
|
+
* You can store references to objects or components for later use.
|
|
485
|
+
* Note that the scene hierarchy can change at runtime (e.g. when objects are added, removed, or re-parented),
|
|
486
|
+
* in which case stored references may become stale.
|
|
487
|
+
*
|
|
488
|
+
* @experimental This API may change in future versions.
|
|
488
489
|
*
|
|
489
490
|
* @example
|
|
490
491
|
* // Toggle auto-rotate on the orbit camera
|
|
@@ -493,12 +494,6 @@ export class Context implements IContext {
|
|
|
493
494
|
* @example
|
|
494
495
|
* // Change the background color
|
|
495
496
|
* 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
497
|
*/
|
|
503
498
|
get sceneData(): SceneData {
|
|
504
499
|
return getSceneData(this);
|
|
@@ -542,15 +537,32 @@ export class Context implements IContext {
|
|
|
542
537
|
connection: NetworkConnection;
|
|
543
538
|
/** @deprecated AssetDatabase is deprecated */
|
|
544
539
|
assets: AssetDatabase;
|
|
545
|
-
|
|
546
|
-
|
|
540
|
+
|
|
541
|
+
/** All registered lights in the scene, maintained by the Light component.
|
|
542
|
+
* @see mainLight for accessing the main directional light in the scene
|
|
543
|
+
*/
|
|
544
|
+
readonly lights = new Array<ILight>();
|
|
545
|
+
|
|
546
|
+
/** The brightest registered directional light, or null if none are registered
|
|
547
|
+
* @see lights
|
|
548
|
+
*/
|
|
549
|
+
get mainLight(): ILight | null {
|
|
550
|
+
let best: ILight | null = null;
|
|
551
|
+
for (const light of this.lights) {
|
|
552
|
+
if (light.type !== "directional") continue;
|
|
553
|
+
if (!best || light.intensity > best.intensity) best = light;
|
|
554
|
+
}
|
|
555
|
+
return best;
|
|
556
|
+
}
|
|
557
|
+
|
|
547
558
|
/** @deprecated Use sceneLighting */
|
|
548
|
-
get rendererData() { return this.sceneLighting }
|
|
559
|
+
private get rendererData() { return this.sceneLighting }
|
|
560
|
+
|
|
549
561
|
/** Access the scene lighting manager to control lighting settings in the context */
|
|
550
|
-
sceneLighting: SceneLighting;
|
|
551
|
-
addressables: Addressables;
|
|
552
|
-
lightmaps: ILightDataRegistry;
|
|
553
|
-
players: PlayerViewManager;
|
|
562
|
+
readonly sceneLighting: SceneLighting;
|
|
563
|
+
readonly addressables: Addressables;
|
|
564
|
+
readonly lightmaps: ILightDataRegistry;
|
|
565
|
+
readonly players: PlayerViewManager;
|
|
554
566
|
|
|
555
567
|
/** Access the LODs manager to control LOD behavior in the context */
|
|
556
568
|
readonly lodsManager: LODsManager;
|
|
@@ -858,6 +870,8 @@ export class Context implements IContext {
|
|
|
858
870
|
this._onBeforeRenderListeners.clear();
|
|
859
871
|
this._onAfterRenderListeners.clear();
|
|
860
872
|
|
|
873
|
+
this.lights.length = 0;
|
|
874
|
+
|
|
861
875
|
if (!this.isManagedExternally) {
|
|
862
876
|
if (this.renderer) {
|
|
863
877
|
this.renderer.renderLists.dispose();
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
+
import { initAnimatorControllerSerializer } from "../engine-components/AnimatorController.js";
|
|
1
2
|
import { initAnimationAutoplay } from "../engine-components/AnimationUtilsAutoplay.js";
|
|
2
3
|
import { initCameraUtils } from "../engine-components/CameraUtils.js";
|
|
4
|
+
import { initVolumeParameterSerializer } from "../engine-components/postprocessing/VolumeParameter.js";
|
|
3
5
|
import { initSceneSwitcherAttributes } from "../engine-components/SceneSwitcher.js";
|
|
4
6
|
import { initSkyboxAttributes } from "../engine-components/Skybox.js";
|
|
7
|
+
import { initAddressableSerializers } from "./engine_addressables.js";
|
|
5
8
|
import { initBuiltinTypes } from "./codegen/register_types.js";
|
|
6
9
|
import { ensureAudioContextIsResumed } from "./engine_audio.js";
|
|
7
10
|
import { initNeedleLoader } from "./engine_loaders.js";
|
|
11
|
+
import { initBuiltinSerializers } from "./engine_serialization_builtin_serializer.js";
|
|
8
12
|
import { initShims } from "./engine_shims.js";
|
|
9
13
|
import { patchTonemapping } from "./engine_tonemapping.js";
|
|
10
14
|
import { initCameraExtensions } from "./js-extensions/Camera.js";
|
|
11
15
|
import { patchLayers } from "./js-extensions/Layers.js";
|
|
12
16
|
import { initObject3DExtensions } from "./js-extensions/Object3D.js";
|
|
13
17
|
import { initVectorExtensions } from "./js-extensions/Vector.js";
|
|
18
|
+
import { SSR } from "./engine_ssr.js";
|
|
19
|
+
import { initLicense } from "./engine_license.js";
|
|
14
20
|
import { initWebComponents } from "./webcomponents/init.js";
|
|
15
21
|
import { initPhysics } from "./engine_physics_rapier.js";
|
|
22
|
+
import { initSpatialConsole } from "./debug/debug_spatial_console.js";
|
|
16
23
|
import { initXR } from "./xr/init.js";
|
|
17
24
|
|
|
18
25
|
let initialized = false;
|
|
@@ -28,10 +35,15 @@ let initialized = false;
|
|
|
28
35
|
*/
|
|
29
36
|
export function initEngine() {
|
|
30
37
|
if (initialized) return;
|
|
38
|
+
if (SSR) return;
|
|
31
39
|
initialized = true;
|
|
32
40
|
|
|
33
41
|
initWebComponents();
|
|
34
42
|
initShims();
|
|
43
|
+
initBuiltinSerializers();
|
|
44
|
+
initAddressableSerializers();
|
|
45
|
+
initAnimatorControllerSerializer();
|
|
46
|
+
initVolumeParameterSerializer();
|
|
35
47
|
patchTonemapping();
|
|
36
48
|
patchLayers();
|
|
37
49
|
initCameraExtensions();
|
|
@@ -46,4 +58,6 @@ export function initEngine() {
|
|
|
46
58
|
initSceneSwitcherAttributes();
|
|
47
59
|
initPhysics();
|
|
48
60
|
initXR();
|
|
61
|
+
initSpatialConsole();
|
|
62
|
+
initLicense();
|
|
49
63
|
}
|