Skip to content

Instantly share code, notes, and snippets.

@Pholith
Last active October 10, 2023 15:42
Show Gist options
  • Save Pholith/585a86b0c7da0dc034aef430c8c2ebe2 to your computer and use it in GitHub Desktop.
Save Pholith/585a86b0c7da0dc034aef430c8c2ebe2 to your computer and use it in GitHub Desktop.
Component to pass Input event like PointerEnter to object displayed throught a RenderTexture
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// Add this component to send input event on physical object rendered on a render texture that is showed on the GUI as a rawImage.
/// This component should be put on the main camera object.
/// The main camera must have a GraphicRaycaster.
/// The camera of the render texture should have a PhysicsRaycaster. If not it will be added. The PhysicRaycaster should be disabled.
/// </summary>
public class RenderTextureRaycaster : BaseRaycaster
{
public GraphicRaycaster graphicRaycaster; // Reference to the PhysicsRaycaster of the main camera
Camera mainCamera; // The main camera
BaseRaycaster mainCameraRaycaster;
public override Camera eventCamera => mainCameraRaycaster.eventCamera;
// Gizmos debug purpose
Ray secondCameraRayOriginPosition;
Ray secondCameraRayComputedPosition;
Ray mainCameraRayGizmo;
RaycastHit hitGizmos;
private void OnDrawGizmos()
{
Gizmos.color = Color.cyan;
Gizmos.DrawRay(secondCameraRayOriginPosition);
Gizmos.color = Color.blue;
Gizmos.DrawRay(secondCameraRayComputedPosition);
Gizmos.DrawSphere(hitGizmos.point, 0.01f);
Gizmos.color = new Color(0.9f, 0.5f, 0.9f);
Gizmos.DrawRay(mainCameraRayGizmo);
}
protected override void Awake()
{
base.Awake();
mainCamera = GetComponent<Camera>();
mainCameraRaycaster = mainCamera.GetComponent<BaseRaycaster>();
}
// Called by Unity when a Raycaster should raycast because it extends BaseRaycaster.
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
// Create a new event data so the next raycaster still have the origin eventData.
PointerEventData newEventData = new PointerEventData(EventSystem.current);
newEventData.position = Input.mousePosition;
List<RaycastResult> raycastResultList = new();
graphicRaycaster.Raycast(newEventData, raycastResultList);
if (raycastResultList.Count > 0 && raycastResultList.First().gameObject.TryGetComponent(out RawImage rawImg))
{
RenderTexture renderTexture = rawImg.texture as RenderTexture;
if (renderTexture == null) return;
Camera secondCamera = GetCameraFromRenderTexture(renderTexture);
if (secondCamera == null) return;
PhysicsRaycaster raycaster = secondCamera.GetOrAddComponent<PhysicsRaycaster>();
// Disable the raycaster to not send event when the mouse is not in the RenderTexture
raycaster.enabled = false;
#if UNITY_EDITOR // Debug only code
secondCameraRayOriginPosition = secondCamera.ScreenPointToRay(newEventData.position);
mainCameraRayGizmo = mainCamera.ScreenPointToRay(newEventData.position);
#endif
raycastResultList.Clear();
Vector2 mouseRelativePositionOnTexture = rawImg.rectTransform.InverseTransformPoint(newEventData.position);
newEventData.position = secondCamera.ScreenToViewportPoint(mouseRelativePositionOnTexture) * new Vector2(secondCamera.pixelWidth, secondCamera.pixelHeight) * 2;
#if UNITY_EDITOR // Debug only code
secondCameraRayComputedPosition = secondCamera.ScreenPointToRay(newEventData.position);
Physics.Raycast(secondCameraRayComputedPosition, out hitGizmos, int.MaxValue, raycaster.eventMask);
#endif
// Remove the Render texture from the list
resultAppendList.RemoveAt(0);
raycaster.Raycast(newEventData, resultAppendList);
}
}
private Camera GetCameraFromRenderTexture(RenderTexture renderTexture)
{
foreach (Camera camera in Camera.allCameras)
{
if (camera.targetTexture != null && camera.targetTexture == renderTexture) return camera;
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment