@inweb/viewer-three 27.1.9 → 27.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/dist/viewer-three.js +94 -35
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +93 -35
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/Viewer.d.ts +1 -1
- package/lib/Viewer/controls/WalkControls.d.ts +6 -1
- package/package.json +5 -5
- package/src/Viewer/Viewer.ts +1 -1
- package/src/Viewer/components/CameraComponent.ts +1 -1
- package/src/Viewer/components/InfoComponent.ts +5 -1
- package/src/Viewer/controls/WalkControls.ts +24 -15
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +68 -21
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +1 -0
- package/src/Viewer/models/ModelImpl.ts +1 -0
|
@@ -1506,14 +1506,19 @@ class WalkControls extends Controls {
|
|
|
1506
1506
|
super(camera, canvas);
|
|
1507
1507
|
this.EYE_HEIGHT = 1.7;
|
|
1508
1508
|
this.FAILING_DISTANCE = 2;
|
|
1509
|
-
this.
|
|
1509
|
+
this.GROUND_FOLLOWING_SKIP_FRAMES = 3;
|
|
1510
|
+
this.GROUND_FOLLOWING_SPEED = 0.4;
|
|
1510
1511
|
this.LOOK_SPEED = 0.1;
|
|
1511
1512
|
this.WALK_SPEED_DELIMITER = 4;
|
|
1512
1513
|
this.WHEEL_SPEED_DELIMITER = 15000;
|
|
1513
1514
|
this.movementSpeed = 0.1;
|
|
1514
1515
|
this.multiplier = 3;
|
|
1516
|
+
this.groundFollowingSkippedFrames = 0;
|
|
1515
1517
|
this.moveWheel = 0;
|
|
1516
1518
|
this.mouseDragOn = false;
|
|
1519
|
+
this._up = new Vector3();
|
|
1520
|
+
this._forward = new Vector3();
|
|
1521
|
+
this._sideways = new Vector3();
|
|
1517
1522
|
this.onPointerDown = (event) => {
|
|
1518
1523
|
if (event.button !== 0)
|
|
1519
1524
|
return;
|
|
@@ -1583,6 +1588,14 @@ class WalkControls extends Controls {
|
|
|
1583
1588
|
this.raycaster = new Raycaster();
|
|
1584
1589
|
this.raycaster.near = 0;
|
|
1585
1590
|
this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
|
|
1591
|
+
this.raycaster.params = {
|
|
1592
|
+
Mesh: {},
|
|
1593
|
+
Line: { threshold: 0 },
|
|
1594
|
+
Line2: { threshold: 0 },
|
|
1595
|
+
LOD: { threshold: 0 },
|
|
1596
|
+
Points: { threshold: 0 },
|
|
1597
|
+
Sprite: { threshold: 0 },
|
|
1598
|
+
};
|
|
1586
1599
|
this.moveKeys = new Set();
|
|
1587
1600
|
this.moveClock = new Clock();
|
|
1588
1601
|
this.quaternion = camera.quaternion.clone();
|
|
@@ -1607,16 +1620,8 @@ class WalkControls extends Controls {
|
|
|
1607
1620
|
super.dispose();
|
|
1608
1621
|
}
|
|
1609
1622
|
updateGroundFollowing() {
|
|
1610
|
-
|
|
1611
|
-
this.raycaster.set(this.object.position,
|
|
1612
|
-
this.raycaster.params = this.raycaster.params = {
|
|
1613
|
-
Mesh: {},
|
|
1614
|
-
Line: { threshold: 0 },
|
|
1615
|
-
Line2: { threshold: 0 },
|
|
1616
|
-
LOD: { threshold: 0 },
|
|
1617
|
-
Points: { threshold: 0 },
|
|
1618
|
-
Sprite: { threshold: 0 },
|
|
1619
|
-
};
|
|
1623
|
+
this._up.copy(this.camera.up).negate();
|
|
1624
|
+
this.raycaster.set(this.object.position, this._up);
|
|
1620
1625
|
const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
|
|
1621
1626
|
if (intersects.length > 0) {
|
|
1622
1627
|
const groundY = intersects[0].point.y;
|
|
@@ -1627,8 +1632,8 @@ class WalkControls extends Controls {
|
|
|
1627
1632
|
update() {
|
|
1628
1633
|
let moved = false;
|
|
1629
1634
|
let upgradeGroundFollowing = false;
|
|
1630
|
-
const forward =
|
|
1631
|
-
const sideways =
|
|
1635
|
+
const forward = this._forward;
|
|
1636
|
+
const sideways = this._sideways;
|
|
1632
1637
|
if (this.moveKeys.size > 0) {
|
|
1633
1638
|
upgradeGroundFollowing = true;
|
|
1634
1639
|
const timeDelta = this.moveClock.getDelta();
|
|
@@ -1680,8 +1685,11 @@ class WalkControls extends Controls {
|
|
|
1680
1685
|
this.moveWheel += -1 * Math.sign(this.moveWheel);
|
|
1681
1686
|
moved = true;
|
|
1682
1687
|
}
|
|
1683
|
-
|
|
1688
|
+
this.groundFollowingSkippedFrames++;
|
|
1689
|
+
if (upgradeGroundFollowing && this.groundFollowingSkippedFrames >= this.GROUND_FOLLOWING_SKIP_FRAMES) {
|
|
1690
|
+
this.groundFollowingSkippedFrames = 0;
|
|
1684
1691
|
this.updateGroundFollowing();
|
|
1692
|
+
}
|
|
1685
1693
|
if (moved) {
|
|
1686
1694
|
this.dispatchEvent({ type: "change" });
|
|
1687
1695
|
}
|
|
@@ -2491,7 +2499,7 @@ class CameraComponent {
|
|
|
2491
2499
|
camera = object;
|
|
2492
2500
|
});
|
|
2493
2501
|
if (camera) {
|
|
2494
|
-
camera.isDefaultCamera = true;
|
|
2502
|
+
camera.userData.isDefaultCamera = true;
|
|
2495
2503
|
camera.scale.set(1, 1, 1);
|
|
2496
2504
|
this.switchCamera(camera);
|
|
2497
2505
|
const mode = this.getCameraMode(camera);
|
|
@@ -2695,6 +2703,7 @@ class InfoComponent {
|
|
|
2695
2703
|
this.viewer.info.optimizedScene.edges = 0;
|
|
2696
2704
|
this.viewer.info.memory.geometries = 0;
|
|
2697
2705
|
this.viewer.info.memory.geometryBytes = 0;
|
|
2706
|
+
this.viewer.info.memory.optimizedGeometryBytes = 0;
|
|
2698
2707
|
this.viewer.info.memory.textures = 0;
|
|
2699
2708
|
this.viewer.info.memory.textureBytes = 0;
|
|
2700
2709
|
this.viewer.info.memory.materials = 0;
|
|
@@ -2731,6 +2740,7 @@ class InfoComponent {
|
|
|
2731
2740
|
this.viewer.info.optimizedScene.edges += info.optimizedScene.edges;
|
|
2732
2741
|
this.viewer.info.memory.geometries += info.memory.geometries;
|
|
2733
2742
|
this.viewer.info.memory.geometryBytes += info.memory.geometryBytes;
|
|
2743
|
+
this.viewer.info.memory.optimizedGeometryBytes += info.memory.optimizedGeometryBytes;
|
|
2734
2744
|
this.viewer.info.memory.textures += info.memory.textures;
|
|
2735
2745
|
this.viewer.info.memory.textureBytes += info.memory.textureBytes;
|
|
2736
2746
|
this.viewer.info.memory.materials += info.memory.materials;
|
|
@@ -2741,7 +2751,9 @@ class InfoComponent {
|
|
|
2741
2751
|
this.viewer.info.performance.loadTime += performance.now() - this.startTime;
|
|
2742
2752
|
console.log("Number of objects:", info.scene.objects);
|
|
2743
2753
|
console.log("Number of objects after optimization:", info.optimizedScene.objects);
|
|
2744
|
-
console.log("
|
|
2754
|
+
console.log("Geometry size:", info.memory.geometryBytes / (1024 * 1024), "MB");
|
|
2755
|
+
console.log("Optimized geometry size:", info.memory.optimizedGeometryBytes / (1024 * 1024), "MB");
|
|
2756
|
+
console.log("Estimated GPU used:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
|
|
2745
2757
|
console.log("File load time:", this.viewer.info.performance.loadTime, "ms");
|
|
2746
2758
|
};
|
|
2747
2759
|
this.resize = () => {
|
|
@@ -3484,6 +3496,7 @@ class ModelImpl {
|
|
|
3484
3496
|
info.scene.edges = Math.floor(totalEdges);
|
|
3485
3497
|
info.memory.geometries = geometries.size;
|
|
3486
3498
|
info.memory.geometryBytes = geometryBytes;
|
|
3499
|
+
info.memory.optimizedGeometryBytes = 0;
|
|
3487
3500
|
info.memory.textures = textures.size;
|
|
3488
3501
|
info.memory.textureBytes = Math.floor(textureBytes);
|
|
3489
3502
|
info.memory.materials = materials.size;
|
|
@@ -3656,6 +3669,7 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3656
3669
|
info.optimizedScene.edges = stats.scene.afterOptimization.edges;
|
|
3657
3670
|
info.memory.geometries = stats.memory.geometries.count;
|
|
3658
3671
|
info.memory.geometryBytes = stats.memory.geometries.bytes;
|
|
3672
|
+
info.memory.optimizedGeometryBytes = stats.memory.geometries.optimizedBytes;
|
|
3659
3673
|
info.memory.textures = stats.memory.textures.count;
|
|
3660
3674
|
info.memory.materials = stats.memory.materials.count;
|
|
3661
3675
|
info.memory.totalEstimatedGpuBytes = stats.memory.totalEstimatedGpuBytes;
|
|
@@ -4327,11 +4341,14 @@ class DynamicGltfLoader {
|
|
|
4327
4341
|
this.structures = [];
|
|
4328
4342
|
this.structureRoots = new Map();
|
|
4329
4343
|
this.memoryLimit = this.getAvailableMemory();
|
|
4344
|
+
this.optimizationMemoryMultiplier = 5;
|
|
4345
|
+
this.memoryEstimationFactor = 1.7;
|
|
4330
4346
|
this.loadedGeometrySize = 0;
|
|
4331
4347
|
this.geometryCache = new Map();
|
|
4332
4348
|
this.materialCache = new Map();
|
|
4333
4349
|
this.textureCache = new Map();
|
|
4334
4350
|
this.currentMemoryUsage = 0;
|
|
4351
|
+
this.pendingMemoryUsage = 0;
|
|
4335
4352
|
this.updateMemoryIndicator();
|
|
4336
4353
|
this.loadedMaterials = new Map();
|
|
4337
4354
|
this.abortController = new AbortController();
|
|
@@ -4387,7 +4404,7 @@ class DynamicGltfLoader {
|
|
|
4387
4404
|
} catch (error) {
|
|
4388
4405
|
console.warn("Error detecting available memory:", error);
|
|
4389
4406
|
}
|
|
4390
|
-
return memoryLimit
|
|
4407
|
+
return memoryLimit;
|
|
4391
4408
|
}
|
|
4392
4409
|
getAbortController() {
|
|
4393
4410
|
return this.abortController;
|
|
@@ -4395,9 +4412,26 @@ class DynamicGltfLoader {
|
|
|
4395
4412
|
abortLoading() {
|
|
4396
4413
|
this.abortController.abort();
|
|
4397
4414
|
}
|
|
4415
|
+
getOptimizedGeometrySize() {
|
|
4416
|
+
let total = 0;
|
|
4417
|
+
const addSize = (obj) => {
|
|
4418
|
+
if (obj && obj.geometry) total += this.estimateGeometrySize(obj);
|
|
4419
|
+
};
|
|
4420
|
+
this.mergedMesh?.forEach(addSize);
|
|
4421
|
+
this.mergedLines?.forEach(addSize);
|
|
4422
|
+
this.mergedLineSegments?.forEach(addSize);
|
|
4423
|
+
this.mergedPoints?.forEach(addSize);
|
|
4424
|
+
return total;
|
|
4425
|
+
}
|
|
4398
4426
|
updateMemoryIndicator() {
|
|
4427
|
+
const optimizedUsage = this.getOptimizedGeometrySize();
|
|
4428
|
+
const totalUsage = this.currentMemoryUsage + optimizedUsage;
|
|
4429
|
+
const totalUsageEstimate = Math.round(totalUsage * this.memoryEstimationFactor);
|
|
4399
4430
|
this.dispatchEvent("geometrymemory", {
|
|
4400
4431
|
currentUsage: this.currentMemoryUsage,
|
|
4432
|
+
optimizedUsage,
|
|
4433
|
+
totalUsage,
|
|
4434
|
+
totalUsageEstimate,
|
|
4401
4435
|
limit: this.memoryLimit,
|
|
4402
4436
|
});
|
|
4403
4437
|
}
|
|
@@ -4449,10 +4483,13 @@ class DynamicGltfLoader {
|
|
|
4449
4483
|
for (const geo of geometries) {
|
|
4450
4484
|
currentMemoryUsage += geo.size;
|
|
4451
4485
|
}
|
|
4452
|
-
|
|
4453
|
-
|
|
4486
|
+
const effectiveLimitForEviction = this.memoryLimit / this.memoryEstimationFactor;
|
|
4487
|
+
if (currentMemoryUsage > effectiveLimitForEviction) {
|
|
4488
|
+
console.log(
|
|
4489
|
+
`Memory usage (${Math.round((currentMemoryUsage * this.memoryEstimationFactor) / (1024 * 1024))}MB est.) exceeds limit`
|
|
4490
|
+
);
|
|
4454
4491
|
for (const geo of geometries) {
|
|
4455
|
-
if (currentMemoryUsage <=
|
|
4492
|
+
if (currentMemoryUsage <= effectiveLimitForEviction) break;
|
|
4456
4493
|
if (this.abortController.signal.aborted) {
|
|
4457
4494
|
throw new DOMException("Loading aborted", "AbortError");
|
|
4458
4495
|
}
|
|
@@ -4536,7 +4573,9 @@ class DynamicGltfLoader {
|
|
|
4536
4573
|
}
|
|
4537
4574
|
const materialCount = uniqueMaterialIds.size;
|
|
4538
4575
|
const textureCount = uniqueTextureIds.size;
|
|
4539
|
-
const
|
|
4576
|
+
const optimizedUsageBytes = this.getOptimizedGeometrySize();
|
|
4577
|
+
const totalUsageBytes = geometryMemoryBytes + optimizedUsageBytes;
|
|
4578
|
+
const estimatedGpuMemoryBytes = Math.round(totalUsageBytes * this.memoryEstimationFactor);
|
|
4540
4579
|
if (!this._webglInfoCache) {
|
|
4541
4580
|
try {
|
|
4542
4581
|
const gl = this.renderer.getContext();
|
|
@@ -4573,7 +4612,12 @@ class DynamicGltfLoader {
|
|
|
4573
4612
|
},
|
|
4574
4613
|
},
|
|
4575
4614
|
memory: {
|
|
4576
|
-
geometries: {
|
|
4615
|
+
geometries: {
|
|
4616
|
+
count: geometryCount,
|
|
4617
|
+
bytes: geometryMemoryBytes,
|
|
4618
|
+
optimizedBytes: optimizedUsageBytes,
|
|
4619
|
+
totalRawBytes: totalUsageBytes,
|
|
4620
|
+
},
|
|
4577
4621
|
textures: { count: textureCount },
|
|
4578
4622
|
materials: { count: materialCount },
|
|
4579
4623
|
totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
|
|
@@ -4585,9 +4629,12 @@ class DynamicGltfLoader {
|
|
|
4585
4629
|
},
|
|
4586
4630
|
};
|
|
4587
4631
|
}
|
|
4588
|
-
async loadNode(nodeId, onLoadFinishCb) {
|
|
4632
|
+
async loadNode(nodeId, onLoadFinishCb, reservedEstimatedSize = 0) {
|
|
4589
4633
|
const node = this.nodes.get(nodeId);
|
|
4590
|
-
if (!node || node.loaded || node.loading)
|
|
4634
|
+
if (!node || node.loaded || node.loading) {
|
|
4635
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4591
4638
|
node.loading = true;
|
|
4592
4639
|
const meshDef = node.structure.getJson().meshes[node.meshIndex];
|
|
4593
4640
|
try {
|
|
@@ -4670,6 +4717,7 @@ class DynamicGltfLoader {
|
|
|
4670
4717
|
if (bufferRequests.length === 0) {
|
|
4671
4718
|
node.loaded = true;
|
|
4672
4719
|
node.loading = false;
|
|
4720
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4673
4721
|
return;
|
|
4674
4722
|
}
|
|
4675
4723
|
bufferRequests.sort((a, b) => a.offset - b.offset);
|
|
@@ -4781,6 +4829,7 @@ class DynamicGltfLoader {
|
|
|
4781
4829
|
}
|
|
4782
4830
|
node.loaded = true;
|
|
4783
4831
|
node.loading = false;
|
|
4832
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4784
4833
|
const geometrySize = this.estimateGeometrySize(node.object);
|
|
4785
4834
|
this.geometryCache.set(node.object.uuid, geometrySize);
|
|
4786
4835
|
this.currentMemoryUsage += geometrySize;
|
|
@@ -4789,6 +4838,7 @@ class DynamicGltfLoader {
|
|
|
4789
4838
|
}
|
|
4790
4839
|
} catch (error) {
|
|
4791
4840
|
node.loading = false;
|
|
4841
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4792
4842
|
if (error.name === "AbortError") {
|
|
4793
4843
|
return;
|
|
4794
4844
|
}
|
|
@@ -5004,15 +5054,18 @@ class DynamicGltfLoader {
|
|
|
5004
5054
|
let loadedCount = 0;
|
|
5005
5055
|
let lastLoadedCount = 0;
|
|
5006
5056
|
const totalNodes = nodesToLoad.length;
|
|
5057
|
+
const progressTotal = { value: totalNodes };
|
|
5007
5058
|
const loadProgress = async () => {
|
|
5008
5059
|
loadedCount++;
|
|
5060
|
+
const total = progressTotal.value;
|
|
5061
|
+
const percentage = total > 0 ? Math.min(100, Math.round((loadedCount / total) * 100)) : 0;
|
|
5009
5062
|
if (loadedCount - lastLoadedCount > 1000) {
|
|
5010
5063
|
lastLoadedCount = loadedCount;
|
|
5011
5064
|
this.updateMemoryIndicator();
|
|
5012
5065
|
this.dispatchEvent("geometryprogress", {
|
|
5013
|
-
percentage
|
|
5066
|
+
percentage,
|
|
5014
5067
|
loaded: loadedCount,
|
|
5015
|
-
total
|
|
5068
|
+
total,
|
|
5016
5069
|
});
|
|
5017
5070
|
this.dispatchEvent("update");
|
|
5018
5071
|
await new Promise((resolve) => {
|
|
@@ -5022,20 +5075,22 @@ class DynamicGltfLoader {
|
|
|
5022
5075
|
};
|
|
5023
5076
|
try {
|
|
5024
5077
|
const loadOperations = [];
|
|
5078
|
+
let memoryLimitReached = false;
|
|
5025
5079
|
for (const nodeId of nodesToLoad) {
|
|
5026
5080
|
if (this.abortController.signal.aborted) {
|
|
5027
5081
|
throw new DOMException("Loading aborted", "AbortError");
|
|
5028
5082
|
}
|
|
5029
5083
|
const estimatedSize = await this.estimateNodeSize(nodeId);
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5084
|
+
const estimated = Number(estimatedSize) || 0;
|
|
5085
|
+
const effectiveLimit = this.memoryLimit / this.optimizationMemoryMultiplier / this.memoryEstimationFactor;
|
|
5086
|
+
if (this.currentMemoryUsage + this.pendingMemoryUsage + estimated > effectiveLimit) {
|
|
5087
|
+
memoryLimitReached = true;
|
|
5088
|
+
progressTotal.value = loadOperations.length;
|
|
5089
|
+
console.log(`Memory limit reached after scheduling ${loadOperations.length} nodes`);
|
|
5090
|
+
break;
|
|
5037
5091
|
}
|
|
5038
|
-
|
|
5092
|
+
this.pendingMemoryUsage += estimated;
|
|
5093
|
+
loadOperations.push(this.loadNode(nodeId, loadProgress, estimated));
|
|
5039
5094
|
}
|
|
5040
5095
|
for (const structure of this.structures) {
|
|
5041
5096
|
loadOperations.push(structure.flushBufferRequests());
|
|
@@ -5043,7 +5098,8 @@ class DynamicGltfLoader {
|
|
|
5043
5098
|
await Promise.all(loadOperations);
|
|
5044
5099
|
this.dispatchEvent("geometryend", {
|
|
5045
5100
|
totalLoaded: loadedCount,
|
|
5046
|
-
totalNodes,
|
|
5101
|
+
totalNodes: progressTotal.value,
|
|
5102
|
+
memoryLimitReached,
|
|
5047
5103
|
});
|
|
5048
5104
|
return loadedCount;
|
|
5049
5105
|
} catch (error) {
|
|
@@ -5353,6 +5409,7 @@ class DynamicGltfLoader {
|
|
|
5353
5409
|
this.transformedGeometries.clear();
|
|
5354
5410
|
this.totalLoadedObjects = 0;
|
|
5355
5411
|
this.currentMemoryUsage = 0;
|
|
5412
|
+
this.pendingMemoryUsage = 0;
|
|
5356
5413
|
this.loadedGeometrySize = 0;
|
|
5357
5414
|
this.abortController = new AbortController();
|
|
5358
5415
|
this.updateMemoryIndicator();
|
|
@@ -5559,6 +5616,7 @@ class DynamicGltfLoader {
|
|
|
5559
5616
|
progress: 100,
|
|
5560
5617
|
message: `Optimization complete! ${this.maxObjectId} objects processed.`,
|
|
5561
5618
|
});
|
|
5619
|
+
this.updateMemoryIndicator();
|
|
5562
5620
|
this.dispatchEvent("update");
|
|
5563
5621
|
}
|
|
5564
5622
|
async mergeMeshGroups(materialGroups, rootGroup) {
|