@xeokit/xeokit-sdk 2.6.27 → 2.6.29

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 (41) hide show
  1. package/README.md +1 -0
  2. package/dist/xeokit-sdk.cjs.js +916 -221
  3. package/dist/xeokit-sdk.es.js +911 -222
  4. package/dist/xeokit-sdk.es5.js +577 -487
  5. package/dist/xeokit-sdk.min.cjs.js +5 -5
  6. package/dist/xeokit-sdk.min.es.js +5 -5
  7. package/dist/xeokit-sdk.min.es5.js +4 -4
  8. package/package.json +1 -1
  9. package/src/extras/PointerCircle/PointerCircle.js +1 -1
  10. package/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsMouseControl.js +22 -41
  11. package/src/plugins/AngleMeasurementsPlugin/AngleMeasurementsTouchControl.js +3 -18
  12. package/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsMouseControl.js +20 -41
  13. package/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsTouchControl.js +4 -20
  14. package/src/plugins/DistanceMeasurementsPlugin/index.js +1 -1
  15. package/src/plugins/StoreyViewsPlugin/StoreyViewsPlugin.js +31 -1
  16. package/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js +20 -8
  17. package/src/plugins/ZonesPlugin/ZonesPlugin.js +1939 -0
  18. package/src/plugins/ZonesPlugin/index.js +1 -1875
  19. package/src/viewer/Viewer.js +2 -2
  20. package/src/viewer/scene/CameraControl/lib/handlers/MousePickHandler.js +1 -1
  21. package/src/viewer/scene/math/MeshSurfaceArea.js +90 -0
  22. package/src/viewer/scene/math/MeshVolume.js +102 -0
  23. package/src/viewer/scene/math/index.js +3 -0
  24. package/src/viewer/scene/math/isTriangleMeshSolid.js +162 -0
  25. package/src/viewer/scene/math/math.js +8 -0
  26. package/src/viewer/scene/model/SceneModel.js +64 -34
  27. package/src/viewer/scene/model/SceneModelEntity.js +46 -1
  28. package/src/viewer/scene/model/SceneModelMesh.js +74 -1
  29. package/src/viewer/scene/model/dtx/lines/DTXLinesLayer.js +7 -1
  30. package/src/viewer/scene/model/dtx/triangles/DTXTrianglesLayer.js +77 -1
  31. package/src/viewer/scene/model/vbo/batching/lines/VBOBatchingLinesLayer.js +6 -1
  32. package/src/viewer/scene/model/vbo/batching/points/VBOBatchingPointsLayer.js +6 -1
  33. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js +33 -12
  34. package/src/viewer/scene/model/vbo/instancing/lines/VBOInstancingLinesLayer.js +6 -1
  35. package/src/viewer/scene/model/vbo/instancing/points/VBOInstancingPointsLayer.js +6 -1
  36. package/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js +31 -10
  37. package/src/viewer/scene/scene/Scene.js +16 -5
  38. package/src/viewer/scene/webgl/occlusion/OcclusionTester.js +0 -1
  39. package/types/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.d.ts +7 -0
  40. package/types/plugins/DistanceMeasurementsPlugin/DistanceMeasurementsPlugin.d.ts +26 -1
  41. package/types/plugins/DistanceMeasurementsPlugin/index.d.ts +3 -1
