@needle-tools/engine 3.2.11-alpha → 3.2.13-alpha

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 (46) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/needle-engine.js +9274 -9206
  3. package/dist/needle-engine.min.js +273 -273
  4. package/dist/needle-engine.umd.cjs +271 -271
  5. package/lib/engine/codegen/register_types.js +2 -0
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/engine_context.js +3 -0
  8. package/lib/engine/engine_context.js.map +1 -1
  9. package/lib/engine/engine_mainloop_utils.js +1 -1
  10. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  11. package/lib/engine/engine_serialization_core.js +2 -0
  12. package/lib/engine/engine_serialization_core.js.map +1 -1
  13. package/lib/engine/engine_types.d.ts +2 -2
  14. package/lib/engine/engine_utils.d.ts +2 -2
  15. package/lib/engine/engine_utils.js +4 -4
  16. package/lib/engine/engine_utils.js.map +1 -1
  17. package/lib/engine-components/Component.d.ts +2 -2
  18. package/lib/engine-components/Component.js +9 -4
  19. package/lib/engine-components/Component.js.map +1 -1
  20. package/lib/engine-components/SceneSwitcher.d.ts +5 -0
  21. package/lib/engine-components/SceneSwitcher.js +32 -4
  22. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  23. package/lib/engine-components/codegen/components.d.ts +1 -0
  24. package/lib/engine-components/codegen/components.js +1 -0
  25. package/lib/engine-components/codegen/components.js.map +1 -1
  26. package/lib/engine-components/export/usdz/USDZExporter.d.ts +9 -4
  27. package/lib/engine-components/export/usdz/USDZExporter.js +77 -25
  28. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  29. package/lib/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +1 -1
  31. package/plugins/vite/alias.js +1 -4
  32. package/plugins/vite/copyfiles.js +46 -40
  33. package/plugins/vite/index.js +2 -0
  34. package/plugins/vite/meta.js +5 -2
  35. package/plugins/vite/peer.js +28 -0
  36. package/plugins/vite/reload.js +17 -13
  37. package/src/engine/codegen/register_types.js +2 -0
  38. package/src/engine/engine_context.ts +2 -0
  39. package/src/engine/engine_mainloop_utils.ts +1 -1
  40. package/src/engine/engine_serialization_core.ts +1 -1
  41. package/src/engine/engine_types.ts +2 -2
  42. package/src/engine/engine_utils.ts +7 -7
  43. package/src/engine-components/Component.ts +9 -4
  44. package/src/engine-components/SceneSwitcher.ts +38 -5
  45. package/src/engine-components/codegen/components.ts +1 -0
  46. package/src/engine-components/export/usdz/USDZExporter.ts +73 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "3.2.11-alpha",
