0
\$\begingroup\$

Here is the excerpt of my script which is calculating the surface area and face area of a mesh on OnMouseDown Event.

    private void OnMouseDown()
    {
         Mesh selectedMesh = this.gameObject.GetComponent<MeshFilter>().mesh;

        float surfaceArea   = CalculateSurfaceArea(selectedMesh);
        float areaFromXFace = CalculateFacingArea(selectedMesh, new Vector3(1, 0, 0));
        float areaFromYFace = CalculateFacingArea(selectedMesh, new Vector3(0, 1, 0));
        float areaFromZFace = CalculateFacingArea(selectedMesh, new Vector3(0, 0, 1));
        float areaFromClickedFace = ProjectionPlaneClickedArea(selectedMesh);    

       string areas =  "\n Surface Area            : " + surfaceArea;
        areas += "\n Area From X-axis Face   : " + areaFromXFace;
        areas += "\n Area From Y-axis Face   : " + areaFromYFace;
        areas += "\n Area From Z-axis Face   : " + areaFromZFace;
        areas += "\n Area From Selected Face : " + areaFromClickedFace;
        Debug.Log("Areas :"+ areas);

     }

     float ProjectionPlaneClickedArea(Mesh selcectedMesh)
     {

        Camera c = Camera.main;
        Ray ray = c.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 100f))
        {
             Debug.DrawRay(ray.origin, ray.direction * 100, Color.yellow);
             Debug.Log("normal :" + hit.normal);
             Debug.Log("Area from normal/direction : " + CalculateFacingArea(selcectedMesh, hit.normal));
                    return CalculateFacingArea(selcectedMesh, hit.normal);
                }
                return 0f;
       }

       float CalculateSurfaceArea(Mesh mesh)
       {
         //Limitation: Note that if your mesh has any double-sided faces or internal faces, they can bias this result.
         var triangles = mesh.triangles;
         var vertices = mesh.vertices;

         double sum = 0.0;

         for (int i = 0; i < triangles.Length; i += 3)               //iterate over the triangles of the mesh
         {
           Vector3 corner = vertices[triangles[i]];            //for each triangle, we'll compute vectors representing the two edges
          Vector3 a = vertices[triangles[i + 1]] - corner;
          Vector3 b = vertices[triangles[i + 2]] - corner;

          sum += Vector3.Cross(a, b).magnitude; //half the magnitude of the cross product of these edges gives us the area of the triangle
         }

        return (float)(sum / 2.0);
     }

     /// <summary>
     /// 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.
   /// </summary>
    /// <param name="mesh"></param>
     /// <param name="direction"></param>
     /// <returns></returns>
      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)    //iterate over the triangles of the mesh
         {
             //"Subtracting each 2nd & 3rd vertex" just gives the two edges of the triangle radiating from the initial corner. 
             Vector3 corner  = vertices[triangles[i]];
             Vector3 a       = vertices[triangles[i + 1]] - corner; 
             Vector3 b       = vertices[triangles[i + 2]] - corner;

            //The cross product divided by two gives the area of the triangle.
            //The dot product is what handles projecting this area onto the plane of interest. 
            float projection = Vector3.Dot(Vector3.Cross(b, a), direction);

            if (projection > 0f)//only sum projected triangles
                 sum += projection;
                }
                return (float)(sum / 2.0);
      }

I am able calculate face area by CalculateFacingArea method it needs a mesh and direction which is working fine, as if i want to calculate the area of x direction i can use it

float areaFromXFace = CalculateFacingArea(selectedMesh, new Vector3(1, 0, 0));

BUT I have to provide the face/direction information dynamically which is i am doing in this function ProjectionPlaneClickedArea using a raycast. It is getting a hit point normals which i am using as a direction and passing it to the function. It is working well with some of the meshes but not with all meshes, for example below image:

enter image description here

As you can see if I click this object from the front then it should calculate Y Face area but it is calculating with X direction. The click point normal value is (1.0, 0.0, 0.1).

While it is working fine if my clicked object axis are something like this:

enter image description here

In this case x is the facing are and it is working fine. I have this value of normal (1.0, 0.0, 0.0).

I am neither geometry/math expert nor a modeler so I cannot identify that its Object's normal problem or it is calculation problem.

Remember I don't want to model all the objects in the same direction/axis, where i can get the direction hard-coded. I want to calculate the direction dynamically but correctly.

\$\endgroup\$
  • \$\begingroup\$ Is the GameObject that the MeshFilter is on or any of its parents rotated in the world at all? Because the normal returned from the raycast will be in world space, not in mesh local space, which is what CalculateFacingArea is expecting. \$\endgroup\$ – Ed Marty Nov 29 '18 at 12:45
  • \$\begingroup\$ Yeah, all object are rotated in different directions. \$\endgroup\$ – Muhammad Faizan Khan Nov 29 '18 at 13:03
0
\$\begingroup\$

You'll need to transform the normal into local coordinates before using it with the mesh.

float ProjectionPlaneClickedArea(Mesh selcectedMesh)
{
        Camera c = Camera.main;
        Ray ray = c.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 100f))
        {
             Vector3 localNormal = hit.transform.InverseTransformDirection(hit.normal);
             return CalculateFacingArea(selcectedMesh, localNormal);
        }
        return 0f;
}
\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.