@inweb/viewer-three 26.8.2 → 26.9.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.
@@ -1547,32 +1547,28 @@ function collect(viewer) {
1547
1547
 
1548
1548
  function zoomTo(viewer, box) {
1549
1549
  if (box.isEmpty()) return;
1550
- const center = box.getCenter(new Vector3);
1551
- const sphere = box.getBoundingSphere(new Sphere);
1550
+ const boxCenter = box.getCenter(new Vector3);
1551
+ const boxSize = box.getBoundingSphere(new Sphere).radius;
1552
1552
  const rendererSize = viewer.renderer.getSize(new Vector2);
1553
1553
  const aspect = rendererSize.x / rendererSize.y;
1554
1554
  const camera = viewer.camera;
1555
1555
  if (camera.isPerspectiveCamera) {
1556
- const offset = new Vector3(0, 0, 1);
1557
- offset.applyQuaternion(camera.quaternion);
1558
- offset.multiplyScalar(sphere.radius / Math.tan(MathUtils.DEG2RAD * camera.fov * .5));
1559
- camera.position.copy(center).add(offset);
1556
+ const offset = new Vector3(0, 0, 1).applyQuaternion(camera.quaternion).multiplyScalar(boxSize / Math.tan(MathUtils.DEG2RAD * camera.fov * .5));
1557
+ camera.position.copy(offset).add(boxCenter);
1560
1558
  camera.updateMatrixWorld();
1561
1559
  }
1562
1560
  if (camera.isOrthographicCamera) {
1563
- camera.top = sphere.radius;
1564
- camera.bottom = -sphere.radius;
1561
+ camera.top = boxSize;
1562
+ camera.bottom = -boxSize;
1565
1563
  camera.left = camera.bottom * aspect;
1566
1564
  camera.right = camera.top * aspect;
1567
1565
  camera.zoom = 1;
1568
1566
  camera.updateProjectionMatrix();
1569
- const offset = new Vector3(0, 0, 1);
1570
- offset.applyQuaternion(camera.quaternion);
1571
- offset.multiplyScalar(viewer.extents.getBoundingSphere(new Sphere).radius * 3);
1572
- camera.position.copy(center).add(offset);
1567
+ const offset = new Vector3(0, 0, 1).applyQuaternion(camera.quaternion).multiplyScalar(viewer.extents.getBoundingSphere(new Sphere).radius * 3);
1568
+ camera.position.copy(offset).add(boxCenter);
1573
1569
  camera.updateMatrixWorld();
1574
1570
  }
1575
- viewer.target.copy(center);
1571
+ viewer.target.copy(boxCenter);
1576
1572
  viewer.update();
1577
1573
  viewer.emitEvent({
1578
1574
  type: "zoom"
@@ -1580,28 +1576,31 @@ function zoomTo(viewer, box) {
1580
1576
  }
1581
1577
 
1582
1578
  const defaultViewPositions = {
1583
- front: new Vector3(0, 0, 1),
1584
- back: new Vector3(0, 0, -1),
1585
- left: new Vector3(-1, 0, 0),
1586
- right: new Vector3(1, 0, 0),
1579
+ front: new Vector3(0, 0, -1),
1580
+ back: new Vector3(0, 0, 1),
1581
+ left: new Vector3(1, 0, 0),
1582
+ right: new Vector3(-1, 0, 0),
1587
1583
  bottom: new Vector3(0, -1, 0),
1588
1584
  top: new Vector3(0, 1, 0),
1589
- ns: new Vector3(-.5, 1, -.5).normalize(),
1590
- sw: new Vector3(.5, 1, -.5).normalize(),
1591
- nw: new Vector3(.5, 1, .5).normalize(),
1592
- se: new Vector3(-.5, 1, .5).normalize()
1585
+ se: new Vector3(-1, 1, -1).normalize(),
1586
+ sw: new Vector3(1, 1, -1).normalize(),
1587
+ ne: new Vector3(-1, 1, 1).normalize(),
1588
+ nw: new Vector3(1, 1, 1).normalize()
1593
1589
  };
1594
1590
 
1595
1591
  function setDefaultViewPosition(viewer, position) {
1596
- const direction = defaultViewPositions[position] || defaultViewPositions["sw"];
1597
- const center = viewer.extents.getCenter(new Vector3);
1598
- const sphere = viewer.extents.getBoundingSphere(new Sphere);
1599
- const offset = direction.clone().multiplyScalar(sphere.radius);
1592
+ const extentsCenter = viewer.extents.getCenter(new Vector3);
1593
+ const extentsSize = viewer.extents.getBoundingSphere(new Sphere).radius * 2;
1594
+ const upY = new Vector3(0, 1, 0);
1595
+ const offsetY = defaultViewPositions[position] || defaultViewPositions["sw"];
1596
+ const up = (new Vector3).copy(viewer.camera.up);
1597
+ const quaternion = (new Quaternion).setFromUnitVectors(upY, up);
1598
+ const offset = (new Vector3).copy(offsetY).applyQuaternion(quaternion);
1600
1599
  const camera = viewer.camera;
1601
- camera.position.copy(center).add(offset);
1602
- camera.lookAt(center);
1600
+ camera.position.copy(offset).multiplyScalar(extentsSize).add(extentsCenter);
1601
+ camera.lookAt(extentsCenter);
1603
1602
  camera.updateMatrixWorld();
1604
- viewer.target.copy(center);
1603
+ viewer.target.copy(extentsCenter);
1605
1604
  viewer.update();
1606
1605
  viewer.emit({
1607
1606
  type: "viewposition",
@@ -1931,8 +1930,12 @@ class LightComponent {
1931
1930
  if (this.viewer.extents.isEmpty()) return;
1932
1931
  const extentsCenter = this.viewer.extents.getCenter(new Vector3);
1933
1932
  const extentsSize = this.viewer.extents.getBoundingSphere(new Sphere).radius;
1934
- const front = (new Vector3).copy(this.viewer.camera.up).cross(new Vector3(1, 0, 0)).negate();
1935
- this.directionalLight.position.copy(this.viewer.camera.up).applyAxisAngle(front, -Math.PI * 30 / 180).multiplyScalar(extentsSize * 2).add(extentsCenter);
1933
+ const upY = new Vector3(0, 1, 0);
1934
+ const frontY = new Vector3(0, 0, -1);
1935
+ const up = (new Vector3).copy(this.viewer.camera.up);
1936
+ const quaternion = (new Quaternion).setFromUnitVectors(upY, up);
1937
+ const front = (new Vector3).copy(frontY).applyQuaternion(quaternion).negate();
1938
+ this.directionalLight.position.copy(up).applyAxisAngle(front, -Math.PI * 30 / 180).multiplyScalar(extentsSize * 2).add(extentsCenter);
1936
1939
  this.directionalLight.target.position.copy(extentsCenter);
1937
1940
  this.frontLight.position.copy(front).multiplyScalar(extentsSize * 2).add(extentsCenter);
1938
1941
  this.frontLight.target.position.copy(extentsCenter);
@@ -2722,11 +2725,12 @@ const MAX_GAP = 128 * 1024;
2722
2725
  const MAX_CHUNK = 30 * 1024 * 1024;
2723
2726
 
2724
2727
  class GltfStructure {
2725
- constructor(id) {
2728
+ constructor(id, loadController) {
2726
2729
  this.id = `${id}`;
2727
2730
  this.json = null;
2728
2731
  this.baseUrl = "";
2729
- this.loadController = null;
2732
+ this.loadController = loadController;
2733
+ this.loader = null;
2730
2734
  this.batchDelay = 10;
2731
2735
  this.maxBatchSize = 5 * 1024 * 1024;
2732
2736
  this.maxRangesPerRequest = 512;
@@ -2737,10 +2741,10 @@ class GltfStructure {
2737
2741
  this.textureCache = new Map;
2738
2742
  this.materialCache = new Map;
2739
2743
  }
2740
- async initialize(loadController) {
2741
- this.json = await loadController.loadJson();
2742
- this.baseUrl = await loadController.baseUrl();
2743
- this.loadController = loadController;
2744
+ async initialize(loader) {
2745
+ this.json = await this.loadController.loadJson();
2746
+ this.baseUrl = await this.loadController.baseUrl();
2747
+ this.loader = loader;
2744
2748
  }
2745
2749
  clear() {
2746
2750
  this.json = null;
@@ -2754,6 +2758,8 @@ class GltfStructure {
2754
2758
  this.disposeMaterials();
2755
2759
  this.textureCache.clear();
2756
2760
  this.materials.clear();
2761
+ this.activeChunkLoads = 0;
2762
+ this.chunkQueue = [];
2757
2763
  }
2758
2764
  getJson() {
2759
2765
  return this.json;
@@ -2831,23 +2837,33 @@ class GltfStructure {
2831
2837
  });
2832
2838
  }
2833
2839
  }
2834
- const promises = finalRanges.map((async range => {
2835
- const length = range.end - range.start;
2836
- const buffer = await this.loadController.loadBinaryData([ {
2837
- offset: range.start,
2838
- length: length
2839
- } ]);
2840
- for (const req of range.requests) {
2841
- const relOffset = req.offset - range.start;
2842
- try {
2843
- req._resolve({
2844
- buffer: buffer,
2845
- relOffset: relOffset,
2846
- length: req.length
2847
- });
2848
- } catch (e) {
2849
- req._reject(e);
2840
+ const promises = finalRanges.map((async (range, index) => {
2841
+ await this.loader.waitForChunkSlot();
2842
+ try {
2843
+ const length = range.end - range.start;
2844
+ const buffer = await this.loadController.loadBinaryData([ {
2845
+ offset: range.start,
2846
+ length: length
2847
+ } ]);
2848
+ for (const req of range.requests) {
2849
+ const relOffset = req.offset - range.start;
2850
+ try {
2851
+ req._resolve({
2852
+ buffer: buffer,
2853
+ relOffset: relOffset,
2854
+ length: req.length
2855
+ });
2856
+ } catch (e) {
2857
+ req._reject(e);
2858
+ }
2850
2859
  }
2860
+ } catch (error) {
2861
+ for (const req of range.requests) {
2862
+ req._reject(error);
2863
+ }
2864
+ console.warn(`Failed to load chunk ${index + 1}/${finalRanges.length} (${range.start}-${range.end}):`, error);
2865
+ } finally {
2866
+ this.loader.releaseChunkSlot();
2851
2867
  }
2852
2868
  }));
2853
2869
  await Promise.all(promises);
@@ -3207,6 +3223,9 @@ class DynamicGltfLoader {
3207
3223
  this.hiddenHandles = new Set;
3208
3224
  this.newOptimizedObjects = new Set;
3209
3225
  this.oldOptimizeObjects = new Set;
3226
+ this.maxConcurrentChunks = 8;
3227
+ this.activeChunkLoads = 0;
3228
+ this.chunkQueue = [];
3210
3229
  }
3211
3230
  setVisibleEdges(visible) {
3212
3231
  this.visibleEdges = visible;
@@ -3564,6 +3583,7 @@ class DynamicGltfLoader {
3564
3583
  this.clear();
3565
3584
  const structureArray = Array.isArray(structures) ? structures : [ structures ];
3566
3585
  for (const structure of structureArray) {
3586
+ await structure.initialize(this);
3567
3587
  this.structures.push(structure);
3568
3588
  }
3569
3589
  for (const structure of this.structures) {
@@ -3875,12 +3895,6 @@ class DynamicGltfLoader {
3875
3895
  clearNodesToLoad() {
3876
3896
  this.nodesToLoad = [];
3877
3897
  }
3878
- async addStructure(loadController) {
3879
- const structure = new GltfStructure;
3880
- await structure.initialize(loadController);
3881
- this.structures.push(structure);
3882
- return structure;
3883
- }
3884
3898
  removeOptimization() {
3885
3899
  this.originalObjects.forEach((obj => obj.visible = true));
3886
3900
  const disposeMerged = obj => {
@@ -3909,6 +3923,7 @@ class DynamicGltfLoader {
3909
3923
  this.originalObjectsToSelection.clear();
3910
3924
  }
3911
3925
  clear() {
3926
+ this.chunkQueue = [];
3912
3927
  this.structures.forEach((structure => {
3913
3928
  if (structure) {
3914
3929
  structure.clear();
@@ -4596,6 +4611,30 @@ class DynamicGltfLoader {
4596
4611
  }
4597
4612
  return extent;
4598
4613
  }
4614
+ setMaxConcurrentChunks(maxChunks) {
4615
+ if (maxChunks < 1) {
4616
+ console.warn("Max concurrent chunks must be at least 1");
4617
+ return;
4618
+ }
4619
+ this.maxConcurrentChunks = maxChunks;
4620
+ }
4621
+ waitForChunkSlot() {
4622
+ if (this.activeChunkLoads < this.maxConcurrentChunks) {
4623
+ this.activeChunkLoads++;
4624
+ return Promise.resolve();
4625
+ }
4626
+ return new Promise((resolve => {
4627
+ this.chunkQueue.push(resolve);
4628
+ }));
4629
+ }
4630
+ releaseChunkSlot() {
4631
+ this.activeChunkLoads--;
4632
+ if (this.chunkQueue.length > 0) {
4633
+ const nextResolve = this.chunkQueue.shift();
4634
+ this.activeChunkLoads++;
4635
+ nextResolve();
4636
+ }
4637
+ }
4599
4638
  }
4600
4639
 
4601
4640
  class GLTFCloudDynamicLoader {
@@ -4676,8 +4715,7 @@ class GLTFCloudDynamicLoader {
4676
4715
  },
4677
4716
  baseUrl: () => Promise.resolve(`${model.httpClient.serverUrl}${model.path}/`)
4678
4717
  };
4679
- const structure = new GltfStructure(model.id);
4680
- await structure.initialize(loadController);
4718
+ const structure = new GltfStructure(model.id, loadController);
4681
4719
  await this.gltfLoader.loadStructure(structure);
4682
4720
  await this.gltfLoader.loadNodes();
4683
4721
  return this;
@@ -4842,13 +4880,13 @@ class Viewer extends EventEmitter2 {
4842
4880
  this.addEventListener("optionschange", (event => this.syncOptions(event.data)));
4843
4881
  this.scene = new Scene;
4844
4882
  this.helpers = new Helpers;
4845
- this.target = new Vector3;
4883
+ this.target = new Vector3(0, 0, 0);
4846
4884
  const pixelRatio = window.devicePixelRatio;
4847
4885
  const rect = canvas.parentElement.getBoundingClientRect();
4848
4886
  const width = rect.width || 1;
4849
4887
  const height = rect.height || 1;
4850
4888
  const aspect = width / height;
4851
- this.camera = new PerspectiveCamera(45, aspect, .01, 1e3);
4889
+ this.camera = new PerspectiveCamera(45, aspect, .001, 1e3);
4852
4890
  this.camera.up.set(0, 1, 0);
4853
4891
  this.camera.position.set(0, 0, 1);
4854
4892
  this.camera.lookAt(this.target);
@@ -5059,10 +5097,6 @@ class Viewer extends EventEmitter2 {
5059
5097
  this.models = [];
5060
5098
  this.scene.clear();
5061
5099
  this.helpers.clear();
5062
- this.models.forEach((model => model.dispose()));
5063
- this.models = [];
5064
- this.helpers.clear();
5065
- this.scene.clear();
5066
5100
  this.syncOptions();
5067
5101
  this.syncOverlay();
5068
5102
  this.update(true);
@@ -5221,7 +5255,7 @@ class Viewer extends EventEmitter2 {
5221
5255
  camera.left = camera.bottom * aspect;
5222
5256
  camera.right = camera.top * aspect;
5223
5257
  camera.near = 0;
5224
- camera.far = extentsSize * 100;
5258
+ camera.far = extentsSize * 1e3;
5225
5259
  camera.zoom = orthogonal_camera.view_to_world_scale;
5226
5260
  camera.updateProjectionMatrix();
5227
5261
  camera.up.copy(getVector3FromPoint3d(orthogonal_camera.up_vector));
@@ -5242,8 +5276,8 @@ class Viewer extends EventEmitter2 {
5242
5276
  const camera = new PerspectiveCamera;
5243
5277
  camera.fov = perspective_camera.field_of_view;
5244
5278
  camera.aspect = aspect;
5245
- camera.near = extentsSize / 100;
5246
- camera.far = extentsSize * 100;
5279
+ camera.near = extentsSize / 1e3;
5280
+ camera.far = extentsSize * 1e3;
5247
5281
  camera.updateProjectionMatrix();
5248
5282
  camera.up.copy(getVector3FromPoint3d(perspective_camera.up_vector));
5249
5283
  camera.position.copy(getVector3FromPoint3d(perspective_camera.view_point));