@needle-tools/engine 4.1.0-beta.5 → 4.1.0-beta.8

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/needle-engine.bundle.js +1170 -1172
  3. package/dist/needle-engine.bundle.light.js +1305 -1307
  4. package/dist/needle-engine.bundle.light.min.js +54 -54
  5. package/dist/needle-engine.bundle.light.umd.cjs +91 -91
  6. package/dist/needle-engine.bundle.min.js +60 -60
  7. package/dist/needle-engine.bundle.umd.cjs +60 -60
  8. package/dist/needle-engine.d.ts +136 -0
  9. package/dist/needle-engine.light.d.ts +10 -14137
  10. package/dist/vendor.js +174 -174
  11. package/dist/vendor.light.js +174 -174
  12. package/dist/vendor.light.min.js +12 -12
  13. package/dist/vendor.light.umd.cjs +13 -13
  14. package/dist/vendor.min.js +12 -12
  15. package/dist/vendor.umd.cjs +13 -13
  16. package/lib/engine/engine_addressables.d.ts +6 -4
  17. package/lib/engine/engine_addressables.js +8 -1
  18. package/lib/engine/engine_addressables.js.map +1 -1
  19. package/lib/engine/engine_context.js +2 -0
  20. package/lib/engine/engine_context.js.map +1 -1
  21. package/lib/engine/engine_gameobject.d.ts +2 -0
  22. package/lib/engine/engine_gameobject.js +5 -2
  23. package/lib/engine/engine_gameobject.js.map +1 -1
  24. package/lib/engine/engine_input.d.ts +19 -6
  25. package/lib/engine/engine_input.js +0 -10
  26. package/lib/engine/engine_input.js.map +1 -1
  27. package/lib/engine/engine_license.js +1 -1
  28. package/lib/engine/engine_license.js.map +1 -1
  29. package/lib/engine/engine_networking_instantiate.d.ts +1 -1
  30. package/lib/engine/engine_scenetools.js +6 -2
  31. package/lib/engine/engine_scenetools.js.map +1 -1
  32. package/lib/engine/webcomponents/needle menu/needle-menu.js +8 -2
  33. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  34. package/lib/engine-components/AvatarLoader.js.map +1 -1
  35. package/lib/engine-components/Component.d.ts +3 -1
  36. package/lib/engine-components/Component.js +3 -4
  37. package/lib/engine-components/Component.js.map +1 -1
  38. package/lib/engine-components/SceneSwitcher.d.ts +1 -1
  39. package/lib/engine-components/SceneSwitcher.js +1 -1
  40. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  41. package/lib/engine-components/webxr/Avatar.js +3 -3
  42. package/lib/engine-components/webxr/Avatar.js.map +1 -1
  43. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  44. package/lib/engine-components-experimental/networking/PlayerSync.js +2 -1
  45. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
  46. package/package.json +9 -4
  47. package/plugins/common/config.cjs +22 -6
  48. package/plugins/common/config.js +27 -11
  49. package/plugins/next/next.js +47 -4
  50. package/src/engine/codegen/register_types.ts +2 -2
  51. package/src/engine/engine_addressables.ts +19 -12
  52. package/src/engine/engine_context.ts +2 -0
  53. package/src/engine/engine_gameobject.ts +12 -3
  54. package/src/engine/engine_input.ts +12 -5
  55. package/src/engine/engine_license.ts +2 -1
  56. package/src/engine/engine_networking_instantiate.ts +1 -1
  57. package/src/engine/engine_scenetools.ts +6 -2
  58. package/src/engine/webcomponents/needle menu/needle-menu.ts +16 -10
  59. package/src/engine-components/AvatarLoader.ts +1 -1
  60. package/src/engine-components/Component.ts +9 -3
  61. package/src/engine-components/SceneSwitcher.ts +1 -1
  62. package/src/engine-components/webxr/Avatar.ts +3 -3
  63. package/src/engine-components/webxr/WebXRImageTracking.ts +5 -4
  64. package/src/engine-components-experimental/networking/PlayerSync.ts +1 -1
@@ -2,8 +2,9 @@ import { fileURLToPath } from 'url';
2
2
  import { dirname, resolve } from 'path';
3
3
  import { tryGetNeedleEngineVersion } from '../common/version.js';
4
4
  import { tryGetGenerator } from '../common/generator.js';
