@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inweb/viewer-three",
3
- "version": "27.2.0",
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.0",
39
- "@inweb/eventemitter2": "~27.2.0",
40
- "@inweb/markup": "~27.2.0",
41
- "@inweb/viewer-core": "~27.2.0"
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",
@@ -134,7 +134,7 @@ export class CameraComponent implements IComponent {
134
134
  });
135
135
 
136
136
  if (camera) {
137
- camera.isDefaultCamera = true;
137
+ camera.userData.isDefaultCamera = true;
138
138
  camera.scale.set(1, 1, 1); // <- Visualize fix
139
139
 
140
140
  this.switchCamera(camera);
@@ -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("Total geometry size:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
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 / 3;
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
- if (currentMemoryUsage > this.memoryLimit) {
228
- console.log(`Memory usage (${Math.round(currentMemoryUsage / (1024 * 1024))}MB) exceeds limit`);
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 <= this.memoryLimit) break;
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 estimatedGpuMemoryBytes = geometryMemoryBytes;
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: { count: geometryCount, bytes: geometryMemoryBytes },
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) return;
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: Math.round((loadedCount / totalNodes) * 100),
926
+ percentage,
883
927
  loaded: loadedCount,
884
- total: totalNodes,
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 + estimatedSize > this.memoryLimit) {
905
- console.log(`Memory limit reached after loading ${loadedCount} nodes`);
906
- this.dispatchEvent("geometryerror", {
907
- message: "Memory limit reached",
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
- loadOperations.push(this.loadNode(nodeId, loadProgress));
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;