Volume of THREE.BufferGeometry()

Hi, community!
There were several similar questions on SO about how to compute the volume of a geometry.
I wrote a function, that computes the volume of an buffer geometry (both indexed and non-indexed):

  function getVolume(geometry) {
    if (!geometry.isBufferGeometry) {
      console.log("'geometry' must be an indexed or non-indexed buffer geometry");
      return 0;
    }
    var isIndexed = geometry.index !== null;
    let position = geometry.attributes.position;
    let sum = 0;
    let p1 = new THREE.Vector3(),
      p2 = new THREE.Vector3(),
      p3 = new THREE.Vector3();
    if (!isIndexed) {
      let faces = position.count / 3;
      for (let i = 0; i < faces; i++) {
        p1.fromBufferAttribute(position, i * 3 + 0);
        p2.fromBufferAttribute(position, i * 3 + 1);
        p3.fromBufferAttribute(position, i * 3 + 2);
        sum += signedVolumeOfTriangle(p1, p2, p3);
      }
    }
    else {
      let index = geometry.index;
      let faces = index.count / 3;
      for (let i = 0; i < faces; i++){
        p1.fromBufferAttribute(position, index.array[i * 3 + 0]);
        p2.fromBufferAttribute(position, index.array[i * 3 + 1]);
        p3.fromBufferAttribute(position, index.array[i * 3 + 2]);
        sum += signedVolumeOfTriangle(p1, p2, p3);
      }
    }
    return sum;
  }

  function signedVolumeOfTriangle(p1, p2, p3) {
    return p1.dot(p2.cross(p3)) / 6.0;
  }

Here is a codepen (have a look at the browser console):

Related SO topics:

PS I hope I didn’t re-invent a bicycle. If I did, just let me know :slight_smile:

17 Likes

I like it. The method is well-known, but I believe having clear and usable code for it available will be helpful. Have you considered doing a PR to three.js, making it a BufferGeometry method?

If I remember right, the method requires the surface to be closed. Would you be able to detect whether or not the surface is closed, perhaps spitting out a warning if it is not?

1 Like

Hello.
How to calculate volume of TextGeometry? Because this method checks for BufferGeometry.
Thanks

@kubinho1032
Hi!
Use THREE.TextBufferGeometry() instead.
Or you can adapt this code to work with usual geometry.

In general, you need to get vertices of each face, something like that:

geometry.faces.forEach( face => {
    p1.copy(geometry.vertices[face.a]);
    p2.copy(geometry.vertices[face.b]);
    p3.copy(geometry.vertices[face.c]);
    sum += signedVolumeOfTriangle(p1, p2, p3);
})
2 Likes

Great!
Thanks!

Does scale of mesh affect volume calculating of BufferGeometry?
I have calculated volume, then changed scale of mesh, finally recalculate the volume and numbers were the same.
Should I do something special to recalculate it after scale change?
Thanks

No, it doesn’t.
Scaling doesn’t change original coordinates of vertices.

Maybe this will help:

geometry.faces.forEach( face => {
    p1.copy(geometry.vertices[face.a]).multiply(mesh.scale);
    p2.copy(geometry.vertices[face.b]).multiply(mesh.scale);
    p3.copy(geometry.vertices[face.c]).multiply(mesh.scale);
    sum += signedVolumeOfTriangle(p1, p2, p3);
})

where mesh is your text mesh.

If the scale is uniform you can just multiply the result by that factor.

1 Like

@Fyrestar
Yeah, also true. We just don’t know what scaling was applied (uniform or non-uniform) :slight_smile:

1 Like

Working fine with TextGeometry and scaling.
But what about BufferGeometry?
Im using method provided in first post in this thread to calculate volume.
Im using three.js for the first time, so sorry if my questions are obvious.

And what about it?
Feel free to modify the code, provided in the main post. You can multiply p1, p2, p3 with scaling vector (if you scale the mesh non-uniformly), or simply multiply the result with the scaling factor (in the case of uniform scaling), like @Fyrestar said before.

In fact, to scale a volume, just multiply the unscaled volume by the product of the three scale dimensions, regardless of whether the scaling is uniform or not (so scale**3 for uniform scale). There is no need to recalculate per face.

could you tell me what the volume type it is? is it cubic meter? or what is this calculated volume?

@patrikx3 Cubic units.
About units, take a look at this topic: Units in Three.js

2 Likes

TL;DR: if you assume 1unit=1metre, then the answer will be in cubic meters. If you assume 1unit=1inch, you’ll get cubic inches, and so on.

2 Likes

Is there a way to update the volume for an animated mesh? It seems to compute the volume of the rest position only.

1 Like

Apparently, it works on pure geometry, i.e. ignoring all transformations that occur in the vertex shader. If you can afford to apply all the transformations/animation to the geometry itself, the method may work as it is. Otherwise, you may have to try a different approach. Hm, it may perhaps be possible to “trick” a fragment shader into generating similar measurements…

What assumptions do you make on the geometry “goodness”?

  • closure, water-tightness
  • properly oriented faces

@oldman I don’t, but the one, who uses it, will do :thinking:

Just for you: three-volume - npm

1 Like