@inweb/viewer-three 26.9.0 → 26.9.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.
Files changed (39) hide show
  1. package/dist/plugins/components/AxesHelperComponent.js +8 -6
  2. package/dist/plugins/components/AxesHelperComponent.js.map +1 -1
  3. package/dist/plugins/components/AxesHelperComponent.min.js +1 -1
  4. package/dist/plugins/components/AxesHelperComponent.module.js +7 -5
  5. package/dist/plugins/components/AxesHelperComponent.module.js.map +1 -1
  6. package/dist/plugins/loaders/IFCXLoader.js.map +1 -1
  7. package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -1
  8. package/dist/plugins/loaders/PotreeLoader.js +160 -0
  9. package/dist/plugins/loaders/PotreeLoader.js.map +1 -0
  10. package/dist/plugins/loaders/PotreeLoader.min.js +1 -0
  11. package/dist/plugins/loaders/PotreeLoader.module.js +68 -0
  12. package/dist/plugins/loaders/PotreeLoader.module.js.map +1 -0
  13. package/dist/viewer-three.js +153 -110
  14. package/dist/viewer-three.js.map +1 -1
  15. package/dist/viewer-three.min.js +3 -3
  16. package/dist/viewer-three.module.js +152 -96
  17. package/dist/viewer-three.module.js.map +1 -1
  18. package/lib/Viewer/Viewer.d.ts +3 -2
  19. package/lib/Viewer/commands/SetDefaultViewPosition.d.ts +2 -2
  20. package/package.json +6 -5
  21. package/plugins/components/AxesHelperComponent.ts +10 -6
  22. package/plugins/loaders/{IFCXCloudLoader.ts → IFCX/IFCXCloudLoader.ts} +1 -1
  23. package/plugins/loaders/{IFCXFileLoader.ts → IFCX/IFCXFileLoader.ts} +1 -1
  24. package/plugins/loaders/Potree/PotreeFileLoader.ts +106 -0
  25. package/plugins/loaders/Potree/PotreeModelImpl.ts +36 -0
  26. package/plugins/loaders/Potree/index.ts +28 -0
  27. package/src/Viewer/Viewer.ts +39 -15
  28. package/src/Viewer/commands/SetDefaultViewPosition.ts +20 -16
  29. package/src/Viewer/commands/ZoomTo.ts +13 -13
  30. package/src/Viewer/components/ExtentsComponent.ts +1 -1
  31. package/src/Viewer/components/LightComponent.ts +8 -6
  32. package/src/Viewer/components/ResizeCanvasComponent.ts +1 -18
  33. package/src/Viewer/draggers/OrbitDragger.ts +9 -0
  34. package/src/Viewer/draggers/WalkDragger.ts +1 -0
  35. package/src/Viewer/helpers/WCSHelper.ts +3 -3
  36. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +37 -12
  37. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +32 -32
  38. package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +2 -2
  39. /package/plugins/loaders/{IFCXLoader.ts → IFCX/index.ts} +0 -0
@@ -0,0 +1,68 @@
1
+ import { ModelImpl, Loader, loaders } from "@inweb/viewer-three";
2
+
3
+ import { Potree, PointColorType, PointSizeType, PointShape } from "potree-core";
4
+
5
+ class PotreeModelImpl extends ModelImpl {
6
+ getExtents(target) {
7
+ return target.union(this.pco.pcoGeometry.boundingBox);
8
+ }
9
+ }
10
+
11
+ class PotreeFileLoader extends Loader {
12
+ constructor(viewer) {
13
+ super();
14
+ this.updatePointClouds = () => {
15
+ const result = this.potree.updatePointClouds(this.pointClouds, this.viewer.camera, this.viewer.renderer);
16
+ if (result.exceededMaxLoadsToGPU || result.nodeLoadPromises.length > 0) this.viewer.update();
17
+ };
18
+ this.viewer = viewer;
19
+ this.potree = new Potree;
20
+ this.pointClouds = [];
21
+ this.viewer.addEventListener("render", this.updatePointClouds);
22
+ this.viewer.addEventListener("changecamera", this.updatePointClouds);
23
+ }
24
+ dispose() {
25
+ this.pointClouds.forEach((pco => pco.dispose()));
26
+ this.viewer.removeEventListener("render", this.updatePointClouds);
27
+ this.viewer.removeEventListener("changecamera", this.updatePointClouds);
28
+ }
29
+ isSupport(file, format) {
30
+ return typeof file === "string" && /(cloud.js|metadata.json)$/i.test(file);
31
+ }
32
+ async load(file, format, params = {}) {
33
+ const path = (params.path || "") + file;
34
+ const index = path.lastIndexOf("/");
35
+ const baseUrl = path.slice(0, index + 1);
36
+ const url = path.slice(index + 1);
37
+ const pco = await this.potree.loadPointCloud(url, baseUrl);
38
+ pco.material.size = 1;
39
+ pco.material.shape = 2;
40
+ pco.material.inputColorEncoding = 1;
41
+ pco.material.outputColorEncoding = 1;
42
+ pco.material.pointColorType = PointColorType.RGB;
43
+ pco.material.pointSizeType = PointSizeType.ADAPTIVE;
44
+ pco.material.shape = PointShape.CIRCLE;
45
+ if (params.position) pco.position.copy(params.position);
46
+ if (params.rotation) pco.rotation.copy(params.rotation);
47
+ if (params.scale) pco.scale.copy(params.scale);
48
+ this.pointClouds.push(pco);
49
+ const modelImpl = new PotreeModelImpl(pco);
50
+ modelImpl.loader = this;
51
+ modelImpl.viewer = this.viewer;
52
+ modelImpl.pco = pco;
53
+ this.viewer.scene.add(pco);
54
+ this.viewer.models.push(modelImpl);
55
+ this.viewer.syncOptions();
56
+ this.viewer.syncOverlay();
57
+ this.viewer.update();
58
+ this.viewer.emitEvent({
59
+ type: "databasechunk",
60
+ data: pco,
61
+ file: file
62
+ });
63
+ return this;
64
+ }
65
+ }
66
+
67
+ loaders.registerLoader("potree-file", (viewer => new PotreeFileLoader(viewer)));
68
+ //# sourceMappingURL=PotreeLoader.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PotreeLoader.module.js","sources":["../../../plugins/loaders/Potree/PotreeModelImpl.ts","../../../plugins/loaders/Potree/PotreeFileLoader.ts","../../../plugins/loaders/Potree/index.ts"],"sourcesContent":["///////////////////////////////////////////////////////////////////////////////\n// Copyright (C) 2002-2025, Open Design Alliance (the \"Alliance\").\n// All rights reserved.\n//\n// This software and its documentation and related materials are owned by\n// the Alliance. The software may only be incorporated into application\n// programs owned by members of the Alliance, subject to a signed\n// Membership Agreement and Supplemental Software License Agreement with the\n// Alliance. The structure and organization of this software are the valuable\n// trade secrets of the Alliance and its suppliers. The software is also\n// protected by copyright law and international treaty provisions. Application\n// programs incorporating this software must include the following statement\n// with their copyright notices:\n//\n// This application incorporates Open Design Alliance software pursuant to a\n// license agreement with Open Design Alliance.\n// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.\n// All rights reserved.\n//\n// By use of this software, its documentation or related materials, you\n// acknowledge and accept the above terms.\n///////////////////////////////////////////////////////////////////////////////\n\nimport { Box3 } from \"three\";\nimport { PointCloudOctree } from \"potree-core\";\nimport { ModelImpl } from \"@inweb/viewer-three\";\n\n// Potree model implementation.\n\nexport class PotreeModelImpl extends ModelImpl {\n public pco: PointCloudOctree;\n\n override getExtents(target: Box3): Box3 {\n return target.union(this.pco.pcoGeometry.boundingBox);\n }\n}\n","///////////////////////////////////////////////////////////////////////////////\n// Copyright (C) 2002-2025, Open Design Alliance (the \"Alliance\").\n// All rights reserved.\n//\n// This software and its documentation and related materials are owned by\n// the Alliance. The software may only be incorporated into application\n// programs owned by members of the Alliance, subject to a signed\n// Membership Agreement and Supplemental Software License Agreement with the\n// Alliance. The structure and organization of this software are the valuable\n// trade secrets of the Alliance and its suppliers. The software is also\n// protected by copyright law and international treaty provisions. Application\n// programs incorporating this software must include the following statement\n// with their copyright notices:\n//\n// This application incorporates Open Design Alliance software pursuant to a\n// license agreement with Open Design Alliance.\n// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.\n// All rights reserved.\n//\n// By use of this software, its documentation or related materials, you\n// acknowledge and accept the above terms.\n///////////////////////////////////////////////////////////////////////////////\n\nimport { Euler, Vector3 } from \"three\";\nimport { PointColorType, PointCloudOctree, PointSizeType, PointShape, Potree } from \"potree-core\";\nimport { Loader, Viewer } from \"@inweb/viewer-three\";\n\nimport { PotreeModelImpl } from \"./PotreeModelImpl\";\n\nexport type PotreeLoadParams = {\n path?: string;\n position?: Vector3;\n rotation?: Euler;\n scale?: Vector3;\n};\n\nexport class PotreeFileLoader extends Loader {\n public viewer: Viewer;\n public potree: Potree;\n public pointClouds: PointCloudOctree[];\n\n constructor(viewer: Viewer) {\n super();\n this.viewer = viewer;\n\n this.potree = new Potree();\n this.pointClouds = [];\n\n this.viewer.addEventListener(\"render\", this.updatePointClouds);\n this.viewer.addEventListener(\"changecamera\", this.updatePointClouds);\n }\n\n override dispose() {\n this.pointClouds.forEach((pco) => pco.dispose());\n\n this.viewer.removeEventListener(\"render\", this.updatePointClouds);\n this.viewer.removeEventListener(\"changecamera\", this.updatePointClouds);\n }\n\n override isSupport(file: any, format?: string): boolean {\n return typeof file === \"string\" && /(cloud.js|metadata.json)$/i.test(file);\n }\n\n override async load(file: any, format?: string, params: PotreeLoadParams = {}): Promise<this> {\n const path = (params.path || \"\") + file;\n const index = path.lastIndexOf(\"/\");\n const baseUrl = path.slice(0, index + 1);\n const url = path.slice(index + 1);\n\n const pco = await this.potree.loadPointCloud(url, baseUrl);\n pco.material.size = 1.0;\n pco.material.shape = 2;\n pco.material.inputColorEncoding = 1;\n pco.material.outputColorEncoding = 1;\n pco.material.pointColorType = PointColorType.RGB; // RGB | DEPTH | HEIGHT | POINT_INDEX | LOD | CLASSIFICATION\n pco.material.pointSizeType = PointSizeType.ADAPTIVE; // ADAPTIVE | FIXED\n pco.material.shape = PointShape.CIRCLE; // CIRCLE | SQUARE\n\n if (params.position) pco.position.copy(params.position);\n if (params.rotation) pco.rotation.copy(params.rotation);\n if (params.scale) pco.scale.copy(params.scale);\n\n this.pointClouds.push(pco);\n\n const modelImpl = new PotreeModelImpl(pco);\n modelImpl.loader = this;\n modelImpl.viewer = this.viewer;\n modelImpl.pco = pco;\n\n this.viewer.scene.add(pco);\n this.viewer.models.push(modelImpl);\n\n this.viewer.syncOptions();\n this.viewer.syncOverlay();\n this.viewer.update();\n\n this.viewer.emitEvent({ type: \"databasechunk\", data: pco, file });\n\n return this;\n }\n\n updatePointClouds = () => {\n const result = this.potree.updatePointClouds(this.pointClouds, this.viewer.camera, this.viewer.renderer);\n if (result.exceededMaxLoadsToGPU || result.nodeLoadPromises.length > 0) this.viewer.update();\n };\n}\n","///////////////////////////////////////////////////////////////////////////////\n// Copyright (C) 2002-2025, Open Design Alliance (the \"Alliance\").\n// All rights reserved.\n//\n// This software and its documentation and related materials are owned by\n// the Alliance. The software may only be incorporated into application\n// programs owned by members of the Alliance, subject to a signed\n// Membership Agreement and Supplemental Software License Agreement with the\n// Alliance. The structure and organization of this software are the valuable\n// trade secrets of the Alliance and its suppliers. The software is also\n// protected by copyright law and international treaty provisions. Application\n// programs incorporating this software must include the following statement\n// with their copyright notices:\n//\n// This application incorporates Open Design Alliance software pursuant to a\n// license agreement with Open Design Alliance.\n// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.\n// All rights reserved.\n//\n// By use of this software, its documentation or related materials, you\n// acknowledge and accept the above terms.\n///////////////////////////////////////////////////////////////////////////////\n\nimport { loaders } from \"@inweb/viewer-three\";\n\nimport { PotreeFileLoader } from \"./PotreeFileLoader\";\n\nloaders.registerLoader(\"potree-file\", (viewer: any) => new PotreeFileLoader(viewer));\n"],"names":["PotreeModelImpl","ModelImpl","getExtents","target","union","this","pco","pcoGeometry","boundingBox","PotreeFileLoader","Loader","constructor","viewer","super","updatePointClouds","result","potree","pointClouds","camera","renderer","exceededMaxLoadsToGPU","nodeLoadPromises","length","update","Potree","addEventListener","dispose","forEach","removeEventListener","isSupport","file","format","test","load","params","path","index","lastIndexOf","baseUrl","slice","url","loadPointCloud","material","size","shape","inputColorEncoding","outputColorEncoding","pointColorType","PointColorType","RGB","pointSizeType","PointSizeType","ADAPTIVE","PointShape","CIRCLE","position","copy","rotation","scale","push","modelImpl","loader","scene","add","models","syncOptions","syncOverlay","emitEvent","type","data","loaders","registerLoader"],"mappings":";;;;AA6BM,MAAOA,wBAAwBC;IAG1B,UAAAC,CAAWC;QAClB,OAAOA,OAAOC,MAAMC,KAAKC,IAAIC,YAAYC;;;;ACGvC,MAAOC,yBAAyBC;IAKpC,WAAAC,CAAYC;QACVC;QA2DFR,KAAiBS,oBAAG;YAClB,MAAMC,SAASV,KAAKW,OAAOF,kBAAkBT,KAAKY,aAAaZ,KAAKO,OAAOM,QAAQb,KAAKO,OAAOO;YAC/F,IAAIJ,OAAOK,yBAAyBL,OAAOM,iBAAiBC,SAAS,GAAGjB,KAAKO,OAAOW;AAAQ;QA5D5FlB,KAAKO,SAASA;QAEdP,KAAKW,SAAS,IAAIQ;QAClBnB,KAAKY,cAAc;QAEnBZ,KAAKO,OAAOa,iBAAiB,UAAUpB,KAAKS;QAC5CT,KAAKO,OAAOa,iBAAiB,gBAAgBpB,KAAKS;;IAG3C,OAAAY;QACPrB,KAAKY,YAAYU,SAASrB,OAAQA,IAAIoB;QAEtCrB,KAAKO,OAAOgB,oBAAoB,UAAUvB,KAAKS;QAC/CT,KAAKO,OAAOgB,oBAAoB,gBAAgBvB,KAAKS;;IAG9C,SAAAe,CAAUC,MAAWC;QAC5B,cAAcD,SAAS,YAAY,6BAA6BE,KAAKF;;IAG9D,UAAMG,CAAKH,MAAWC,QAAiBG,SAA2B,CAAA;QACzE,MAAMC,QAAQD,OAAOC,QAAQ,MAAML;QACnC,MAAMM,QAAQD,KAAKE,YAAY;QAC/B,MAAMC,UAAUH,KAAKI,MAAM,GAAGH,QAAQ;QACtC,MAAMI,MAAML,KAAKI,MAAMH,QAAQ;QAE/B,MAAM9B,YAAYD,KAAKW,OAAOyB,eAAeD,KAAKF;QAClDhC,IAAIoC,SAASC,OAAO;QACpBrC,IAAIoC,SAASE,QAAQ;QACrBtC,IAAIoC,SAASG,qBAAqB;QAClCvC,IAAIoC,SAASI,sBAAsB;QACnCxC,IAAIoC,SAASK,iBAAiBC,eAAeC;QAC7C3C,IAAIoC,SAASQ,gBAAgBC,cAAcC;QAC3C9C,IAAIoC,SAASE,QAAQS,WAAWC;QAEhC,IAAIpB,OAAOqB,UAAUjD,IAAIiD,SAASC,KAAKtB,OAAOqB;QAC9C,IAAIrB,OAAOuB,UAAUnD,IAAImD,SAASD,KAAKtB,OAAOuB;QAC9C,IAAIvB,OAAOwB,OAAOpD,IAAIoD,MAAMF,KAAKtB,OAAOwB;QAExCrD,KAAKY,YAAY0C,KAAKrD;QAEtB,MAAMsD,YAAY,IAAI5D,gBAAgBM;QACtCsD,UAAUC,SAASxD;QACnBuD,UAAUhD,SAASP,KAAKO;QACxBgD,UAAUtD,MAAMA;QAEhBD,KAAKO,OAAOkD,MAAMC,IAAIzD;QACtBD,KAAKO,OAAOoD,OAAOL,KAAKC;QAExBvD,KAAKO,OAAOqD;QACZ5D,KAAKO,OAAOsD;QACZ7D,KAAKO,OAAOW;QAEZlB,KAAKO,OAAOuD,UAAU;YAAEC,MAAM;YAAiBC,MAAM/D;YAAKwB;;QAE1D,OAAOzB;;;;ACvEXiE,QAAQC,eAAe,gBAAgB3D,UAAgB,IAAIH,iBAAiBG"}
@@ -80902,6 +80902,12 @@ void main() {
80902
80902
  this.viewer.target.copy(this.orbit.target);
80903
80903
  this.viewer.update();
80904
80904
  switch (this.orbit.state) {
80905
+ case STATE.ROTATE:
80906
+ case STATE.TOUCH_ROTATE:
80907
+ this.viewer.emitEvent({
80908
+ type: "orbit",
80909
+ });
80910
+ break;
80905
80911
  case STATE.PAN:
80906
80912
  case STATE.TOUCH_PAN:
80907
80913
  this.viewer.emitEvent({
@@ -80921,6 +80927,7 @@ void main() {
80921
80927
  });
80922
80928
  break;
80923
80929
  }
80930
+ this.viewer.emitEvent({ type: "changecamera" });
80924
80931
  this.changed = true;
80925
80932
  };
80926
80933
  this.stopContextMenu = (event) => {
@@ -81724,6 +81731,7 @@ void main() {
81724
81731
  };
81725
81732
  this.controlsChange = () => {
81726
81733
  this.viewer.update();
81734
+ this.viewer.emitEvent({ type: "changecamera" });
81727
81735
  };
81728
81736
  this.walkspeedChange = (event) => {
81729
81737
  this.viewer.emitEvent(event);
@@ -82047,32 +82055,32 @@ void main() {
82047
82055
  function zoomTo(viewer, box) {
82048
82056
  if (box.isEmpty())
82049
82057
  return;
82050
- const center = box.getCenter(new Vector3());
82051
- const sphere = box.getBoundingSphere(new Sphere());
82058
+ const boxCenter = box.getCenter(new Vector3());
82059
+ const boxSize = box.getBoundingSphere(new Sphere()).radius;
82052
82060
  const rendererSize = viewer.renderer.getSize(new Vector2());
82053
82061
  const aspect = rendererSize.x / rendererSize.y;
82054
82062
  const camera = viewer.camera;
82055
82063
  if (camera.isPerspectiveCamera) {
82056
- const offset = new Vector3(0, 0, 1);
82057
- offset.applyQuaternion(camera.quaternion);
82058
- offset.multiplyScalar(sphere.radius / Math.tan(MathUtils.DEG2RAD * camera.fov * 0.5));
82059
- camera.position.copy(center).add(offset);
82064
+ const offset = new Vector3(0, 0, 1)
82065
+ .applyQuaternion(camera.quaternion)
82066
+ .multiplyScalar(boxSize / Math.tan(MathUtils.DEG2RAD * camera.fov * 0.5));
82067
+ camera.position.copy(offset).add(boxCenter);
82060
82068
  camera.updateMatrixWorld();
82061
82069
  }
82062
82070
  if (camera.isOrthographicCamera) {
82063
- camera.top = sphere.radius;
82064
- camera.bottom = -sphere.radius;
82071
+ camera.top = boxSize;
82072
+ camera.bottom = -boxSize;
82065
82073
  camera.left = camera.bottom * aspect;
82066
82074
  camera.right = camera.top * aspect;
82067
82075
  camera.zoom = 1;
82068
82076
  camera.updateProjectionMatrix();
82069
- const offset = new Vector3(0, 0, 1);
82070
- offset.applyQuaternion(camera.quaternion);
82071
- offset.multiplyScalar(viewer.extents.getBoundingSphere(new Sphere()).radius * 3);
82072
- camera.position.copy(center).add(offset);
82077
+ const offset = new Vector3(0, 0, 1)
82078
+ .applyQuaternion(camera.quaternion)
82079
+ .multiplyScalar(viewer.extents.getBoundingSphere(new Sphere()).radius * 3);
82080
+ camera.position.copy(offset).add(boxCenter);
82073
82081
  camera.updateMatrixWorld();
82074
82082
  }
82075
- viewer.target.copy(center);
82083
+ viewer.target.copy(boxCenter);
82076
82084
  viewer.update();
82077
82085
  viewer.emitEvent({ type: "zoom" });
82078
82086
  }
@@ -82100,27 +82108,30 @@ void main() {
82100
82108
  // acknowledge and accept the above terms.
82101
82109
  ///////////////////////////////////////////////////////////////////////////////
82102
82110
  const defaultViewPositions = {
82103
- front: new Vector3(0, 0, 1),
82104
- back: new Vector3(0, 0, -1),
82105
- left: new Vector3(-1, 0, 0),
82106
- right: new Vector3(1, 0, 0),
82111
+ front: new Vector3(0, 0, -1),
82112
+ back: new Vector3(0, 0, 1),
82113
+ left: new Vector3(1, 0, 0),
82114
+ right: new Vector3(-1, 0, 0),
82107
82115
  bottom: new Vector3(0, -1, 0),
82108
82116
  top: new Vector3(0, 1, 0),
82109
- ns: new Vector3(-0.5, 1.0, -0.5).normalize(),
82110
- sw: new Vector3(0.5, 1.0, -0.5).normalize(),
82111
- nw: new Vector3(0.5, 1.0, 0.5).normalize(),
82112
- se: new Vector3(-0.5, 1.0, 0.5).normalize(),
82117
+ se: new Vector3(-1, 1, -1).normalize(),
82118
+ sw: new Vector3(1, 1, -1).normalize(),
82119
+ ne: new Vector3(-1, 1, 1).normalize(),
82120
+ nw: new Vector3(1, 1, 1).normalize(),
82113
82121
  };
82114
82122
  function setDefaultViewPosition(viewer, position) {
82115
- const direction = defaultViewPositions[position] || defaultViewPositions["sw"];
82116
- const center = viewer.extents.getCenter(new Vector3());
82117
- const sphere = viewer.extents.getBoundingSphere(new Sphere());
82118
- const offset = direction.clone().multiplyScalar(sphere.radius);
82123
+ const extentsCenter = viewer.extents.getCenter(new Vector3());
82124
+ const extentsSize = viewer.extents.getBoundingSphere(new Sphere()).radius * 2;
82125
+ const upY = new Vector3(0, 1, 0);
82126
+ const offsetY = defaultViewPositions[position] || defaultViewPositions["sw"];
82127
+ const up = new Vector3().copy(viewer.camera.up);
82128
+ const quaternion = new Quaternion().setFromUnitVectors(upY, up);
82129
+ const offset = new Vector3().copy(offsetY).applyQuaternion(quaternion);
82119
82130
  const camera = viewer.camera;
82120
- camera.position.copy(center).add(offset);
82121
- camera.lookAt(center);
82131
+ camera.position.copy(offset).multiplyScalar(extentsSize).add(extentsCenter);
82132
+ camera.lookAt(extentsCenter);
82122
82133
  camera.updateMatrixWorld();
82123
- viewer.target.copy(center);
82134
+ viewer.target.copy(extentsCenter);
82124
82135
  viewer.update();
82125
82136
  viewer.emit({ type: "viewposition", data: position });
82126
82137
  zoomTo(viewer, viewer.extents);
@@ -82802,7 +82813,7 @@ void main() {
82802
82813
  const extents = new Box3();
82803
82814
  this.viewer.models.forEach((model) => model.getExtents(extents));
82804
82815
  this.viewer.extents.copy(extents);
82805
- extents.getCenter(this.viewer.target);
82816
+ this.viewer.extents.getCenter(this.viewer.target);
82806
82817
  };
82807
82818
  this.viewer = viewer;
82808
82819
  this.viewer.addEventListener("databasechunk", this.syncExtents);
@@ -82857,12 +82868,13 @@ void main() {
82857
82868
  return;
82858
82869
  const extentsCenter = this.viewer.extents.getCenter(new Vector3());
82859
82870
  const extentsSize = this.viewer.extents.getBoundingSphere(new Sphere()).radius;
82860
- const front = new Vector3()
82861
- .copy(this.viewer.camera.up)
82862
- .cross(new Vector3(1, 0, 0))
82863
- .negate();
82871
+ const upY = new Vector3(0, 1, 0);
82872
+ const frontY = new Vector3(0, 0, -1);
82873
+ const up = new Vector3().copy(this.viewer.camera.up);
82874
+ const quaternion = new Quaternion().setFromUnitVectors(upY, up);
82875
+ const front = new Vector3().copy(frontY).applyQuaternion(quaternion).negate();
82864
82876
  this.directionalLight.position
82865
- .copy(this.viewer.camera.up)
82877
+ .copy(up)
82866
82878
  .applyAxisAngle(front, (-Math.PI * 30) / 180)
82867
82879
  .multiplyScalar(extentsSize * 2)
82868
82880
  .add(extentsCenter);
@@ -82968,21 +82980,7 @@ void main() {
82968
82980
  const { width, height } = entries[0].contentRect;
82969
82981
  if (!width || !height)
82970
82982
  return; // <- invisible viewer, or viewer with parent removed
82971
- const camera = this.viewer.camera;
82972
- const aspect = width / height;
82973
- if (camera.isPerspectiveCamera) {
82974
- camera.aspect = aspect;
82975
- camera.updateProjectionMatrix();
82976
- }
82977
- if (camera.isOrthographicCamera) {
82978
- camera.left = camera.bottom * aspect;
82979
- camera.right = camera.top * aspect;
82980
- camera.updateProjectionMatrix();
82981
- }
82982
- this.viewer.renderer.setSize(width, height, true);
82983
- this.viewer.composer.setSize(width, height);
82984
- this.viewer.update(true);
82985
- this.viewer.emitEvent({ type: "resize", width, height });
82983
+ this.viewer.setSize(width, height);
82986
82984
  };
82987
82985
  this.viewer = viewer;
82988
82986
  this.resizeObserver = new ResizeObserver(this.resizeViewer);
@@ -84462,9 +84460,9 @@ void main() {
84462
84460
  this.size = 160;
84463
84461
  this.orthoCamera = new OrthographicCamera(-2, 2, 2, -2, 0, 4);
84464
84462
  this.orthoCamera.position.set(0, 0, 2);
84465
- const matRed = new MeshBasicMaterial({ toneMapped: false, color: "#aa0000" });
84466
- const matGreen = new MeshBasicMaterial({ toneMapped: false, color: "#00aa00" });
84467
- const matBlue = new MeshBasicMaterial({ toneMapped: false, color: "#0000aa" });
84463
+ const matRed = new MeshBasicMaterial({ toneMapped: false, color: "#dd0000" });
84464
+ const matGreen = new MeshBasicMaterial({ toneMapped: false, color: "#00dd00" });
84465
+ const matBlue = new MeshBasicMaterial({ toneMapped: false, color: "#0000dd" });
84468
84466
  const spriteRed = this.getSpriteMaterial(matRed.color, "X");
84469
84467
  const spriteGreen = this.getSpriteMaterial(matGreen.color, "Y");
84470
84468
  const spriteBlue = this.getSpriteMaterial(matBlue.color, "Z");
@@ -90265,11 +90263,12 @@ void main() {
90265
90263
  const MAX_CHUNK = 30 * 1024 * 1024; // 100 MB
90266
90264
 
90267
90265
  class GltfStructure {
90268
- constructor(id) {
90266
+ constructor(id, loadController) {
90269
90267
  this.id = `${id}`;
90270
90268
  this.json = null;
90271
90269
  this.baseUrl = "";
90272
- this.loadController = null;
90270
+ this.loadController = loadController;
90271
+ this.loader = null;
90273
90272
  this.batchDelay = 10;
90274
90273
  this.maxBatchSize = 5 * 1024 * 1024;
90275
90274
  this.maxRangesPerRequest = 512;
@@ -90281,10 +90280,10 @@ void main() {
90281
90280
  this.materialCache = new Map();
90282
90281
  }
90283
90282
 
90284
- async initialize(loadController) {
90285
- this.json = await loadController.loadJson();
90286
- this.baseUrl = await loadController.baseUrl();
90287
- this.loadController = loadController;
90283
+ async initialize(loader) {
90284
+ this.json = await this.loadController.loadJson();
90285
+ this.baseUrl = await this.loadController.baseUrl();
90286
+ this.loader = loader;
90288
90287
  }
90289
90288
 
90290
90289
  clear() {
@@ -90297,10 +90296,12 @@ void main() {
90297
90296
  this.batchTimeout = null;
90298
90297
  }
90299
90298
 
90300
- // Clear materials and textures
90301
90299
  this.disposeMaterials();
90302
90300
  this.textureCache.clear();
90303
90301
  this.materials.clear();
90302
+
90303
+ this.activeChunkLoads = 0;
90304
+ this.chunkQueue = [];
90304
90305
  }
90305
90306
 
90306
90307
  getJson() {
@@ -90379,35 +90380,32 @@ void main() {
90379
90380
  finalRanges.push({ start, end, requests });
90380
90381
  }
90381
90382
  }
90382
- /*
90383
- for (const range of finalRanges) {
90384
- const length = range.end - range.start;
90385
- const buffer = await this.loadController.loadBinaryData([
90386
- { offset: range.start, length: length }
90387
- ]);
90388
- for (const req of range.requests) {
90389
- const relOffset = req.offset - range.start;
90390
- try {
90391
- req._resolve({ buffer, relOffset, length: req.length });
90392
- } catch (e) {
90393
- req._reject(e);
90394
- }
90395
- }
90396
- }
90397
- */
90398
90383
 
90399
- const promises = finalRanges.map(async (range) => {
90400
- const length = range.end - range.start;
90401
- const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }]);
90402
- for (const req of range.requests) {
90403
- const relOffset = req.offset - range.start;
90404
- try {
90405
- req._resolve({ buffer, relOffset, length: req.length });
90406
- } catch (e) {
90407
- req._reject(e);
90384
+ const promises = finalRanges.map(async (range, index) => {
90385
+ await this.loader.waitForChunkSlot();
90386
+
90387
+ try {
90388
+ const length = range.end - range.start;
90389
+ const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }]);
90390
+
90391
+ for (const req of range.requests) {
90392
+ const relOffset = req.offset - range.start;
90393
+ try {
90394
+ req._resolve({ buffer, relOffset, length: req.length });
90395
+ } catch (e) {
90396
+ req._reject(e);
90397
+ }
90408
90398
  }
90399
+ } catch (error) {
90400
+ for (const req of range.requests) {
90401
+ req._reject(error);
90402
+ }
90403
+ console.warn(`Failed to load chunk ${index + 1}/${finalRanges.length} (${range.start}-${range.end}):`, error);
90404
+ } finally {
90405
+ this.loader.releaseChunkSlot();
90409
90406
  }
90410
90407
  });
90408
+
90411
90409
  await Promise.all(promises);
90412
90410
 
90413
90411
  this.pendingRequests = [];
@@ -90825,6 +90823,10 @@ void main() {
90825
90823
  this.hiddenHandles = new Set();
90826
90824
  this.newOptimizedObjects = new Set();
90827
90825
  this.oldOptimizeObjects = new Set();
90826
+
90827
+ this.maxConcurrentChunks = 8;
90828
+ this.activeChunkLoads = 0;
90829
+ this.chunkQueue = [];
90828
90830
  }
90829
90831
 
90830
90832
  setVisibleEdges(visible) {
@@ -91234,6 +91236,7 @@ void main() {
91234
91236
  const structureArray = Array.isArray(structures) ? structures : [structures];
91235
91237
 
91236
91238
  for (const structure of structureArray) {
91239
+ await structure.initialize(this);
91237
91240
  this.structures.push(structure);
91238
91241
  }
91239
91242
 
@@ -91625,13 +91628,6 @@ void main() {
91625
91628
  this.nodesToLoad = [];
91626
91629
  }
91627
91630
 
91628
- async addStructure(loadController) {
91629
- const structure = new GltfStructure();
91630
- await structure.initialize(loadController);
91631
- this.structures.push(structure);
91632
- return structure;
91633
- }
91634
-
91635
91631
  removeOptimization() {
91636
91632
  this.originalObjects.forEach((obj) => (obj.visible = true));
91637
91633
 
@@ -91663,7 +91659,8 @@ void main() {
91663
91659
  }
91664
91660
 
91665
91661
  clear() {
91666
- // Clear all structures
91662
+ this.chunkQueue = [];
91663
+
91667
91664
  this.structures.forEach((structure) => {
91668
91665
  if (structure) {
91669
91666
  structure.clear();
@@ -91704,7 +91701,6 @@ void main() {
91704
91701
  });
91705
91702
  this.loadedMeshes.clear();
91706
91703
 
91707
- // Clear all structure roots and their children
91708
91704
  this.structureRoots.forEach((rootGroup) => {
91709
91705
  if (rootGroup) {
91710
91706
  rootGroup.traverse((child) => {
@@ -91724,7 +91720,6 @@ void main() {
91724
91720
  });
91725
91721
  this.structureRoots.clear();
91726
91722
 
91727
- // Clear all optimized objects
91728
91723
  this.mergedMesh.forEach((mesh) => {
91729
91724
  if (mesh.geometry) mesh.geometry.dispose();
91730
91725
  if (mesh.material) {
@@ -92477,7 +92472,6 @@ void main() {
92477
92472
  });
92478
92473
  }
92479
92474
 
92480
- // Возвращает bounding box для конкретной структуры
92481
92475
  getStructureGeometryExtent(structureId) {
92482
92476
  const extent = new Box3();
92483
92477
  for (const [nodeId, node] of this.nodes.entries()) {
@@ -92495,6 +92489,35 @@ void main() {
92495
92489
  }
92496
92490
  return extent;
92497
92491
  }
92492
+
92493
+ setMaxConcurrentChunks(maxChunks) {
92494
+ if (maxChunks < 1) {
92495
+ console.warn("Max concurrent chunks must be at least 1");
92496
+ return;
92497
+ }
92498
+ this.maxConcurrentChunks = maxChunks;
92499
+ }
92500
+
92501
+ waitForChunkSlot() {
92502
+ if (this.activeChunkLoads < this.maxConcurrentChunks) {
92503
+ this.activeChunkLoads++;
92504
+ return Promise.resolve();
92505
+ }
92506
+
92507
+ return new Promise((resolve) => {
92508
+ this.chunkQueue.push(resolve);
92509
+ });
92510
+ }
92511
+
92512
+ releaseChunkSlot() {
92513
+ this.activeChunkLoads--;
92514
+
92515
+ if (this.chunkQueue.length > 0) {
92516
+ const nextResolve = this.chunkQueue.shift();
92517
+ this.activeChunkLoads++;
92518
+ nextResolve();
92519
+ }
92520
+ }
92498
92521
  }
92499
92522
 
92500
92523
  ///////////////////////////////////////////////////////////////////////////////
@@ -92540,6 +92563,7 @@ void main() {
92540
92563
  this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
92541
92564
  this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
92542
92565
  this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
92566
+ // this.gltfLoader.setMaxConcurrentChunks(this.viewer.options.maxConcurrentChunks);
92543
92567
  this.gltfLoader.addEventListener("databasechunk", (data) => {
92544
92568
  const modelImpl = new DynamicModelImpl(scene);
92545
92569
  modelImpl.loader = this;
@@ -92583,8 +92607,7 @@ void main() {
92583
92607
  },
92584
92608
  baseUrl: () => Promise.resolve(`${model.httpClient.serverUrl}${model.path}/`),
92585
92609
  };
92586
- const structure = new GltfStructure(model.id);
92587
- await structure.initialize(loadController);
92610
+ const structure = new GltfStructure(model.id, loadController);
92588
92611
  await this.gltfLoader.loadStructure(structure);
92589
92612
  await this.gltfLoader.loadNodes();
92590
92613
  return this;
@@ -109764,7 +109787,7 @@ void main() {
109764
109787
  this.target = new Vector3();
109765
109788
  this._activeDragger = null;
109766
109789
  this._components = [];
109767
- this.renderTime = 0;
109790
+ this._renderTime = 0;
109768
109791
  this.render = this.render.bind(this);
109769
109792
  this.update = this.update.bind(this);
109770
109793
  this._markup = new KonvaMarkup();
@@ -109790,13 +109813,13 @@ void main() {
109790
109813
  this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
109791
109814
  this.scene = new Scene();
109792
109815
  this.helpers = new Helpers();
109793
- this.target = new Vector3();
109816
+ this.target = new Vector3(0, 0, 0);
109794
109817
  const pixelRatio = window.devicePixelRatio;
109795
109818
  const rect = canvas.parentElement.getBoundingClientRect();
109796
109819
  const width = rect.width || 1;
109797
109820
  const height = rect.height || 1;
109798
109821
  const aspect = width / height;
109799
- this.camera = new PerspectiveCamera(45, aspect, 0.01, 1000);
109822
+ this.camera = new PerspectiveCamera(45, aspect, 0.001, 1000);
109800
109823
  this.camera.up.set(0, 1, 0);
109801
109824
  this.camera.position.set(0, 0, 1);
109802
109825
  this.camera.lookAt(this.target);
@@ -109828,6 +109851,7 @@ void main() {
109828
109851
  this.composer.addPass(this.fxaaPass);
109829
109852
  this.composer.addPass(this.ssaaRenderPass);
109830
109853
  this.composer.addPass(this.outputPass);
109854
+ this.composer.setSize(width, height);
109831
109855
  this.canvas = canvas;
109832
109856
  this.canvasEvents.forEach((x) => canvas.addEventListener(x, this.canvaseventlistener));
109833
109857
  this._markup.initialize(this.canvas, this.canvasEvents, this, this);
@@ -109836,8 +109860,8 @@ void main() {
109836
109860
  }
109837
109861
  this.syncOptions();
109838
109862
  this.syncOverlay();
109839
- this.renderTime = performance.now();
109840
- this.render(this.renderTime);
109863
+ this._renderTime = performance.now();
109864
+ this.render(this._renderTime);
109841
109865
  if (typeof onProgress === "function")
109842
109866
  onProgress(new ProgressEvent("progress", { lengthComputable: true, loaded: 1, total: 1 }));
109843
109867
  this.emitEvent({ type: "initializeprogress", data: 1, loaded: 1, total: 1 });
@@ -109889,8 +109913,27 @@ void main() {
109889
109913
  isInitialized() {
109890
109914
  return !!this.renderer;
109891
109915
  }
109916
+ setSize(width, height, updateStyle = true) {
109917
+ if (!this.renderer)
109918
+ return;
109919
+ const camera = this.camera;
109920
+ const aspect = width / height;
109921
+ if (camera.isPerspectiveCamera) {
109922
+ camera.aspect = aspect;
109923
+ camera.updateProjectionMatrix();
109924
+ }
109925
+ if (camera.isOrthographicCamera) {
109926
+ camera.left = camera.bottom * aspect;
109927
+ camera.right = camera.top * aspect;
109928
+ camera.updateProjectionMatrix();
109929
+ }
109930
+ this.renderer.setSize(width, height, updateStyle);
109931
+ this.composer.setSize(width, height);
109932
+ this.update(true);
109933
+ this.emitEvent({ type: "resize", width, height });
109934
+ }
109892
109935
  update(force = false) {
109893
- this.renderNeeded = true;
109936
+ this._renderNeeded = true;
109894
109937
  if (force)
109895
109938
  this.render();
109896
109939
  this.emitEvent({ type: "update", data: force });
@@ -109899,13 +109942,13 @@ void main() {
109899
109942
  var _a, _b;
109900
109943
  if (!this.renderer)
109901
109944
  return;
109902
- if (!(this.renderNeeded || force))
109945
+ if (!this._renderNeeded && !force)
109903
109946
  return;
109904
109947
  if (!time)
109905
109948
  time = performance.now();
109906
- const deltaTime = (time - this.renderTime) / 1000;
109907
- this.renderTime = time;
109908
- this.renderNeeded = false;
109949
+ const deltaTime = (time - this._renderTime) / 1000;
109950
+ this._renderTime = time;
109951
+ this._renderNeeded = false;
109909
109952
  if (this.options.antialiasing === true || this.options.antialiasing === "msaa") {
109910
109953
  this.renderer.render(this.scene, this.camera);
109911
109954
  this.renderer.render(this.helpers, this.camera);
@@ -110202,7 +110245,7 @@ void main() {
110202
110245
  camera.left = camera.bottom * aspect;
110203
110246
  camera.right = camera.top * aspect;
110204
110247
  camera.near = 0;
110205
- camera.far = extentsSize * 100;
110248
+ camera.far = extentsSize * 1000;
110206
110249
  camera.zoom = orthogonal_camera.view_to_world_scale;
110207
110250
  camera.updateProjectionMatrix();
110208
110251
  camera.up.copy(getVector3FromPoint3d(orthogonal_camera.up_vector));
@@ -110223,8 +110266,8 @@ void main() {
110223
110266
  const camera = new PerspectiveCamera();
110224
110267
  camera.fov = perspective_camera.field_of_view;
110225
110268
  camera.aspect = aspect;
110226
- camera.near = extentsSize / 100;
110227
- camera.far = extentsSize * 100;
110269
+ camera.near = extentsSize / 1000;
110270
+ camera.far = extentsSize * 1000;
110228
110271
  camera.updateProjectionMatrix();
110229
110272
  camera.up.copy(getVector3FromPoint3d(perspective_camera.up_vector));
110230
110273
  camera.position.copy(getVector3FromPoint3d(perspective_camera.view_point));