@@ -53,8 +53,10 @@
|
|
53
53
|
"environment-image"?: string,
|
54
54
|
}
|
55
55
|
|
56
|
-
type
|
56
|
+
export type TonemappingAttributeOptions = "none" | "linear" | "neutral" | "agx";
|
57
|
+
type RenderingAttributes = {
|
57
58
|
"contactshadows"?: boolean,
|
59
|
+
"tone-mapping"?: TonemappingAttributeOptions
|
58
60
|
}
|
59
61
|
|
60
62
|
/**
|
@@ -66,5 +68,5 @@
|
|
66
68
|
& Partial<Omit<HTMLElement, "style">>
|
67
69
|
& LoadingAttributes
|
68
70
|
& SkyboxAttributes
|
69
|
-
&
|
71
|
+
& RenderingAttributes
|
70
72
|
;
|
@@ -1,7 +1,10 @@
|
|
1
|
+
import { AgXToneMapping, LinearToneMapping, NeutralToneMapping, NoToneMapping } from "three";
|
2
|
+
|
1
3
|
import { getLoader, registerLoader } from "../engine/engine_gltf.js";
|
2
4
|
import { GameObject } from "../engine-components/Component.js";
|
3
5
|
import { isDevEnvironment, showBalloonWarning } from "./debug/index.js";
|
4
6
|
import { VERSION } from "./engine_constants.js";
|
7
|
+
import { TonemappingAttributeOptions } from "./engine_element_attributes.js";
|
5
8
|
import { calculateProgress01, EngineLoadingView, type ILoadingViewHandler } from "./engine_element_loading.js";
|
6
9
|
import { arContainerClassName,AROverlayHandler } from "./engine_element_overlay.js";
|
7
10
|
import { hasCommercialLicense } from "./engine_license.js";
|
@@ -34,7 +37,8 @@
|
|
34
37
|
"loadfinished",
|
35
38
|
"dracoDecoderPath",
|
36
39
|
"dracoDecoderType",
|
37
|
-
"ktx2DecoderPath"
|
40
|
+
"ktx2DecoderPath",
|
41
|
+
"tone-mapping",
|
38
42
|
]
|
39
43
|
|
40
44
|
// https://developers.google.com/web/fundamentals/web-components/customelements
|
@@ -293,6 +297,10 @@
|
|
293
297
|
if (debug) console.log("ktx2DecoderPath", newValue);
|
294
298
|
setKtx2TranscoderPath(newValue);
|
295
299
|
break;
|
300
|
+
case "tone-mapping": {
|
301
|
+
this.applyTonemapping();
|
302
|
+
break;
|
303
|
+
}
|
296
304
|
}
|
297
305
|
}
|
298
306
|
|
@@ -432,6 +440,8 @@
|
|
432
440
|
this._context.alias = alias;
|
433
441
|
this._createContextPromise = this._context.create(args);
|
434
442
|
await this._createContextPromise;
|
443
|
+
this.applyTonemapping();
|
444
|
+
|
435
445
|
if (debug) console.warn("--------------\nNeedle Engine: finished loading " + loadId + "\n", filesToLoad, `Aborted? ${controller.signal.aborted}`);
|
436
446
|
if (this._loadId !== loadId || controller.signal.aborted) {
|
437
447
|
console.log("Loading finished but aborted...")
|
@@ -454,6 +464,27 @@
|
|
454
464
|
}));
|
455
465
|
}
|
456
466
|
|
467
|
+
private applyTonemapping() {
|
468
|
+
if (!this._context?.renderer) return;
|
469
|
+
|
470
|
+
// set tonemapping if configured
|
471
|
+
const attribute = this.getAttribute("tone-mapping") as TonemappingAttributeOptions | null | undefined;
|
472
|
+
switch (attribute?.toLowerCase()) {
|
473
|
+
case "none":
|
474
|
+
this._context.renderer.toneMapping = NoToneMapping;
|
475
|
+
break;
|
476
|
+
case "linear":
|
477
|
+
this._context.renderer.toneMapping = LinearToneMapping;
|
478
|
+
break;
|
479
|
+
case "neutral":
|
480
|
+
this._context.renderer.toneMapping = NeutralToneMapping;
|
481
|
+
break;
|
482
|
+
case "agx":
|
483
|
+
this._context.renderer.toneMapping = AgXToneMapping;
|
484
|
+
break;
|
485
|
+
}
|
486
|
+
}
|
487
|
+
|
457
488
|
private onXRSessionStarted = () => {
|
458
489
|
const xrSessionMode = this.context.xrSessionMode;
|
459
490
|
if (xrSessionMode === "immersive-ar")
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import type { Context } from "../../engine_context.js";
|
3
3
|
import { hasCommercialLicense, hasProLicense, onLicenseCheckResultChanged } from "../../engine_license.js";
|
4
4
|
import { isLocalNetwork } from "../../engine_networking_utils.js";
|
5
|
-
import { getParam } from "../../engine_utils.js";
|
5
|
+
import { getParam, isMobileDevice } from "../../engine_utils.js";
|
6
6
|
import { onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
|
7
7
|
import { ButtonsFactory } from "../buttons.js";
|
8
8
|
import { ensureFonts, loadFont } from "../fonts.js";
|
@@ -137,7 +137,10 @@
|
|
137
137
|
* Call to add or remove a button to the menu to show a QR code for the current page
|
138
138
|
* If enabled=true then a button will be added to the menu that will show a QR code for the current page when clicked.
|
139
139
|
*/
|
140
|
-
showQRCodeButton(enabled: boolean): HTMLButtonElement | null {
|
140
|
+
showQRCodeButton(enabled: boolean | "desktop-only"): HTMLButtonElement | null {
|
141
|
+
if (enabled === "desktop-only") {
|
142
|
+
enabled = !isMobileDevice();
|
143
|
+
}
|
141
144
|
if (!enabled) {
|
142
145
|
const button = ButtonsFactory.getOrCreate().qrButton;
|
143
146
|
if (button) button.style.display = "none";
|
@@ -436,6 +439,7 @@
|
|
436
439
|
ensureFonts();
|
437
440
|
// add to document head AND shadow dom to work
|
438
441
|
// using a static font because the variable font is quite large
|
442
|
+
// eslint-disable-next-line no-secrets/no-secrets
|
439
443
|
const fontUrl = "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0";
|
440
444
|
// const fontUrl = "./include/fonts/MaterialSymbolsOutlined.css"; // for offline support
|
441
445
|
loadFont(fontUrl, { loadedCallback: () => { this.handleSizeChange() } });
|
@@ -389,6 +389,12 @@
|
|
389
389
|
|
390
390
|
// setup session init args, make sure we have default values
|
391
391
|
if (!init) init = {};
|
392
|
+
|
393
|
+
// Fix: define these here because VariantLauncher crashes otherwise. Spec: https://immersive-web.github.io/webxr/#feature-dependencies
|
394
|
+
// Issue: https://linear.app/needle/issue/NE-5136
|
395
|
+
init.optionalFeatures ??= [];
|
396
|
+
init.requiredFeatures ??= [];
|
397
|
+
|
392
398
|
switch (mode) {
|
393
399
|
|
394
400
|
// Setup VR initialization parameters
|
@@ -734,6 +734,9 @@
|
|
734
734
|
camera.updateProjectionMatrix();
|
735
735
|
box.getCenter(center);
|
736
736
|
|
737
|
+
const box_size = new Vector3();
|
738
|
+
box.getSize(box_size);
|
739
|
+
|
737
740
|
// project this box into camera space
|
738
741
|
box.applyMatrix4(camera.matrixWorldInverse);
|
739
742
|
|
@@ -770,8 +773,9 @@
|
|
770
773
|
this.autoTarget = false;
|
771
774
|
|
772
775
|
// TODO: this doesnt take the Camera component nearClipPlane into account
|
773
|
-
|
774
|
-
camera.
|
776
|
+
const boundsMax = Math.max(box_size.x, box_size.y, box_size.z);
|
777
|
+
camera.near = (distance / 100);
|
778
|
+
camera.far = boundsMax + distance * 10;
|
775
779
|
|
776
780
|
camera.updateMatrixWorld();
|
777
781
|
camera.updateProjectionMatrix();
|