Needle Engine

Changes between version 3.7.1-alpha and 3.6.15
Files changed (18) hide show
  1. src/include/three/ARButton.js +6 -9
  2. src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +1 -3
  3. src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +4 -13
  4. src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +6 -19
  5. src/engine/debug/debug_console.ts +9 -36
  6. src/engine/debug/debug_overlay.ts +20 -19
  7. src/engine/engine_context.ts +4 -10
  8. src/engine/engine_element_extras.ts +0 -5
  9. src/engine/engine_element_loading.ts +1 -1
  10. src/engine/engine_element_overlay.ts +26 -45
  11. src/engine/engine_element.ts +2 -35
  12. src/engine/engine_input.ts +1 -6
  13. src/engine/extensions/NEEDLE_progressive.ts +8 -20
  14. src/engine-components/Renderer.ts +2 -4
  15. src/engine-components/export/usdz/ThreeUSDZExporter.ts +10 -5
  16. src/engine-components/export/usdz/USDZExporter.ts +5 -22
  17. src/engine-components/export/usdz/extensions/USDZText.ts +1 -3
  18. src/engine-components/webxr/WebXR.ts +3 -20
src/include/three/ARButton.js CHANGED
@@ -50,13 +50,11 @@
50
50
  // Workaround: seems WebXR Viewer has a non-standard behaviour when it comes to DOM Overlay and Canvas;
51
51
  // HTMLElements that are inside the Canvas element are not visible in the DOM Overlay.
52
52
  const isWebXRViewer = /WebXRViewer\//i.test( navigator.userAgent );
