Needle Engine

Changes between version 3.41.0-alpha.5 and 3.41.1-alpha
Files changed (5) hide show
  1. src/engine/engine_element_attributes.ts +4 -2
  2. src/engine/engine_element.ts +32 -1
  3. src/engine/webcomponents/needle menu/needle-menu.ts +6 -2
  4. src/engine/xr/NeedleXRSession.ts +6 -0
  5. src/engine-components/OrbitControls.ts +6 -2
src/engine/engine_element_attributes.ts CHANGED
@@ -53,8 +53,10 @@
53
53
  "environment-image"?: string,
54
54
  }
55
55
 
56
- type ContactShadowAttributes = {
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
- & ContactShadowAttributes
71
+ & RenderingAttributes
70
72
  ;
src/engine/engine_element.ts CHANGED
@@ -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")
src/engine/webcomponents/needle menu/needle-menu.ts CHANGED
@@ -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() } });
src/engine/xr/NeedleXRSession.ts CHANGED
@@ -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
src/engine-components/OrbitControls.ts CHANGED
@@ -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
- camera.near = distance / 1000;
774
- camera.far = distance * 100;
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();