The manual says that “An example use case for this event would be if you are rendering meshes with Graphics.DrawMesh, and would like to be able to select the GameObject that represents these meshes.” That got me excited, but…how do you implement the event?
public class PickDrawMeshGameObject : MonoBehaviour {
public Mesh mesh;
public Material material;
public void Update() {
Graphics.DrawMesh(mesh, Vector3.zero, Quaternion.identity, material, 0);
}
#if UNITY_EDITOR
public void OnEnable() {
HandleUtility.pickGameObjectCustomPasses += CustomPickGameObjectCallback;
}
public void OnDisable() {
HandleUtility.pickGameObjectCustomPasses -= CustomPickGameObjectCallback;
}
public GameObject CustomPickGameObjectCallback(Camera cam, int layers, Vector2 position,
GameObject[] ignore, GameObject[] filter, out int materialIndex){
// ??
materialIndex = 0;
return null;
}
#endif
}

using UnityEngine;
[ExecuteAlways]
public class PickGameObjectCustomPasses : MonoBehaviour
{
[SerializeField] MeshData[] _data = new MeshData[0];
void OnEnable ()
{
#if UNITY_EDITOR
UnityEditor.HandleUtility.pickGameObjectCustomPasses += OnPickGameObjectCustomPasses;
#endif
}
void OnDisable ()
{
#if UNITY_EDITOR
UnityEditor.HandleUtility.pickGameObjectCustomPasses -= OnPickGameObjectCustomPasses;
#endif
}
void Update ()
{
for( int i=0 ; i<_data.Length ; i++ )
{
var meshData = _data*;
Graphics.DrawMeshInstanced( meshData.mesh , 0 , meshData.material , meshData.matrices );
}
}
#if UNITY_EDITOR
void OnDrawGizmos ()
{
for( int i=0 ; i<_data.Length ; i++ )
{
var meshData = _data*;
Bounds bounds = meshData.mesh.bounds;
for( int m=0 ; m<meshData.matrices.Length ; m++ )
{
Matrix4x4 matrix = meshData.matrices[m];
Gizmos.color = Color.HSVToRGB( (float)new System.Random(17*i+m).NextDouble() , 1 , 1 );
Bounds aabb = new Bounds{ center=matrix.MultiplyPoint(bounds.center) , extents=bounds.extents };
Gizmos.DrawWireCube( aabb.center , aabb.size );
}
}
}
GameObject OnPickGameObjectCustomPasses ( Camera cam , int layers , Vector2 position , GameObject[] ignore , GameObject[] filter , out int materialIndex )
{
bool hit = false;
var ray = cam.ScreenPointToRay( position );
_// Debug.DrawLine( ray.origin , ray.origin+ray.direction*100f , Color.magenta , 1f );_
for( int i=0 ; !hit && i<_data.Length ; i++ )
{
var meshData = _data*;
Bounds bounds = meshData.mesh.bounds;
for( int m=0 ; !hit && m<meshData.matrices.Length ; m++ )
{
Matrix4x4 matrix = meshData.matrices[m];
Bounds aabb = new Bounds{ center=matrix.MultiplyPoint(bounds.center) , extents=bounds.extents };
hit = aabb.IntersectRay(ray);
}
}
/// Index of the Renderer component in the material array that is closest to the specified position.
/// If the picked object does not contain a MeshRenderer, or the picking intersection does not fall within a mesh boundary, this returns -1.
materialIndex = -1;
return hit ? gameObject : null;// there is only one gameObject to select in this case
}
#endif
[System.Serializable]
public struct MeshData : ISerializationCallbackReceiver
{
public Mesh mesh;
public Material material;
public Vector3[] positions;
[Header("Read Only")]
public Matrix4x4[] matrices;
void ISerializationCallbackReceiver.OnBeforeSerialize ()
{
if( positions==null ) positions = new Vector3[]{ Vector3.zero };
if( matrices==null || matrices.Length!=positions.Length ) matrices = new Matrix4x4[ positions.Length ];
for( int i=0 ; i<positions.Length ; i++ )
matrices _= Matrix4x4.Translate( positions *);
}
void ISerializationCallbackReceiver.OnAfterDeserialize () {}
}
}