diff --git a/examples/measurement/areaVolume.html b/examples/measurement/areaVolume.html
new file mode 100644
index 0000000000..de52054eeb
--- /dev/null
+++ b/examples/measurement/areaVolume.html
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+ xeokit Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/viewer/scene/model/SceneModelEntity.js b/src/viewer/scene/model/SceneModelEntity.js
index 92b55edf32..0b5b95bced 100644
--- a/src/viewer/scene/model/SceneModelEntity.js
+++ b/src/viewer/scene/model/SceneModelEntity.js
@@ -640,6 +640,18 @@ export class SceneModelEntity {
this.model.glRedraw();
}
+ get area() {
+ let acc = 0;
+ this.meshes.forEach(m => acc += m.area);
+ return acc;
+ }
+
+ get volume() {
+ let acc = 0;
+ this.meshes.forEach(m => acc += m.volume);
+ return acc;
+ }
+
get saoEnabled() {
return this.model.saoEnabled;
}
diff --git a/src/viewer/scene/model/SceneModelMesh.js b/src/viewer/scene/model/SceneModelMesh.js
index 9a30c37ff1..f061246eda 100644
--- a/src/viewer/scene/model/SceneModelMesh.js
+++ b/src/viewer/scene/model/SceneModelMesh.js
@@ -332,6 +332,14 @@ export class SceneModelMesh {
return this._aabbWorld;
}
+ get area() {
+ return this.layer._portions[this.portionId]._metrics.area;
+ }
+
+ get volume() {
+ return this.layer._portions[this.portionId]._metrics.volume;
+ }
+
/**
* @private
*/
diff --git a/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js b/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js
index 8c96e19e73..3770645f61 100644
--- a/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js
+++ b/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js
@@ -372,6 +372,46 @@ export class VBOBatchingTrianglesLayer {
portion.quantizedPositions = quantizedPositions.slice(start, end);
}
}
+
+ {
+ const indices = buffer.indices;
+ const worldMatrix = this.model.worldMatrix;
+ const positionsDecodeMatrix = state.positionsDecodeMatrix;
+ const pts = [ math.vec3(), math.vec3(), math.vec3() ];
+ const tmpVec3 = math.vec3();
+
+ this._portions.forEach((portion, i) => {
+ let volume = 0;
+ let area = 0;
+
+ const indicesBaseIndex = portion.indicesBaseIndex;
+ const indicesLastIndex = indicesBaseIndex + portion.numIndices;
+
+ for (let faceIdx = indicesBaseIndex; faceIdx < indicesLastIndex; faceIdx += 3) {
+ for (let faceOff = 0; faceOff < 3; ++faceOff) {
+ const i = 3 * indices[faceIdx + faceOff];
+ const worldPos = pts[faceOff];
+
+ // based on getEachVertex
+ worldPos[0] = quantizedPositions[i];
+ worldPos[1] = quantizedPositions[i + 1];
+ worldPos[2] = quantizedPositions[i + 2];
+ math.decompressPosition(worldPos, positionsDecodeMatrix);
+ math.transformPoint3(worldMatrix, worldPos, worldPos);
+ }
+
+ volume += math.dotVec3(pts[0], math.cross3Vec3(pts[1], pts[2], tmpVec3));
+ math.subVec3(pts[1], pts[0], pts[1]);
+ math.subVec3(pts[2], pts[0], pts[2]);
+ area += math.lenVec3(math.cross3Vec3(pts[1], pts[2], tmpVec3));
+ }
+
+ portion._metrics = {
+ area: area / 2,
+ volume: volume / 6
+ };
+ });
+ }
}
if (buffer.normals.length > 0) { // Normals are already oct-encoded
diff --git a/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js b/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js
index 694d3c2321..36eb510185 100644
--- a/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js
+++ b/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js
@@ -252,6 +252,44 @@ export class VBOInstancingTrianglesLayer {
const portion = {};
+ {
+ const indices = this._state.geometry.indices;
+ const quantizedPositions = this._state.geometry.positionsCompressed;
+ const positionsDecodeMatrix = this._state.geometry.positionsDecodeMatrix;
+ const pts = [ math.vec3(), math.vec3(), math.vec3() ];
+ const tmpVec3 = math.vec3();
+
+ let volume = 0;
+ let area = 0;
+
+ const indicesBaseIndex = 0;
+ const indicesLastIndex = indices.length;
+
+ for (let faceIdx = indicesBaseIndex; faceIdx < indicesLastIndex; faceIdx += 3) {
+ for (let faceOff = 0; faceOff < 3; ++faceOff) {
+ const i = 3 * indices[faceIdx + faceOff];
+ const worldPos = pts[faceOff];
+
+ // based on getEachVertex
+ worldPos[0] = quantizedPositions[i];
+ worldPos[1] = quantizedPositions[i + 1];
+ worldPos[2] = quantizedPositions[i + 2];
+ math.decompressPosition(worldPos, positionsDecodeMatrix);
+ math.transformPoint3(meshMatrix, worldPos, worldPos);
+ }
+
+ volume += math.dotVec3(pts[0], math.cross3Vec3(pts[1], pts[2], tmpVec3));
+ math.subVec3(pts[1], pts[0], pts[1]);
+ math.subVec3(pts[2], pts[0], pts[2]);
+ area += math.lenVec3(math.cross3Vec3(pts[1], pts[2], tmpVec3));
+ }
+
+ portion._metrics = {
+ area: area / 2,
+ volume: volume / 6
+ };
+ }
+
if (this.model.scene.pickSurfacePrecisionEnabled) {
portion.matrix = meshMatrix.slice();
portion.inverseMatrix = null; // Lazy-computed in precisionRayPickSurface