@inweb/viewer-three 27.2.0 → 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 +72 -21
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +71 -21
- package/dist/viewer-three.module.js.map +1 -1
- package/package.json +5 -5
- package/src/Viewer/components/CameraComponent.ts +1 -1
- package/src/Viewer/components/InfoComponent.ts +5 -1
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inweb/viewer-three",
|
|
3
|
-
"version": "27.2.
|
|
3
|
+
"version": "27.2.1",
|
|
4
4
|
"description": "JavaScript library for rendering CAD and BIM files in a browser using Three.js",
|
|
5
5
|
"homepage": "https://cloud.opendesign.com/docs/index.html",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"docs": "typedoc"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@inweb/client": "~27.2.
|
|
39
|
-
"@inweb/eventemitter2": "~27.2.
|
|
40
|
-
"@inweb/markup": "~27.2.
|
|
41
|
-
"@inweb/viewer-core": "~27.2.
|
|
38
|
+
"@inweb/client": "~27.2.1",
|
|
39
|
+
"@inweb/eventemitter2": "~27.2.1",
|
|
40
|
+
"@inweb/markup": "~27.2.1",
|
|
41
|
+
"@inweb/viewer-core": "~27.2.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@streamparser/json": "^0.0.22",
|
|
@@ -100,6 +100,7 @@ export class InfoComponent implements IComponent {
|
|
|
100
100
|
|
|
101
101
|
this.viewer.info.memory.geometries = 0;
|
|
102
102
|
this.viewer.info.memory.geometryBytes = 0;
|
|
103
|
+
this.viewer.info.memory.optimizedGeometryBytes = 0;
|
|
103
104
|
this.viewer.info.memory.textures = 0;
|
|
104
105
|
this.viewer.info.memory.textureBytes = 0;
|
|
105
106
|
this.viewer.info.memory.materials = 0;
|
|
@@ -141,6 +142,7 @@ export class InfoComponent implements IComponent {
|
|
|
141
142
|
|
|
142
143
|
this.viewer.info.memory.geometries += info.memory.geometries;
|
|
143
144
|
this.viewer.info.memory.geometryBytes += info.memory.geometryBytes;
|
|
145
|
+
this.viewer.info.memory.optimizedGeometryBytes += info.memory.optimizedGeometryBytes;
|
|
144
146
|
this.viewer.info.memory.textures += info.memory.textures;
|
|
145
147
|
this.viewer.info.memory.textureBytes += info.memory.textureBytes;
|
|
146
148
|
this.viewer.info.memory.materials += info.memory.materials;
|
|
@@ -153,7 +155,9 @@ export class InfoComponent implements IComponent {
|
|
|
153
155
|
|
|
154
156
|
console.log("Number of objects:", info.scene.objects);
|
|
155
157
|
console.log("Number of objects after optimization:", info.optimizedScene.objects);
|
|
156
|
-
console.log("
|
|
158
|
+
console.log("Geometry size:", info.memory.geometryBytes / (1024 * 1024), "MB");
|
|
159
|
+
console.log("Optimized geometry size:", info.memory.optimizedGeometryBytes / (1024 * 1024), "MB");
|
|
160
|
+
console.log("Estimated GPU used:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
|
|
157
161
|
|
|
158
162
|
console.log("File load time:", this.viewer.info.performance.loadTime, "ms");
|
|
159
163
|
};
|
|
@@ -60,11 +60,18 @@ export class DynamicGltfLoader {
|
|
|
60
60
|
this.structureRoots = new Map();
|
|
61
61
|
|
|
62
62
|
this.memoryLimit = this.getAvailableMemory();
|
|
63
|
+
this.optimizationMemoryMultiplier = 5;
|
|
64
|
+
/**
|
|
65
|
+
* Real memory ~1.7x raw geometry (Three.js objects, Map/Set, buffers overhead). Used for limit
|
|
66
|
+
* checks and display.
|
|
67
|
+
*/
|
|
68
|
+
this.memoryEstimationFactor = 1.7;
|
|
63
69
|
this.loadedGeometrySize = 0;
|
|
64
70
|
this.geometryCache = new Map();
|
|
65
71
|
this.materialCache = new Map();
|
|
66
72
|
this.textureCache = new Map();
|
|
67
73
|
this.currentMemoryUsage = 0;
|
|
74
|
+
this.pendingMemoryUsage = 0;
|
|
68
75
|
|
|
69
76
|
this.updateMemoryIndicator();
|
|
70
77
|
|
|
@@ -145,7 +152,7 @@ export class DynamicGltfLoader {
|
|
|
145
152
|
console.warn("Error detecting available memory:", error);
|
|
146
153
|
}
|
|
147
154
|
|
|
148
|
-
return memoryLimit
|
|
155
|
+
return memoryLimit;
|
|
149
156
|
}
|
|
150
157
|
|
|
151
158
|
getAbortController() {
|
|
@@ -156,9 +163,27 @@ export class DynamicGltfLoader {
|
|
|
156
163
|
this.abortController.abort();
|
|
157
164
|
}
|
|
158
165
|
|
|
166
|
+
getOptimizedGeometrySize() {
|
|
167
|
+
let total = 0;
|
|
168
|
+
const addSize = (obj) => {
|
|
169
|
+
if (obj && obj.geometry) total += this.estimateGeometrySize(obj);
|
|
170
|
+
};
|
|
171
|
+
this.mergedMesh?.forEach(addSize);
|
|
172
|
+
this.mergedLines?.forEach(addSize);
|
|
173
|
+
this.mergedLineSegments?.forEach(addSize);
|
|
174
|
+
this.mergedPoints?.forEach(addSize);
|
|
175
|
+
return total;
|
|
176
|
+
}
|
|
177
|
+
|
|
159
178
|
updateMemoryIndicator() {
|
|
179
|
+
const optimizedUsage = this.getOptimizedGeometrySize();
|
|
180
|
+
const totalUsage = this.currentMemoryUsage + optimizedUsage;
|
|
181
|
+
const totalUsageEstimate = Math.round(totalUsage * this.memoryEstimationFactor);
|
|
160
182
|
this.dispatchEvent("geometrymemory", {
|
|
161
183
|
currentUsage: this.currentMemoryUsage,
|
|
184
|
+
optimizedUsage,
|
|
185
|
+
totalUsage,
|
|
186
|
+
totalUsageEstimate,
|
|
162
187
|
limit: this.memoryLimit,
|
|
163
188
|
});
|
|
164
189
|
}
|
|
@@ -224,11 +249,14 @@ export class DynamicGltfLoader {
|
|
|
224
249
|
currentMemoryUsage += geo.size;
|
|
225
250
|
}
|
|
226
251
|
|
|
227
|
-
|
|
228
|
-
|
|
252
|
+
const effectiveLimitForEviction = this.memoryLimit / this.memoryEstimationFactor;
|
|
253
|
+
if (currentMemoryUsage > effectiveLimitForEviction) {
|
|
254
|
+
console.log(
|
|
255
|
+
`Memory usage (${Math.round((currentMemoryUsage * this.memoryEstimationFactor) / (1024 * 1024))}MB est.) exceeds limit`
|
|
256
|
+
);
|
|
229
257
|
|
|
230
258
|
for (const geo of geometries) {
|
|
231
|
-
if (currentMemoryUsage <=
|
|
259
|
+
if (currentMemoryUsage <= effectiveLimitForEviction) break;
|
|
232
260
|
|
|
233
261
|
if (this.abortController.signal.aborted) {
|
|
234
262
|
throw new DOMException("Loading aborted", "AbortError");
|
|
@@ -325,7 +353,9 @@ export class DynamicGltfLoader {
|
|
|
325
353
|
}
|
|
326
354
|
const materialCount = uniqueMaterialIds.size;
|
|
327
355
|
const textureCount = uniqueTextureIds.size;
|
|
328
|
-
const
|
|
356
|
+
const optimizedUsageBytes = this.getOptimizedGeometrySize();
|
|
357
|
+
const totalUsageBytes = geometryMemoryBytes + optimizedUsageBytes;
|
|
358
|
+
const estimatedGpuMemoryBytes = Math.round(totalUsageBytes * this.memoryEstimationFactor);
|
|
329
359
|
|
|
330
360
|
if (!this._webglInfoCache) {
|
|
331
361
|
try {
|
|
@@ -366,7 +396,12 @@ export class DynamicGltfLoader {
|
|
|
366
396
|
},
|
|
367
397
|
},
|
|
368
398
|
memory: {
|
|
369
|
-
geometries: {
|
|
399
|
+
geometries: {
|
|
400
|
+
count: geometryCount,
|
|
401
|
+
bytes: geometryMemoryBytes,
|
|
402
|
+
optimizedBytes: optimizedUsageBytes,
|
|
403
|
+
totalRawBytes: totalUsageBytes,
|
|
404
|
+
},
|
|
370
405
|
textures: { count: textureCount },
|
|
371
406
|
materials: { count: materialCount },
|
|
372
407
|
totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
|
|
@@ -379,9 +414,12 @@ export class DynamicGltfLoader {
|
|
|
379
414
|
};
|
|
380
415
|
}
|
|
381
416
|
|
|
382
|
-
async loadNode(nodeId, onLoadFinishCb) {
|
|
417
|
+
async loadNode(nodeId, onLoadFinishCb, reservedEstimatedSize = 0) {
|
|
383
418
|
const node = this.nodes.get(nodeId);
|
|
384
|
-
if (!node || node.loaded || node.loading)
|
|
419
|
+
if (!node || node.loaded || node.loading) {
|
|
420
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
385
423
|
|
|
386
424
|
node.loading = true;
|
|
387
425
|
const meshDef = node.structure.getJson().meshes[node.meshIndex];
|
|
@@ -471,6 +509,7 @@ export class DynamicGltfLoader {
|
|
|
471
509
|
if (bufferRequests.length === 0) {
|
|
472
510
|
node.loaded = true;
|
|
473
511
|
node.loading = false;
|
|
512
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
474
513
|
return;
|
|
475
514
|
}
|
|
476
515
|
bufferRequests.sort((a, b) => a.offset - b.offset);
|
|
@@ -597,6 +636,7 @@ export class DynamicGltfLoader {
|
|
|
597
636
|
}
|
|
598
637
|
node.loaded = true;
|
|
599
638
|
node.loading = false;
|
|
639
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
600
640
|
const geometrySize = this.estimateGeometrySize(node.object);
|
|
601
641
|
this.geometryCache.set(node.object.uuid, geometrySize);
|
|
602
642
|
this.currentMemoryUsage += geometrySize;
|
|
@@ -605,6 +645,7 @@ export class DynamicGltfLoader {
|
|
|
605
645
|
}
|
|
606
646
|
} catch (error) {
|
|
607
647
|
node.loading = false;
|
|
648
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
608
649
|
|
|
609
650
|
if (error.name === "AbortError") {
|
|
610
651
|
return;
|
|
@@ -872,16 +913,19 @@ export class DynamicGltfLoader {
|
|
|
872
913
|
let loadedCount = 0;
|
|
873
914
|
let lastLoadedCount = 0;
|
|
874
915
|
const totalNodes = nodesToLoad.length;
|
|
916
|
+
const progressTotal = { value: totalNodes };
|
|
875
917
|
|
|
876
918
|
const loadProgress = async () => {
|
|
877
919
|
loadedCount++;
|
|
920
|
+
const total = progressTotal.value;
|
|
921
|
+
const percentage = total > 0 ? Math.min(100, Math.round((loadedCount / total) * 100)) : 0;
|
|
878
922
|
if (loadedCount - lastLoadedCount > 1000) {
|
|
879
923
|
lastLoadedCount = loadedCount;
|
|
880
924
|
this.updateMemoryIndicator();
|
|
881
925
|
this.dispatchEvent("geometryprogress", {
|
|
882
|
-
percentage
|
|
926
|
+
percentage,
|
|
883
927
|
loaded: loadedCount,
|
|
884
|
-
total
|
|
928
|
+
total,
|
|
885
929
|
});
|
|
886
930
|
|
|
887
931
|
this.dispatchEvent("update");
|
|
@@ -894,23 +938,25 @@ export class DynamicGltfLoader {
|
|
|
894
938
|
|
|
895
939
|
try {
|
|
896
940
|
const loadOperations = [];
|
|
941
|
+
let memoryLimitReached = false;
|
|
897
942
|
for (const nodeId of nodesToLoad) {
|
|
898
943
|
if (this.abortController.signal.aborted) {
|
|
899
944
|
throw new DOMException("Loading aborted", "AbortError");
|
|
900
945
|
}
|
|
901
946
|
|
|
902
947
|
const estimatedSize = await this.estimateNodeSize(nodeId);
|
|
948
|
+
const estimated = Number(estimatedSize) || 0;
|
|
949
|
+
const effectiveLimit = this.memoryLimit / this.optimizationMemoryMultiplier / this.memoryEstimationFactor;
|
|
903
950
|
|
|
904
|
-
if (this.currentMemoryUsage +
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
this.dispatchEvent("update");
|
|
910
|
-
return loadedCount;
|
|
951
|
+
if (this.currentMemoryUsage + this.pendingMemoryUsage + estimated > effectiveLimit) {
|
|
952
|
+
memoryLimitReached = true;
|
|
953
|
+
progressTotal.value = loadOperations.length;
|
|
954
|
+
console.log(`Memory limit reached after scheduling ${loadOperations.length} nodes`);
|
|
955
|
+
break;
|
|
911
956
|
}
|
|
912
957
|
|
|
913
|
-
|
|
958
|
+
this.pendingMemoryUsage += estimated;
|
|
959
|
+
loadOperations.push(this.loadNode(nodeId, loadProgress, estimated));
|
|
914
960
|
}
|
|
915
961
|
|
|
916
962
|
for (const structure of this.structures) {
|
|
@@ -921,7 +967,8 @@ export class DynamicGltfLoader {
|
|
|
921
967
|
|
|
922
968
|
this.dispatchEvent("geometryend", {
|
|
923
969
|
totalLoaded: loadedCount,
|
|
924
|
-
totalNodes,
|
|
970
|
+
totalNodes: progressTotal.value,
|
|
971
|
+
memoryLimitReached,
|
|
925
972
|
});
|
|
926
973
|
|
|
927
974
|
return loadedCount;
|
|
@@ -1004,8 +1051,6 @@ export class DynamicGltfLoader {
|
|
|
1004
1051
|
const transformedBox = node.geometryExtents.clone();
|
|
1005
1052
|
const structureRoot = node.structure ? this.structureRoots.get(node.structure.id) : null;
|
|
1006
1053
|
|
|
1007
|
-
// Calculate relative transformation from node.group to structureRoot
|
|
1008
|
-
// This matches the logic used in merge methods
|
|
1009
1054
|
if (node.group) {
|
|
1010
1055
|
const relativeMatrix = new Matrix4();
|
|
1011
1056
|
let currentObject = node.group;
|
|
@@ -1278,6 +1323,7 @@ export class DynamicGltfLoader {
|
|
|
1278
1323
|
|
|
1279
1324
|
this.totalLoadedObjects = 0;
|
|
1280
1325
|
this.currentMemoryUsage = 0;
|
|
1326
|
+
this.pendingMemoryUsage = 0;
|
|
1281
1327
|
this.loadedGeometrySize = 0;
|
|
1282
1328
|
|
|
1283
1329
|
this.abortController = new AbortController();
|
|
@@ -1527,6 +1573,7 @@ export class DynamicGltfLoader {
|
|
|
1527
1573
|
message: `Optimization complete! ${this.maxObjectId} objects processed.`,
|
|
1528
1574
|
});
|
|
1529
1575
|
|
|
1576
|
+
this.updateMemoryIndicator();
|
|
1530
1577
|
this.dispatchEvent("update");
|
|
1531
1578
|
}
|
|
1532
1579
|
|
|
@@ -49,6 +49,7 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
49
49
|
|
|
50
50
|
info.memory.geometries = stats.memory.geometries.count;
|
|
51
51
|
info.memory.geometryBytes = stats.memory.geometries.bytes;
|
|
52
|
+
info.memory.optimizedGeometryBytes = stats.memory.geometries.optimizedBytes;
|
|
52
53
|
info.memory.textures = stats.memory.textures.count;
|
|
53
54
|
info.memory.materials = stats.memory.materials.count;
|
|
54
55
|
info.memory.totalEstimatedGpuBytes = stats.memory.totalEstimatedGpuBytes;
|
|
@@ -259,6 +259,7 @@ export class ModelImpl implements IModelImpl {
|
|
|
259
259
|
|
|
260
260
|
info.memory.geometries = geometries.size;
|
|
261
261
|
info.memory.geometryBytes = geometryBytes;
|
|
262
|
+
info.memory.optimizedGeometryBytes = 0;
|
|
262
263
|
info.memory.textures = textures.size;
|
|
263
264
|
info.memory.textureBytes = Math.floor(textureBytes);
|
|
264
265
|
info.memory.materials = materials.size;
|