53
- const overlayElement = options.domOverlay.root;
54
- if (isWebXRViewer)
55
- {
53
+ if (isWebXRViewer) {
56
54
  if(options.domOverlay?.root) {
57
- originalDomOverlayParent = overlayElement.parentNode;
58
- if (originalDomOverlayParent)
59
- {
55
+ const overlayElement = options.domOverlay.root;
56
+ originalDomOverlayParent = overlayElement.parentElement;
57
+ if (originalDomOverlayParent) {
60
58
  console.log("Reparent DOM Overlay to body", overlayElement, overlayElement.style.display);
61
59
  // mozilla webxr does hide elements on session start
62
60
  // this is only necessary if we generated the overlay element
@@ -92,13 +90,12 @@
92
90
 
93
91
  button.textContent = 'START AR';
94
92
 
95
- const overlayElement = options.domOverlay.root;
96
93
  // if we reparented the DOM overlay, we're reverting it here
97
94
  if (originalDomOverlayParent)
98
- originalDomOverlayParent.appendChild(overlayElement);
95
+ originalDomOverlayParent.appendChild(options.domOverlay.root);
99
96
 
100
97
  if (ARButtonControlsDomOverlay)
101
- overlayElement.style.display = 'none';
98
+ options.domOverlay.root.style.display = 'none';
102
99
 
103
100
  currentSession = null;
104
101
 
src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts CHANGED
@@ -26,10 +26,7 @@
26
26
  continue;
27
27
 
28
28
  const clipName = audioSource.clip.split("/").pop();
29
- // TODO: ensure audio clip doesnt start with number
30
-
31
29
 
32
- // TODO: store clipname in file and use in onAfterSerialize instead of creating it again!
33
30
  if (!this.files.includes(audioSource.clip)) {
34
31
  this.files.push(audioSource.clip);
35
32
  }
@@ -49,6 +46,7 @@
49
46
  }
50
47
 
51
48
  async onAfterSerialize(context: USDZExporterContext) {
49
+ console.warn("onAfterSerialize", this);
52
50
  // write the files to the context.
53
51
  for (const file of this.files) {
54
52
 
src/engine-components/export/usdz/extensions/behavior/Behaviour.ts CHANGED
@@ -6,8 +6,8 @@
6
6
 
7
7
  export interface UsdzBehaviour {
8
8
  createBehaviours?(ext: BehaviorExtension, model: USDObject, context: IContext): void;
9
- beforeCreateDocument?(ext: BehaviorExtension, context: IContext): void | Promise<void>;
10
- afterCreateDocument?(ext: BehaviorExtension, context: IContext): void | Promise<void>
9
+ beforeCreateDocument?(ext: BehaviorExtension, context: IContext): void;
10
+ afterCreateDocument?(ext: BehaviorExtension, context: IContext): void;
11
11
  afterSerialize?(ext: BehaviorExtension, context: IContext): void;
12
12
  }
13
13
 
@@ -29,28 +29,19 @@
29
29
 
30
30
 
31
31
  onBeforeBuildDocument(context) {
32
- const beforeCreateDocumentPromises : Array<Promise<any>> = [];
33
32
  context.root.traverse(e => {
34
33
  GameObject.foreachComponent(e, (comp) => {
35
34
  const c = comp as unknown as UsdzBehaviour;
36
- // Test if the components has any of the behaviour type methods
37
35
  if (
38
36
  typeof c.createBehaviours === "function" ||
39
37
  typeof c.beforeCreateDocument === "function" ||
40
- typeof c.afterCreateDocument === "function" ||
41
- typeof c.afterSerialize === "function"
38
+ typeof c.afterCreateDocument === "function"
42
39
  ) {
43
40
  this.behaviourComponents.push(c);
44
- // run beforeCreateDocument. We run them in parallel if any of them is async because the order in which this is invoked on the components is not guaranteed anyways
45
- // (or at least no behaviour component should rely on another to have finished this method)
46
- const res = c.beforeCreateDocument?.call(c, this, context);
47
- if(res instanceof Promise) {
48
- beforeCreateDocumentPromises.push(res);
49
- }
41
+ c.beforeCreateDocument?.call(c, this, context);
50
42
  }
51
43
  }, false);
52
44
  });
53
- return Promise.all(beforeCreateDocumentPromises);
54
45
  }
55
46
 
56
47
  onExportObject(_object, model: USDObject, context) {
src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts CHANGED
@@ -12,7 +12,6 @@
12
12
  import { BehaviorExtension, UsdzBehaviour } from "./Behaviour";
13
13
  import { ActionBuilder, ActionModel, AuralMode, BehaviorModel, IBehaviorElement, MotionType, PlayAction, Space, TriggerBuilder } from "./BehavioursBuilder";
14
14
  import { AudioSource } from "../../../../AudioSource";
15
- import { NEEDLE_progressive } from "../../../../../engine/extensions/NEEDLE_progressive";
16
15
 
17
16
  export class ChangeTransformOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
18
17
 
@@ -151,9 +150,6 @@
151
150
  @serializable(Material)
152
151
  variantMaterial?: Material;
153
152
 
154
- @serializable()
155
- fadeDuration : number = 0;
156
-
157
153
  private _objectsWithThisMaterial: Renderer[] = [];
158
154
 
159
155
  awake() {
@@ -180,17 +176,9 @@
180
176
  private static _materialTriggersPerId: { [key: string]: ChangeMaterialOnClick[] } = {}
181
177
 
182
178
 
183
- async beforeCreateDocument(_ext: BehaviorExtension, _context) {
179
+ beforeCreateDocument(_ext: BehaviorExtension, _context) {
184
180
  this.targetModels = [];
185
181
  ChangeMaterialOnClick._materialTriggersPerId = {}
186
-
187
- // Ensure that the progressive textures have been loaded for all variants and materials
188
- if (this.materialToSwitch) {
189
- await NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, this.materialToSwitch, 0);
190
- }
191
- if(this.variantMaterial) {
192
- await NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, this.variantMaterial, 0);
193
- }
194
182
  }
195
183
 
196
184
 
@@ -238,19 +226,17 @@
238
226
  const start: ActionModel[] = [];
239
227
  const select: ActionModel[] = [];
240
228
 
241
- const fadeDuration = Math.max(0, this.fadeDuration);
242
-
243
229
  // the order here matters
244
230
  for (const target of this.targetModels) {
245
- const hideOriginal = ActionBuilder.fadeAction(target, fadeDuration, false);
231
+ const hideOriginal = ActionBuilder.fadeAction(target, 0, false);
246
232
  select.push(hideOriginal);
247
233
  }
248
234
  for (const v of otherVariants) {
249
- select.push(ActionBuilder.fadeAction(v, fadeDuration, false));
235
+ select.push(ActionBuilder.fadeAction(v, 0, false));
250
236
  }
251
237
  for (const v of myVariants) {
252
- start.push(ActionBuilder.fadeAction(v, fadeDuration, false));
253
- select.push(ActionBuilder.fadeAction(v, fadeDuration, true));
238
+ start.push(ActionBuilder.fadeAction(v, 0, false));
239
+ select.push(ActionBuilder.fadeAction(v, 0, true));
254
240
  }
255
241
 
256
242
  ext.addBehavior(new BehaviorModel("Select " + this.selfModel.name,
@@ -450,6 +436,7 @@
450
436
  newAudioSource.spatialBlend = 1;
451
437
  newAudioSource.volume = 1;
452
438
  newAudioSource.loop = false;
439
+
453
440
  this.target = newAudioSource;
454
441
  }
455
442
  }
src/engine/debug/debug_console.ts CHANGED
@@ -20,31 +20,19 @@
20
20
  if (isLocalNetwork())
21
21
  console.log("Add the ?console query parameter to the url to show the debug console (on mobile it will automatically open for local development when your get errors)");
22
22
  const isMobile = isMobileDevice();
23
- if (isMobile)
24
- {
23
+ if (isMobile) {
25
24
  beginWatchingLogs();
26
25
  createConsole(true);
27
- if (isMobile)
28
- {
26
+ if (isMobile) {
29
27
  const engineElement = document.querySelector("needle-engine");
30
- // setTimeout(() => {
31
- // const el = getConsoleElement();
32
- // console.log(el);
33
- // if (el) {
34
- // const overlay = engineElement["getAROverlayContainer"]?.call(engineElement);
35
- // overlay.appendChild(el);
36
- // }
37
- // }, 1000)
38
28
  engineElement?.addEventListener("enter-ar", () => {
39
29
  if (showConsole || consoleInstance || getErrorCount() > 0) {
40
30
  if (getParam("noerrors")) return;
41
- // TODO: this doesnt work with shadow DOM since styles are added to the HEAD by the console script
42
- // and therefor not applied anymore when the element is moved to the overlay container
43
- // const overlay = engineElement["getAROverlayContainer"]?.call(engineElement);
44
- // const consoleElement = getConsoleElement();
45
- // if (consoleElement && overlay) {
46
- // overlay.appendChild(consoleElement);
47
- // }
31
+ const overlay = engineElement["getAROverlayContainer"]?.call(engineElement);
32
+ const consoleElement = getConsoleElement();
33
+ if (consoleElement && overlay) {
34
+ overlay.appendChild(consoleElement);
35
+ }
48
36
  }
49
37
  });
50
38
  engineElement?.addEventListener("exit-ar", () => {
@@ -115,6 +103,7 @@
115
103
  isLoading = true;
116
104
 
117
105
  const script = document.createElement("script");
106
+ script.src = "https://unpkg.com/vconsole@latest/dist/vconsole.min.js";
118
107
  script.onload = () => {
119
108
  isLoading = false;
120
109
  isVisible = true;
@@ -126,21 +115,6 @@
126
115
  consoleHtmlElement[$defaultConsoleParent] = consoleHtmlElement.parentElement;
127
116
  consoleHtmlElement.style.position = "absolute";
128
117
  consoleHtmlElement.style.zIndex = Number.MAX_SAFE_INTEGER.toString();
129
- // const styleSheetList = document.styleSheets;
130
- // for (let i = 0; i < styleSheetList.length; i++) {
131
- // const styleSheet = styleSheetList[i];
132
- // const firstRule = styleSheet.cssRules[0] as CSSStyleRule;
133
- // if(firstRule && firstRule.selectorText === "#__vconsole") {
134
- // console.log("found vconsole style sheet");
135
- // const styleTag = document.createElement("style");
136
- // styleTag.innerHTML = "#__needleconsole {}";
137
- // for (let j = 0; j < styleSheet.cssRules.length; j++) {
138
- // const rule = styleSheet.cssRules[j] as CSSStyleRule;
139
- // styleTag.innerHTML += rule.cssText;
140
- // }
141
- // consoleHtmlElement.appendChild(styleTag);
142
- // }
143
- // }
144
118
  }
145
119
  consoleInstance.setSwitchPosition(20, 10);
146
120
  consoleSwitchButton = getConsoleSwitchButton();
@@ -189,13 +163,12 @@
189
163
  }
190
164
  }
191
165
  `;
192
- consoleHtmlElement?.prepend(styles);
166
+ document.head.appendChild(styles);
193
167
  if (startHidden === true)
194
168
  hideDebugConsole();
195
169
  }
196
170
 
197
171
  };
198
- script.src = "https://unpkg.com/vconsole@latest/dist/vconsole.min.js";
199
172
  document.body.appendChild(script);
200
173
  }
201
174
 
src/engine/debug/debug_overlay.ts CHANGED
@@ -97,7 +97,7 @@
97
97
  }
98
98
  message = newMessage;
99
99
  }
100
- if (!message || message.length <= 0) return;
100
+ if (message.length <= 0) return;
101
101
  showMessage(type, domElement, message);
102
102
  }
103
103
 
@@ -185,25 +185,26 @@
185
185
  const container = document.createElement("div");
186
186
  errorsMap.set(domElement, container);
187
187
  container.setAttribute("data-needle_engine_debug_overlay", "");
188
+ container.classList.add(arContainerClassName);
189
+ container.classList.add("desktop");
188
190
  container.classList.add("debug-container");
189
- container.style.cssText = `
190
- position: absolute;
191
- top: 0;
192
- right: 5px;
193
- padding-top: 0px;
194
- max-width: 70%;
195
- max-height: calc(100% - 5px);
196
- z-index: 1000;
197
- pointer-events: scroll;
198
- display: flex;
199
- align-items: end;
200
- flex-direction: column;
201
- color: white;
202
- overflow: auto;
203
- `
204
- if (domElement.shadowRoot)
205
- domElement.shadowRoot.appendChild(container);
206
- else domElement.appendChild(container);
191
+ container.style.position = "absolute";
192
+ container.style.top = "0";
193
+ container.style.right = "5px";
194
+ container.style.paddingTop = "0px";
195
+ container.style.maxWidth = "70%";
196
+ container.style.maxHeight = "calc(100% - 5px)";
197
+ container.style.zIndex = "1000";
198
+ // container.style.pointerEvents = "none";
199
+ container.style.pointerEvents = "scroll";
200
+ // container.style["-webkit-overflow-scrolling"] = "touch";
201
+ container.style.display = "flex";
202
+ container.style.alignItems = "end";
203
+ container.style.flexDirection = "column";
204
+ container.style.color = "white";
205
+ container.style.overflow = "auto";
206
+ // container.style.border = "1px solid red";
207
+ domElement.appendChild(container);
207
208
 
208
209
  const style = document.createElement("style");
209
210
  style.innerHTML = logsContainerStyles;
src/engine/engine_context.ts CHANGED
@@ -140,12 +140,6 @@
140
140
  /** the <needle-engine> HTML element */
141
141
  domElement: HTMLElement;
142
142
 
143
- appendHTMLElement(element: HTMLElement) {
144
- if (this.domElement.shadowRoot)
145
- return this.domElement.shadowRoot.appendChild(element);
146
- else return this.domElement.appendChild(element);
147
- }
148
-
149
143
  get resolutionScaleFactor() { return this._resolutionScaleFactor; }
150
144
  /** use to scale the resolution up or down of the renderer. default is 1 */
151
145
  set resolutionScaleFactor(val: number) {
@@ -320,7 +314,7 @@
320
314
  this._disposeCallbacks.push(() => this._intersectionObserver?.disconnect());
321
315
  }
322
316
 
323
- private createRenderer() {
317
+ private createRenderer(domElement?: HTMLElement) {
324
318
  this.renderer?.dispose();
325
319
 
326
320
  const params: WebGLRendererParameters = {
@@ -328,7 +322,7 @@
328
322
  };
329
323
 
330
324
  // get canvas already configured in the Needle Engine Web Component
331
- const canvas = this.domElement?.shadowRoot?.querySelector("canvas");
325
+ const canvas = domElement?.shadowRoot?.querySelector("canvas");
332
326
  if (canvas) params.canvas = canvas;
333
327
 
334
328
  this.renderer = new WebGLRenderer(params);
@@ -722,8 +716,8 @@
722
716
  this._sizeChanged = true;
723
717
 
724
718
  if (this._stats) {
725
- this._stats.showPanel(0);
726
- this.domElement.shadowRoot?.appendChild(this._stats.dom);
719
+ this._stats.showPanel(1);
720
+ this.domElement.appendChild(this._stats.dom);
727
721
  }
728
722
 
729
723
  if (debug)
src/engine/engine_element_extras.ts CHANGED
@@ -4,11 +4,6 @@
4
4
  DO NOT IMPORT ENGINE_ELEMENT FROM HERE
5
5
  */
6
6
 
7
- /**
8
- * Call with the name of an attribute that you want to receive change events for
9
- * This is useful for example if you want to add custom attributes to <needle-engine>
10
- * Use the addAttributeChangeCallback utility methods to register callback events
11
- */
12
7
  export async function registerObservableAttribute(name: string) {
13
8
  const { NeedleEngineHTMLElement } = await import("./engine_element");
14
9
  if (!NeedleEngineHTMLElement.observedAttributes.includes(name))
src/engine/engine_element_loading.ts CHANGED
@@ -100,7 +100,7 @@
100
100
  }
101
101
 
102
102
  onLoadingUpdate(progress: LoadingProgressArgs | ProgressEvent | number, message?: string) {
103
- if (!this._loadingElement?.parentNode) {
103
+ if (!this._loadingElement?.parentElement) {
104
104
  return;
105
105
  }
106
106
  // console.log(callback.name, callback.progress.loaded / callback.progress.total, callback.index + "/" + callback.count);
src/engine/engine_element_overlay.ts CHANGED
@@ -30,7 +30,7 @@
30
30
  this.currentSession = session;
31
31
  this.arContainer = overlayContainer;
32
32
 
33
- const arElements = context.domElement.shadowRoot!.querySelectorAll(`.${arContainerClassName}`);
33
+ const arElements = context.domElement.querySelectorAll(`.${arContainerClassName}`);
34
34
  arElements.forEach(el => {
35
35
  if (!el) return;
36
36
  if (el === this.arContainer) return;
@@ -76,7 +76,7 @@
76
76
  // Canvas is not in DOM anymore after AR using Mozilla XR
77
77
  const canvas = _context.renderer.domElement;
78
78
  if (canvas) {
79
- _context.domElement.shadowRoot?.prepend(canvas);
79
+ _context.domElement.insertBefore(canvas, _context.domElement.firstChild);
80
80
  }
81
81
 
82
82
  // Fix visibility
@@ -92,45 +92,35 @@
92
92
  }
93
93
 
94
94
  findOrCreateARContainer(element: HTMLElement): HTMLElement {
95
- if(debug) console.log("findOrCreateARContainer");
96
95
  // search in the needle-engine element
97
96
  if (element.classList.contains(arContainerClassName)) {
98
- if(debug) console.log("Found overlay container in needle-engine element");
99
97
  return element;
100
98
  }
101
- if (element.shadowRoot) {
102
- const el = element.shadowRoot!.querySelector(`.${arContainerClassName}`);
103
- if (el) {
104
- if(debug) console.log("Found overlay container in needle-engine element");
105
- return el as HTMLElement;
106
- };
99
+ if (element.children) {
100
+ for (let i = 0; i < element.children.length; i++) {
101
+ const ch = element.children[i] as HTMLElement;
102
+ if (!ch || !ch.classList) continue;
103
+ if (ch.classList.contains(arContainerClassName)) {
104
+ return ch;
105
+ }
106
+ }
107
107
  }
108
108
 
109
109
  // search in document as well; "ar" element could live outside needle-engine element
110
110
  const arElements = document.getElementsByClassName(arContainerClassName);
111
- if (arElements && arElements.length > 0){
112
- if(debug) console.log("Found overlay container in document");
111
+ if (arElements && arElements.length > 0)
113
112
  return arElements[0] as HTMLElement;
114
- }
115
113
 
116
114
  if (debug)
117
115
  console.log("No overlay container found in document - generating new ony");
118
116
  const el = document.createElement("div");
119
117
  el.classList.add(arContainerClassName);
120
- el.style.cssText = `
121
- position: fixed;
122
- top: 0;
123
- left: 0;
124
- width: 100%;
125
- height: 100%;
126
- display: flex;
127
- visibility: visible;
128
- z-index: 9999;
129
- pointer-events: none;
130
- // background: rgba(0,0,0,1);
131
- `;
132
- if(debug) this.createFallbackCloseARButton(element);
133
- return this.appendElement(el, element) as HTMLElement;
118
+ el.style.position = "absolute";
119
+ el.style.width = "100%";
120
+ el.style.height = "100%";
121
+ el.style.display = "flex";
122
+ el.style.visibility = "visible";
123
+ return element.appendChild(el);
134
124
  }
135
125
 
136
126
  private onRequestedEndAR() {
@@ -145,33 +135,24 @@
145
135
  }
146
136
 
147
137
  private createFallbackCloseARButton(element: HTMLElement) {
148
- const quitARSlot = document.createElement("slot");
149
- quitARSlot.setAttribute("name", "quit-ar");
150
- this.appendElement(quitARSlot, element);
151
- if(debug) quitARSlot.addEventListener('click', () => console.log("Clicked fallback close button"));
152
- quitARSlot.addEventListener('click', this.closeARCallback);
153
- this._createdAROnlyElements.push(quitARSlot);
154
- // for mozilla XR reparenting we have to make sure the close button is clickable so we set it on the element directly
155
- // it's in general perhaps more safe to set it on the element to ensure it's clickable
156
- quitARSlot.style.pointerEvents = "auto";
157
-
158
138
  var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
159
- svg.classList.add("quit-ar-button");
160
139
  svg.setAttribute('width', "38px");
161
140
  svg.setAttribute('height', "38px");
162
- quitARSlot.appendChild(svg);
141
+ svg.style.position = 'absolute';
142
+ svg.style.right = '20px';
143
+ svg.style.top = '40px';
144
+ svg.style.zIndex = '9999';
145
+ svg.style.pointerEvents = 'auto';
146
+ svg.addEventListener('click', this.closeARCallback);
147
+ element.appendChild(svg);
148
+ this._createdAROnlyElements.push(svg);
163
149
 
164
150
  var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
165
151
  path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28');
166
152
  path.setAttribute('stroke', '#ddd');
167
153
  path.setAttribute('stroke-width', "3px");
168
154
  svg.appendChild(path);
169
- if(debug) console.log("Created fallback close button", svg, element);
155
+ this._createdAROnlyElements.push(path);
170
156
  }
171
157
 
172
- private appendElement(element: Element, parent: HTMLElement) {
173
- if(parent.shadowRoot) return parent.shadowRoot.appendChild(element);
174
- return parent.appendChild(element);
175
- }
176
-
177
158
  }
src/engine/engine_element.ts CHANGED
@@ -91,41 +91,9 @@
91
91
  constructor() {
92
92
  super();
93
93
  this._overlay_ar = new AROverlayHandler();
94
+ this._context = new Context({ domElement: this });
94
95
  // TODO: do we want to rename this event?
95
96
  this.addEventListener("ready", this.onReady);
96
-
97
- this.attachShadow({mode: 'open'});
98
- const template = document.createElement('template');
99
- template.innerHTML =
100
- `<style>
101
- :host {
102
- position: relative;
103
- display: block;
104
- width: 600px;
105
- height: 300px;
106
- }
107
- :host canvas {
108
- position: absolute;
109
- user-select: none;
110
- touch-action: none;
111
- }
112
- :host slot[name="quit-ar"]:hover {
113
- cursor: pointer;
114
- }
115
- :host .quit-ar-button {
116
- position: absolute;
117
- top: 40px;
118
- right: 20px;
119
- z-index: 9999;
120
- }
121
- </style>
122
- <canvas></canvas>
123
- `;
124
-
125
- if (this.shadowRoot)
126
- this.shadowRoot.appendChild(template.content.cloneNode(true));
127
-
128
- this._context = new Context({ domElement: this });
129
97
  this.addEventListener("error", this.onError);
130
98
  }
131
99
 
@@ -169,7 +137,7 @@
169
137
  }
170
138
 
171
139
  attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
172
- if (debug) console.log("attributeChangedCallback", name, _oldValue, newValue);
140
+ // console.log(name, oldValue, newValue);
173
141
  switch (name) {
174
142
  case "src":
175
143
  if (debug) console.warn("src changed to type:", typeof newValue, ", from \"", _oldValue, "\" to \"", newValue, "\"")
@@ -445,7 +413,6 @@
445
413
  this.classList.add(arSessionActiveClassName);
446
414
  this.classList.remove(desktopSessionActiveClassName);
447
415
  const arContainer = this.getAROverlayContainer();
448
- if(debug) console.warn("onSetupAR:", arContainer)
449
416
  if (arContainer) {
450
417
  arContainer.classList.add(arSessionActiveClassName);
451
418
  arContainer.classList.remove(desktopSessionActiveClassName);
src/engine/engine_input.ts CHANGED
@@ -397,12 +397,7 @@
397
397
  // if(evt.target === this.context.renderer.domElement) return true;
398
398
  // const css = window.getComputedStyle(evt.target as HTMLElement);
399
399
  // if(css.pointerEvents === "all") return false;
400
-
401
- // We only check the target elements here since the canvas may be overlapped by other elements
402
- // in which case we do not want to use the input (e.g. if a HTML element is being triggered)
403
- if(evt.target === this.context.renderer.domElement) return true;
404
- if(evt.target === this.context.domElement) return true;
405
- return false;
400
+ return evt.target === this.context.renderer.domElement;
406
401
  }
407
402
 
408
403
  private keysPressed: { [key: KeyCode | string]: { pressed: boolean, frame: number, startFrame: number, key: string, code: KeyCode | string } } = {};
src/engine/extensions/NEEDLE_progressive.ts CHANGED
@@ -39,38 +39,30 @@
39
39
  export class NEEDLE_progressive implements GLTFLoaderPlugin {
40
40
 
41
41
  static assignTextureLOD(context: Context, source: SourceIdentifier | undefined, material: Material, level: number = 0) {
42
- if (!material) return Promise.resolve(null);
42
+ if (!material) return;
43
43
 
44
- const promises: Promise<Texture | null>[] = [];
45
-
46
44
  for (let slot of Object.keys(material)) {
47
45
  const val = material[slot];
48
- if (val?.isTexture) {
49
- const task = this.assignTextureLODForSlot(context, source, material, level, slot, val);
50
- promises.push(task);
51
- }
46
+ if (val?.isTexture)
47
+ this.assignTextureLODForSlot(context, source, material, level, slot, val);
52
48
  }
53
49
 
54
50
  if (material instanceof RawShaderMaterial) {
55
51
  // iterate uniforms
56
52
  for (let slot of Object.keys(material.uniforms)) {
57
53
  const val = material.uniforms[slot].value;
58
- if (val?.isTexture) {
59
- const task = this.assignTextureLODForSlot(context, source, material, level, slot, val);
60
- promises.push(task);
61
- }
54
+ if (val?.isTexture)
55
+ this.assignTextureLODForSlot(context, source, material, level, slot, val);
62
56
  }
63
57
  }
64
-
65
- return promises;
66
58
  }
67
59
 
68
- private static assignTextureLODForSlot(context: Context, source: SourceIdentifier | undefined, material: Material, level: number, slot: string, val: any): Promise<Texture | null> {
69
- if (val?.isTexture !== true) return Promise.resolve(null);
60
+ private static assignTextureLODForSlot(context: Context, source: SourceIdentifier | undefined, material: Material, level: number, slot: string, val: any) {
61
+ if (val?.isTexture !== true) return;
70
62
 
71
63
  if (debug) console.log("-----------\n", "FIND", material.name, slot, val?.name, val?.userData, val, material);
72
64
 
73
- return NEEDLE_progressive.getOrLoadTexture(context, source, material, slot, val, level).then(t => {
65
+ NEEDLE_progressive.getOrLoadTexture(context, source, material, slot, val, level).then(t => {
74
66
  if (t?.isTexture === true) {
75
67
 
76
68
  if (debug) console.log("Assign LOD", material.name, slot, t.name, t["guid"], material, "Prev:", val, "Now:", t, "\n--------------");
@@ -91,11 +83,7 @@
91
83
  }
92
84
  entry.lod0 = t;
93
85
  }
94
-
95
- return t;
96
86
  }
97
-
98
- return null;
99
87
  });
100
88
  }
101
89
 
src/engine-components/Renderer.ts CHANGED
@@ -184,7 +184,7 @@
184
184
  this.changed = true;
185
185
  }
186
186
 
187
- private getMaterial(index: number) : Material | null {
187
+ private getMaterial(index: number) {
188
188
  index = this.resolveIndex(index);
189
189
  if (index < 0) return null;
190
190
  const obj = this._targets;
@@ -683,11 +683,9 @@
683
683
  if (debugProgressiveLoading) {
684
684
  console.log("Load material LOD", material.name);
685
685
  }
686
- return NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, material);
686
+ NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, material);
687
687
  }
688
688
  }
689
-
690
- return Promise.resolve(true);
691
689
  }
692
690
 
693
691
  applySettings(go: THREE.Object3D) {
src/engine-components/export/usdz/ThreeUSDZExporter.ts CHANGED
@@ -440,11 +440,11 @@
440
440
  const materials = context.materials;
441
441
  const textures = context.textures;
442
442
 
443
- await invokeAll( context, 'onBeforeBuildDocument' );
443
+ invokeAll( context, 'onBeforeBuildDocument' );
444
444
 
445
445
  traverseVisible( scene, context.document, context );
446
446
 
447
- await invokeAll( context, 'onAfterBuildDocument' );
447
+ invokeAll( context, 'onAfterBuildDocument' );
448
448
 
449
449
  parseDocument( context );
450
450
 
@@ -704,10 +704,15 @@
704
704
  if ( typeof ext[ name ] === 'function' ) {
705
705
 
706
706
  const method = ext[ name ];
707
- const res = method.call( ext, context, writer );
708
- if(res instanceof Promise) {
709
- await res;
707
+
708
+ const isAsync = method.constructor.name === "AsyncFunction";
709
+
710
+ if ( isAsync ) {
711
+ await method.call( ext, context, writer );
712
+ } else {
713
+ method.call( ext, context, writer );
710
714
  }
715
+
711
716
  }
712
717
 
713
718
  }
src/engine-components/export/usdz/USDZExporter.ts CHANGED
@@ -16,7 +16,6 @@
16
16
  import { BehaviorExtension } from "./extensions/behavior/Behaviour";
17
17
  import { AudioExtension } from "./extensions/behavior/AudioExtension";
18
18
  import { TextExtension } from "./extensions/USDZText";
19
- import { Renderer } from "../../Renderer"
20
19
 
21
20
  const debug = getParam("debugusdz");
22
21
 
@@ -137,7 +136,7 @@
137
136
  }
138
137
 
139
138
  async exportAsync() {
140
-
139
+
141
140
  let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
142
141
  if (!hasProLicense()) name += "-MadeWithNeedle";
143
142
  name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
@@ -147,7 +146,7 @@
147
146
 
148
147
  // see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
149
148
  const overlay = this.buildQuicklookOverlay();
150
- if (debug) console.log(overlay);
149
+ if(debug) console.log(overlay);
151
150
  const callToAction = overlay.callToAction ? encodeURIComponent(overlay.callToAction) : "";
152
151
  const checkoutTitle = overlay.checkoutTitle ? encodeURIComponent(overlay.checkoutTitle) : "";
153
152
  const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
@@ -168,22 +167,6 @@
168
167
 
169
168
  if (!this.objectToExport) return;
170
169
 
171
- // trigger progressive textures to be loaded:
172
- const renderers = GameObject.getComponentsInChildren(this.objectToExport, Renderer);
173
- const progressiveLoading = new Array<Promise<any>>();
174
- for (const rend of renderers) {
175
- for (const mat of rend.sharedMaterials) {
176
- if (mat) {
177
- const task = rend.loadProgressiveTextures(mat);
178
- if (task instanceof Promise)
179
- progressiveLoading.push(task);
180
- }
181
- }
182
- }
183
- if (debug) showBalloonMessage("Load textures: " + progressiveLoading.length);
184
- await Promise.all(progressiveLoading);
185
- if(debug) showBalloonMessage("Load textures: done");
186
-
187
170
  // make sure we apply the AR scale
188
171
  if (this.webARSessionRoot) {
189
172
  const scene = this.webARSessionRoot.gameObject;
@@ -209,7 +192,7 @@
209
192
 
210
193
  //@ts-ignore
211
194
  exporter.debug = debug;
212
-
195
+
213
196
  // sanitize anchoring types
214
197
  if (this.anchoringType !== "plane" && this.anchoringType !== "none" && this.anchoringType !== "image" && this.anchoringType !== "face")
215
198
  this.anchoringType = "plane";
@@ -243,7 +226,7 @@
243
226
 
244
227
  // see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
245
228
  const overlay = this.buildQuicklookOverlay();
246
- if (debug) console.log(overlay);
229
+ if(debug) console.log(overlay);
247
230
  const callToAction = overlay.callToAction ? encodeURIComponent(overlay.callToAction) : "";
248
231
  const checkoutTitle = overlay.checkoutTitle ? encodeURIComponent(overlay.checkoutTitle) : "";
249
232
  const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
@@ -342,7 +325,7 @@
342
325
  else {
343
326
  this.webxr.createARButton = false;
344
327
  this.webxr.createVRButton = false;
345
- let container = this.context.domElement.shadowRoot!.querySelector(".webxr-buttons");
328
+ let container = window.document.querySelector(".webxr-buttons");
346
329
  if (!container) {
347
330
  container = document.createElement("div");
348
331
  container.classList.add("webxr-buttons");
src/engine-components/export/usdz/extensions/USDZText.ts CHANGED
@@ -164,9 +164,7 @@
164
164
 
165
165
  // model.matrix.scale(new Vector3(100, 100, 100));
166
166
  newModel.addEventListener("serialize", (writer: USDWriter, _context: USDZExporterContext) => {
167
- let txt = text.text;
168
- txt = txt.replace(/\n/g, "\\n");
169
- const textObj = TextBuilder.multiLine(txt, width, height, HorizontalAlignment.center, VerticalAlignment.bottom, TextWrapMode.flowing);
167
+ const textObj = TextBuilder.multiLine(text.text, width, height, HorizontalAlignment.center, VerticalAlignment.bottom, TextWrapMode.flowing);
170
168
  this.setTextAlignment(textObj, text.alignment);
171
169
  this.setOverflow(textObj, text);
172
170
  if (newModel.material)
src/engine-components/webxr/WebXR.ts CHANGED
@@ -18,7 +18,6 @@
18
18
  import { XRFlag, XRState, XRStateFlag } from "../XRFlag";
19
19
  import { showBalloonWarning } from '../../engine/debug';
20
20
 
21
- const debugWebXR = getParam("debugwebxr");
22
21
 
23
22
  export async function detectARSupport() {
24
23
  if(isMozillaXR()) return true;
@@ -239,26 +238,10 @@
239
238
  let arButton, vrButton;
240
239
  const buttonsContainer = document.createElement('div');
241
240
  buttonsContainer.classList.add("webxr-buttons");
242
- buttonsContainer.style.cssText = `
243
- position: fixed;
244
- bottom: 21px;
245
- left: 50%;
246
- transform: translate(-50%, 0%);
247
- z-index: 1000;
248
-
249
- display: flex;
250
- flex-direction: row;
251
- justify-content: center;
252
- align-items: flex-start;
253
- gap: 10px;
254
- `;
255
- this.context.appendHTMLElement(buttonsContainer);
241
+ this.context.domElement.append(buttonsContainer);
256
242
 
257
- const forceButtons = debugWebXR;
258
- if(debugWebXR) console.log("ARSupported?", arSupported, "VRSupported?", vrSupported);
259
-
260
243
  // AR support
261
- if (forceButtons || (this.createARButton && this.enableAR && arSupported))
244
+ if (this.enableAR && this.createARButton && arSupported)
262
245
  {
263
246
  arButton = WebXR.createARButton(this);
264
247
  this._arButton = arButton;
@@ -266,7 +249,7 @@
266
249
  }
267
250
 
268
251
  // VR support
269
- if (forceButtons || (this.createVRButton && this.enableVR && vrSupported)) {
252
+ if (this.createVRButton && this.enableVR && vrSupported) {
270
253
  vrButton = WebXR.createVRButton(this);
271
254
  this._vrButton = vrButton;
272
255
  buttonsContainer.appendChild(vrButton);