Get ProBuilder Mesh Material With Raycast

Does anyone know how to get a material from a ProBuilder Mesh using a raycast? I’m trying to play different sounds depending on the material, kinda like you would do with the terrain, or even tagged objects.

Hi @atmosgames, are you trying to get the correct material from a mesh with multiple sub-meshes?

Or you can take a look at the following test script?

using System.Linq;
using UnityEngine;

public class TestRaycastMaterial : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out hit))
            {
                MeshFilter meshFilter = hit.collider.GetComponent<MeshFilter>();
                if (meshFilter == null)
                {
                    return;
                }

                Mesh mesh = meshFilter.sharedMesh ?? meshFilter.mesh;
                if (mesh == null)
                {
                    return;
                }

                Renderer renderer = meshFilter.GetComponent<Renderer>();
                if (renderer == null)
                {
                    return;
                }

                int subMeshIndex = GetSubMeshIndex(mesh, hit.triangleIndex);

                Material[] materials = renderer.sharedMaterials ?? renderer.materials;

                int materialIndex;

                // If there are more materials than there are sub-meshes,
                // Unity renders the last sub-mesh with each of the remaining materials,
                // one on top of the next.
                if (materials.Length > mesh.subMeshCount &&
                    subMeshIndex == mesh.subMeshCount - 1)
                {
                    materialIndex = materials.Length - 1;
                }
                else
                {
                    materialIndex = subMeshIndex;
                }

                Material hitMaterial = materials.ElementAtOrDefault(materialIndex);

                Debug.Log($"Hit material: {hitMaterial?.name}", hitMaterial);
            }
        }
    }


    private int GetSubMeshIndex(Mesh mesh, int triangleIndex)
    {
        for (int subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex++)
        {
            int[] subMeshTriangles = mesh.GetTriangles(subMeshIndex);

            for (int i = 0; i < subMeshTriangles.Length; i += 3)
            {
                if (subMeshTriangles _== mesh.triangles[triangleIndex * 3] &&_

subMeshTriangles[i + 1] == mesh.triangles[triangleIndex * 3 + 1] &&
subMeshTriangles[i + 2] == mesh.triangles[triangleIndex * 3 + 2])
{
return subMeshIndex;
}
}
}

return -1;
}
}

I’ve found a solution! As long as you know what the hit information is via ray cast below your character, the following works wonderfully:

 public Texture FindSubmeshTexture()
        {
            MeshCollider collider = controller.groundHit.collider as MeshCollider;
            // Remember to handle case where collider is null because you hit a non-mesh primitive...

            Mesh mesh = collider.sharedMesh;

            // There are 3 indices stored per triangle
            int limit = controller.groundHit.triangleIndex * 3;
            int submesh;
            for (submesh = 0; submesh < mesh.subMeshCount; submesh++)
            {
                int numIndices = mesh.GetTriangles(submesh).Length;
                if (numIndices > limit)
                    break;

                limit -= numIndices;
            }
            Texture myTexture = collider.GetComponent<MeshRenderer>().sharedMaterials[submesh].mainTexture;
            Debug.Log(myTexture);
            return myTexture;
        }