@@ -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
|
-
|
54
|
-
if (isWebXRViewer)
|
55
|
-
{
|
53
|
+
if (isWebXRViewer) {
|
56
54
|
if(options.domOverlay?.root) {
|
57
|
-
|
58
|
-
|
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(
|
95
|
+
originalDomOverlayParent.appendChild(options.domOverlay.root);
|
99
96
|
|
100
97
|
if (ARButtonControlsDomOverlay)
|
101
|
-
|
98
|
+
options.domOverlay.root.style.display = 'none';
|
102
99
|
|
103
100
|
currentSession = null;
|
104
101
|
|
@@ -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
|
|
@@ -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
|
10
|
-
afterCreateDocument?(ext: BehaviorExtension, context: IContext): 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
|
-
|
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) {
|
@@ -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
|
-
|
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,
|
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,
|
235
|
+
select.push(ActionBuilder.fadeAction(v, 0, false));
|
250
236
|
}
|
251
237
|
for (const v of myVariants) {
|
252
|
-
start.push(ActionBuilder.fadeAction(v,
|
253
|
-
select.push(ActionBuilder.fadeAction(v,
|
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
|
}
|
@@ -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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
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
|
|
@@ -97,7 +97,7 @@
|
|
97
97
|
}
|
98
98
|
message = newMessage;
|
99
99
|
}
|
100
|
-
if (
|
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.
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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;
|
@@ -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 =
|
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(
|
726
|
-
this.domElement.
|
719
|
+
this._stats.showPanel(1);
|
720
|
+
this.domElement.appendChild(this._stats.dom);
|
727
721
|
}
|
728
722
|
|
729
723
|
if (debug)
|
@@ -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))
|
@@ -100,7 +100,7 @@
|
|
100
100
|
}
|
101
101
|
|
102
102
|
onLoadingUpdate(progress: LoadingProgressArgs | ProgressEvent | number, message?: string) {
|
103
|
-
if (!this._loadingElement?.
|
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);
|
@@ -30,7 +30,7 @@
|
|
30
30
|
this.currentSession = session;
|
31
31
|
this.arContainer = overlayContainer;
|
32
32
|
|
33
|
-
const arElements = context.domElement.
|
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.
|
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.
|
102
|
-
|
103
|
-
|
104
|
-
if
|
105
|
-
|
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.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
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
|
-
|
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
|
}
|
@@ -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
|
-
|
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);
|
@@ -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 } } = {};
|
@@ -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
|
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
|
-
|
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
|
-
|
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)
|
69
|
-
if (val?.isTexture !== true) return
|
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
|
-
|
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
|
|
@@ -184,7 +184,7 @@
|
|
184
184
|
this.changed = true;
|
185
185
|
}
|
186
186
|
|
187
|
-
private getMaterial(index: number)
|
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
|
-
|
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) {
|
@@ -440,11 +440,11 @@
|
|
440
440
|
const materials = context.materials;
|
441
441
|
const textures = context.textures;
|
442
442
|
|
443
|
-
|
443
|
+
invokeAll( context, 'onBeforeBuildDocument' );
|
444
444
|
|
445
445
|
traverseVisible( scene, context.document, context );
|
446
446
|
|
447
|
-
|
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
|
-
|
708
|
-
|
709
|
-
|
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
|
}
|
@@ -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
|
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
|
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 =
|
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");
|
@@ -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
|
-
|
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)
|
@@ -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
|
-
|
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 (
|
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 (
|
252
|
+
if (this.createVRButton && this.enableVR && vrSupported) {
|
270
253
|
vrButton = WebXR.createVRButton(this);
|
271
254
|
this._vrButton = vrButton;
|
272
255
|
buttonsContainer.appendChild(vrButton);
|