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;
}