3
+ "version": "3.2.13-alpha",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "type": "module",
@@ -33,10 +33,7 @@ export const needleViteAlias = (command, config, userSettings) => {
33
33
  return {
34
34
  name: "needle-alias",
35
35
  config(config) {
36
- setTimeout(() => {
37
- console.log('[needle-alias] ProjectDirectory: ' + projectDir);
38
- }, 150);
39
-
36
+ console.log('[needle-alias] ProjectDirectory: ' + projectDir);
40
37
  if (!config.resolve) config.resolve = {};
41
38
  if (!config.resolve.alias) config.resolve.alias = {};
42
39
  const aliasDict = config.resolve.alias;
@@ -11,57 +11,63 @@ export const needleCopyFiles = (command, config, userSettings) => {
11
11
  return;
12
12
  }
13
13
 
14
- const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
15
-
16
14
  return {
17
15
  name: 'needle-copy-files',
18
- apply: 'build',
19
- async closeBundle() {
20
- const baseDir = process.cwd();
21
- const pluginName = "needle-copy-files";
16
+ buildStart() {
17
+ return run(false, config);
18
+ },
19
+ closeBundle() {
20
+ return run(true, config);
21
+ },
22
+ }
23
+ }
24
+
25
+ async function run(isBuild, config) {
26
+ const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
22
27
 
23
- let assetsDirName = "assets";
24
- let outdirName = "dist";
28
+ const baseDir = process.cwd();
29
+ const pluginName = "needle-copy-files";
25
30
 
26
- const needleConfig = tryLoadProjectConfig();
27
- if(needleConfig){
28
- assetsDirName = needleConfig.assetsDirectory;
29
- }
31
+ let assetsDirName = "assets";
32
+ let outdirName = "dist";
30
33
 
31
- const outDir = resolve(baseDir, outdirName);
32
- if (!existsSync(outDir)) {
33
- mkdirSync(outDir);
34
- }
34
+ const needleConfig = tryLoadProjectConfig();
35
+ if (needleConfig) {
36
+ assetsDirName = needleConfig.assetsDirectory;
37
+ }
35
38
 
36
- if (copyIncludesFromEngine !== false) {
37
- // copy include from engine
38
- const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
39
- if (existsSync(engineIncludeDir)) {
40
- console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
41
- const targetDir = resolve(baseDir, 'include');
42
- copyRecursiveSync(engineIncludeDir, targetDir);
43
- }
44
- }
39
+ if (copyIncludesFromEngine !== false) {
40
+ // copy include from engine
41
+ const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
42
+ if (existsSync(engineIncludeDir)) {
43
+ console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
44
+ const projectIncludeDir = resolve(baseDir, 'include');
45
+ copyRecursiveSync(engineIncludeDir, projectIncludeDir);
46
+ }
47
+ }
45
48
 
46
- // copy assets dir
47
- const assetsDir = resolve(baseDir, assetsDirName);
48
- if (existsSync(assetsDir)) {
49
- console.log(`[${pluginName}] - Copy assets to ${outdirName}/${builtAssetsDirectory()}`)
50
- const targetDir = resolve(outDir, 'assets');
51
- copyRecursiveSync(assetsDir, targetDir);
52
- }
53
- // copy include dir
54
- const includeDir = resolve(baseDir, 'include');
55
- if (existsSync(includeDir)) {
56
- console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
57
- const targetDir = resolve(outDir, 'include');
58
- copyRecursiveSync(includeDir, targetDir);
59
- }
49
+ if (isBuild) {
50
+ const outDir = resolve(baseDir, outdirName);
51
+ if (!existsSync(outDir)) {
52
+ mkdirSync(outDir);
53
+ }
54
+ // copy assets dir
55
+ const assetsDir = resolve(baseDir, assetsDirName);
56
+ if (existsSync(assetsDir)) {
57
+ console.log(`[${pluginName}] - Copy assets to ${outdirName}/${builtAssetsDirectory()}`)
58
+ const targetDir = resolve(outDir, 'assets');
59
+ copyRecursiveSync(assetsDir, targetDir);
60
+ }
61
+ // copy include dir
62
+ const includeDir = resolve(baseDir, 'include');
63
+ if (existsSync(includeDir)) {
64
+ console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
65
+ const targetDir = resolve(outDir, 'include');
66
+ copyRecursiveSync(includeDir, targetDir);
60
67
  }
61
68
  }
62
69
  }
63
70
 
64
-
65
71
  function copyRecursiveSync(src, dest) {
66
72
  var exists = existsSync(src);
67
73
  var stats = exists && statSync(src);
@@ -8,6 +8,7 @@ import { needleCopyFiles } from "./copyfiles.js";
8
8
  import { needleViteAlias } from "./alias.js";
9
9
  import { needleTransformCodegen } from "./transform-codegen.js";
10
10
  import { needleLicense } from "./license.js";
11
+ import { needlePeerjs } from "./peer.js";
11
12
 
12
13
  export * from "./gzip.js";
13
14
  export * from "./config.js";
@@ -30,6 +31,7 @@ export const needlePlugins = async (command, config, userSettings) => {
30
31
  needleCopyFiles(command, config, userSettings),
31
32
  needleTransformCodegen(command, config, userSettings),
32
33
  needleDrop(command, config, userSettings),
34
+ needlePeerjs(command, config, userSettings)
33
35
  ];
34
36
  array.push(await editorConnection(command, config, userSettings, array));
35
37
  return array;
@@ -91,6 +91,9 @@ export const needleMeta = (command, config, userSettings) => {
91
91
  }
92
92
  }
93
93
 
94
+ // if(!tags.filter(t => t.attrs?.name === "generator"))
95
+ tags.push({ tag: 'meta', attrs: { name: 'generator', content: 'Needle' } });
96
+
94
97
  return { html, tags }
95
98
  },
96
99
  }
@@ -124,8 +127,8 @@ function removeMetaTag(html, name) {
124
127
  function insertNeedleCredits(html) {
125
128
  const needleCredits = `<!-- 🌵 Made with Needle — https://needle.tools -->`;
126
129
  html = html.replace(
127
- /<head>/,
128
- needleCredits + "\n<head>",
130
+ /<head/,
131
+ needleCredits + "\n<head",
129
132
  );
130
133
  return html;
131
134
  }
@@ -0,0 +1,28 @@
1
+
2
+ const peerjsString = `/* needle: injected fix for peerjs */
3
+ window.global = window;
4
+ var parcelRequire;`
5
+
6
+ export const needlePeerjs = (command, config, userSettings) => {
7
+
8
+ if (userSettings.noPeer === true) return;
9
+
10
+ return {
11
+ name: 'needle-peerjs',
12
+ transformIndexHtml: {
13
+ enforce: 'pre',
14
+ transform(html, _ctx) {
15
+ return {
16
+ html,
17
+ tags: [
18
+ {
19
+ tag: 'script',
20
+ children: peerjsString,
21
+ injectTo: 'body-prepend',
22
+ },
23
+ ]
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
@@ -61,22 +61,26 @@ export const needleReload = (command, config, userSettings) => {
61
61
  transformIndexHtml: {
62
62
  enforce: 'pre',
63
63
  transform(html, _) {
64
- if (config?.allowHotReload === false) return [html];
65
- if (userSettings?.allowHotReload === false) return [html];
64
+ if (config?.allowHotReload === false) return html;
65
+ if (userSettings?.allowHotReload === false) return html;
66
66
  const file = path.join(__dirname, 'reload-client.js');
67
- return [
68
- {
69
- tag: 'script',
70
- attrs: {
71
- type: 'module',
67
+ return {
68
+ html,
69
+ tags: [
70
+ {
71
+ tag: 'script',
72
+ attrs: {
73
+ type: 'module',
74
+ },
75
+ children: readFileSync(file, 'utf8'),
76
+ injectTo: 'body',
72
77
  },
73
- children: readFileSync(file, 'utf8'),
74
- injectTo: 'body',
75
- },
76
- ];
78
+ ]
79
+ }
80
+
77
81
  },
78
- },
79
- };
82
+ }
83
+ }
80
84
  }
81
85
 
82
86
 
@@ -108,6 +108,7 @@ import { PlayerSync } from "../../engine-components-experimental/networking/Play
108
108
  import { PointerEventData } from "../../engine-components/ui/PointerEvents";
109
109
  import { PostProcessingHandler } from "../../engine-components/postprocessing/PostProcessingHandler";
110
110
  import { PresentationMode } from "../../engine-components-experimental/Presentation";
111
+ import { QuickLookOverlay } from "../../engine-components/export/usdz/USDZExporter";
111
112
  import { RawImage } from "../../engine-components/ui/Image";
112
113
  import { Raycaster } from "../../engine-components/ui/Raycaster";
113
114
  import { Rect } from "../../engine-components/ui/RectTransform";
@@ -296,6 +297,7 @@ TypeStore.add("PlayerSync", PlayerSync);
296
297
  TypeStore.add("PointerEventData", PointerEventData);
297
298
  TypeStore.add("PostProcessingHandler", PostProcessingHandler);
298
299
  TypeStore.add("PresentationMode", PresentationMode);
300
+ TypeStore.add("QuickLookOverlay", QuickLookOverlay);
299
301
  TypeStore.add("RawImage", RawImage);
300
302
  TypeStore.add("Raycaster", Raycaster);
301
303
  TypeStore.add("Rect", Rect);
@@ -34,6 +34,7 @@ const debug = utils.getParam("debugSetup");
34
34
  const stats = utils.getParam("stats");
35
35
  const debugActive = utils.getParam("debugactive");
36
36
  const debugframerate = utils.getParam("debugframerate");
37
+ const debugCoroutine = utils.getParam("debugcoroutine");
37
38
 
38
39
  // this is where functions that setup unity scenes will be pushed into
39
40
  // those will be accessed from our custom html element to load them into their context
@@ -859,6 +860,7 @@ export class Context implements IContext {
859
860
  // TODO we might want to keep coroutines playing even if the component is disabled or inactive
860
861
  const remove = !evt.comp || evt.comp.destroyed || !evt.main || evt.comp["enabled"] === false;
861
862
  if (remove) {
863
+ if (debugCoroutine) console.log("Removing coroutine", evt.comp, evt.comp["enabled"])
862
864
  evts.splice(i, 1);
863
865
  --i;
864
866
  continue;
@@ -150,7 +150,7 @@ export function processNewScripts(context: IContext) {
150
150
 
151
151
  export function processRemoveFromScene(script: IComponent) {
152
152
  if (!script) return;
153
- script.__internalDisable();
153
+ script.__internalDisable(true);
154
154
  removeScriptFromContext(script, script.context);
155
155
  }
156
156
 
@@ -279,7 +279,7 @@ export function deserializeObject(obj: ISerializable, serializedData: object, co
279
279
  for (const key in typeInfo) {
280
280
  const serializedEntryInfo = typeInfo[key];
281
281
  const data = serializedData[key];
282
-
282
+ if(debug) console.log(key, data, obj, serializedEntryInfo)
283
283
 
284
284
  if (obj[key] !== undefined && data === undefined) {
285
285
  // if a field is marked as serialized and has some default value
@@ -123,9 +123,9 @@ export interface IComponent {
123
123
  /** @internal */
124
124
  __internalStart();
125
125
  /** @internal */
126
- __internalEnable();
126
+ __internalEnable(isAddingOrRemovingFromScene?: boolean);
127
127
  /** @internal */
128
- __internalDisable();
128
+ __internalDisable(isAddingOrRemovingFromScene?: boolean);
129
129
  /** @internal */
130
130
  __internalDestroy();
131
131
  /** @internal */
@@ -97,12 +97,12 @@ export function setOrAddParamsToUrl(url: URLSearchParams, paramName: string, par
97
97
  url.append(paramName, paramValue.toString());
98
98
  }
99
99
 
100
- export function pushState(title: string, urlParams: URLSearchParams) {
101
- window.history.pushState(null, title, "?" + urlParams.toString());
100
+ export function pushState(title: string, urlParams: URLSearchParams, state?: any) {
101
+ window.history.pushState(state, title, "?" + urlParams.toString());
102
102
  }
103
103
 
104
- export function setState(title: string, urlParams: URLSearchParams) {
105
- window.history.replaceState(null, title, "?" + urlParams.toString());
104
+ export function setState(title: string, urlParams: URLSearchParams, state?: any) {
105
+ window.history.replaceState(state, title, "?" + urlParams.toString());
106
106
  }
107
107
 
108
108
  // for room id
@@ -212,7 +212,7 @@ const debugGetPath = getParam("debugresolveurl");
212
212
  export const relativePathPrefix = "rel:";
213
213
 
214
214
  /** @deprecated use resolveUrl instead */
215
- export function getPath(source:SourceIdentifier|undefined, uri:string) : string {
215
+ export function getPath(source: SourceIdentifier | undefined, uri: string): string {
216
216
  return resolveUrl(source, uri);
217
217
  }
218
218
  /**
@@ -226,7 +226,7 @@ export function resolveUrl(source: SourceIdentifier | undefined, uri: string): s
226
226
  if (debugGetPath) console.warn("getPath: uri is undefined, returning uri", uri);
227
227
  return uri;
228
228
  }
229
- if(uri.startsWith("./")) {
229
+ if (uri.startsWith("./")) {
230
230
  return uri;
231
231
  }
232
232
  if (uri.startsWith("http")) {
@@ -237,7 +237,7 @@ export function resolveUrl(source: SourceIdentifier | undefined, uri: string): s
237
237
  if (debugGetPath) console.warn("getPath: source is undefined, returning uri", uri);
238
238
  return uri;
239
239
  }
240
- if(uri.startsWith(relativePathPrefix)){
240
+ if (uri.startsWith(relativePathPrefix)) {
241
241
  uri = uri.substring(4);
242
242
  }
243
243
  const pathIndex = source.lastIndexOf("/");
@@ -470,7 +470,7 @@ export class Component implements IComponent, EventTarget {
470
470
 
471
471
 
472
472
  /** @internal */
473
- __internalEnable(): boolean {
473
+ __internalEnable(isAddingToScene?: boolean): boolean {
474
474
  if (this.__destroyed) {
475
475
  if (isDevEnvironment()) {
476
476
  console.warn("[Needle Engine Dev] Trying to enable destroyed component");
@@ -481,7 +481,10 @@ export class Component implements IComponent, EventTarget {
481
481
  // But a user can change enable during awake
482
482
  if (!this.__didAwake) return false;
483
483
  if (this.__didEnable) {
484
- this.__isEnabled = true;
484
+ // We dont want to change the enable state if we are adding to scene
485
+ // But we want to change the state when e.g. a user changes the enable state during awake
486
+ if (isAddingToScene !== true)
487
+ this.__isEnabled = true;
485
488
  return false;
486
489
  }
487
490
  // console.trace("INTERNAL ENABLE");
@@ -492,12 +495,14 @@ export class Component implements IComponent, EventTarget {
492
495
  }
493
496
 
494
497
  /** @internal */
495
- __internalDisable() {
498
+ __internalDisable(isRemovingFromScene?: boolean) {
496
499
  // Don't change enable before awake
497
500
  // But a user can change enable during awake
498
501
  if (!this.__didAwake) return;
499
502
  if (!this.__didEnable) {
500
- this.__isEnabled = false;
503
+ // We dont want to change the enable state if we are removing from a scene
504
+ if (isRemovingFromScene !== true)
505
+ this.__isEnabled = false;
501
506
  return;
502
507
  }
503
508
  this.__didEnable = false;
@@ -14,7 +14,7 @@ ContextRegistry.registerCallback(ContextEvent.ContextRegistered, async _ => {
14
14
  // We need to defer import to not get issues with circular dependencies
15
15
  import("../engine/engine_element").then(res => {
16
16
  const webcomponent = res.NeedleEngineHTMLElement;
17
- if(debug) console.log("SceneSwitcher: registering scene attribute", webcomponent.observedAttributes);
17
+ if (debug) console.log("SceneSwitcher: registering scene attribute", webcomponent.observedAttributes);
18
18
  if (!webcomponent.observedAttributes.includes(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME))
19
19
  webcomponent.observedAttributes.push(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
20
20
  });
@@ -22,6 +22,12 @@ ContextRegistry.registerCallback(ContextEvent.ContextRegistered, async _ => {
22
22
 
23
23
  const couldNotLoadScenePromise = Promise.resolve(false);
24
24
 
25
+ export type LoadSceneEvent = {
26
+ switcher: SceneSwitcher;
27
+ scene: AssetReference;
28
+ index: number;
29
+ }
30
+
25
31
  export class SceneSwitcher extends Behaviour {
26
32
 
27
33
  @serializable(AssetReference)
@@ -108,7 +114,17 @@ export class SceneSwitcher extends Behaviour {
108
114
  let wasUsingHistory = this.useHistory;
109
115
  try {
110
116
  this.useHistory = false;
111
- await this.tryLoadFromQueryParam();
117
+ let didResolve = false;
118
+ if (this.queryParameterName)
119
+ didResolve = await this.tryLoadFromQueryParam();
120
+ if (!didResolve) {
121
+ const state = _state.state;
122
+ if (state !== null && state.startsWith(this.guid)) {
123
+ const value = state.substr(this.guid.length + 2);
124
+ console.log(value);
125
+ await this.trySelectSceneFromValue(value);
126
+ }
127
+ }
112
128
  }
113
129
  finally {
114
130
  this.useHistory = wasUsingHistory;
@@ -192,7 +208,15 @@ export class SceneSwitcher extends Behaviour {
192
208
  const index = this._currentIndex = this.scenes?.indexOf(scene) ?? -1;
193
209
  this._currentScene = scene;
194
210
  try {
211
+ const loadStartEvt = new CustomEvent<LoadSceneEvent>("loadscene-start", { detail: { scene: scene, switcher: this, index: index } })
212
+ this.dispatchEvent(loadStartEvt);
195
213
  await scene.loadAssetAsync();
214
+ const finishedEvt = new CustomEvent<LoadSceneEvent>("loadscene-finished", { detail: { scene: scene, switcher: this, index: index } });
215
+ this.dispatchEvent(finishedEvt);
216
+ if (finishedEvt.defaultPrevented) {
217
+ if (debug) console.warn("Adding loaded scene prevented:", scene, finishedEvt);
218
+ return false;
219
+ }
196
220
  if (!scene.asset) {
197
221
  if (debug) console.warn("Failed loading scene:", scene);
198
222
  return false;
@@ -201,9 +225,18 @@ export class SceneSwitcher extends Behaviour {
201
225
  GameObject.add(scene.asset, this.gameObject);
202
226
  if (this.useSceneLighting)
203
227
  this.context.sceneLighting.enable(scene)
204
- // save the loaded scene as an url parameter
205
- if (this.queryParameterName?.length)
206
- setParamWithoutReload(this.queryParameterName, index.toString(), this.useHistory);
228
+ if (this.useHistory) {
229
+ // save the loaded scene as an url parameter
230
+ if (this.queryParameterName?.length)
231
+ setParamWithoutReload(this.queryParameterName, index.toString(), this.useHistory);
232
+ // or set the history state without updating the url parameter
233
+ else {
234
+ const lastState = history.state;
235
+ const stateName = this.guid + "::" + index;
236
+ if (lastState !== stateName)
237
+ history.pushState(stateName, "unused", location.href);
238
+ }
239
+ }
207
240
  return true;
208
241
  }
209
242
  }
@@ -103,6 +103,7 @@ export { PlayableDirector } from "../timeline/PlayableDirector";
103
103
  export { PlayerColor } from "../PlayerColor";
104
104
  export { PointerEventData } from "../ui/PointerEvents";
105
105
  export { PostProcessingHandler } from "../postprocessing/PostProcessingHandler";
106
+ export { QuickLookOverlay } from "../export/usdz/USDZExporter";
106
107
  export { RawImage } from "../ui/Image";
107
108
  export { Raycaster } from "../ui/Raycaster";
108
109
  export { Rect } from "../ui/RectTransform";