1
\$\begingroup\$

I want to calculate the mass, center of mass, and inertia tensor of player created objects. Some of the objects will be hollow instead of solid. I am creating a closed triangle mesh for their object. Note that this is for a physics building game so I want the values to be as accurate as possible (within reason).

Based on the references listed below, I can get the properties for a solid object by creating a tetrahedron from each triangle of the mesh and calculating the signed volume which gives the mass (volume * density) and the center of mass. I even got the inertia tensor calculation working.

How do I do the same for a hollow object?

  1. For the mass and center of mass, I am iterating through the triangles of the mesh and totaling their area and calculating the area-weighted average of their positions, then multiplying the surface area by a "density" value to get the mass.

     public static (float, Vector3) CalculateSurfaceArea(Mesh mesh)
     {
         Vector3[] vertices = mesh.vertices;
         int[] triangles = mesh.triangles;
    
         float totalArea = 0f;
         Vector3 temp = Vector3.zero;
    
         for (int i = 0; i <= triangles.Length - 3; i += 3)
         {
             Vector3 v1 = vertices[triangles[i]];
             Vector3 v2 = vertices[triangles[i + 1]];
             Vector3 v3 = vertices[triangles[i + 2]];
             Vector3 edge21 = v2 - v1;
             Vector3 edge31 = v3 - v1;
    
             float area = Vector3.Cross(edge21, edge31).magnitude / 2f; // area of the triangle
             totalArea += area;
    
             Vector3 triCenter = (v1 + v2 + v3) / 3f; // center of the triangle
             temp += triCenter * area;
         }
    
         Vector3 areaCenter = temp / totalArea;
    
         return (totalArea, areaCenter);
     }
    
  2. For the inertia tensor, I am trying a similar approach where I iterate through all the triangles and total up their moments of inertia and using the parallel-axis theorem to account for their positions but (a) I am not sure this is correct and (b) how do I calculate the products of inertia (Ixy, Ixz, Iyz)?

     public static (Vector3, Quaternion) CalculateHollowInertiaTensor(Mesh mesh)
     {
         Vector3[] vertices = mesh.vertices;
         int[] triangles = mesh.triangles;
    
         double Ixx = 0f;
         double Iyy = 0f;
         double Izz = 0f;
         double Ixy = 0f;
         double Ixz = 0f;
         double Iyz = 0f;
    
         for (int i = 0; i <= triangles.Length - 3; i += 3)
         {
             Vector3 v1 = vertices[triangles[i]];
             Vector3 v2 = vertices[triangles[i + 1]];
             Vector3 v3 = vertices[triangles[i + 2]];
             Vector3 edge21 = v2 - v1;
             Vector3 edge31 = v3 - v1;
             Vector3 center = (v1 + v2 + v3) / 3f; // center of the triangle
             Vector3 offset = center - v1;
    
             float area = Vector3.Cross(edge21, edge31).magnitude / 2f; // area of the triangle
    
             // Moment of inertia of triangle rotating around the first vertex
             // https://en.wikipedia.org/wiki/List_of_moments_of_inertia
             // I = (1/6)m(P.P + P.Q + Q.Q)
             float triIxx = (edge21.y * edge21.y + edge21.z * edge21.z + edge21.y * edge31.y + edge21.z * edge31.z + edge31.y * edge31.y + edge31.z * edge31.z) / 6f;
             float triIyy = (edge21.x * edge21.x + edge21.z * edge21.z + edge21.x * edge31.x + edge21.z * edge31.z + edge31.x * edge31.x + edge31.z * edge31.z) / 6f;
             float triIzz = (edge21.x * edge21.x + edge21.y * edge21.y + edge21.x * edge31.x + edge21.y * edge31.y + edge31.x * edge31.x + edge31.y * edge31.y) / 6f;
    
             // Shift to the center of the triangle
             triIxx -= offset.y * offset.y + offset.z * offset.z;
             triIyy -= offset.x * offset.x + offset.z * offset.z;
             triIzz -= offset.x * offset.x + offset.y * offset.y;
    
             // Shift to the origin (using parallel-axis theorem)
             triIxx += center.y * center.y + center.z * center.z;
             triIyy += center.x * center.x + center.z * center.z;
             triIzz += center.x * center.x + center.y * center.y;
    
             Ixx += triIxx * area;
             Iyy += triIyy * area;
             Izz += triIzz * area;
             //Ixy += area * center.x * center.y;
             //Ixz += area * center.x * center.z;
             //Iyz += area * center.y * center.z;
         }
    
         Matrix<double> inertiaTensor = Matrix<double>.Build.Dense(3, 3);
         inertiaTensor[0, 0] = Ixx;
         inertiaTensor[1, 1] = Iyy;
         inertiaTensor[2, 2] = Izz;
         inertiaTensor[0, 1] = inertiaTensor[1, 0] = -Ixy;
         inertiaTensor[0, 2] = inertiaTensor[2, 0] = -Ixz;
         inertiaTensor[1, 2] = inertiaTensor[2, 1] = -Iyz;
    
         Debug.Log(inertiaTensor);
    
         //Matrix<double> inertiaTensorInverse = inertiaTensor.Inverse();
    
         // Find the principal axes and simplified inertia tensor
         Evd<double> evd = inertiaTensor.Evd();
         Vector3 inertiaTensorDiagonal = MakeVector3(evd.EigenValues.Real());
         Quaternion inertiaTensorRotation = Quaternion.LookRotation(
             MakeVector3(evd.EigenVectors.Column(2)),
             MakeVector3(evd.EigenVectors.Column(1))
             );
    
         return (inertiaTensorDiagonal, inertiaTensorRotation);
     }
    

References I've been using: (would like some more :))

\$\endgroup\$

0

You must log in to answer this question.

Browse other questions tagged .