5
- import { getMeta } from '../common/config.cjs';
5
+ import { getConfig, getMeta } from '../common/config.cjs';
6
6
  import { alias } from './alias.cjs';
7
+ import { createBuildInfoFile } from '../common/buildinfo.js';
7
8
 
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
@@ -24,12 +25,37 @@ export const needleNext = (nextConfig, userSettings) => {
24
25
  reactStrictMode: true,
25
26
  };
26
27
 
28
+ const needleConfig = getConfig();
29
+
27
30
  // add transpile packages
28
31
  if (!nextConfig.transpilePackages) {
29
32
  nextConfig.transpilePackages = [];
30
33
  }
31
34
  nextConfig.transpilePackages.push("three", "peerjs", "three-mesh-ui");
32
35
 
36
+ if (nextConfig.output === undefined) {
37
+ console.log("Set output to 'export' (see 'https://nextjs.org/docs/pages/building-your-application/deploying/static-exports#configuration' for more information)");
38
+ nextConfig.output = "export";
39
+ // we *also* need to turn OFF image optimization for static HTML files to be generated
40
+ // see https://github.com/vercel/next.js/issues/40240
41
+ if (nextConfig.images === undefined) {
42
+ nextConfig.images = {
43
+ unoptimized: true
44
+ }
45
+ }
46
+ }
47
+ if (nextConfig.distDir == undefined) {
48
+ console.log("Export to 'dist'");
49
+ if (needleConfig?.buildDirectory) {
50
+ console.log(`Using build directory from needle config: ${needleConfig.buildDirectory}`);
51
+ nextConfig.distDir = needleConfig.buildDirectory;
52
+ }
53
+ else {
54
+ console.log("Using default build directory 'dist'. You can override the output directory via the needle config or by setting nextConfig.distDir");
55
+ nextConfig.distDir = "dist";
56
+ }
57
+ }
58
+
33
59
  // add webpack config
34
60
  if (!nextConfig.webpack) nextConfig.webpack = nextWebPack;
