@needle-tools/engine 4.16.6 → 4.16.7-beta.1
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/SKILL.md +229 -27
- package/dist/{needle-engine.bundle-BN_X4iTv.js → needle-engine.bundle-BgvXJBTN.js} +830 -827
- package/dist/{needle-engine.bundle-D-slQEcs.umd.cjs → needle-engine.bundle-DNlAFP3L.umd.cjs} +89 -89
- package/dist/{needle-engine.bundle-DY-lNV9M.min.js → needle-engine.bundle-dqsgn9ja.min.js} +70 -70
- package/dist/needle-engine.d.ts +19 -16
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +2 -0
- package/lib/engine/xr/NeedleXRSession.js +24 -6
- package/lib/engine/xr/NeedleXRSession.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/VideoPlayer.d.ts +1 -0
- package/lib/engine-components/VideoPlayer.js +5 -1
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/ui/Canvas.d.ts +1 -1
- package/lib/engine-components/ui/Canvas.js +2 -8
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/package.json +1 -1
- package/plugins/vite/build-pipeline.js +16 -6
- package/src/engine/xr/NeedleXRSession.ts +21 -5
- package/src/engine-components/ReflectionProbe.ts +2 -0
- package/src/engine-components/VideoPlayer.ts +4 -1
- package/src/engine-components/ui/Canvas.ts +2 -8
|
@@ -112,14 +112,14 @@ export async function needleBuildPipeline(command, config, userSettings) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
let shouldRun = false;
|
|
115
|
-
|
|
116
|
-
if (
|
|
115
|
+
// Check env var (set by Unity/Blender integrations) or CLI arg (for manual invocation)
|
|
116
|
+
if (process.env.NEEDLE_BUILD_PRODUCTION === "true" || process.argv.indexOf("--production") >= 0) {
|
|
117
117
|
shouldRun = true;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
if (!shouldRun) {
|
|
121
121
|
if (command === "build") {
|
|
122
|
-
log("Skipping build pipeline because this is a development build.\n- Invoke with `--production` to run the build pipeline.\n- For example \"vite build -- --production\".");
|
|
122
|
+
log("Skipping build pipeline because this is a development build.\n- Invoke with `--production` to run the build pipeline.\n- For example \"vite build -- --production\" or set NEEDLE_BUILD_PRODUCTION=true.");
|
|
123
123
|
}
|
|
124
124
|
await new Promise((resolve, _) => setTimeout(resolve, 1000));
|
|
125
125
|
return null;
|
|
@@ -387,12 +387,21 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
387
387
|
else {
|
|
388
388
|
// First check if the user passed in a specific version to use via the vite config
|
|
389
389
|
let version = opts.buildPipeline?.version;
|
|
390
|
-
|
|
390
|
+
let versionSource = version ? "vite" : "";
|
|
391
|
+
// If not, check env var (set by Unity/Blender integrations)
|
|
392
|
+
if (!version && process.env.NEEDLE_BUILD_PIPELINE_VERSION) {
|
|
393
|
+
version = process.env.NEEDLE_BUILD_PIPELINE_VERSION;
|
|
394
|
+
versionSource = "env";
|
|
395
|
+
}
|
|
396
|
+
// Fallback: check CLI arg for backwards compatibility
|
|
391
397
|
if (!version) {
|
|
392
398
|
for (let i = 0; i < process.argv.length; i++) {
|
|
393
399
|
if (process.argv[i] === "--build-pipeline-version" && i < process.argv.length - 1) {
|
|
394
400
|
const value = process.argv[i + 1]?.replace(/['"]+/g, '').trim();
|
|
395
|
-
if (value)
|
|
401
|
+
if (value) {
|
|
402
|
+
version = value;
|
|
403
|
+
versionSource = "arg";
|
|
404
|
+
}
|
|
396
405
|
break;
|
|
397
406
|
}
|
|
398
407
|
}
|
|
@@ -400,8 +409,9 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
400
409
|
// Otherwise we default to the stable version on npm
|
|
401
410
|
if (!version) version = "stable";
|
|
402
411
|
|
|
412
|
+
const versionInfo = versionSource ? `'${version}' (${versionSource})` : `'${version}'`;
|
|
403
413
|
const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}" \"${tempOutputPath}\"`;
|
|
404
|
-
log(`Running compression locally using version
|
|
414
|
+
log(`Running compression locally using version ${versionInfo}`);
|
|
405
415
|
proc = exec(cmd, { env: commandEnv });
|
|
406
416
|
}
|
|
407
417
|
let pipelineSpinnerIndex = 0;
|
|
@@ -1017,6 +1017,8 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1017
1017
|
private readonly _xr_update_scripts: INeedleXRSessionEventReceiver[] = [];
|
|
1018
1018
|
/** scripts that are in the scene but inactive (e.g. disabled parent gameObject) */
|
|
1019
1019
|
private readonly _inactive_scripts: INeedleXRSessionEventReceiver[] = [];
|
|
1020
|
+
/** tracks scripts that have received onEnterXR — prevents spurious onLeaveXR calls */
|
|
1021
|
+
private readonly _scripts_in_xr = new Set<INeedleXRSessionEventReceiver>();
|
|
1020
1022
|
private readonly _controllerAdded: ControllerChangedEvt[];
|
|
1021
1023
|
private readonly _controllerRemoved: ControllerChangedEvt[];
|
|
1022
1024
|
private readonly _originalCameraWorldPosition?: Vector3 | null;
|
|
@@ -1295,8 +1297,10 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1295
1297
|
// even if they might already be destroyed e.g. by the WebXR component (it destroys the default controller scripts)
|
|
1296
1298
|
// they should still receive this callback to be properly cleaned up
|
|
1297
1299
|
for (const listener of this._xr_scripts) {
|
|
1300
|
+
this._scripts_in_xr.delete(listener);
|
|
1298
1301
|
listener?.onLeaveXR?.({ xr: this });
|
|
1299
1302
|
}
|
|
1303
|
+
this._scripts_in_xr.clear();
|
|
1300
1304
|
|
|
1301
1305
|
this.sync?.onExitXR(this);
|
|
1302
1306
|
|
|
@@ -1498,8 +1502,12 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1498
1502
|
}
|
|
1499
1503
|
|
|
1500
1504
|
//performance.mark('NeedleXRSession update scripts start');
|
|
1501
|
-
//
|
|
1502
|
-
|
|
1505
|
+
// check all XR scripts for destroyed or inactive state
|
|
1506
|
+
// this must cover _xr_scripts (not just _xr_update_scripts) so that scripts
|
|
1507
|
+
// without onUpdateXR are also detected as inactive when removed from the scene
|
|
1508
|
+
// iterate backwards since markInactive/removeScript modifies the array
|
|
1509
|
+
for (let i = this._xr_scripts.length - 1; i >= 0; i--) {
|
|
1510
|
+
const script = this._xr_scripts[i];
|
|
1503
1511
|
if (script.destroyed === true) {
|
|
1504
1512
|
this._script_to_remove.push(script);
|
|
1505
1513
|
continue;
|
|
@@ -1508,6 +1516,10 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1508
1516
|
this.markInactive(script);
|
|
1509
1517
|
continue;
|
|
1510
1518
|
}
|
|
1519
|
+
}
|
|
1520
|
+
// invoke update on scripts that have onUpdateXR
|
|
1521
|
+
for (const script of this._xr_update_scripts) {
|
|
1522
|
+
if (script.destroyed || script.activeAndEnabled === false) continue;
|
|
1511
1523
|
if (script.onUpdateXR) script.onUpdateXR(args);
|
|
1512
1524
|
}
|
|
1513
1525
|
//performance.mark('NeedleXRSession update scripts end');
|
|
@@ -1630,9 +1642,11 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1630
1642
|
const script = this._inactive_scripts[i];
|
|
1631
1643
|
if (script.activeAndEnabled) {
|
|
1632
1644
|
this._inactive_scripts.splice(i, 1);
|
|
1633
|
-
|
|
1634
|
-
this.
|
|
1635
|
-
|
|
1645
|
+
// addScript returns false if already re-added (e.g. via new_scripts_xr processing)
|
|
1646
|
+
if (this.addScript(script)) {
|
|
1647
|
+
this.invokeCallback_EnterXR(script);
|
|
1648
|
+
for (const ctrl of this.controllers) this.invokeCallback_ControllerAdded(script, ctrl);
|
|
1649
|
+
}
|
|
1636
1650
|
}
|
|
1637
1651
|
}
|
|
1638
1652
|
}
|
|
@@ -1653,6 +1667,7 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1653
1667
|
}
|
|
1654
1668
|
|
|
1655
1669
|
private invokeCallback_EnterXR(script: INeedleXRSessionEventReceiver) {
|
|
1670
|
+
this._scripts_in_xr.add(script);
|
|
1656
1671
|
if (script.onEnterXR) {
|
|
1657
1672
|
script.onEnterXR({ xr: this });
|
|
1658
1673
|
}
|
|
@@ -1668,6 +1683,7 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1668
1683
|
}
|
|
1669
1684
|
}
|
|
1670
1685
|
private invokeCallback_LeaveXR(script: INeedleXRSessionEventReceiver) {
|
|
1686
|
+
if (!this._scripts_in_xr.delete(script)) return;
|
|
1671
1687
|
if (script.onLeaveXR && !script.destroyed) {
|
|
1672
1688
|
script.onLeaveXR({ xr: this });
|
|
1673
1689
|
}
|
|
@@ -286,6 +286,8 @@ export class ReflectionProbe extends Behaviour {
|
|
|
286
286
|
const current = block.getOverride("envMap")?.value;
|
|
287
287
|
if (current === this.texture) {
|
|
288
288
|
block.removeOveride("envMap");
|
|
289
|
+
block.removeOveride("envMapRotation");
|
|
290
|
+
block.removeOveride("envMapIntensity");
|
|
289
291
|
}
|
|
290
292
|
}
|
|
291
293
|
}
|
|
@@ -428,6 +428,8 @@ export class VideoPlayer extends Behaviour {
|
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
private _playErrors: number = 0;
|
|
432
|
+
|
|
431
433
|
/** start playing the video source */
|
|
432
434
|
play() {
|
|
433
435
|
if (!this._videoElement) this.create(false);
|
|
@@ -448,7 +450,8 @@ export class VideoPlayer extends Behaviour {
|
|
|
448
450
|
if (debug) console.log("Video Play()", this.clip, this._videoElement, this.time);
|
|
449
451
|
this._videoElement.currentTime = this.time;
|
|
450
452
|
this._videoElement.play().catch(err => {
|
|
451
|
-
console.
|
|
453
|
+
if (this._playErrors++ < 10) console.error(err);
|
|
454
|
+
else if (this._playErrors === 10) console.error("Multiple errors playing video, further errors will be suppressed. Use 'debugvideo' param to see all errors.");
|
|
452
455
|
// https://developer.chrome.com/blog/play-request-was-interrupted/
|
|
453
456
|
if (debug)
|
|
454
457
|
console.error("Error playing video", err, "CODE=" + err.code, this.videoElement?.src, this);
|
|
@@ -4,7 +4,7 @@ import * as ThreeMeshUI from 'three-mesh-ui'
|
|
|
4
4
|
import { Mathf } from "../../engine/engine_math.js";
|
|
5
5
|
import { serializable } from "../../engine/engine_serialization_decorator.js";
|
|
6
6
|
import { FrameEvent } from "../../engine/engine_setup.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getParam } from "../../engine/engine_utils.js";
|
|
8
8
|
import { type NeedleXREventArgs } from "../../engine/xr/api.js";
|
|
9
9
|
import { Camera } from "../Camera.js";
|
|
10
10
|
import { GameObject } from "../Component.js";
|
|
@@ -236,19 +236,13 @@ export class Canvas extends UIRootComponent implements ICanvas {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
onEnterXR(args: NeedleXREventArgs) {
|
|
240
240
|
// workaround for https://linear.app/needle/issue/NE-4114
|
|
241
241
|
if (this.screenspace) {
|
|
242
242
|
if (args.xr.isVR || args.xr.isPassThrough) {
|
|
243
243
|
this.gameObject.visible = false;
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
-
else {
|
|
247
|
-
this.gameObject.visible = false;
|
|
248
|
-
await delayForFrames(1).then(()=>{
|
|
249
|
-
this.gameObject.visible = true;
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
246
|
}
|
|
253
247
|
onLeaveXR(args: NeedleXREventArgs): void {
|
|
254
248
|
if (this.screenspace) {
|