2
\$\begingroup\$

I am looking for a way to calculate surface area of a 3D mesh object and found this code from a Unity forum:

 public float Area(Mesh m)
 {
     Vector3[] mVertices = m.vertices;
     Vector3 result = Vector3.zero;
     for (int p = mVertices.Length - 1, q = 0; q < mVertices.Length; p = q++)
     {
         result += Vector3.Cross(mVertices[q], mVertices[p]);
     }
     Debug.Log(result);
     result *= 0.5f;
     return result.magnitude;
 }

But the problem is that it is not providing the correct area as I have found from testing the fbx with Revit. Revit is showing a different surface area from what this code shows.

Here is a sample mesh

Front

enter image description here

Back enter image description here

Top enter image description here

Its surface area as calculated by the function above is 97.4913,
while in Revit it's actually 52.811 \$m^2\$.

I don't know how revit calculate it but it provide area and volume information automatically (i have no idea how even revit works). enter image description here

\$\endgroup\$
9
  • \$\begingroup\$ This is off by almost factor 2. Maybe you have double-sided polygons in that mesh which Unity counts twice but Revit only counts as one? \$\endgroup\$
    – Philipp
    Nov 26, 2018 at 11:47
  • \$\begingroup\$ good point @Philipp! but i have other object as well and difference is not remaing the same. This should be work equally with all meshes. sometime it near the result which i am calculating and sometime its thrice of the revit calculation. \$\endgroup\$ Nov 26, 2018 at 12:10
  • \$\begingroup\$ Like one of another mesh it is 23.8991 in unity while 23.612 meter square in revit. another object is 5.17 while in revit its area calculation is 15.318 meter square. \$\endgroup\$ Nov 26, 2018 at 12:12
  • 1
    \$\begingroup\$ Now that you've shown us what you're doing in Revit: yes, you're computing the surface area of A FACE or A PROJECTION of the mesh (ie. "how much wallpaper do we need to cover one side?") Not the whole surface area of the mesh ("how much wallpaper do we need to cover the whole thing?") Which value do you want in the end? What are you using this surface area calculation for? \$\endgroup\$
    – DMGregory
    Nov 26, 2018 at 13:48
  • 1
    \$\begingroup\$ @MuhammadFaizanKhan If you really just want to know how Revit calculates surface area, then we can not help you. Revit is closed source, so only the developers at Autodesk know how it works. But if you would provide us with an actual problem related to your game, then we might be able to help you. \$\endgroup\$
    – Philipp
    Nov 26, 2018 at 14:11

2 Answers 2

8
\$\begingroup\$

Looking at the formula you're using, it looks like it's designed to calculate the area of a planar polygon, where the vertices are all in clockwise / counterclockwise order about the perimeter.

That's not the same as a 3D mesh.

Try something like this instead:

  • we'll iterate over the triangles of the mesh
  • for each triangle, we'll compute vectors representing the two edges
  • half the magnitude of the cross product of these edges gives us the area of the triangle
  • we'll sum these triangle areas to accumulate the full surface area

Note that if your mesh has any double-sided faces or internal faces, they can bias this result.

float CalculateSurfaceArea(Mesh mesh) {
    var triangles = mesh.triangles;
    var vertices = mesh.vertices;

    double sum = 0.0;

    for(int i = 0; i < triangles.Length; i += 3) {
        Vector3 corner = vertices[triangles[i]];
        Vector3 a = vertices[triangles[i + 1]] - corner;
        Vector3 b = vertices[triangles[i + 2]] - corner;

        sum += Vector3.Cross(a, b).magnitude;
    }

    return (float)(sum/2.0);
}

Since it now looks like you want to compute just the area of the mesh facing in a particular direction, you can try this version instead. Pass the direction you want to view the mesh from and it should compute the projected surface area of the faces facing at least partially in that direction.

float CalculateFacingArea(Mesh mesh, Vector3 direction) {
    direction = direction.normalized;
    var triangles = mesh.triangles;
    var vertices = mesh.vertices;

    double sum = 0.0;

    for(int i = 0; i < triangles.Length; i += 3) {
        Vector3 corner = vertices[triangles[i]];
        Vector3 a = vertices[triangles[i + 1]] - corner;
        Vector3 b = vertices[triangles[i + 2]] - corner;

        float projection = Vector3.Dot(Vector3.Cross(b, a), direction);
        if (projection > 0f)
            sum += projection;
    }

    return (float)(sum/2.0);
}
\$\endgroup\$
17
  • \$\begingroup\$ The object is full model from both side as the picture depicted in the question. I have applied your method but result are not same according to the revit tool calculation. I don't know how revit calculating the area \$\endgroup\$ Nov 26, 2018 at 13:30
  • \$\begingroup\$ Could it be giving you the area of a section of the mesh (eg. like the square footage of a floorplan, rather than the total amount of surface including the walls and ceiling)? Why don't you edit your question to show us how you're computing your surface area in Revit, so we can investigate that angle. \$\endgroup\$
    – DMGregory
    Nov 26, 2018 at 13:35
  • \$\begingroup\$ I have no problem to share with you but l really don't know how revit calculate it. please see the updated question i have updated \$\endgroup\$ Nov 26, 2018 at 13:43
  • \$\begingroup\$ Since it looks like you're trying to compute the area of a projection of the mesh, you can try the new version I've added instead. This filters out back faces and only sums up the projections of the surface area of forward-facing polygons in your chosen plane. \$\endgroup\$
    – DMGregory
    Nov 26, 2018 at 14:25
  • \$\begingroup\$ I have tested your code and its working fine but the problem is that each object has different front facing axis, i mean different x y z directions so for each object i have to provide different facing direction. \$\endgroup\$ Nov 27, 2018 at 10:47
1
\$\begingroup\$

Shamelessly building on DMGregorys answer..

You can also define this as an extension method for the Mesh class.

// Put this in some static Util class
public static float CalculateSurfaceArea(this Mesh mesh) {
    var triangles = mesh.triangles;
    var vertices = mesh.vertices;

    double sum = 0.0;

    for (int i = 0; i < triangles.Length; i += 3) {
        Vector3 corner = vertices[triangles[i]];
        Vector3 a = vertices[triangles[i + 1]] - corner;
        Vector3 b = vertices[triangles[i + 2]] - corner;

        sum += Vector3.Cross(a, b).magnitude;
    }

    return (float)(sum / 2.0);
}

you can now simply call

Mesh mesh = Surface.GetComponent<MeshFilter>().sharedMesh;
float area = mesh.CalculateSurfaceArea();
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .