RaycastHit, triangleIndex, and submeshes - which submesh?

A raycast hit will give you the triangle index hit. What if the mesh has submeshes? There is no submesh index.

Is it ok to assume that the triangle list is sorted such that the submesh is the one modulo the number of vertices in preceding lists?

In other words, lets say submesh 0 has 10 verts, submesh 1 has 15 verts, and submesh 2 has 12 verts. And the raycast hit says ‘triangle index 12’

Does that mean submesh 1? IOW submesh 0 is from 0-9, submesh 1 is 10-24, and submesh2 is 25-37?

How do I map a material to the submesh?

  • main mesh contains all triangles of this mesh and also all that can be accessed via submeshes.
  • submeshes contains tris that can be accessed also via main mesh
  • submeshes are always exists in any mesh, at least one
  • sum of all submeshes tris is equal to main mesh tris
  • submeshes are used to apply different materials to tris

so finally, when you got from raycast hit to triangle index 50, you should search it in main mesh (it contains all tris including triangle 50), then get vertices for this triangle, and find triangle with those vertices in submeshes

bonus: MeshAnaliser.cs

using UnityEngine;
using System.Collections;
public class MeshAnaliser : MonoBehaviour
{
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
            {
                Mesh m = GetMesh(gameObject);
                if (m)
                {
                    int[] hittedTriangle = new int[] 
                    {
                        m.triangles[hit.triangleIndex * 3], 
                        m.triangles[hit.triangleIndex * 3 + 1], 
                        m.triangles[hit.triangleIndex * 3 + 2] 
                    };
                    for (int i = 0; i < m.subMeshCount; i++)
                    {
                        int[] subMeshTris = m.GetTriangles(i);
                        for (int j = 0; j < subMeshTris.Length; j += 3)
                        {
                            if (subMeshTris[j] == hittedTriangle[0] &&
                                subMeshTris[j + 1] == hittedTriangle[1] &&
                                subMeshTris[j + 2] == hittedTriangle[2])
                            {
                                Debug.Log(string.Format("triangle index:{0} submesh index:{1} submesh triangle index:{2}", hit.triangleIndex, i, j / 3));
                            }
                        }
                    }
                }
            }
        }
    }
    static Mesh GetMesh(GameObject go)
    {
        if (go)
        {
            MeshFilter mf = go.GetComponent();
            if (mf)
            {
                Mesh m = mf.sharedMesh;
                if (!m) { m = mf.mesh; }
                if (m)
                {
                    return m;
                }
            }
        }
        return (Mesh)null;
    }
}

I ran some tests using a procedurally-generated mesh. As artie suspected in his original post, the triangle lists for the submeshes seem to be stored in order in the global triangle list. The ordering is determined by the submesh index, not the order of creation of the submeshes.

So if you do something like:

mesh.SetTriangles (submesh0, 0);
mesh.SetTriangles (submesh1, 1);
mesh.SetTriangles (submesh2, 2);

The full triangle list will consist of all the vertex indices for submesh0, followed by all the vertex indices for submesh1, followed by all the vertex indices for submesh2.

Efficient method:

static int GetSubmeshIndex(int triangleIndex, Mesh mesh) {
    int triangleCount = 0;
    for (int i = 0; i < mesh.subMeshCount; ++i) {
        var triangles = mesh.GetTriangles(i);
        triangleCount += triangles.Length / 3;
        if (triangleIndex < triangleCount) return i;
    }
    return 0;
}