@combeenation/3d-viewer 12.1.2 → 12.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@combeenation/3d-viewer",
3
- "version": "12.1.2",
3
+ "version": "12.2.1",
4
4
  "description": "Combeenation 3D Viewer",
5
5
  "homepage": "https://github.com/Combeenation/3d-viewer#readme",
6
6
  "bugs": {
@@ -7,7 +7,7 @@ import { TagManager } from '../manager/tagManager';
7
7
  import { VariantInstanceManager } from '../manager/variantInstanceManager';
8
8
  import { SpecStorage } from '../store/specStorage';
9
9
  import { backgroundDomeName, envHelperMetadataName } from '../util/babylonHelper';
10
- import { showWorldCoordinates } from '../util/debugHelper';
10
+ import { hideWorldCoordinates, showWorldCoordinates } from '../util/debugHelper';
11
11
  import { getIsScaledDownDevice } from '../util/deviceHelper';
12
12
  import { debounce, loadJson } from '../util/resourceHelper';
13
13
  import { getCustomCbnBabylonLoaderPlugin } from '../util/sceneLoaderHelper';
@@ -45,8 +45,6 @@ import { isString } from 'lodash-es';
45
45
  * The class does nothing on its own and needs to {@link bootstrap}
46
46
  */
47
47
  export class Viewer extends EventBroadcaster {
48
- public static readonly BOUNDING_BOX_NAME = '__bounding_box__';
49
-
50
48
  protected _scene: Scene | null = null;
51
49
 
52
50
  protected _animationManager: AnimationManager | null = null;
@@ -498,9 +496,9 @@ The inspector can only be used in development builds.`);
498
496
  }
499
497
 
500
498
  /**
501
- * Calculates the bounding box from all visible meshes on the scene.
499
+ * Calculates the bounding box information from all visible meshes on the scene.
502
500
  */
503
- public async calculateBoundingBox(excludeGeometry?: ExcludedGeometryList): Promise<Mesh> {
501
+ public calculateBoundingInfo(excludeGeometry?: ExcludedGeometryList): BoundingInfo {
504
502
  if (this.scene.meshes.length === 0) {
505
503
  throw new Error('There are currently no meshes on the scene.');
506
504
  }
@@ -508,15 +506,13 @@ The inspector can only be used in development builds.`);
508
506
  const { max, min } = this.scene.meshes
509
507
  .filter(mesh => {
510
508
  const isEnabled = mesh.isEnabled();
511
- // ignore the existing bounding box mesh for calculating the current one
512
- const isNotBBoxMesh = Viewer.BOUNDING_BOX_NAME !== mesh.id;
513
509
  // ignore meshes with invalid bounding infos
514
510
  const hasValidBBoxInfo = mesh.getBoundingInfo().boundingSphere.radius > 0;
515
511
  // ignore meshes with infinite distance, typically these are sky boxes
516
512
  const hasInfiniteDistance = mesh.infiniteDistance;
517
513
  // ignore excluded meshes
518
514
  const isExcluded = excludeGeometry ? isNodeIncludedInExclusionList(mesh, excludeGeometry) : false;
519
- return isEnabled && isNotBBoxMesh && hasValidBBoxInfo && !hasInfiniteDistance && !isExcluded;
515
+ return isEnabled && hasValidBBoxInfo && !hasInfiniteDistance && !isExcluded;
520
516
  })
521
517
  .reduce(
522
518
  (accBBoxMinMax, curMesh, idx) => {
@@ -529,12 +525,8 @@ The inspector can only be used in development builds.`);
529
525
  { max: new Vector3(), min: new Vector3() }
530
526
  );
531
527
 
532
- let boundingBox = this.scene.getMeshByName(Viewer.BOUNDING_BOX_NAME) as Mesh;
533
- if (!boundingBox) {
534
- boundingBox = new Mesh(Viewer.BOUNDING_BOX_NAME, this.scene);
535
- }
536
- boundingBox.setBoundingInfo(new BoundingInfo(min, max));
537
- return boundingBox;
528
+ const boundingInfo = new BoundingInfo(min, max);
529
+ return boundingInfo;
538
530
  }
539
531
 
540
532
  /**
@@ -566,10 +558,10 @@ The inspector can only be used in development builds.`);
566
558
  }
567
559
 
568
560
  // get bounding box of all visible meshes, this is the base for the autofocus algorithm
569
- const boundingBox = await this.calculateBoundingBox(exclude);
561
+ const boundingInfo = this.calculateBoundingInfo(exclude);
570
562
 
571
- const radius = boundingBox.getBoundingInfo().boundingSphere.radius;
572
- const center = boundingBox.getBoundingInfo().boundingSphere.center;
563
+ const radius = boundingInfo.boundingSphere.radius;
564
+ const center = boundingInfo.boundingSphere.center;
573
565
  const diameter = radius * 2;
574
566
 
575
567
  // set lower radius limit on edge of bounding sphere to make sure that we can't dive into the meshes
@@ -627,10 +619,17 @@ The inspector can only be used in development builds.`);
627
619
  }
628
620
 
629
621
  /**
630
- * Show coordinate system with given dimension (for debugging purpose).
622
+ * Show world coordinate system with given dimension (for debugging purpose).
631
623
  */
632
624
  public showWorldCoordinates(dimension: number) {
633
- showWorldCoordinates(this.scene, dimension);
625
+ showWorldCoordinates(dimension);
626
+ }
627
+
628
+ /**
629
+ * Hide world coordinate system.
630
+ */
631
+ public hideWorldCoordinates() {
632
+ hideWorldCoordinates();
634
633
  }
635
634
 
636
635
  /**
@@ -279,9 +279,6 @@ export class GltfExportManager {
279
279
  return false;
280
280
  }
281
281
  // TODO: think of adding "BackgroundHelper" and nodes with "infiniteDistance" here as well, at least in AR mode
282
- if (node.name === Viewer.BOUNDING_BOX_NAME) {
283
- return false;
284
- }
285
282
  if (!node.isEnabled()) {
286
283
  return false;
287
284
  }
@@ -299,10 +299,6 @@ export class TagManager {
299
299
  created means that there is no scenario where new nodes should get TagManager parameters applied.
300
300
  Instead, we check for the state to be enabled via the `onEnabledStateChangedObservable` below.
301
301
  */
302
- //if (node.name === Viewer.BOUNDING_BOX_NAME || !this.viewer.variantInstances.areAllDefinitionsCreated) {
303
- if (node.name === Viewer.BOUNDING_BOX_NAME) {
304
- return;
305
- }
306
302
  const onEnabledStateChangedObserver = node.onEnabledStateChangedObservable.add(async state => {
307
303
  if (!state) {
308
304
  // if the node is disabled, means ignoring also "ghost nodes"
@@ -1,10 +1,11 @@
1
- import { Vector3 } from '@babylonjs/core';
2
1
  import { AxesViewer } from '@babylonjs/core/Debug/axesViewer';
3
2
  import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture';
4
3
  import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
5
4
  import { Color3 } from '@babylonjs/core/Maths/math.color';
5
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector';
6
6
  import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
7
7
  import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
8
+ import { UtilityLayerRenderer } from '@babylonjs/core/Rendering/utilityLayerRenderer';
8
9
 
9
10
  type DebugAxisKeys = 'X' | 'Y' | 'Z';
10
11
  type DebugAxisConfig = { color: Color3; position: Vector3 };
@@ -16,27 +17,42 @@ const _DEBUG_AXIS_MAP: Record<DebugAxisKeys, DebugAxisConfig> = {
16
17
  };
17
18
 
18
19
  const _WORLD_COORD_ROOT_KEY = '__world_coordinates__';
20
+ let _AXES_VIEWER: AxesViewer;
19
21
 
20
22
  /**
21
23
  * Create debug coordinate system located in world origin.
22
24
  * This function is based on Babylon.js `AxesViewer`, axis texts are added manually.
23
25
  */
24
- export function showWorldCoordinates(scene: Scene, dimension: number) {
25
- const existingRoot = scene.getTransformNodeByName(_WORLD_COORD_ROOT_KEY);
26
- if (existingRoot) {
27
- // make sure to remove already existing debug coordinate systems
28
- existingRoot.dispose();
29
- }
26
+ export function showWorldCoordinates(dimension: number) {
27
+ // make sure to remove already existing debug coordinate systems
28
+ hideWorldCoordinates();
29
+
30
+ // draw in utility layer, so that there is no interaction with the actually scene content (eg: glb export, autofocus)
31
+ const utilityLayerScene = UtilityLayerRenderer.DefaultUtilityLayer.utilityLayerScene;
30
32
 
31
- const worldCoordRoot = new TransformNode(_WORLD_COORD_ROOT_KEY, scene);
33
+ const worldCoordRoot = new TransformNode('__world_coordinates__', utilityLayerScene);
32
34
  // axes viewer coordinate system is a bit too large
33
35
  // multiply with unify factor to create arrows which exactly match the length of the input dimension
34
36
  const factor = _getWorldCoordinatesAxesUnifyFactor();
35
- const axesViewer = new AxesViewer(scene, dimension * factor);
37
+ _AXES_VIEWER = new AxesViewer(utilityLayerScene, dimension * factor);
38
+
39
+ _prepareWorldCoordinateAxis('X', _AXES_VIEWER.xAxis, worldCoordRoot, dimension, utilityLayerScene);
40
+ _prepareWorldCoordinateAxis('Y', _AXES_VIEWER.yAxis, worldCoordRoot, dimension, utilityLayerScene);
41
+ _prepareWorldCoordinateAxis('Z', _AXES_VIEWER.zAxis, worldCoordRoot, dimension, utilityLayerScene);
42
+ }
43
+
44
+ /**
45
+ * Remove meshes and materials that are associated with the debug world coordinates system
46
+ */
47
+ export function hideWorldCoordinates() {
48
+ const utilityLayerScene = UtilityLayerRenderer.DefaultUtilityLayer.utilityLayerScene;
49
+
50
+ _AXES_VIEWER?.dispose();
36
51
 
37
- _prepareWorldCoordinateAxis('X', axesViewer.xAxis, worldCoordRoot, dimension, scene);
38
- _prepareWorldCoordinateAxis('Y', axesViewer.yAxis, worldCoordRoot, dimension, scene);
39
- _prepareWorldCoordinateAxis('Z', axesViewer.zAxis, worldCoordRoot, dimension, scene);
52
+ const worldCoordRoot = utilityLayerScene.getTransformNodeByName(_WORLD_COORD_ROOT_KEY);
53
+ if (worldCoordRoot) {
54
+ worldCoordRoot.dispose(false, true);
55
+ }
40
56
  }
41
57
 
42
58
  /**
@@ -49,26 +65,29 @@ function _prepareWorldCoordinateAxis(
49
65
  axis: TransformNode,
50
66
  root: TransformNode,
51
67
  dimension: number,
52
- scene: Scene
68
+ utilityLayerScene: Scene
53
69
  ) {
54
70
  // create unique names
55
71
  axis.name = `${_WORLD_COORD_ROOT_KEY}.${text}`;
56
72
  axis.parent = root;
57
- axis.getChildMeshes().forEach((child, idx) => (child.name = `${_WORLD_COORD_ROOT_KEY}.${text}.cylinder_${idx}`));
58
73
 
59
74
  // create text mesh via dynamic texture
60
- const dynamicTexture = new DynamicTexture(`${_WORLD_COORD_ROOT_KEY}.${text}`, 50, scene, true);
75
+ const dynamicTexture = new DynamicTexture(`${_WORLD_COORD_ROOT_KEY}.${text}`, 50, utilityLayerScene, true);
61
76
  dynamicTexture.hasAlpha = true;
62
77
  // 42.5 is a magic offset, so that the text is vertically centered for font size 50px
63
78
  // horizontal centering works well with the standard behaviour of Babylon.js (setting "null" as "width")
64
79
  dynamicTexture.drawText(text, null, 42.5, 'bold 50px Arial', 'white', 'transparent', true);
65
80
 
66
- const material = new StandardMaterial(`${_WORLD_COORD_ROOT_KEY}.${text}`, scene);
81
+ const material = new StandardMaterial(`${_WORLD_COORD_ROOT_KEY}.${text}`, utilityLayerScene);
67
82
  material.disableLighting = true;
68
83
  material.emissiveColor = _DEBUG_AXIS_MAP[text].color;
69
84
  material.diffuseTexture = dynamicTexture;
70
85
 
71
- const plane = MeshBuilder.CreatePlane(`${_WORLD_COORD_ROOT_KEY}.${text}.TextPlane`, { size: dimension / 10 }, scene);
86
+ const plane = MeshBuilder.CreatePlane(
87
+ `${_WORLD_COORD_ROOT_KEY}.${text}.TextPlane`,
88
+ { size: dimension / 10 },
89
+ utilityLayerScene
90
+ );
72
91
  // make sure that text is located outside of arrow
73
92
  plane.position = _DEBUG_AXIS_MAP[text].position.multiplyByFloats(
74
93
  dimension * 1.05,