35
61
  else {
@@ -43,11 +69,11 @@ export const needleNext = (nextConfig, userSettings) => {
43
69
  function nextWebPack(config, { buildId, dev, isServer, defaultLoaders, webpack }) {
44
70
 
45
71
  // TODO: get public identifier key from license server
46
-
72
+
47
73
  const meta = getMeta();
48
74
  let useRapier = true;
49
75
  if (userSettings.useRapier === false) useRapier = false;
50
- else if (meta.useRapier === false) useRapier = false;
76
+ else if (meta && meta.useRapier === false) useRapier = false;
51
77
  // add defines
52
78
  const webpackModule = userSettings.modules?.webpack;
53
79
  const definePlugin = webpackModule && new webpackModule.DefinePlugin({
@@ -68,7 +94,6 @@ export const needleNext = (nextConfig, userSettings) => {
68
94
  test: /engine_license\.(ts|js)$/,
69
95
  loader: resolve(__dirname, 'license.cjs')
70
96
  });
71
-
72
97
  // add mesh bvh worker transform
73
98
  config.module.rules.push({
74
99
  test: /generateMeshBVH.worker\.js$/,
@@ -77,6 +102,24 @@ export const needleNext = (nextConfig, userSettings) => {
77
102
 
78
103
  alias(config);
79
104
 
105
+ // these hooks are invoked but nextjs deletes the files again:
106
+ // add webpack done plugin https://webpack.js.org/api/compiler-hooks/
107
+ // config.plugins.push({
108
+ // apply(compiler) {
109
+ // compiler.hooks.shutdown.tap('NeedleDonePlugin', (stats) => {
110
+ // return createBuildInfoFile(nextConfig.distDir);
111
+ // });
112
+ // }
113
+ // });
114
+ // so as a workaround for above's problem:
115
+ // hook into process quit event since there doesn't seem to be a next hook for "after emit"
116
+ // node's beforeExit event is not called :(
117
+ process.on('exit', (code) => {
118
+ if (code === 0)
119
+ return createBuildInfoFile(nextConfig.distDir);
120
+ });
121
+
122
+
80
123
  return config;
81
124
  }
82
125
 
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable */
2
2
  import { TypeStore } from "./../engine_typestore.js"
3
-
3
+
4
4
  // Import types
5
5
  import { AlignmentConstraint } from "../../engine-components/AlignmentConstraint.js";
6
6
  import { Animation } from "../../engine-components/Animation.js";
@@ -220,7 +220,7 @@ import { XRFlag } from "../../engine-components/webxr/XRFlag.js";
220
220
  import { PlayerSync } from "../../engine-components-experimental/networking/PlayerSync.js";
221
221
  import { PlayerState } from "../../engine-components-experimental/networking/PlayerSync.js";
222
222
  import { PresentationMode } from "../../engine-components-experimental/Presentation.js";
223
-
223
+
224
224
  // Register types
225
225
  TypeStore.add("AlignmentConstraint", AlignmentConstraint);
226
226
  TypeStore.add("Animation", Animation);
@@ -5,10 +5,10 @@ import { destroy, type IInstantiateOptions, instantiate, InstantiateOptions, isD
5
5
  import { getLoader } from "./engine_gltf.js";
6
6
  import { processNewScripts } from "./engine_mainloop_utils.js";
7
7
  import { BlobStorage } from "./engine_networking_blob.js";
8
- import { registerPrefabProvider, syncInstantiate,SyncInstantiateOptions } from "./engine_networking_instantiate.js";
8
+ import { registerPrefabProvider, syncInstantiate, SyncInstantiateOptions } from "./engine_networking_instantiate.js";
9
9
  import { SerializationContext, TypeSerializer } from "./engine_serialization_core.js";
10
10
  import { Context } from "./engine_setup.js";
11
- import type { IComponent, IGameObject, SourceIdentifier } from "./engine_types.js";
11
+ import type { GLTF, IComponent, IGameObject, SourceIdentifier } from "./engine_types.js";
12
12
 
13
13
  const debug = getParam("debugaddressables");
14
14
 
@@ -140,8 +140,11 @@ export class AssetReference {
140
140
 
141
141
  private static currentlyInstantiating: Map<string, number> = new Map<string, number>();
142
142
 
143
+ /** @returns true if this is an AssetReference instance */
144
+ get isAssetReference() { return true; }
145
+
143
146
  /** The loaded asset */
144
- get asset(): any {
147
+ get asset(): Object3D | null {
145
148
  return this._glbRoot ?? this._asset;
146
149
  }
147
150
 
@@ -183,7 +186,7 @@ export class AssetReference {
183
186
  private _hashedUri: string;
184
187
 
185
188
  private _isLoadingRawBinary: boolean = false;
186
- private _rawBinary?: ArrayBuffer | null;
189
+ private _rawBinary?: ArrayBufferLike | null;
187
190
 
188
191
  /** @internal */
189
192
  constructor(uri: string, hash?: string, asset: any = null) {
@@ -197,7 +200,7 @@ export class AssetReference {
197
200
  registerPrefabProvider(this._url, this.onResolvePrefab.bind(this));
198
201
  }
199
202
 
200
- private async onResolvePrefab(url: string): Promise<IGameObject | null> {
203
+ private async onResolvePrefab(url: string): Promise<Object3D | null> {
201
204
  if (url === this.url) {
202
205
  if (this.mustLoad) await this.loadAssetAsync();
203
206
  if (this.asset) {
@@ -208,7 +211,7 @@ export class AssetReference {
208
211
  }
209
212
 
210
213
  private get mustLoad() {
211
- return !this.asset || this.asset.__destroyed === true || isDestroyed(this.asset) === true;
214
+ return !this.asset || (this.asset as any).__destroyed === true || isDestroyed(this.asset) === true;
212
215
  }
213
216
 
214
217
  /**
@@ -220,8 +223,8 @@ export class AssetReference {
220
223
  if (this.asset) {
221
224
  if (debug) console.log("Unload", this.asset);
222
225
  // TODO: we need a way to remove objects from the context (including components) without actually "destroying" them
223
- if (this.asset.scene)
224
- destroy(this.asset.scene, true, true);
226
+ if ("scene" in this.asset && this.asset.scene)
227
+ destroy(this.asset.scene as Object3D, true, true);
225
228
  destroy(this.asset, true, true);
226
229
  }
227
230
  this.asset = null;
@@ -234,7 +237,7 @@ export class AssetReference {
234
237
  }
235
238
 
236
239
  /** loads the asset binary without creating an instance */
237
- async preload(): Promise<ArrayBuffer | null> {
240
+ async preload(): Promise<ArrayBufferLike | null> {
238
241
  if (!this.mustLoad) return null;
239
242
  if (this._isLoadingRawBinary) return null;
240
243
  if (this._rawBinary !== undefined) return this._rawBinary;
@@ -268,6 +271,10 @@ export class AssetReference {
268
271
  // we should "address" (LUL) this
269
272
  // console.log("START LOADING");
270
273
  if (this._rawBinary) {
274
+ if (!(this._rawBinary instanceof ArrayBuffer)) {
275
+ console.error("Invalid raw binary data", this._rawBinary);
276
+ return null;
277
+ }
271
278
  this._loading = getLoader().parseSync(context, this._rawBinary, this.url, null);
272
279
  this.raiseProgressEvent(new ProgressEvent("progress", { loaded: this._rawBinary.byteLength, total: this._rawBinary.byteLength }));
273
280
  }
@@ -283,7 +290,6 @@ export class AssetReference {
283
290
  this._glbRoot = this.tryGetActualGameObjectRoot(res);
284
291
  this._loading = undefined;
285
292
  if (res) {
286
-
287
293
  // Make sure the loaded roots all have a reference to this AssetReference
288
294
  // that was originally loading it.
289
295
  // We need this when the loaded asset is being disposed
@@ -302,10 +308,11 @@ export class AssetReference {
302
308
  }
303
309
  return this.asset;
304
310
  }
311
+ return null;
305
312
  }
306
313
 
307
314
  /** loads and returns a new instance of `asset` */
308
- async instantiate(parent?: Object3D | IInstantiateOptions) {
315
+ async instantiate(parent?: Object3D | IInstantiateOptions | null) {
309
316
  return this.onInstantiate(parent, false);
310
317
  }
311
318
 
@@ -332,7 +339,7 @@ export class AssetReference {
332
339
  }
333
340
  }
334
341
 
335
- private async onInstantiate(opts?: Object3D | IInstantiateOptions | SyncInstantiateOptions, networked: boolean = false, saveOnServer?: boolean) : Promise<Object3D | null> {
342
+ private async onInstantiate(opts?: Object3D | IInstantiateOptions | SyncInstantiateOptions | null, networked: boolean = false, saveOnServer?: boolean): Promise<Object3D | null> {
336
343
  const context = Context.Current;
337
344
 
338
345
  // clone the instantiate options immediately
@@ -158,6 +158,8 @@ export class Context implements IContext {
158
158
  // Note: this is due to a bug on OSX devices. See NE-5370
159
159
  powerPreference: (DeviceUtilities.isiOS() || DeviceUtilities.isMacOS()) ? "default" : "high-performance",
160
160
  stencil: true,
161
+ // logarithmicDepthBuffer: true,
162
+ // reverseDepthBuffer: true, // https://github.com/mrdoob/three.js/issues/29770
161
163
  };
162
164
  /** The default parameters that will be used when creating a new WebGLRenderer.
163
165
  * Modify in global context to change the default parameters for all new contexts.
@@ -1,5 +1,6 @@
1
1
  import { Bone, Object3D, Quaternion, SkinnedMesh, Vector3 } from "three";
2
2
 
3
+ import { type AssetReference } from "./engine_addressables.js";
3
4
  import { __internalNotifyObjectDestroyed as __internalRemoveReferences, disposeObjectResources } from "./engine_assetdatabase.js";
4
5
  import { ComponentLifecycleEvents } from "./engine_components_internal.js";
5
6
  import { activeInHierarchyFieldName } from "./engine_constants.js";
@@ -10,7 +11,7 @@ import { InstantiateIdProvider } from "./engine_networking_instantiate.js";
10
11
  import { assign } from "./engine_serialization_core.js";
11
12
  import { Context, registerComponent } from "./engine_setup.js";
12
13
  import { logHierarchy, setWorldPosition, setWorldQuaternion } from "./engine_three_utils.js";
13
- import { type Constructor, type GuidsMap, type IComponent as Component, type IComponent, IEventList,type IGameObject as GameObject, type UIDProvider } from "./engine_types.js";
14
+ import { type Constructor, type GuidsMap, type IComponent as Component, type IComponent, IEventList, type IGameObject as GameObject, type UIDProvider } from "./engine_types.js";
14
15
  import { getParam, tryFindObject } from "./engine_utils.js";
15
16
  import { apply } from "./js-extensions/index.js";
16
17
 
@@ -307,9 +308,17 @@ declare type InstantiateReferenceMap = Record<string, ObjectCloneReference>;
307
308
  */
308
309
  export declare type InstantiateContext = Readonly<InstantiateReferenceMap>;
309
310
 
310
- export function instantiate(instance: GameObject | Object3D, opts: IInstantiateOptions | null = null): GameObject {
311
+ export function instantiate(instance: AssetReference, opts?: IInstantiateOptions | null): Promise<Object3D | null>
312
+ export function instantiate(instance: GameObject | Object3D, opts?: IInstantiateOptions | null): GameObject
313
+ export function instantiate(instance: AssetReference | GameObject | Object3D, opts?: IInstantiateOptions | null | undefined): GameObject | Promise<Object3D | null> {
314
+
315
+ if ("isAssetReference" in instance) {
316
+ return instance.instantiate(opts ?? undefined);
317
+ }
318
+
311
319
  let options: InstantiateOptions | null = null;
312
- if (opts !== null) {
320
+
321
+ if (opts !== null && opts !== undefined) {
313
322
  // if x is defined assume this is a vec3 - this is just to not break everything at once and stay a little bit backwards compatible
314
323
  if (opts["x"] !== undefined) {
315
324
  options = new InstantiateOptions();
@@ -36,8 +36,15 @@ export const enum InputEvents {
36
36
  KeyUp = "keyup",
37
37
  KeyPressed = "keypress"
38
38
  }
39
+
39
40
  /** e.g. `pointerdown` */
40
- export type InputEventNames = EnumToPrimitiveUnion<InputEvents>;
41
+ type PointerEventNames = EnumToPrimitiveUnion<PointerEnumType>;
42
+ type KeyboardEventNames = EnumToPrimitiveUnion<KeyboardEnumType>;
43
+ export type InputEventNames = PointerEventNames | KeyboardEventNames;
44
+
45
+ declare type PointerEventListener = (evt: NEPointerEvent) => void;
46
+ declare type KeyboardEventListener = (evt: NEKeyboardEvent) => void;
47
+ declare type InputEventListener = PointerEventListener | KeyboardEventListener;
41
48
 
42
49
 
43
50
 
@@ -217,10 +224,6 @@ export class KeyEventArgs {
217
224
 
218
225
 
219
226
 
220
- declare type PointerEventListener = (evt: NEPointerEvent) => void;
221
- declare type KeyboardEventListener = (evt: NEKeyboardEvent) => void;
222
- declare type InputEventListener = PointerEventListener | KeyboardEventListener;
223
-
224
227
  export enum InputEventQueue {
225
228
  Early = -100,
226
229
  Default = 0,
@@ -254,6 +257,8 @@ export class Input implements IInput {
254
257
  * @param callback The callback to call when the event is triggered
255
258
  * @param options The options for adding the event listener
256
259
  */
260
+ addEventListener(type: PointerEventNames, callback: PointerEventListener, options?: EventListenerOptions);
261
+ addEventListener(type: KeyboardEventNames, callback: KeyboardEventListener, options?: EventListenerOptions);
257
262
  addEventListener(type: InputEvents | InputEventNames, callback: InputEventListener, options?: EventListenerOptions): void {
258
263
  if (!this._eventListeners[type]) this._eventListeners[type] = [];
259
264
 
@@ -284,6 +289,8 @@ export class Input implements IInput {
284
289
  * @param callback The callback to remove
285
290
  * @param options The options for removing the event listener
286
291
  */
292
+ removeEventListener(type: PointerEventNames, callback: PointerEventListener, options?: EventListenerOptions);
293
+ removeEventListener(type: KeyboardEventNames, callback: KeyboardEventListener, options?: EventListenerOptions);
287
294
  removeEventListener(type: InputEvents | InputEventNames, callback: InputEventListener, options?: EventListenerOptions): void {
288
295
  if (!this._eventListeners[type]) return;
289
296
  if (!callback) return;
@@ -6,6 +6,8 @@ import { getParam } from "./engine_utils.js";
6
6
 
7
7
  const debug = getParam("debuglicense");
8
8
 
9
+ const _licenseCheckResultChangedCallbacks: ((result: boolean) => void)[] = [];
10
+
9
11
  // This is modified by a bundler (e.g. vite)
10
12
  // Do not edit manually
11
13
  let NEEDLE_ENGINE_LICENSE_TYPE: string = "basic";
@@ -36,7 +38,6 @@ export function hasCommercialLicense() {
36
38
  return hasProLicense() || hasIndieLicense();
37
39
  }
38
40
 
39
- const _licenseCheckResultChangedCallbacks: ((result: boolean) => void)[] = [];
40
41
 
41
42
  /** @internal */
42
43
  export function onLicenseCheckResultChanged(cb: (result: boolean) => void) {
@@ -350,7 +350,7 @@ function instantiateSeeded(obj: GameObject, opts: IInstantiateOptions | null): {
350
350
  return { seed: seed, instance: instance };
351
351
  }
352
352
 
353
- export declare type PrefabProviderCallback = (guid: string) => Promise<GameObject | null>;
353
+ export declare type PrefabProviderCallback = (guid: string) => Promise<Object3D | null>;
354
354
 
355
355
  const registeredPrefabProviders: { [key: string]: PrefabProviderCallback } = {};
356
356
 
@@ -183,10 +183,14 @@ export async function parseSync(context: Context, data: string | ArrayBuffer, pa
183
183
  // https://threejs.org/docs/#examples/en/loaders/GLTFLoader.parse
184
184
  // so we make sure that "path" is never a file path
185
185
  let gltfLoaderPath = path.split("?")[0].trimEnd();
186
- if (gltfLoaderPath.endsWith(".glb") || gltfLoaderPath.endsWith(".gltf")) {
186
+ // This assumes that the path is a FILE path and not already a directory
187
+ // (it does not end with "/") – see https://linear.app/needle/issue/NE-6075
188
+ {
187
189
  // strip file from path
188
190
  const parts = gltfLoaderPath.split("/");
189
- parts.pop();
191
+ // check if the last part is a /, otherwise remove it
192
+ if (parts.length > 0 && parts[parts.length - 1] !== "")
193
+ parts.pop();
190
194
  gltfLoaderPath = parts.join("/");
191
195
  if (!gltfLoaderPath.endsWith("/")) gltfLoaderPath += "/";
192
196
  }
@@ -1,7 +1,7 @@
1
1
  import type { Context } from "../../engine_context.js";
2
2
  import { hasCommercialLicense, onLicenseCheckResultChanged } from "../../engine_license.js";
3
3
  import { isLocalNetwork } from "../../engine_networking_utils.js";
4
- import { DeviceUtilities,getParam } from "../../engine_utils.js";
4
+ import { DeviceUtilities, getParam } from "../../engine_utils.js";
5
5
  import { onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
6
6
  import { ButtonsFactory } from "../buttons.js";
7
7
  import { ensureFonts, iconFontUrl, loadFont } from "../fonts.js";
@@ -256,7 +256,12 @@ export class NeedleMenuElement extends HTMLElement {
256
256
  if (!element && domElement.shadowRoot) {
257
257
  element = domElement.shadowRoot.querySelector(elementName);
258
258
  }
259
+ // if no needle-menu was found in the domelement then we search the document body
259
260
  if (!element) {
261
+ element = window.document.body.querySelector(elementName) as NeedleMenuElement | null;
262
+ }
263
+ if (!element) {
264
+ // OK no menu element exists yet anywhere
260
265
  element = NeedleMenuElement.create() as NeedleMenuElement;
261
266
  if (domElement.shadowRoot)
262
267
  domElement.shadowRoot.appendChild(element);
@@ -682,14 +687,15 @@ export class NeedleMenuElement extends HTMLElement {
682
687
  });
683
688
 
684
689
  try {
685
- // if the user has a license then we CAN hide the needle logo
686
- onLicenseCheckResultChanged(res => {
687
- if (res == true && hasCommercialLicense() && !debugNonCommercial) {
688
- let visible = this._userRequestedLogoVisible;
689
- if(visible === undefined) visible = false;
690
- this.#onSetLogoVisible(visible);
691
- }
692
- });
690
+ // if the user has a license then we CAN hide the needle logo
691
+ // calling this method immediately will cause an issue with vite bundling tho
692
+ window.requestAnimationFrame(() => onLicenseCheckResultChanged(res => {
693
+ if (res == true && hasCommercialLicense() && !debugNonCommercial) {
694
+ let visible = this._userRequestedLogoVisible;
695
+ if (visible === undefined) visible = false;
696
+ this.#onSetLogoVisible(visible);
697
+ }
698
+ }));
693
699
  } catch (e) {
694
700
  console.error("[Needle Menu] License check failed.", e);
695
701
  }
@@ -824,7 +830,7 @@ export class NeedleMenuElement extends HTMLElement {
824
830
  this.logoContainer.style.display = "";
825
831
  this.logoContainer.style.opacity = "1";
826
832
  this.logoContainer.style.visibility = "visible";
827
- if(visible) {
833
+ if (visible) {
828
834
  this.root.classList.remove("logo-hidden");
829
835
  this.root.classList.add("logo-visible");
830
836
  }
@@ -52,7 +52,7 @@ export class AvatarLoader {
52
52
  if (!root) {
53
53
  const opts = new InstantiateOptions();
54
54
  // opts.parent = context.scene.uuid;
55
- root = GameObject.instantiate(utils.tryFindObject(avatarId, context.scene), opts);
55
+ root = GameObject.instantiate(utils.tryFindObject(avatarId, context.scene) as Object3D, opts);
56
56
  }
57
57
  }
58
58
  else root = avatarId;
@@ -1,6 +1,7 @@
1
1
  import { Euler, Object3D, Quaternion, Scene, Vector3 } from "three";
2
2
 
3
3
  import { isDevEnvironment } from "../engine/debug/index.js";
4
+ import { type AssetReference } from "../engine/engine_addressables.js";
4
5
  import { addComponent, destroyComponentInstance, findObjectOfType, findObjectsOfType, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, removeComponent } from "../engine/engine_components.js";
5
6
  import { activeInHierarchyFieldName } from "../engine/engine_constants.js";
6
7
  import { destroy, findByGuid, foreachComponent, HideFlags, type IInstantiateOptions, instantiate, isActiveInHierarchy, isActiveSelf, isDestroyed, isUsingInstancing, markAsInstancedRendered, setActive } from "../engine/engine_gameobject.js";
@@ -117,15 +118,20 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
117
118
  */
118
119
  public static instantiateSynced(instance: GameObject | Object3D | null, opts: SyncInstantiateOptions): GameObject | null {
119
120
  if (!instance) return null;
120
- return syncInstantiate(instance as any, opts) as GameObject | null;
121
+ return syncInstantiate(instance, opts) as GameObject | null;
121
122
  }
122
123
 
123
124
  /** Creates a new instance of the provided object (like cloning it including all components and children)
124
125
  * @param instance object to instantiate
125
126
  * @param opts options for the instantiation (e.g. with what parent, position, etc.)
126
127
  */
127
- public static instantiate(instance: GameObject | Object3D, opts: IInstantiateOptions | null = null): GameObject {
128
- return instantiate(instance, opts) as GameObject;
128
+ public static instantiate(instance: AssetReference, opts?: IInstantiateOptions | null | undefined): Promise<Object3D | null>
129
+ public static instantiate(instance: GameObject | Object3D, opts?: IInstantiateOptions | null | undefined): GameObject
130
+ public static instantiate(instance: AssetReference | GameObject | Object3D, opts: IInstantiateOptions | null | undefined = null): GameObject | Promise<Object3D | null> {
131
+ if ('isAssetReference' in instance) {
132
+ return instantiate(instance as AssetReference, opts);
133
+ }
134
+ return instantiate(instance as GameObject | Object3D, opts) as GameObject;
129
135
  }
130
136
 
131
137
  /** Destroys a object on all connected clients (if you are in a networked session)
@@ -760,7 +760,7 @@ export class SceneSwitcher extends Behaviour {
760
760
  }
761
761
  this._lastLoadingScene = this.loadingScene;
762
762
  if (!this._loadingScenePromise) {
763
- this._loadingScenePromise = this.loadingScene?.loadAssetAsync();
763
+ this._loadingScenePromise = this.loadingScene?.loadAssetAsync().then(res => res != null);
764
764
  }
765
765
  await this._loadingScenePromise;
766
766
  if (this._isCurrentlyLoading && this.loadingScene?.asset) {
@@ -101,7 +101,7 @@ export class Avatar extends Behaviour {
101
101
  headObj.quaternion.x *= -1;
102
102
 
103
103
  // HACK: XRFlag limitation workaround to make sure first person user head is never rendered
104
- if (this.context.time.frameCount % 10 === 0) {
104
+ if (this.context.time.frameCount % 10 === 0 && this.head.asset) {
105
105
  const xrflags = GameObject.getComponentsInChildren(this.head.asset, XRFlag);
106
106
  for (const flag of xrflags) {
107
107
  flag.enabled = false;
@@ -242,9 +242,9 @@ export class Avatar extends Behaviour {
242
242
  await this.loadAvatarObjects(this.head, this.leftHand, this.rightHand);
243
243
 
244
244
  this._leftHandMeshes = [];
245
- this.leftHand.asset.traverse((obj) => { if ((obj as Mesh)?.isMesh) this._leftHandMeshes!.push(obj); });
245
+ this.leftHand.asset?.traverse((obj) => { if ((obj as Mesh)?.isMesh) this._leftHandMeshes!.push(obj as Mesh); });
246
246
  this._rightHandMeshes = [];
247
- this.rightHand.asset.traverse((obj) => { if ((obj as Mesh)?.isMesh) this._rightHandMeshes!.push(obj); });
247
+ this.rightHand.asset?.traverse((obj) => { if ((obj as Mesh)?.isMesh) this._rightHandMeshes!.push(obj as Mesh); });
248
248
 
249
249
  if (PlayerState.isLocalPlayer(this.gameObject)) {
250
250
  this._syncTransforms = GameObject.getComponentsInChildren(this.gameObject, SyncedTransform);
@@ -4,6 +4,7 @@ import { Object3DEventMap } from "three";
4
4
  import { isDevEnvironment, showBalloonWarning } from "../../engine/debug/index.js";
5
5
  import { AssetReference } from "../../engine/engine_addressables.js";
6
6
  import { serializable } from "../../engine/engine_serialization.js";
7
+ import { IGameObject } from "../../engine/engine_types.js";
7
8
  import { CircularBuffer, DeviceUtilities, getParam } from "../../engine/engine_utils.js";
8
9
  import { type NeedleXREventArgs, NeedleXRSession } from "../../engine/xr/api.js";
9
10
  import { IUSDExporterExtension } from "../../engine-components/export/usdz/Extension.js";
@@ -355,7 +356,7 @@ export class WebXRImageTracking extends Behaviour {
355
356
  }
356
357
  }
357
358
 
358
- private readonly imageToObjectMap = new Map<WebXRImageTrackingModel, { object: GameObject | null, frames: number, lastTrackingTime: number }>();
359
+ private readonly imageToObjectMap = new Map<WebXRImageTrackingModel, { object: Object3D | null, frames: number, lastTrackingTime: number }>();
359
360
  private readonly currentImages: WebXRTrackedImage[] = [];
360
361
 
361
362
 
@@ -456,7 +457,7 @@ export class WebXRImageTracking extends Behaviour {
456
457
  trackedData = { object: null, frames: 0, lastTrackingTime: Date.now() };
457
458
  this.imageToObjectMap.set(model, trackedData);
458
459
 
459
- model.object.loadAssetAsync().then((asset: GameObject | null) => {
460
+ model.object.loadAssetAsync().then((asset: Object3D | null) => {
460
461
  if (model.createObjectInstance && asset) {
461
462
  asset = GameObject.instantiate(asset);
462
463
  }
@@ -475,7 +476,7 @@ export class WebXRImageTracking extends Behaviour {
475
476
  if (xr.rig) {
476
477
  xr.rig.gameObject.add(asset);
477
478
  image.applyToObject(asset);
478
- if (!asset.activeSelf)
479
+ if (!(asset as IGameObject).activeSelf)
479
480
  GameObject.setActive(asset, true);
480
481
  // InstancingUtil.markDirty(asset);
481
482
  }
@@ -503,7 +504,7 @@ export class WebXRImageTracking extends Behaviour {
503
504
  xr.rig.gameObject.add(trackedData.object);
504
505
 
505
506
  image.applyToObject(trackedData.object, this.smooth ? this.context.time.deltaTimeUnscaled * 3 : undefined);
506
- if (!trackedData.object.activeSelf) {
507
+ if (!(trackedData.object as IGameObject).activeSelf) {
507
508
  GameObject.setActive(trackedData.object, true);
508
509
  }
509
510
  // InstancingUtil.markDirty(trackedData.object);
@@ -36,7 +36,7 @@ export class PlayerSync extends Behaviour {
36
36
  const assetReference = AssetReference.getOrCreateFromUrl(url);
37
37
  if (!assetReference.asset) {
38
38
  const i = await assetReference.loadAssetAsync();
39
- GameObject.getOrAddComponent(i, PlayerState);
39
+ if(i) GameObject.getOrAddComponent(i, PlayerState);
40
40
  }
41
41
  const ps = new PlayerSync();
42
42
  ps._internalInit(init);