@@ -46,7 +46,7 @@ class Viewer {
46
46
  * @throws {String} Throws an exception when both canvasId or canvasElement are missing or they aren't pointing to a valid HTMLCanvasElement.
47
47
  * @param {Boolean} [cfg.alphaDepthMask=true] Whether writing into the depth buffer is enabled or disabled when rendering transparent objects.
48
48
  * @param {Boolean} [cfg.entityOffsetsEnabled=false] Whether to enable {@link Entity#offset}. For best performance, only set this ````true```` when you need to use {@link Entity#offset}.
49
- * @param {Boolean} [cfg.pickSurfacePrecisionEnabled=false] Whether to enable full-precision accuracy when surface picking with {@link Scene#pick}. Note that when ````true````, this configuration will increase the amount of browser memory used by the Viewer. The ````pickSurfacePrecision```` option for ````Scene#pick```` only works if this is set ````true````.
49
+ * @param {Boolean} [cfg.readableGeometryEnabled=false] Whether to enable full-precision accuracy when surface picking with {@link Scene#pick}. Note that when ````true````, this configuration will increase the amount of browser memory used by the Viewer. The ````pickSurfacePrecision```` option for ````Scene#pick```` only works if this is set ````true````.
50
50
  * @param {Boolean} [cfg.logarithmicDepthBufferEnabled=false] Whether to enable logarithmic depth buffer. When this is true,
51
51
  * you can set huge values for {@link Perspective#far} and {@link Ortho#far}, to push the far clipping plane back so
52
52
  * that it does not clip huge models.
@@ -116,7 +116,7 @@ class Viewer {
116
116
  saoEnabled: cfg.saoEnabled,
117
117
  alphaDepthMask: (cfg.alphaDepthMask !== false),
118
118
  entityOffsetsEnabled: (!!cfg.entityOffsetsEnabled),
119
- pickSurfacePrecisionEnabled: (!!cfg.pickSurfacePrecisionEnabled),
119
+ readableGeometryEnabled: (!!cfg.readableGeometryEnabled),
120
120
  logarithmicDepthBufferEnabled: (!!cfg.logarithmicDepthBufferEnabled),
121
121
  pbrEnabled: (!!cfg.pbrEnabled),
122
122
  colorTextureEnabled: (cfg.colorTextureEnabled !== false),
@@ -266,7 +266,7 @@ class MousePickHandler {
266
266
 
267
267
  this._timeout = setTimeout(() => {
268
268
 
269
- if (firstClickPickResult) {
269
+ if (firstClickPickResult && firstClickPickResult.worldPos) {
270
270
 
271
271
  cameraControl.fire("picked", firstClickPickResult, true);
272
272
 
@@ -0,0 +1,90 @@
1
+ import {math} from "./math.js";
2
+
3
+ const v1 = math.vec3();
4
+ const v2 = math.vec3();
5
+ const v3 = math.vec3();
6
+ const v4 = math.vec3();
7
+ const v5 = math.vec3();
8
+ const v6 = math.vec3();
9
+
10
+ /**
11
+ * Calculates the surface area of triangle meshes.
12
+ */
13
+ export class MeshSurfaceArea {
14
+ constructor() {
15
+ this.vertices = [];
16
+ this.indices = [];
17
+ this.reset();
18
+ }
19
+
20
+ /**
21
+ * Resets, ready to add vertices and indices for a new mesh.
22
+ */
23
+ reset() {
24
+ this.lenVertices = 0;
25
+ this.lenIndices = 0;
26
+ }
27
+
28
+ /**
29
+ * Adds a vertex.
30
+ * @param vertex
31
+ */
32
+ addVertex(vertex) {
33
+ this.vertices[this.lenVertices++] = vertex[0];
34
+ this.vertices[this.lenVertices++] = vertex[1];
35
+ this.vertices[this.lenVertices++] = vertex[2];
36
+ }
37
+
38
+ /**
39
+ * Adds an index.
40
+ * @param index
41
+ */
42
+ addIndex(index) {
43
+ this.indices[this.lenIndices++] = index;
44
+ }
45
+
46
+ /**
47
+ * Gets the surface area of the mesh.
48
+ *
49
+ * @returns {number}
50
+ */
51
+ get surfaceArea() {
52
+
53
+ const vertices = this.vertices;
54
+ const indices = this.indices;
55
+
56
+ let surfaceArea = 0;
57
+
58
+ for (let i = 0; i < this.lenIndices; i += 3) {
59
+
60
+ const index1 = indices[i] * 3;
61
+ const index2 = indices[i + 1] * 3;
62
+ const index3 = indices[i + 2] * 3;
63
+
64
+ v1[0] = vertices[index1];
65
+ v1[1] = vertices[index1 + 1];
66
+ v1[2] = vertices[index1 + 2];
67
+
68
+ v2[0] = vertices[index2];
69
+ v2[1] = vertices[index2 + 1];
70
+ v2[2] = vertices[index2 + 2];
71
+
72
+ v3[0] = vertices[index3];
73
+ v3[1] = vertices[index3 + 1];
74
+ v3[2] = vertices[index3 + 2];
75
+
76
+ math.subVec3(v1, v2, v4);
77
+ math.subVec3(v3, v2, v5);
78
+
79
+ surfaceArea += math.lenVec3(math.cross3Vec3(v4, v5, v6)) * 0.5;
80
+ }
81
+
82
+ return surfaceArea;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Singleton instance of {@link MeshSurfaceArea}.
88
+ * @type {MeshSurfaceArea}
89
+ */
90
+ export const meshSurfaceArea = new MeshSurfaceArea();
@@ -0,0 +1,102 @@
1
+ import {math} from "./math.js";
2
+ import {isTriangleMeshSolid} from "./isTriangleMeshSolid.js";
3
+
4
+ const v1 = math.vec3();
5
+ const v2 = math.vec3();
6
+ const v3 = math.vec3();
7
+
8
+ /**
9
+ * Calculates the volume of triangle meshes.
10
+ */
11
+ export class MeshVolume {
12
+ constructor() {
13
+ this.vertices = [];
14
+ this.indices = [];
15
+ this.reset();
16
+ }
17
+
18
+ /**
19
+ * Resets, ready to add vertices and indices for a new mesh.
20
+ */
21
+ reset() {
22
+ this.lenVertices = 0;
23
+ this.lenIndices = 0;
24
+ this.primitive = null;
25
+ }
26
+
27
+ setPrimitive(primitive) {
28
+ this.primitive = primitive;
29
+ }
30
+
31
+ /**
32
+ * Adds a vertex.
33
+ * @param vertex
34
+ */
35
+ addVertex(vertex) {
36
+ this.vertices[this.lenVertices++] = vertex[0];
37
+ this.vertices[this.lenVertices++] = vertex[1];
38
+ this.vertices[this.lenVertices++] = vertex[2];
39
+ }
40
+
41
+ /**
42
+ * Adds an index.
43
+ * @param index
44
+ */
45
+ addIndex(index) {
46
+ this.indices[this.lenIndices++] = index;
47
+ }
48
+
49
+ /**
50
+ * Gets the volume of the mesh.
51
+ *
52
+ * The mesh must be a closed solid.
53
+ *
54
+ * @returns {number} The volume of the mesh, or -1 if the mesh is not solid, in which case volume cannot be determined.
55
+ */
56
+ get volume() {
57
+
58
+ const vertices = this.vertices;
59
+ const indices = this.indices;
60
+
61
+ if (this.primitive !== "solid" && this.primitive !== "surface" && this.primitive !== "triangles") {
62
+ return -1;
63
+ }
64
+
65
+ if (this.primitive !== "solid" && !isTriangleMeshSolid(indices, vertices)) {
66
+ return -1;
67
+ }
68
+
69
+ let volume = 0;
70
+
71
+ for (let i = 0; i < this.lenIndices; i += 3) {
72
+
73
+ const index1 = indices[i] * 3;
74
+ const index2 = indices[i + 1] * 3;
75
+ const index3 = indices[i + 2] * 3;
76
+
77
+ v1[0] = vertices[index1];
78
+ v1[1] = vertices[index1 + 1];
79
+ v1[2] = vertices[index1 + 2];
80
+
81
+ v2[0] = vertices[index2];
82
+ v2[1] = vertices[index2 + 1];
83
+ v2[2] = vertices[index2 + 2];
84
+
85
+ v3[0] = vertices[index3];
86
+ v3[1] = vertices[index3 + 1];
87
+ v3[2] = vertices[index3 + 2];
88
+
89
+ math.cross3Vec3(v1, v2, v1);
90
+
91
+ volume += math.dotVec3(v1, v3);
92
+ }
93
+
94
+ return volume;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Singleton instance of {@link MeshVolume}.
100
+ * @type {MeshVolume}
101
+ */
102
+ export const meshVolume = new MeshVolume();
@@ -1,3 +1,6 @@
1
1
  export * from "./math.js";
2
2
  export * from "./Frustum.js";
3
3
  export * from "./rtcCoords.js";
4
+ export * from "./MeshVolume.js";
5
+ export * from "./MeshSurfaceArea.js";
6
+ export * from "./isTriangleMeshSolid.js";
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Uses edge adjacency counts to identify if the given triangle mesh can be rendered with backface culling enabled.
3
+ *
4
+ * If all edges are connected to exactly two triangles, then the mesh will likely be a closed solid, and we can safely
5
+ * render it with backface culling enabled.
6
+ *
7
+ * Otherwise, the mesh is a surface, and we must render it with backface culling disabled.
8
+ *
9
+ * @private
10
+ */
11
+ const isTriangleMeshSolid = (indices, positions) => {
12
+
13
+ const vertexIndexMapping = [];
14
+ let edges = [];
15
+
16
+ function compareIndexPositions(a, b) {
17
+ let posA, posB;
18
+
19
+ for (let i = 0; i < 3; i++) {
20
+ posA = positions [a * 3 + i];
21
+ posB = positions [b * 3 + i];
22
+
23
+ if (posA !== posB) {
24
+ return posB - posA;
25
+ }
26
+ }
27
+
28
+ return 0;
29
+ };
30
+
31
+ // Group together indices corresponding to same position coordinates
32
+ let newIndices = indices.slice().sort(compareIndexPositions);
33
+
34
+ // Calculate the mapping:
35
+ // - from original index in indices array
36
+ // - to indices-for-unique-positions
37
+ let uniqueVertexIndex = null;
38
+
39
+ for (let i = 0, len = newIndices.length; i < len; i++) {
40
+ if (i === 0 || 0 !== compareIndexPositions(
41
+ newIndices[i],
42
+ newIndices[i - 1],
43
+ )) {
44
+ // different position
45
+ uniqueVertexIndex = newIndices [i];
46
+ }
47
+
48
+ vertexIndexMapping [
49
+ newIndices[i]
50
+ ] = uniqueVertexIndex;
51
+ }
52
+
53
+ // Generate the list of edges
54
+ for (let i = 0, len = indices.length; i < len; i += 3) {
55
+
56
+ const a = vertexIndexMapping[indices[i]];
57
+ const b = vertexIndexMapping[indices[i + 1]];
58
+ const c = vertexIndexMapping[indices[i + 2]];
59
+
60
+ let a2 = a;
61
+ let b2 = b;
62
+ let c2 = c;
63
+
64
+ if (a > b && a > c) {
65
+ if (b > c) {
66
+ a2 = a;
67
+ b2 = b;
68
+ c2 = c;
69
+ } else {
70
+ a2 = a;
71
+ b2 = c;
72
+ c2 = b;
73
+ }
74
+ } else if (b > a && b > c) {
75
+ if (a > c) {
76
+ a2 = b;
77
+ b2 = a;
78
+ c2 = c;
79
+ } else {
80
+ a2 = b;
81
+ b2 = c;
82
+ c2 = a;
83
+ }
84
+ } else if (c > a && c > b) {
85
+ if (a > b) {
86
+ a2 = c;
87
+ b2 = a;
88
+ c2 = b;
89
+ } else {
90
+ a2 = c;
91
+ b2 = b;
92
+ c2 = a;
93
+ }
94
+ }
95
+
96
+ edges[i + 0] = [
97
+ a2, b2
98
+ ];
99
+ edges[i + 1] = [
100
+ b2, c2
101
+ ];
102
+
103
+ if (a2 > c2) {
104
+ const temp = c2;
105
+ c2 = a2;
106
+ a2 = temp;
107
+ }
108
+
109
+ edges[i + 2] = [
110
+ c2, a2
111
+ ];
112
+ }
113
+
114
+ // Group semantically equivalent edgdes together
115
+ function compareEdges(e1, e2) {
116
+ let a, b;
117
+
118
+ for (let i = 0; i < 2; i++) {
119
+ a = e1[i];
120
+ b = e2[i];
121
+
122
+ if (b !== a) {
123
+ return b - a;
124
+ }
125
+ }
126
+
127
+ return 0;
128
+ }
129
+
130
+ edges = edges.slice(0, indices.length);
131
+
132
+ edges.sort(compareEdges);
133
+
134
+ // Make sure each edge is used exactly twice
135
+ let sameEdgeCount = 0;
136
+
137
+ for (let i = 0; i < edges.length; i++) {
138
+ if (i === 0 || 0 !== compareEdges(
139
+ edges[i], edges[i - 1]
140
+ )) {
141
+ // different edge
142
+ if (0 !== i && sameEdgeCount !== 2) {
143
+ return false;
144
+ }
145
+
146
+ sameEdgeCount = 1;
147
+ } else {
148
+ // same edge
149
+ sameEdgeCount++;
150
+ }
151
+ }
152
+
153
+ if (edges.length > 0 && sameEdgeCount !== 2) {
154
+ return false;
155
+ }
156
+
157
+ // Each edge is used exactly twice, this is a
158
+ // watertight surface and hence a solid geometry.
159
+ return true;
160
+ };
161
+
162
+ export {isTriangleMeshSolid};
@@ -3998,6 +3998,14 @@ const math = {
3998
3998
  return aabb[0] > p[0] || aabb[3] < p[0] || aabb[1] > p[1] || aabb[4] < p[1] || aabb[2] > p[2] || aabb[5] < p[2];
3999
3999
  },
4000
4000
 
4001
+ point3AABB3AbsoluteIntersect(aabb, p) {
4002
+ return (
4003
+ aabb[0] <= p[0] && aabb[3] >= p[0] &&
4004
+ aabb[1] <= p[1] && aabb[4] >= p[1] &&
4005
+ aabb[2] <= p[2] && aabb[5] >= p[2]
4006
+ );
4007
+ },
4008
+
4001
4009
  /**
4002
4010
  *
4003
4011
  * @param dir
@@ -1155,7 +1155,10 @@ export class SceneModel extends Component {
1155
1155
  this._meshList = [];
1156
1156
 
1157
1157
  this.layerList = []; // For GL state efficiency when drawing, InstancingLayers are in first part, BatchingLayers are in second
1158
+ this._layersToFinalize = [];
1159
+
1158
1160
  this._entityList = [];
1161
+ this._entitiesToFinalize = [];
1159
1162
 
1160
1163
  this._geometries = {};
1161
1164
  this._dtxBuckets = {}; // Geometries with optimizations used for data texture representation
@@ -1231,6 +1234,7 @@ export class SceneModel extends Component {
1231
1234
  this._numTriangles = 0;
1232
1235
  this._numLines = 0;
1233
1236
  this._numPoints = 0;
1237
+ this._layersFinalized = false;
1234
1238
 
1235
1239
  this._edgeThreshold = cfg.edgeThreshold || 10;
1236
1240
 
@@ -3172,7 +3176,7 @@ export class SceneModel extends Component {
3172
3176
  let dtxLayer = this._dtxLayers[layerId];
3173
3177
  if (dtxLayer) {
3174
3178
  if (!dtxLayer.canCreatePortion(cfg)) {
3175
- dtxLayer.finalize();
3179
+ // dtxLayer.finalize();
3176
3180
  delete this._dtxLayers[layerId];
3177
3181
  dtxLayer = null;
3178
3182
  } else {
@@ -3183,16 +3187,17 @@ export class SceneModel extends Component {
3183
3187
  case "triangles":
3184
3188
  case "solid":
3185
3189
  case "surface":
3186
- dtxLayer = new DTXTrianglesLayer(this, {layerIndex: 0, origin}); // layerIndex is set in #finalize()
3190
+ dtxLayer = new DTXTrianglesLayer(this, {layerIndex: 0, origin, primitive}); // layerIndex is set in #finalize()
3187
3191
  break;
3188
3192
  case "lines":
3189
- dtxLayer = new DTXLinesLayer(this, {layerIndex: 0, origin}); // layerIndex is set in #finalize()
3193
+ dtxLayer = new DTXLinesLayer(this, {layerIndex: 0, origin, primitive}); // layerIndex is set in #finalize()
3190
3194
  break;
3191
3195
  default:
3192
3196
  return;
3193
3197
  }
3194
3198
  this._dtxLayers[layerId] = dtxLayer;
3195
3199
  this.layerList.push(dtxLayer);
3200
+ this._layersToFinalize.push(dtxLayer);
3196
3201
  return dtxLayer;
3197
3202
  }
3198
3203
 
@@ -3224,7 +3229,8 @@ export class SceneModel extends Component {
3224
3229
  origin,
3225
3230
  maxGeometryBatchSize: this._maxGeometryBatchSize,
3226
3231
  solid: (cfg.primitive === "solid"),
3227
- autoNormals: true
3232
+ autoNormals: true,
3233
+ primitive: cfg.primitive
3228
3234
  });
3229
3235
  break;
3230
3236
  case "solid":
@@ -3239,7 +3245,8 @@ export class SceneModel extends Component {
3239
3245
  origin,
3240
3246
  maxGeometryBatchSize: this._maxGeometryBatchSize,
3241
3247
  solid: (cfg.primitive === "solid"),
3242
- autoNormals: true
3248
+ autoNormals: true,
3249
+ primitive: cfg.primitive
3243
3250
  });
3244
3251
  break;
3245
3252
  case "surface":
@@ -3254,7 +3261,8 @@ export class SceneModel extends Component {
3254
3261
  origin,
3255
3262
  maxGeometryBatchSize: this._maxGeometryBatchSize,
3256
3263
  solid: (cfg.primitive === "solid"),
3257
- autoNormals: true
3264
+ autoNormals: true,
3265
+ primitive: cfg.primitive
3258
3266
  });
3259
3267
  break;
3260
3268
  case "lines":
@@ -3266,7 +3274,8 @@ export class SceneModel extends Component {
3266
3274
  positionsDecodeMatrix: cfg.positionsDecodeMatrix, // Can be undefined
3267
3275
  uvDecodeMatrix: cfg.uvDecodeMatrix, // Can be undefined
3268
3276
  origin,
3269
- maxGeometryBatchSize: this._maxGeometryBatchSize
3277
+ maxGeometryBatchSize: this._maxGeometryBatchSize,
3278
+ primitive: cfg.primitive
3270
3279
  });
3271
3280
  break;
3272
3281
  case "points":
@@ -3278,7 +3287,8 @@ export class SceneModel extends Component {
3278
3287
  positionsDecodeMatrix: cfg.positionsDecodeMatrix, // Can be undefined
3279
3288
  uvDecodeMatrix: cfg.uvDecodeMatrix, // Can be undefined
3280
3289
  origin,
3281
- maxGeometryBatchSize: this._maxGeometryBatchSize
3290
+ maxGeometryBatchSize: this._maxGeometryBatchSize,
3291
+ primitive: cfg.primitive
3282
3292
  });
3283
3293
  break;
3284
3294
  }
@@ -3294,6 +3304,7 @@ export class SceneModel extends Component {
3294
3304
  }
3295
3305
  this._vboBatchingLayers[layerId] = vboBatchingLayer;
3296
3306
  this.layerList.push(vboBatchingLayer);
3307
+ this._layersToFinalize.push(vboBatchingLayer);
3297
3308
  return vboBatchingLayer;
3298
3309
  }
3299
3310
 
@@ -3386,6 +3397,7 @@ export class SceneModel extends Component {
3386
3397
  }
3387
3398
  this._vboInstancingLayers[layerId] = vboInstancingLayer;
3388
3399
  this.layerList.push(vboInstancingLayer);
3400
+ this._layersToFinalize.push(vboInstancingLayer);
3389
3401
  return vboInstancingLayer;
3390
3402
  }
3391
3403
 
@@ -3486,38 +3498,47 @@ export class SceneModel extends Component {
3486
3498
  lodCullable); // Internally sets SceneModelEntity#parent to this SceneModel
3487
3499
  this._entityList.push(entity);
3488
3500
  this._entities[cfg.id] = entity;
3501
+ this._entitiesToFinalize.push(entity);
3489
3502
  this.numEntities++;
3490
3503
  }
3491
3504
 
3492
3505
  /**
3493
- * Finalizes this SceneModel.
3494
- *
3495
- * Once finalized, you can't add anything more to this SceneModel.
3506
+ * Pre-renders all meshes that have been added, even if the SceneModel has not bee finalized yet.
3507
+ * This is use for progressively showing the SceneModel while it is being loaded or constructed.
3508
+ * @returns {boolean}
3496
3509
  */
3497
- finalize() {
3510
+ preFinalize() {
3498
3511
  if (this.destroyed) {
3499
- return;
3512
+ return false;
3513
+ }
3514
+ if (this._layersToFinalize.length === 0) {
3515
+ return false;
3500
3516
  }
3501
3517
  this._createDummyEntityForUnusedMeshes();
3502
- for (let i = 0, len = this.layerList.length; i < len; i++) {
3503
- const layer = this.layerList[i];
3518
+ for (let i = 0, len = this._layersToFinalize.length; i < len; i++) {
3519
+ const layer = this._layersToFinalize[i];
3504
3520
  layer.finalize();
3505
3521
  }
3506
- this._geometries = {};
3507
- this._dtxBuckets = {};
3508
- this._textures = {};
3509
- this._textureSets = {};
3510
- this._dtxLayers = {};
3511
- this._vboInstancingLayers = {};
3512
3522
  this._vboBatchingLayers = {};
3513
- for (let i = 0, len = this._entityList.length; i < len; i++) {
3514
- const entity = this._entityList[i];
3523
+ this._vboInstancingLayers = {};
3524
+ this._dtxLayers = {};
3525
+ this._layersToFinalize = [];
3526
+ for (let i = 0, len = this._entitiesToFinalize.length; i < len; i++) {
3527
+ const entity = this._entitiesToFinalize[i];
3515
3528
  entity._finalize();
3516
3529
  }
3517
- for (let i = 0, len = this._entityList.length; i < len; i++) {
3518
- const entity = this._entityList[i];
3530
+ for (let i = 0, len = this._entitiesToFinalize.length; i < len; i++) {
3531
+ const entity = this._entitiesToFinalize[i];
3519
3532
  entity._finalize2();
3520
3533
  }
3534
+ this._entitiesToFinalize = [];
3535
+ this.scene._aabbDirty = true;
3536
+ this._viewMatrixDirty = true;
3537
+ this._matrixDirty = true;
3538
+ this._aabbDirty = true;
3539
+ this._setWorldMatrixDirty();
3540
+ this._sceneModelDirty();
3541
+ this.position = this._position;
3521
3542
  // Sort layers to reduce WebGL shader switching when rendering them
3522
3543
  this.layerList.sort((a, b) => {
3523
3544
  if (a.sortId < b.sortId) {
@@ -3533,15 +3554,23 @@ export class SceneModel extends Component {
3533
3554
  layer.layerIndex = i;
3534
3555
  }
3535
3556
  this.glRedraw();
3536
- this.scene._aabbDirty = true;
3537
- this._viewMatrixDirty = true;
3538
- this._matrixDirty = true;
3539
- this._aabbDirty = true;
3540
-
3541
- this._setWorldMatrixDirty();
3542
- this._sceneModelDirty();
3557
+ this._layersFinalized = true;
3558
+ }
3543
3559
 
3544
- this.position = this._position;
3560
+ /**
3561
+ * Finalizes this SceneModel.
3562
+ *
3563
+ * Once finalized, you can't add anything more to this SceneModel.
3564
+ */
3565
+ finalize() {
3566
+ if (this.destroyed) {
3567
+ return;
3568
+ }
3569
+ this.preFinalize();
3570
+ this._geometries = {};
3571
+ this._dtxBuckets = {};
3572
+ this._textures = {};
3573
+ this._textureSets = {};
3545
3574
  }
3546
3575
 
3547
3576
  /** @private */
@@ -3579,7 +3608,7 @@ export class SceneModel extends Component {
3579
3608
  _createDummyEntityForUnusedMeshes() {
3580
3609
  const unusedMeshIds = Object.keys(this._unusedMeshes);
3581
3610
  if (unusedMeshIds.length > 0) {
3582
- const entityId = `${this.id}-dummyEntityForUnusedMeshes`;
3611
+ const entityId = `${this.id}-${math.createUUID()}`;
3583
3612
  this.warn(`Creating dummy SceneModelEntity "${entityId}" for unused SceneMeshes: [${unusedMeshIds.join(",")}]`)
3584
3613
  this.createEntity({
3585
3614
  id: entityId,
@@ -3951,6 +3980,7 @@ export class SceneModel extends Component {
3951
3980
  for (let i = 0, len = this._entityList.length; i < len; i++) {
3952
3981
  this._entityList[i]._destroy();
3953
3982
  }
3983
+ this._layersToFinalize = {};
3954
3984
  // Object.entries(this._geometries).forEach(([id, geometry]) => {
3955
3985
  // geometry.destroy();
3956
3986
  // });
@@ -633,7 +633,7 @@ export class SceneModelEntity {
633
633
  for (let i = 0, len = this.meshes.length; i < len; i++) {
634
634
  this.meshes[i]._setOffset(this._offset);
635
635
  }
636
- this._aabbDirty = true;
636
+ this._aabbDirty = true;
637
637
  this.model._aabbDirty = true;
638
638
  this.scene._aabbDirty = true;
639
639
  this.scene._objectOffsetUpdated(this, offset);
@@ -650,6 +650,51 @@ export class SceneModelEntity {
650
650
  }
651
651
  }
652
652
 
653
+ getEachIndex(callback) {
654
+ for (let i = 0, len = this.meshes.length; i < len; i++) {
655
+ this.meshes[i].getEachIndex(callback)
656
+ }
657
+ }
658
+
659
+ /**
660
+ * Returns the volume of this SceneModelEntity.
661
+ *
662
+ * Only works when {@link Scene.readableGeometryEnabled | Scene.readableGeometryEnabled} is `true` and the
663
+ * SceneModelEntity contains solid triangle meshes; returns `0` otherwise.
664
+ *
665
+ * @returns {number}
666
+ */
667
+ get volume() {
668
+ let volume = 0;
669
+ for (let i = 0, len = this.meshes.length; i < len; i++) {
670
+ const meshVolume = this.meshes[i].volume;
671
+ if (meshVolume < 0) {
672
+ return -1;
673
+ }
674
+ volume += meshVolume;
675
+ }
676
+ return volume;
677
+ }
678
+
679
+ /**
680
+ * Returns the surface area of this SceneModelEntity.
681
+ *
682
+ * Only works when {@link Scene.readableGeometryEnabled | Scene.readableGeometryEnabled} is `true` and the
683
+ * SceneModelEntity contains triangle meshes; returns `0` otherwise.
684
+ *
685
+ * @returns {number}
686
+ */
687
+ get surfaceArea() {
688
+ let surfaceArea = 0;
689
+ for (let i = 0, len = this.meshes.length; i < len; i++) {
690
+ const meshSurfaceArea = this.meshes[i].surfaceArea;
691
+ if (meshSurfaceArea >= 0) {
692
+ surfaceArea += meshSurfaceArea;
693
+ }
694
+ }
695
+ return surfaceArea > 0 ? surfaceArea : -1;
696
+ }
697
+
653
698
  _getFlag(flag) {
654
699
  return !!(this._flags & flag);
655
700
  }