- Home /
How do you use HandleUtility.pickGameObjectCustomPasses?
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
}
Answer by andrew-lukasik · May 05, 2021 at 12:11 PM
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[i];
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[i];
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[i];
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[i] = Matrix4x4.Translate( positions[i] );
}
void ISerializationCallbackReceiver.OnAfterDeserialize () {}
}
}
In practical cases you probably would want to recalculate Bounds
for rotation and scale as well. Maybe even test ray against mesh too. But you get the idea.
Your answer
Follow this Question
Related Questions
How can I replace the behaviour of a GUILayout.scrollView on MouseWheel event? 2 Answers
Problem with collision detection 0 Answers
Generic Unity Event triggered from Runtime with listener added in Custom Unity Editor 0 Answers
Casting and receiving non static event from two instances problem 1 Answer
How do i get the child of an GameObject the pointer is hovering over? 0 Answers