diff --git a/examples/measurement/areaVolume.html b/examples/measurement/areaVolume.html new file mode 100644 index 000000000..de52054ee --- /dev/null +++ b/examples/measurement/areaVolume.html @@ -0,0 +1,222 @@ + + + + + + + xeokit Example + + + + + + + + + + +
+
+

ZonesPlugin

+

Creating 3D zones inside a model

+

In this example, we're loading a BIM model and create zones inside it

+ +

Customize

+
+ + +

Resources

+ +
+ + + diff --git a/src/viewer/scene/model/SceneModelEntity.js b/src/viewer/scene/model/SceneModelEntity.js index 92b55edf3..0b5b95bce 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 9a30c37ff..f061246ed 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 8c96e19e7..3770645f6 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 694d3c232..36eb51018 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