Can someone optimise this function farther for me? (volume of a mesh)

pulled this code in from elsewhere, it’s two functions actually. It accurately calculates the volume of a mesh, which is what i need.

it seems to work perfectly for my purposes, but it takes a little while. In my benchmarking it seems to average out around 0.0002 seconds per call, for the size of mesh i’m using it on. which isn’t much, but it’s something. I’m using it to calculate the volume of lots of little objects at authortime and it’s adding a bit of an annoying wait. The stopwatch calls are in there for testing its speed.

It’s a bit too slow for runtime use at present i think, but i don’t need it for that. still i’m repeating these calculations frequently and speeding this up would speed me up. I used it to replace volume-approximation code in an editor extension i’m using. The extension previously just multiplied the bounds of the collider together, which always gave a too-large result. Fine for most purposes, but i need more precision.

Can anyone improve on this, or is it already just about perfect?

    public static 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 static float MeshVolume(MeshFilter meshFilter)
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Mesh mesh = meshFilter.sharedMesh;
        float volume = 0;
   
        Vector3[] vertices = mesh.vertices;
        int[] triangles = mesh.triangles;
   
        for (int i = 0; i < mesh.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);
        }

        volume *= meshFilter.gameObject.transform.localScale.x * meshFilter.gameObject.transform.localScale.y * meshFilter.gameObject.transform.localScale.z;
        timer.Stop();
        print("Meshvolume calculated, time spent so far: " + timer.Elapsed);
        return Mathf.Abs(volume);
    }

Most importantly, don’t use mesh.triangles.Length in the loop, since it’s getting a new copy of the array every iteration. You have a local copy of the array, so use that; triangles.Length. More trivially, creating the p1/p2/p3 structs isn’t really necessary and takes a bit of time (though it makes the code look nicer), so plug the vertices directly into the SignedVolumeOfTriangle call instead.

–Eric

1 Like

okay, done, what i have now is:

    public static 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 static float MeshVolume(MeshFilter meshFilter)
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Mesh mesh = meshFilter.sharedMesh;
        float volume = 0;
    
        Vector3[] vertices = mesh.vertices;
        int[] triangles = mesh.triangles;
        int listLength = triangles.Length;
    
        for (int i = 0; i < listLength; i += 3)
        {
            volume += SignedVolumeOfTriangle(vertices[triangles[i]], vertices[triangles[i + 1]], vertices[triangles[i + 2]]);
        }

        volume *= meshFilter.gameObject.transform.localScale.x * meshFilter.gameObject.transform.localScale.y * meshFilter.gameObject.transform.localScale.z;
        timer.Stop();
        print("Meshvolume calculated, time spent so far: " + timer.Elapsed);
        return Mathf.Abs(volume);
    }

it seems to be running mildly faster already. is there anything farther that can be done?