- Home /
How would one calculate a 3d Mesh volume in Unity?
Basicly, how could I calculate the volume of a 3d Mesh in Unity?
I have found this, pseudocode, I guess (not sure if it's pseudocode, just a guess):
public float SignedVolumeOfTriangle(Vector p1, Vector p2, Vector p3) {
var v321 = p3.X*p2.Y*p1.Z;
var v231 = p2.X*p3.Y*p1.Z;
var v312 = p3.X*p1.Y*p2.Z;
var v132 = p1.X*p3.Y*p2.Z;
var v213 = p2.X*p1.Y*p3.Z;
var v123 = p1.X*p2.Y*p3.Z;
return (1.0f/6.0f)*(-v321 + v231 + v312 - v132 - v213 + v123);
}
P.S. SignedVolumeOfTriangle();
calculates volume of Tetrahedron, calculated from those triangles. followed by this:
public float VolumeOfMesh(Mesh mesh) {
var vols = from t in mesh.Triangles
select SignedVolumeOfTriangle(t.P1, t.P2, t.P3);
return Math.Abs(vols.Sum());
}
But I have no idea how to translate it to JavaScript or C# for use in Unity... Could someone point me into the right direction on how to do this?
Thanks in advance!
Triangles have area, not volume. Are you looking for surface area?
@flaviusxvii, no I am looking for volume, and that function there is a copy paste from StackOverflow question, about, how to calculate volume. I have no idea how to do that kind of calculation, therefore, I don't actually know what that function is doing... that's why I'm asking here.
This tetrahedron-based computation assumes the origin is within the object and that all triangles can "see" the origin without looking outside the volume. (I was going to say the object had to be convex, but that's not quite true.)
I think it has to be convex and it has to have its center (0, 0, 0) inside the volume.
Answer by Statement · Mar 17, 2011 at 12:34 AM
Given your code works as intended, here's how you'd typically use it.
using UnityEngine;
public class MeshVolume : MonoBehaviour
{
void Start()
{
Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
float volume = VolumeOfMesh(mesh);
string msg = "The volume of the mesh is " + volume + " cube units.";
Debug.Log(msg);
}
public float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
{
float v321 = p3.x * p2.y * p1.z;
float v231 = p2.x * p3.y * p1.z;
float v312 = p3.x * p1.y * p2.z;
float v132 = p1.x * p3.y * p2.z;
float v213 = p2.x * p1.y * p3.z;
float v123 = p1.x * p2.y * p3.z;
return (1.0f / 6.0f) * (-v321 + v231 + v312 - v132 - v213 + v123);
}
public float VolumeOfMesh(Mesh mesh)
{
float volume = 0;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
for (int i = 0; i < triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
volume += SignedVolumeOfTriangle(p1, p2, p3);
}
return Mathf.Abs(volume);
}
}
This worked for me except that at line 39 I added:
volume *= this.transform.localScale.x * this.transform.localScale.y * this.transform.localScale.z;
so that scaling of the objects was taken into account.
that's not very good code, CoalCzar. doesn't "this" refer to the script itself? how can a script have a transform. In any case your addition assumes the code will be run from within an object, which is very limited. This is the sort of thing that should be put in a static helper class.
I think what's really needed is to pass the $$anonymous$$eshFilter, rather than the mesh. extract the mesh within the volume finding function, and also use this
volume *= meshFilter.gameObject.transform.localScale.x * meshFilter.gameObject.transform.localScale.y * meshFilter.gameObject.transform.localScale.z;
and this goes on line 27
I'm sorry how the code shall work? just by attaching it to the mesh in the inspector? Because no calculation is done if it is attached.
This equation always gets me thinking. A tri is a surface element. There is no way of knowing the depth of the triangle. I believe this equation makes assumptions about the the triangle, that the forth point would be relative to the other 3. But there is no way of knowing the real forth point with only 3 points. On a convex mesh you could create the forth point but if the mesh isn't convex then you simply cannot work it out from surface tris even if you had a central point. Perhaps I'm wrong but that's how it appears to me.
So how do you calculate the volume? If you add a RigidBody to the mesh then as long as the density is 1 the mass will equal the volume.
Answer by Statement · Mar 16, 2011 at 07:25 PM
Actually, that looks to me as valid C# code. By the looks of it, it should work (Given you have such a Mesh class). You might have to resolve namespaces System and System.Linq though.
I am not quite sure what you are looking to do, this seems to calculate the volume of a mesh (cubic units), was that just what you had in mind? Take a look at the Unity3d Mesh reference to see how you could get the triangles.
Uhm, well, but how can I get the coordinates of points from those triangles? $$anonymous$$esh.triangles;
returned only one integer, ie: 42.
You said "I am not quite sure what you are looking to do", as simple as "calculate the volume of 3D mesh".
Get point i for triangle j with p = mesh.vertices[mesh.triangles[i + 3 * j]] -- i is 0, 1, 2 for the three corners of the triangle, and j goes from 0 to mesh.triangles.length/3 - 1.
Yeah Egons, the thing I asked this is because I've seen people find a script and try to use it when they think they will solve another problem than it was intended for :)
Answer by yoyo · Mar 16, 2011 at 08:16 PM
For a rough (over-)estimate, you can use the bounding volume ...
float volume = mesh.bounds.size.x * mesh.bounds.size.y * mesh.bounds.size.z;
This won't work for all purposes, but could be useful, depending what you need to do.
Nah, I'm looking for a function to calculate real mesh volume, not boundary box.
Oh well, I tried. ;) $$anonymous$$aybe it'll help someone else looking at this later.
Seems to be what I need. I just need an approximation of the size of the mesh, so this is great, thanks!
Answer by matteo1000g · Sep 25, 2018 at 08:59 AM
To me, it sounds like there is a bit of explanation needed about how and why this formula works since we do have some assumptions. Basically, it is using the vectorial equation for the computation of the volume of a tetrahedron, explained and illustrated here: https://math.stackexchange.com/questions/1603651/volume-of-tetrahedron-using-cross-and-dot-product This assumes that the vectors a, b and c define the edges of the tetrahedron and not the point coordinates of its vertices. Note: If you assume that the 4th point of the tetrahedron is on the origin, then you can use a, b and c as the coordinates of the vertices. Otherwise, if you have 4 vertices coordinates (let's call them d,e,f,g), you can use the same equation but you should use: a = e-d; b = f-d; c=g-d.
Also, please be aware that this equation works only with convex volume. You should envision to use the volume computation through voxelization (explained here: http://blog.wolfire.com/2009/11/Triangle-mesh-voxelization) if you want something more robust to complex shapes (but be aware that it is much slower to compute). However, I did not found an easy to use implementation, all those I found were giving compile errors in complex codes and I did not have time to figure it out.
Note2: @HoverX : Using Unity's RigidBody SetDensity method computes, to my understanding the volume of the Collider's box of your mesh (generally a Capsule). So it is a very coarse approximation.
So, implementing the same volume computation using the 4 vertices of the tetrahedron and defining the fourth vertex of every tetrahedron as the center of mass of the later, one gets the following implementation:
using UnityEngine;
public float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 o)
{
Vector3 v1 = p1 - o;
Vector3 v2 = p2 - o;
Vector3 v3 = p3 - o;
return Vector3.Dot(Vector3.Cross(v1, v2), v3) / 6f; ;
}
public float VolumeOfMesh(Mesh mesh)
{
float volume = 0;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
Vector3 o = new Vector3(0f, 0f, 0f);
// Computing the center mass of the polyhedron as the fourth element of each mesh
for (int i = 0; i < triangles.Length; i++)
{
o += vertices[triangles[i]];
}
o = o / mesh.triangles.Length;
// Computing the sum of the volumes of all the sub-polyhedrons
for (int i = 0; i < triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
volume += SignedVolumeOfTriangle(p1, p2, p3, o);
}
return Mathf.Abs(volume);
}
Note that this is actually called the tripple product which calculates the volume of a "parallelepiped". This is made up of 6 equal tetrahedra hence we divide by 6. It can be easily envisioned if one remembers that the length of the vector you get from a cross product is equal to the area of the parallelogram the two vectors describe. The volume of a parallelepiped is just the base times it's height. So we need to project the diagonal length onto the normal of the base surface. That's implicitly done by the dot product since the area vector we get from the cross product is the scaled normal of the base.
To understand why it actually works regardless of the reference point you may have a look at the 2d equivalent which works the same way. I've posted two comments over here with a gif animation. The second comment shows why the 2d version doesn't work for polygons which intersect itself.
Note that I think it should actually work for non convex meshes. Of course it only works for meshes which are actually closed and also don't have self intersecting surfaces. Though it's hard to imagine or visualize the 3d case.