Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simplify collision mesh of road-like system?

Tags:

c#

unity3d

Given a tile-based road or sidewalk system in Unity in 3d like shown, I'm getting mini-bumps of rigidbodies moving over their seams (even for same vertical position & scale and when using box colliders for each tile). Is there a good way to e.g. combine the collision mesh into one super-simplified & all-flat mesh? I've had no luck with e.g. exporting as Object (neither with my mesh simplifier), as the apparent mini-bumps persist when re-importing and getting rid of the individual box colliders. My requirement isn't that it has to be real-time (though that would be a bit of a plus, if no downsides). Thanks!

A road system

like image 952
Philipp Lenssen Avatar asked Jan 17 '20 17:01

Philipp Lenssen


1 Answers

You can try Mesh.CombineMeshes to make a big collider out of all the small ones.

[RequireComponent(typeof(MeshCollider))]
public class MeshCompositeCollider : MonoBehaviour
{
    void Start()
    {
        var meshColliders = GetComponentsInChildren<MeshCollider>();
        var combine = new CombineInstance[meshColliders.Length];

        for (int i = 0; i < meshColliders.Length; i++)
        {
            combine[i].mesh = meshColliders[i].sharedMesh;
            combine[i].transform = meshColliders[i].transform.localToWorldMatrix;
            meshColliders[i].enabled = false;
        }

        var compositeMesh = new Mesh();
        compositeMesh.CombineMeshes(combine);
        //WeldVertices(compositeMesh);
        GetComponent<MeshCollider>().sharedMesh = compositeMesh;
        GetComponent<MeshCollider>().enabled = true;
    }
}

One thing to note: the resulting combined mesh is in world space coordinates, so if the gameObject this gets attached to has any transform changes they will be applied too.

This might be enough if you have the MeshCollider -> Cooking Options -> Weld Colocated Vertices enabled which should combine the vertices that have the same position.

If not, you can try to weld the vertices yourself to see if that fixes the problem.

public static void WeldVertices(Mesh aMesh, float aMaxDistDelta = 0.01f)
{
    var aMaxDelta = aMaxDistDelta * aMaxDistDelta;
    var verts = aMesh.vertices;    
    List<int> newVerts = new List<int>();
    int[] map = new int[verts.Length];
    // create mapping and filter duplicates.
    for (int i = 0; i < verts.Length; i++)
    {
        var p = verts[i];

        bool duplicate = false;
        for (int i2 = 0; i2 < newVerts.Count; i2++)
        {
            int a = newVerts[i2];
            if ((verts[a] - p).sqrMagnitude <= aMaxDelta)
            {
                map[i] = i2;
                duplicate = true;
                break;
            }
        }
        if (!duplicate)
        {
            map[i] = newVerts.Count;
            newVerts.Add(i);
        }
    }
    // create new vertices
    var verts2 = new Vector3[newVerts.Count];    
    for (int i = 0; i < newVerts.Count; i++)
    {
        int a = newVerts[i];
        verts2[i] = verts[a];        
    }
    // map the triangle to the new vertices
    var tris = aMesh.triangles;
    for (int i = 0; i < tris.Length; i++)
    {
        tris[i] = map[tris[i]];
    }

    aMesh.Clear();       
    aMesh.vertices = verts2;
    aMesh.triangles = tris;                
}


Here is an example (before and after): Above 6 colliders with clear gaps, below 1 collider with no gaps.

enter image description here

like image 149
Pluto Avatar answered Sep 21 '22 17:09

Pluto