@needle-tools/engine 4.5.0 → 4.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{needle-engine.bundle-8e64768b.js → needle-engine.bundle-99bb72c3.js} +7 -7
- package/dist/{needle-engine.bundle-4e21a0eb.min.js → needle-engine.bundle-ac883b5a.min.js} +2 -2
- package/dist/{needle-engine.bundle-0293e87a.light.js → needle-engine.bundle-adcbea18.light.js} +7 -7
- package/dist/{needle-engine.bundle-1679a09a.light.min.js → needle-engine.bundle-b57b2055.light.min.js} +2 -2
- package/dist/{needle-engine.bundle-2f187b48.light.umd.cjs → needle-engine.bundle-bcb5a31b.light.umd.cjs} +4 -4
- package/dist/{needle-engine.bundle-4366d297.umd.cjs → needle-engine.bundle-caf79082.umd.cjs} +4 -4
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.light.js +2 -2
- package/dist/needle-engine.light.min.js +1 -1
- package/dist/needle-engine.light.umd.cjs +1 -1
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_loaders.callbacks.d.ts +1 -0
- package/lib/engine/engine_loaders.callbacks.js +1 -0
- package/lib/engine/engine_loaders.callbacks.js.map +1 -1
- package/lib/engine/engine_utils_format.js +2 -2
- package/lib/engine/engine_utils_format.js.map +1 -1
- package/package.json +1 -1
- package/plugins/vite/build-pipeline.js +22 -6
- package/src/engine/engine_loaders.callbacks.ts +1 -0
- package/src/engine/engine_utils_format.ts +2 -2
- package/lib/engine/engine.d.ts +0 -4
- package/lib/engine/engine.js +0 -12
- package/lib/engine/engine.js.map +0 -1
- package/lib/engine/engine_element.d.ts +0 -113
- package/lib/engine/engine_element.js +0 -833
- package/lib/engine/engine_element.js.map +0 -1
- package/lib/engine/engine_element_attributes.d.ts +0 -72
- package/lib/engine/engine_element_attributes.js +0 -2
- package/lib/engine/engine_element_attributes.js.map +0 -1
- package/lib/engine/engine_element_extras.d.ts +0 -6
- package/lib/engine/engine_element_extras.js +0 -14
- package/lib/engine/engine_element_extras.js.map +0 -1
- package/lib/engine/engine_element_loading.d.ts +0 -44
- package/lib/engine/engine_element_loading.js +0 -350
- package/lib/engine/engine_element_loading.js.map +0 -1
- package/lib/engine/engine_element_overlay.d.ts +0 -21
- package/lib/engine/engine_element_overlay.js +0 -167
- package/lib/engine/engine_element_overlay.js.map +0 -1
- package/lib/engine/engine_scenetools.d.ts +0 -50
- package/lib/engine/engine_scenetools.js +0 -322
- package/lib/engine/engine_scenetools.js.map +0 -1
- package/lib/engine/engine_web_api.d.ts +0 -12
- package/lib/engine/engine_web_api.js +0 -113
- package/lib/engine/engine_web_api.js.map +0 -1
- package/lib/engine-components/FlyControls.d.ts +0 -10
- package/lib/engine-components/FlyControls.js +0 -29
- package/lib/engine-components/FlyControls.js.map +0 -1
|
@@ -1,833 +0,0 @@
|
|
|
1
|
-
import { AgXToneMapping, LinearToneMapping, NeutralToneMapping, NoToneMapping } from "three";
|
|
2
|
-
import { registerLoader } from "../engine/engine_gltf.js";
|
|
3
|
-
import { isDevEnvironment, showBalloonWarning } from "./debug/index.js";
|
|
4
|
-
import { PUBLIC_KEY, VERSION } from "./engine_constants.js";
|
|
5
|
-
import { calculateProgress01, EngineLoadingView } from "./engine_element_loading.js";
|
|
6
|
-
import { arContainerClassName, AROverlayHandler } from "./engine_element_overlay.js";
|
|
7
|
-
import { hasCommercialLicense } from "./engine_license.js";
|
|
8
|
-
import { setDracoDecoderPath, setDracoDecoderType, setKtx2TranscoderPath } from "./engine_loaders.js";
|
|
9
|
-
import { NeedleLoader } from "./engine_scenetools.js";
|
|
10
|
-
import { Context } from "./engine_setup.js";
|
|
11
|
-
import { getParam } from "./engine_utils.js";
|
|
12
|
-
import { RGBAColor } from "./js-extensions/RGBAColor.js";
|
|
13
|
-
import { ensureFonts } from "./webcomponents/fonts.js";
|
|
14
|
-
//
|
|
15
|
-
// registering loader here too to make sure it's imported when using engine via vanilla js
|
|
16
|
-
registerLoader(NeedleLoader);
|
|
17
|
-
const debug = getParam("debugwebcomponent");
|
|
18
|
-
const htmlTagName = "needle-engine";
|
|
19
|
-
const vrContainerClassName = "vr";
|
|
20
|
-
const desktopContainerClassname = "desktop";
|
|
21
|
-
const knownClasses = [arContainerClassName, vrContainerClassName, desktopContainerClassname];
|
|
22
|
-
const arSessionActiveClassName = "ar-session-active";
|
|
23
|
-
const desktopSessionActiveClassName = "desktop-session-active";
|
|
24
|
-
const observedAttributes = [
|
|
25
|
-
"public-key",
|
|
26
|
-
"version",
|
|
27
|
-
"hash",
|
|
28
|
-
"src",
|
|
29
|
-
"camera-controls",
|
|
30
|
-
"loadstart",
|
|
31
|
-
"progress",
|
|
32
|
-
"loadfinished",
|
|
33
|
-
"dracoDecoderPath",
|
|
34
|
-
"dracoDecoderType",
|
|
35
|
-
"ktx2DecoderPath",
|
|
36
|
-
"tone-mapping",
|
|
37
|
-
"tone-mapping-exposure",
|
|
38
|
-
"background-blurriness",
|
|
39
|
-
"background-color",
|
|
40
|
-
];
|
|
41
|
-
// https://developers.google.com/web/fundamentals/web-components/customelements
|
|
42
|
-
/**
|
|
43
|
-
* <needle-engine> web component. See {@link NeedleEngineAttributes} attributes for supported attributes
|
|
44
|
-
* The needle engine web component creates and manages a needle engine context which is responsible for rendering a 3D scene using threejs.
|
|
45
|
-
* The needle engine context is created when the src attribute is set and disposed when the needle engine is removed from the document (you can prevent this by setting the keep-alive attribute to true).
|
|
46
|
-
* The needle engine context is accessible via the context property on the needle engine element (e.g. document.querySelector("needle-engine").context).
|
|
47
|
-
* @link https://engine.needle.tools/docs/reference/needle-engine-attributes
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* <needle-engine src="https://example.com/scene.glb"></needle-engine>
|
|
51
|
-
* @example
|
|
52
|
-
* <needle-engine src="https://example.com/scene.glb" camera-controls="false"></needle-engine>
|
|
53
|
-
*/
|
|
54
|
-
export class NeedleEngineHTMLElement extends HTMLElement {
|
|
55
|
-
static get observedAttributes() {
|
|
56
|
-
return observedAttributes;
|
|
57
|
-
}
|
|
58
|
-
get loadingProgress01() { return this._loadingProgress01; }
|
|
59
|
-
get loadingFinished() { return this.loadingProgress01 > .999; }
|
|
60
|
-
/**
|
|
61
|
-
* If set to false the camera controls are disabled. Default is true.
|
|
62
|
-
* @type {boolean | null}
|
|
63
|
-
* @memberof NeedleEngineAttributes
|
|
64
|
-
* @example
|
|
65
|
-
* <needle-engine camera-controls="false"></needle-engine>
|
|
66
|
-
* @example
|
|
67
|
-
* <needle-engine camera-controls="true"></needle-engine>
|
|
68
|
-
* @example
|
|
69
|
-
* <needle-engine camera-controls></needle-engine>
|
|
70
|
-
* @example
|
|
71
|
-
* <needle-engine></needle-engine>
|
|
72
|
-
* @returns {boolean | null} if the attribute is not set it returns null
|
|
73
|
-
*/
|
|
74
|
-
get cameraControls() {
|
|
75
|
-
const attr = this.getAttribute("camera-controls");
|
|
76
|
-
if (attr == null)
|
|
77
|
-
return null;
|
|
78
|
-
if (attr === null || attr === "False" || attr === "false" || attr === "0" || attr === "none")
|
|
79
|
-
return false;
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Get the current context for this web component instance. The context is created when the src attribute is set and the loading has finished.
|
|
84
|
-
* The context is disposed when the needle engine is removed from the document (you can prevent this by setting the keep-alive attribute to true).
|
|
85
|
-
* @returns {Promise<Context>} a promise that resolves to the context when the loading has finished
|
|
86
|
-
*/
|
|
87
|
-
getContext() {
|
|
88
|
-
return new Promise((res, _rej) => {
|
|
89
|
-
if (this._context && this.loadingFinished) {
|
|
90
|
-
res(this._context);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
const cb = () => {
|
|
94
|
-
this.removeEventListener("loadfinished", cb);
|
|
95
|
-
if (this._context && this.loadingFinished) {
|
|
96
|
-
res(this._context);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
this.addEventListener("loadfinished", cb);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Get the context that is created when the src attribute is set and the loading has finished.
|
|
105
|
-
*/
|
|
106
|
-
get context() { return this._context; }
|
|
107
|
-
_context;
|
|
108
|
-
_overlay_ar;
|
|
109
|
-
_loadingProgress01 = 0;
|
|
110
|
-
_loadingView;
|
|
111
|
-
_previousSrc = null;
|
|
112
|
-
/** set to true after <needle-engine> did load completely at least once. Set to false when <needle-engine> is removed from the document */
|
|
113
|
-
_didFullyLoad = false;
|
|
114
|
-
constructor() {
|
|
115
|
-
super();
|
|
116
|
-
this._overlay_ar = new AROverlayHandler();
|
|
117
|
-
// TODO: do we want to rename this event?
|
|
118
|
-
this.addEventListener("ready", this.onReady);
|
|
119
|
-
ensureFonts();
|
|
120
|
-
this.attachShadow({ mode: 'open' });
|
|
121
|
-
const template = document.createElement('template');
|
|
122
|
-
template.innerHTML = `<style>
|
|
123
|
-
@import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap');
|
|
124
|
-
|
|
125
|
-
:host {
|
|
126
|
-
position: absolute;
|
|
127
|
-
display: block;
|
|
128
|
-
width: max(600px, 100%);
|
|
129
|
-
height: max(300px, 100%);
|
|
130
|
-
touch-action: none;
|
|
131
|
-
|
|
132
|
-
-webkit-tap-highlight-color: transparent;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
@media (max-width: 600px) {
|
|
136
|
-
:host {
|
|
137
|
-
width: 100%;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
@media (max-height: 300px) {
|
|
141
|
-
:host {
|
|
142
|
-
height: 100%;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
:host > div.canvas-wrapper {
|
|
147
|
-
width: 100%;
|
|
148
|
-
height: 100%;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
:host canvas {
|
|
152
|
-
position: absolute;
|
|
153
|
-
user-select: none;
|
|
154
|
-
-webkit-user-select: none;
|
|
155
|
-
|
|
156
|
-
/** allow touch panning but no pinch zoom **/
|
|
157
|
-
/** but this doesnt work yet:
|
|
158
|
-
* touch-action: pan-x, pan-y;
|
|
159
|
-
**/
|
|
160
|
-
|
|
161
|
-
-webkit-touch-callout: none;
|
|
162
|
-
-webkit-user-drag: none;
|
|
163
|
-
-webkit-user-modify: none;
|
|
164
|
-
|
|
165
|
-
left: 0;
|
|
166
|
-
top: 0;
|
|
167
|
-
}
|
|
168
|
-
:host .content {
|
|
169
|
-
position: absolute;
|
|
170
|
-
top: 0;
|
|
171
|
-
width: 100%;
|
|
172
|
-
height: 100%;
|
|
173
|
-
visibility: visible;
|
|
174
|
-
z-index: 500; /* < must be less than the webxr buttons element */
|
|
175
|
-
pointer-events: none;
|
|
176
|
-
}
|
|
177
|
-
:host .overlay-content {
|
|
178
|
-
position: absolute;
|
|
179
|
-
user-select: auto;
|
|
180
|
-
pointer-events: all;
|
|
181
|
-
}
|
|
182
|
-
:host slot[name="quit-ar"]:hover {
|
|
183
|
-
cursor: pointer;
|
|
184
|
-
}
|
|
185
|
-
:host .quit-ar-button {
|
|
186
|
-
position: absolute;
|
|
187
|
-
// top: env(titlebar-area-y); /** this doesnt work **/
|
|
188
|
-
top: 60px; /** camera access needs a bit more space **/
|
|
189
|
-
right: 20px;
|
|
190
|
-
z-index: 9999;
|
|
191
|
-
}
|
|
192
|
-
</style>
|
|
193
|
-
<div class="canvas-wrapper"> <!-- this wrapper is necessary for WebXR https://github.com/meta-quest/immersive-web-emulator/issues/55 -->
|
|
194
|
-
<canvas></canvas>
|
|
195
|
-
</div>
|
|
196
|
-
<div class="content">
|
|
197
|
-
<slot class="overlay-content"></slot>
|
|
198
|
-
</div>
|
|
199
|
-
`;
|
|
200
|
-
if (this.shadowRoot)
|
|
201
|
-
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
|
202
|
-
this._context = new Context({ domElement: this });
|
|
203
|
-
this.addEventListener("error", this.onError);
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* @internal
|
|
207
|
-
*/
|
|
208
|
-
async connectedCallback() {
|
|
209
|
-
if (debug) {
|
|
210
|
-
console.log("<needle-engine> connected");
|
|
211
|
-
}
|
|
212
|
-
this.setPublicKey();
|
|
213
|
-
this.setVersion();
|
|
214
|
-
this.addEventListener("xr-session-started", this.onXRSessionStarted);
|
|
215
|
-
this.onSetupDesktop();
|
|
216
|
-
if (!this.getAttribute("src")) {
|
|
217
|
-
const global = globalThis["needle:codegen_files"];
|
|
218
|
-
if (debug)
|
|
219
|
-
console.log("src is null, trying to load from globalThis[\"needle:codegen_files\"]", global);
|
|
220
|
-
if (global) {
|
|
221
|
-
if (debug)
|
|
222
|
-
console.log("globalThis[\"needle:codegen_files\"]", global);
|
|
223
|
-
this.setAttribute("src", global);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (debug)
|
|
227
|
-
console.log("src", this.getAttribute("src"));
|
|
228
|
-
// we have to wait because codegen does set the src attribute when it's loaded
|
|
229
|
-
// which might happen after the element is connected
|
|
230
|
-
// if the `src` is then still null we want to initialize the default scene
|
|
231
|
-
const loadId = this._loadId;
|
|
232
|
-
setTimeout(() => {
|
|
233
|
-
if (this.isConnected === false)
|
|
234
|
-
return;
|
|
235
|
-
if (loadId !== this._loadId)
|
|
236
|
-
return;
|
|
237
|
-
this.onLoad();
|
|
238
|
-
}, 1);
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* @internal
|
|
242
|
-
*/
|
|
243
|
-
disconnectedCallback() {
|
|
244
|
-
this.removeEventListener("xr-session-started", this.onXRSessionStarted);
|
|
245
|
-
this._didFullyLoad = false;
|
|
246
|
-
const keepAlive = this.getAttribute("keep-alive");
|
|
247
|
-
const dispose = keepAlive == undefined || (keepAlive?.length > 0 && keepAlive !== "true" && keepAlive !== "1");
|
|
248
|
-
if (debug)
|
|
249
|
-
console.warn("<needle-engine> disconnected, keep-alive: \"" + keepAlive + "\"", typeof keepAlive, "Dispose=", dispose);
|
|
250
|
-
if (dispose) {
|
|
251
|
-
if (debug)
|
|
252
|
-
console.warn("<needle-engine> dispose");
|
|
253
|
-
this._context?.dispose();
|
|
254
|
-
this._context = null;
|
|
255
|
-
this._lastSourceFiles = null;
|
|
256
|
-
this._loadId += 1;
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
if (debug)
|
|
260
|
-
console.warn("<needle-engine> is not disposed because keep-alive is set");
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* @internal
|
|
265
|
-
*/
|
|
266
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
267
|
-
if (debug)
|
|
268
|
-
console.log("attributeChangedCallback", name, oldValue, newValue);
|
|
269
|
-
switch (name) {
|
|
270
|
-
case "src":
|
|
271
|
-
if (debug)
|
|
272
|
-
console.warn("<needle-engine src>\nchanged from \"", oldValue, "\" to \"", newValue, "\"");
|
|
273
|
-
this.onLoad();
|
|
274
|
-
// this._watcher?.onSourceChanged(newValue);
|
|
275
|
-
break;
|
|
276
|
-
case "hash":
|
|
277
|
-
if (this._context) {
|
|
278
|
-
this._context.hash = newValue;
|
|
279
|
-
}
|
|
280
|
-
break;
|
|
281
|
-
case "loadstart":
|
|
282
|
-
case "progress":
|
|
283
|
-
case "loadfinished":
|
|
284
|
-
if (typeof newValue === "string" && newValue.length > 0) {
|
|
285
|
-
if (debug)
|
|
286
|
-
console.log(name + " attribute changed", newValue);
|
|
287
|
-
this.registerEventFromAttribute(name, newValue);
|
|
288
|
-
}
|
|
289
|
-
break;
|
|
290
|
-
case "dracoDecoderPath":
|
|
291
|
-
if (debug)
|
|
292
|
-
console.log("dracoDecoderPath", newValue);
|
|
293
|
-
setDracoDecoderPath(newValue);
|
|
294
|
-
break;
|
|
295
|
-
case "dracoDecoderType":
|
|
296
|
-
if (newValue === "wasm" || newValue === "js") {
|
|
297
|
-
if (debug)
|
|
298
|
-
console.log("dracoDecoderType", newValue);
|
|
299
|
-
setDracoDecoderType(newValue);
|
|
300
|
-
}
|
|
301
|
-
else
|
|
302
|
-
console.error("Invalid dracoDecoderType", newValue, "expected js or wasm");
|
|
303
|
-
break;
|
|
304
|
-
case "ktx2DecoderPath":
|
|
305
|
-
if (debug)
|
|
306
|
-
console.log("ktx2DecoderPath", newValue);
|
|
307
|
-
setKtx2TranscoderPath(newValue);
|
|
308
|
-
break;
|
|
309
|
-
case "tone-mapping": {
|
|
310
|
-
this.applyAttributes();
|
|
311
|
-
break;
|
|
312
|
-
}
|
|
313
|
-
case "tone-mapping-exposure": {
|
|
314
|
-
this.applyAttributes();
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
317
|
-
case "background-blurriness": {
|
|
318
|
-
const value = parseFloat(newValue);
|
|
319
|
-
if (value != undefined && this._context) {
|
|
320
|
-
this._context.scene.backgroundBlurriness = value;
|
|
321
|
-
}
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
case "background-color": {
|
|
325
|
-
this.applyAttributes();
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
case "public-key": {
|
|
329
|
-
if (newValue != PUBLIC_KEY)
|
|
330
|
-
this.setPublicKey();
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
case "version": {
|
|
334
|
-
if (newValue != VERSION)
|
|
335
|
-
this.setVersion();
|
|
336
|
-
break;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
_loadId = 0;
|
|
341
|
-
_abortController = null;
|
|
342
|
-
_lastSourceFiles = null;
|
|
343
|
-
_createContextPromise = null;
|
|
344
|
-
async onLoad() {
|
|
345
|
-
if (!this.isConnected)
|
|
346
|
-
return;
|
|
347
|
-
if (!this._context) {
|
|
348
|
-
if (debug)
|
|
349
|
-
console.warn("Create new context");
|
|
350
|
-
this._context = new Context({ domElement: this });
|
|
351
|
-
}
|
|
352
|
-
if (!this._context) {
|
|
353
|
-
console.error("Needle Engine: Context not initialized");
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
const filesToLoad = this.getSourceFiles();
|
|
357
|
-
if (!this.checkIfSourceHasChanged(filesToLoad, this._lastSourceFiles)) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
// Abort previous loading (if it's still running)
|
|
361
|
-
if (this._abortController) {
|
|
362
|
-
if (debug)
|
|
363
|
-
console.warn("Abort previous loading process");
|
|
364
|
-
this._abortController.abort();
|
|
365
|
-
this._abortController = null;
|
|
366
|
-
}
|
|
367
|
-
this._lastSourceFiles = filesToLoad;
|
|
368
|
-
const loadId = ++this._loadId;
|
|
369
|
-
if (filesToLoad === null || filesToLoad === undefined || filesToLoad.length <= 0) {
|
|
370
|
-
if (debug)
|
|
371
|
-
console.warn("Clear scene", filesToLoad);
|
|
372
|
-
this._context.clear();
|
|
373
|
-
if (loadId !== this._loadId)
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
const alias = this.getAttribute("alias");
|
|
377
|
-
this.classList.add("loading");
|
|
378
|
-
// Loading start events
|
|
379
|
-
const allowOverridingDefaultLoading = hasCommercialLicense();
|
|
380
|
-
// default loading can be overriden by calling preventDefault in the onload start event
|
|
381
|
-
this.ensureLoadStartIsRegistered();
|
|
382
|
-
let useDefaultLoading = this.dispatchEvent(new CustomEvent("loadstart", {
|
|
383
|
-
detail: {
|
|
384
|
-
context: this._context,
|
|
385
|
-
alias: alias
|
|
386
|
-
},
|
|
387
|
-
cancelable: true
|
|
388
|
-
}));
|
|
389
|
-
if (allowOverridingDefaultLoading) {
|
|
390
|
-
// Handle the <needle-engine hide-loading-overlay> attribute
|
|
391
|
-
const hideOverlay = this.getAttribute("hide-loading-overlay");
|
|
392
|
-
if (hideOverlay !== null && hideOverlay !== undefined && hideOverlay !== "0") {
|
|
393
|
-
useDefaultLoading = false;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
// for local development we allow overriding the loading screen - but we notify the user that it won't work in a deployed environment
|
|
397
|
-
if (useDefaultLoading === false && !allowOverridingDefaultLoading) {
|
|
398
|
-
if (!isDevEnvironment())
|
|
399
|
-
useDefaultLoading = true;
|
|
400
|
-
console.warn("Needle Engine: You need a commercial license to override the default loading view. Visit https://needle.tools/pricing");
|
|
401
|
-
if (isDevEnvironment())
|
|
402
|
-
showBalloonWarning("You need a <a target=\"_blank\" href=\"https://needle.tools/pricing\">commercial license</a> to override the default loading view. This will not work in production.");
|
|
403
|
-
}
|
|
404
|
-
// create the loading view idf necessary
|
|
405
|
-
if (!this._loadingView && useDefaultLoading)
|
|
406
|
-
this._loadingView = new EngineLoadingView(this);
|
|
407
|
-
if (useDefaultLoading) {
|
|
408
|
-
// Only show the loading screen immedialty if we haven't loaded anything before
|
|
409
|
-
if (this._didFullyLoad !== true)
|
|
410
|
-
this._loadingView?.onLoadingBegin("begin load");
|
|
411
|
-
else {
|
|
412
|
-
// If we have loaded a glb previously and are loading a new glb due to e.g. src change
|
|
413
|
-
// we don't want to show the loading screen immediately to avoid blinking if the glb to be loaded is tiny
|
|
414
|
-
// so we wait a bit and only show the loading screen if the loading takes longer than a short moment
|
|
415
|
-
setTimeout(() => {
|
|
416
|
-
// If the loading progress is already above ~ 70% we also don't need to show the loading screen anymore
|
|
417
|
-
if (this._loadingView && this._loadingProgress01 < 0.3 && this._loadId === loadId)
|
|
418
|
-
this._loadingView.onLoadingBegin("begin load");
|
|
419
|
-
}, 300);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (debug)
|
|
423
|
-
console.warn("--------------\nNeedle Engine: Begin loading " + loadId + "\n", filesToLoad);
|
|
424
|
-
this.onBeforeBeginLoading();
|
|
425
|
-
const loadedFiles = [];
|
|
426
|
-
const progressEventDetail = {
|
|
427
|
-
context: this._context,
|
|
428
|
-
name: "",
|
|
429
|
-
progress: {},
|
|
430
|
-
index: 0,
|
|
431
|
-
count: filesToLoad.length,
|
|
432
|
-
totalProgress01: this._loadingProgress01
|
|
433
|
-
};
|
|
434
|
-
const progressEvent = new CustomEvent("progress", { detail: progressEventDetail });
|
|
435
|
-
const displayNames = new Array();
|
|
436
|
-
const controller = new AbortController();
|
|
437
|
-
this._abortController = controller;
|
|
438
|
-
const args = {
|
|
439
|
-
files: filesToLoad,
|
|
440
|
-
abortSignal: controller.signal,
|
|
441
|
-
onLoadingProgress: evt => {
|
|
442
|
-
if (debug)
|
|
443
|
-
console.debug("Loading progress: ", evt);
|
|
444
|
-
if (controller.signal.aborted)
|
|
445
|
-
return;
|
|
446
|
-
const index = evt.index;
|
|
447
|
-
if (!displayNames[index] && evt.name) {
|
|
448
|
-
displayNames[index] = getDisplayName(evt.name);
|
|
449
|
-
}
|
|
450
|
-
evt.name = displayNames[index];
|
|
451
|
-
if (useDefaultLoading)
|
|
452
|
-
this._loadingView?.onLoadingUpdate(evt);
|
|
453
|
-
progressEventDetail.name = evt.name;
|
|
454
|
-
progressEventDetail.progress = evt.progress;
|
|
455
|
-
this._loadingProgress01 = calculateProgress01(evt);
|
|
456
|
-
progressEventDetail.totalProgress01 = this._loadingProgress01;
|
|
457
|
-
this.dispatchEvent(progressEvent);
|
|
458
|
-
},
|
|
459
|
-
onLoadingFinished: (_index, file, glTF) => {
|
|
460
|
-
if (debug)
|
|
461
|
-
console.debug(`Finished loading \"${file}\" (aborted? ${controller.signal.aborted})`);
|
|
462
|
-
if (controller.signal.aborted)
|
|
463
|
-
return;
|
|
464
|
-
if (glTF) {
|
|
465
|
-
loadedFiles.push({
|
|
466
|
-
src: file,
|
|
467
|
-
file: glTF
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
const currentHash = this.getAttribute("hash");
|
|
473
|
-
if (currentHash !== null && currentHash !== undefined)
|
|
474
|
-
this._context.hash = currentHash;
|
|
475
|
-
this._context.alias = alias;
|
|
476
|
-
this._createContextPromise = this._context.create(args);
|
|
477
|
-
const success = await this._createContextPromise;
|
|
478
|
-
this.applyAttributes();
|
|
479
|
-
if (debug)
|
|
480
|
-
console.warn("--------------\nNeedle Engine: finished loading " + loadId + "\n", filesToLoad, `Aborted? ${controller.signal.aborted}`);
|
|
481
|
-
if (controller.signal.aborted) {
|
|
482
|
-
console.log("Loading finished but aborted...");
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
if (this._loadId !== loadId) {
|
|
486
|
-
console.log("Load id changed during loading process");
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
this._loadingProgress01 = 1;
|
|
490
|
-
if (useDefaultLoading && success) {
|
|
491
|
-
this._loadingView?.onLoadingUpdate(1, "creating scene");
|
|
492
|
-
}
|
|
493
|
-
this._didFullyLoad = true;
|
|
494
|
-
this.classList.remove("loading");
|
|
495
|
-
this.classList.add("loading-finished");
|
|
496
|
-
this.dispatchEvent(new CustomEvent("loadfinished", {
|
|
497
|
-
detail: {
|
|
498
|
-
context: this._context,
|
|
499
|
-
src: alias,
|
|
500
|
-
loadedFiles: loadedFiles,
|
|
501
|
-
}
|
|
502
|
-
}));
|
|
503
|
-
}
|
|
504
|
-
applyAttributes() {
|
|
505
|
-
// set tonemapping if configured
|
|
506
|
-
if (this._context?.renderer) {
|
|
507
|
-
const attribute = this.getAttribute("tonemapping") || this.getAttribute("tone-mapping");
|
|
508
|
-
switch (attribute?.toLowerCase()) {
|
|
509
|
-
case "none":
|
|
510
|
-
this._context.renderer.toneMapping = NoToneMapping;
|
|
511
|
-
break;
|
|
512
|
-
case "linear":
|
|
513
|
-
this._context.renderer.toneMapping = LinearToneMapping;
|
|
514
|
-
break;
|
|
515
|
-
case "neutral":
|
|
516
|
-
this._context.renderer.toneMapping = NeutralToneMapping;
|
|
517
|
-
break;
|
|
518
|
-
case "agx":
|
|
519
|
-
this._context.renderer.toneMapping = AgXToneMapping;
|
|
520
|
-
break;
|
|
521
|
-
default:
|
|
522
|
-
if (attribute !== null && attribute !== undefined) {
|
|
523
|
-
console.warn("Invalid tone-mapping attribute: " + attribute);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
const exposure = this.getAttribute("tone-mapping-exposure");
|
|
527
|
-
if (exposure !== null && exposure !== undefined) {
|
|
528
|
-
const value = parseFloat(exposure);
|
|
529
|
-
if (!isNaN(value))
|
|
530
|
-
this._context.renderer.toneMappingExposure = value;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
const backgroundBlurriness = this.getAttribute("background-blurriness");
|
|
534
|
-
if (backgroundBlurriness !== null && backgroundBlurriness !== undefined) {
|
|
535
|
-
const value = parseFloat(backgroundBlurriness);
|
|
536
|
-
if (value !== undefined && this._context) {
|
|
537
|
-
this._context.scene.backgroundBlurriness = value;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
const backgroundColor = this.getAttribute("background-color");
|
|
541
|
-
if (this._context?.renderer) {
|
|
542
|
-
if (typeof backgroundColor === "string" && backgroundColor.length > 0) {
|
|
543
|
-
const rgbaColor = RGBAColor.fromColorRepresentation(backgroundColor);
|
|
544
|
-
if (debug)
|
|
545
|
-
console.debug("<needle-engine> background-color changed, str:", backgroundColor, "→", rgbaColor);
|
|
546
|
-
this._context.renderer.setClearColor(rgbaColor, rgbaColor.alpha);
|
|
547
|
-
this.context.scene.background = null;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
onXRSessionStarted = () => {
|
|
552
|
-
const xrSessionMode = this.context.xrSessionMode;
|
|
553
|
-
if (xrSessionMode === "immersive-ar")
|
|
554
|
-
this.onEnterAR(this.context.xrSession);
|
|
555
|
-
else if (xrSessionMode === "immersive-vr")
|
|
556
|
-
this.onEnterVR(this.context.xrSession);
|
|
557
|
-
// handle session end:
|
|
558
|
-
this.context.xrSession?.addEventListener("end", () => {
|
|
559
|
-
this.dispatchEvent(new CustomEvent("xr-session-ended", { detail: { session: this.context.xrSession, context: this._context, sessionMode: xrSessionMode } }));
|
|
560
|
-
if (xrSessionMode === "immersive-ar")
|
|
561
|
-
this.onExitAR(this.context.xrSession);
|
|
562
|
-
else if (xrSessionMode === "immersive-vr")
|
|
563
|
-
this.onExitVR(this.context.xrSession);
|
|
564
|
-
});
|
|
565
|
-
};
|
|
566
|
-
/** called by the context when the first frame has been rendered */
|
|
567
|
-
onReady = () => this._loadingView?.onLoadingFinished();
|
|
568
|
-
onError = () => this._loadingView?.setMessage("Loading failed!");
|
|
569
|
-
internalSetLoadingMessage(str) {
|
|
570
|
-
this._loadingView?.setMessage(str);
|
|
571
|
-
}
|
|
572
|
-
getSourceFiles() {
|
|
573
|
-
const src = this.getAttribute("src");
|
|
574
|
-
if (!src)
|
|
575
|
-
return [];
|
|
576
|
-
let filesToLoad;
|
|
577
|
-
// When using globalThis the src is an array already
|
|
578
|
-
if (Array.isArray(src)) {
|
|
579
|
-
filesToLoad = src;
|
|
580
|
-
}
|
|
581
|
-
// When assigned from codegen the src is a stringified array
|
|
582
|
-
else if (src.startsWith("[") && src.endsWith("]")) {
|
|
583
|
-
filesToLoad = JSON.parse(src);
|
|
584
|
-
}
|
|
585
|
-
// src.toString for an array produces a comma separated list
|
|
586
|
-
else if (src.includes(",")) {
|
|
587
|
-
filesToLoad = src.split(",");
|
|
588
|
-
}
|
|
589
|
-
else
|
|
590
|
-
filesToLoad = [src];
|
|
591
|
-
// filter out invalid or empty strings
|
|
592
|
-
for (let i = filesToLoad.length - 1; i >= 0; i--) {
|
|
593
|
-
const file = filesToLoad[i];
|
|
594
|
-
if (file === "null" || file === "undefined" || file?.length <= 0)
|
|
595
|
-
filesToLoad.splice(i, 1);
|
|
596
|
-
}
|
|
597
|
-
return filesToLoad;
|
|
598
|
-
}
|
|
599
|
-
checkIfSourceHasChanged(current, previous) {
|
|
600
|
-
if (current?.length !== previous?.length)
|
|
601
|
-
return true;
|
|
602
|
-
if (current == null && previous !== null)
|
|
603
|
-
return true;
|
|
604
|
-
if (current !== null && previous == null)
|
|
605
|
-
return true;
|
|
606
|
-
if (current !== null && previous !== null) {
|
|
607
|
-
for (let i = 0; i < current?.length; i++) {
|
|
608
|
-
if (current[i] !== previous[i])
|
|
609
|
-
return true;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return false;
|
|
613
|
-
}
|
|
614
|
-
_previouslyRegisteredMap = new Map();
|
|
615
|
-
ensureLoadStartIsRegistered() {
|
|
616
|
-
const attributeValue = this.getAttribute("loadstart");
|
|
617
|
-
if (attributeValue)
|
|
618
|
-
this.registerEventFromAttribute("loadstart", attributeValue);
|
|
619
|
-
}
|
|
620
|
-
registerEventFromAttribute(eventName, code) {
|
|
621
|
-
const prev = this._previouslyRegisteredMap.get(eventName);
|
|
622
|
-
if (prev) {
|
|
623
|
-
this._previouslyRegisteredMap.delete(eventName);
|
|
624
|
-
this.removeEventListener(eventName, prev);
|
|
625
|
-
}
|
|
626
|
-
if (typeof code === "string" && code.length > 0) {
|
|
627
|
-
try {
|
|
628
|
-
// indirect eval https://esbuild.github.io/content-types/#direct-eval
|
|
629
|
-
const fn = (0, eval)(code);
|
|
630
|
-
// const fn = new Function(newValue);
|
|
631
|
-
if (typeof fn === "function") {
|
|
632
|
-
this._previouslyRegisteredMap.set(eventName, fn);
|
|
633
|
-
this.addEventListener(eventName, evt => fn?.call(globalThis, this._context, evt));
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
catch (err) {
|
|
637
|
-
console.error("Error registering event " + eventName + "=\"" + code + "\" failed with the following error:\n", err);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
setPublicKey() {
|
|
642
|
-
if (PUBLIC_KEY && PUBLIC_KEY.length > 0)
|
|
643
|
-
this.setAttribute("public-key", PUBLIC_KEY);
|
|
644
|
-
}
|
|
645
|
-
setVersion() {
|
|
646
|
-
if (VERSION && VERSION.length > 0) {
|
|
647
|
-
this.setAttribute("version", VERSION);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* @internal
|
|
652
|
-
*/
|
|
653
|
-
getAROverlayContainer() {
|
|
654
|
-
return this._overlay_ar.createOverlayContainer(this);
|
|
655
|
-
}
|
|
656
|
-
/**
|
|
657
|
-
* @internal
|
|
658
|
-
*/
|
|
659
|
-
getVROverlayContainer() {
|
|
660
|
-
for (let i = 0; i < this.children.length; i++) {
|
|
661
|
-
const ch = this.children[i];
|
|
662
|
-
if (ch.classList.contains("vr"))
|
|
663
|
-
return ch;
|
|
664
|
-
}
|
|
665
|
-
return null;
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* @internal
|
|
669
|
-
*/
|
|
670
|
-
onEnterAR(session) {
|
|
671
|
-
this.onSetupAR();
|
|
672
|
-
const overlayContainer = this.getAROverlayContainer();
|
|
673
|
-
this._overlay_ar.onBegin(this._context, overlayContainer, session);
|
|
674
|
-
this.dispatchEvent(new CustomEvent("enter-ar", { detail: { session: session, context: this._context, htmlContainer: this._overlay_ar?.ARContainer } }));
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* @internal
|
|
678
|
-
*/
|
|
679
|
-
onExitAR(session) {
|
|
680
|
-
this._overlay_ar.onEnd(this._context);
|
|
681
|
-
this.onSetupDesktop();
|
|
682
|
-
this.dispatchEvent(new CustomEvent("exit-ar", { detail: { session: session, context: this._context, htmlContainer: this._overlay_ar?.ARContainer } }));
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* @internal
|
|
686
|
-
*/
|
|
687
|
-
onEnterVR(session) {
|
|
688
|
-
this.onSetupVR();
|
|
689
|
-
this.dispatchEvent(new CustomEvent("enter-vr", { detail: { session: session, context: this._context } }));
|
|
690
|
-
}
|
|
691
|
-
/**
|
|
692
|
-
* @internal
|
|
693
|
-
*/
|
|
694
|
-
onExitVR(session) {
|
|
695
|
-
this.onSetupDesktop();
|
|
696
|
-
this.dispatchEvent(new CustomEvent("exit-vr", { detail: { session: session, context: this._context } }));
|
|
697
|
-
}
|
|
698
|
-
onSetupAR() {
|
|
699
|
-
this.classList.add(arSessionActiveClassName);
|
|
700
|
-
this.classList.remove(desktopSessionActiveClassName);
|
|
701
|
-
const arContainer = this.getAROverlayContainer();
|
|
702
|
-
if (debug)
|
|
703
|
-
console.warn("onSetupAR:", arContainer);
|
|
704
|
-
if (arContainer) {
|
|
705
|
-
arContainer.classList.add(arSessionActiveClassName);
|
|
706
|
-
arContainer.classList.remove(desktopSessionActiveClassName);
|
|
707
|
-
}
|
|
708
|
-
this.foreachHtmlElement(ch => this.setupElementsForMode(ch, arContainerClassName));
|
|
709
|
-
}
|
|
710
|
-
onSetupVR() {
|
|
711
|
-
this.classList.remove(arSessionActiveClassName);
|
|
712
|
-
this.classList.remove(desktopSessionActiveClassName);
|
|
713
|
-
this.foreachHtmlElement(ch => this.setupElementsForMode(ch, vrContainerClassName));
|
|
714
|
-
}
|
|
715
|
-
onSetupDesktop() {
|
|
716
|
-
this.classList.remove(arSessionActiveClassName);
|
|
717
|
-
this.classList.add(desktopSessionActiveClassName);
|
|
718
|
-
const arContainer = this.getAROverlayContainer();
|
|
719
|
-
if (arContainer) {
|
|
720
|
-
arContainer.classList.remove(arSessionActiveClassName);
|
|
721
|
-
arContainer.classList.add(desktopSessionActiveClassName);
|
|
722
|
-
}
|
|
723
|
-
this.foreachHtmlElement(ch => this.setupElementsForMode(ch, desktopContainerClassname));
|
|
724
|
-
}
|
|
725
|
-
setupElementsForMode(el, currentSessionType, _session = null) {
|
|
726
|
-
if (el === this._context?.renderer?.domElement)
|
|
727
|
-
return;
|
|
728
|
-
if (el.id === "VRButton" || el.id === "ARButton")
|
|
729
|
-
return;
|
|
730
|
-
const classList = el.classList;
|
|
731
|
-
if (classList.contains(currentSessionType)) {
|
|
732
|
-
el.style.visibility = "visible";
|
|
733
|
-
if (el.style.display === "none")
|
|
734
|
-
el.style.display = "block";
|
|
735
|
-
}
|
|
736
|
-
else {
|
|
737
|
-
// only modify style for elements that have a known class (e.g. marked as vr ar desktop)
|
|
738
|
-
for (const known of knownClasses) {
|
|
739
|
-
if (el.classList.contains(known)) {
|
|
740
|
-
el.style.visibility = "hidden";
|
|
741
|
-
el.style.display = "none";
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
foreachHtmlElement(cb) {
|
|
747
|
-
for (let i = 0; i < this.children.length; i++) {
|
|
748
|
-
const ch = this.children[i];
|
|
749
|
-
if (ch.style)
|
|
750
|
-
cb(ch);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
onBeforeBeginLoading() {
|
|
754
|
-
const customDracoDecoderPath = this.getAttribute("dracoDecoderPath");
|
|
755
|
-
if (customDracoDecoderPath) {
|
|
756
|
-
if (debug)
|
|
757
|
-
console.log("using custom draco decoder path", customDracoDecoderPath);
|
|
758
|
-
setDracoDecoderPath(customDracoDecoderPath);
|
|
759
|
-
}
|
|
760
|
-
const customDracoDecoderType = this.getAttribute("dracoDecoderType");
|
|
761
|
-
if (customDracoDecoderType) {
|
|
762
|
-
if (debug)
|
|
763
|
-
console.log("using custom draco decoder type", customDracoDecoderType);
|
|
764
|
-
setDracoDecoderType(customDracoDecoderType);
|
|
765
|
-
}
|
|
766
|
-
const customKtx2DecoderPath = this.getAttribute("ktx2DecoderPath");
|
|
767
|
-
if (customKtx2DecoderPath) {
|
|
768
|
-
if (debug)
|
|
769
|
-
console.log("using custom ktx2 decoder path", customKtx2DecoderPath);
|
|
770
|
-
setKtx2TranscoderPath(customKtx2DecoderPath);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
if (typeof window !== "undefined" && !window.customElements.get(htmlTagName))
|
|
775
|
-
window.customElements.define(htmlTagName, NeedleEngineHTMLElement);
|
|
776
|
-
function getDisplayName(str) {
|
|
777
|
-
if (str.startsWith("blob:")) {
|
|
778
|
-
return "blob";
|
|
779
|
-
}
|
|
780
|
-
const parts = str.split("/");
|
|
781
|
-
let name = parts[parts.length - 1];
|
|
782
|
-
// Remove params
|
|
783
|
-
const paramsIndex = name.indexOf("?");
|
|
784
|
-
if (paramsIndex > 0)
|
|
785
|
-
name = name.substring(0, paramsIndex);
|
|
786
|
-
const equalSign = name.indexOf("=");
|
|
787
|
-
if (equalSign > 0)
|
|
788
|
-
name = name.substring(equalSign);
|
|
789
|
-
const extension = name.split(".").pop();
|
|
790
|
-
const extensions = ["glb", "gltf", "usdz", "usd", "fbx", "obj", "mtl"];
|
|
791
|
-
const matchedIndex = !extension ? -1 : extensions.indexOf(extension.toLowerCase());
|
|
792
|
-
if (extension && matchedIndex >= 0) {
|
|
793
|
-
name = name.substring(0, name.length - extension.length - 1);
|
|
794
|
-
}
|
|
795
|
-
name = decodeURIComponent(name);
|
|
796
|
-
if (name.length > 3) {
|
|
797
|
-
let displayName = "";
|
|
798
|
-
let lastCharacterWasSpace = false;
|
|
799
|
-
const ignoredCharacters = ["(", ")", "[", "]", "{", "}", ":", ";", ",", ".", "!", "?"];
|
|
800
|
-
for (let i = 0; i < name.length; i++) {
|
|
801
|
-
let c = name[i];
|
|
802
|
-
if (c === "_" || c === "-")
|
|
803
|
-
c = " ";
|
|
804
|
-
if (c === ' ' && displayName.length <= 0)
|
|
805
|
-
continue;
|
|
806
|
-
const isIgnored = ignoredCharacters.includes(c);
|
|
807
|
-
if (isIgnored)
|
|
808
|
-
continue;
|
|
809
|
-
const isFirstCharacter = displayName.length === 0;
|
|
810
|
-
if (isFirstCharacter) {
|
|
811
|
-
c = c.toUpperCase();
|
|
812
|
-
}
|
|
813
|
-
if (lastCharacterWasSpace && c === " ") {
|
|
814
|
-
continue;
|
|
815
|
-
}
|
|
816
|
-
if (lastCharacterWasSpace) {
|
|
817
|
-
c = c.toUpperCase();
|
|
818
|
-
}
|
|
819
|
-
lastCharacterWasSpace = false;
|
|
820
|
-
displayName += c;
|
|
821
|
-
if (c === " ") {
|
|
822
|
-
lastCharacterWasSpace = true;
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
if (isDevEnvironment() && name !== displayName)
|
|
826
|
-
console.debug("Generated display name: \"" + name + "\" → \"" + displayName + "\"");
|
|
827
|
-
return displayName.trim();
|
|
828
|
-
}
|
|
829
|
-
if (isDevEnvironment())
|
|
830
|
-
console.debug("Loading: use default name", name);
|
|
831
|
-
return name;
|
|
832
|
-
}
|
|
833
|
-
//# sourceMappingURL=engine_element.js.map
|