using System;
using Rive.Components;
using Rive.Utils;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Rive.Components
{
///
/// Provides input to a RiveTextureRenderer and the RivePanel it displays.
///
internal class TexturePanelInputProvider : MonoBehaviour, IPanelInputProvider, IPointerDownHandler, IPointerUpHandler, IPointerMoveHandler, IPointerExitHandler, IPointerEnterHandler
{
[HideInInspector]
[SerializeField] private RiveTextureRenderer m_rivePanelTextureRenderer;
private bool m_hasLoggedWrongColliderTypeError = false;
private Vector2 m_lastNormalizedLocalPointInPanel = Vector2.zero;
public event Action PointerPressed;
public event Action PointerReleased;
public event Action PointerMoved;
public event Action PointerExited;
public event Action PointerEntered;
private IRivePanel RivePanel
{
get
{
if (m_rivePanelTextureRenderer == null)
{
return null;
}
return m_rivePanelTextureRenderer.RivePanel;
}
}
private bool IsSupportedCollider(Collider collider)
{
return collider is MeshCollider;
}
void OnEnable()
{
if (m_rivePanelTextureRenderer == null)
{
m_rivePanelTextureRenderer = GetComponent();
if (m_rivePanelTextureRenderer == null)
{
DebugLogger.Instance.LogWarning($"No {nameof(RiveTextureRenderer)} component found on the GameObject - {gameObject.name}");
}
}
}
///
/// Tries to get the normalized local point in the frame from the RaycastResult.
///
/// The RaycastResult to get the normalized local point from.
/// The normalized local point in the frame.
/// True if the normalized local point was successfully retrieved, false otherwise.
private bool TryGetNormalizedLocalPointInPanel(RaycastResult raycastResult, out Vector2 normalizedLocalPointInFrame)
{
normalizedLocalPointInFrame = Vector2.zero;
if (RivePanel == null)
{
return false;
}
if (raycastResult.gameObject == null)
{
return false;
}
Camera camera = raycastResult.module != null ? raycastResult.module.eventCamera : null;
if (camera == null)
{
return false;
}
Ray ray = camera.ScreenPointToRay(raycastResult.screenPosition);
if (!Physics.Raycast(ray, out RaycastHit hit))
{
return false;
}
// Get the collider from the hit object.
Collider collider = hit.collider;
if (collider == null)
{
DebugLogger.Instance.Log("Collider is null.");
return false;
}
Vector2 pixelUV;
if (IsSupportedCollider(collider))
{
// For mesh colliders, we can use the texture coordinates directly
pixelUV = hit.textureCoord;
}
else
{
LogWrongColliderErrorIfNeeded();
return false;
}
normalizedLocalPointInFrame = pixelUV;
return true;
}
private void LogWrongColliderErrorIfNeeded()
{
if (!m_hasLoggedWrongColliderTypeError)
{
DebugLogger.Instance.LogWarning($"Only MeshColliders are supported for pointer input on Rive Panels. Make sure the collider on the GameObject is a MeshCollider, or set the {nameof(PointerInputMode)} to {nameof(PointerInputMode.DisablePointerInput)} on the {nameof(RiveTextureRenderer)}.");
m_hasLoggedWrongColliderTypeError = true;
}
}
private void ProcessEvent(PointerEventData eventData, Action pointerHandler)
{
if (pointerHandler == null)
{
return;
}
if (RivePanel == null || m_rivePanelTextureRenderer.PointerInputMode == PointerInputMode.DisablePointerInput || !RivePanel.Enabled)
{
return;
}
bool pointIsInPanel = TryGetNormalizedLocalPointInPanel(eventData.pointerCurrentRaycast, out Vector2 normalizedLocalPointInPanel);
if (pointIsInPanel)
{
// Store the last known point in case we need to call the event with it later (like with OnPointerExit).
m_lastNormalizedLocalPointInPanel = normalizedLocalPointInPanel;
}
else
{
// This would be the case with OnPointerExit, because the raycast wouldn't have anything to hit, so we call the event with the last known point.
normalizedLocalPointInPanel = m_lastNormalizedLocalPointInPanel;
}
int pointerId = eventData.pointerId;
pointerHandler?.Invoke(new PanelPointerEvent(normalizedLocalPointInPanel, pointerId));
}
public void OnPointerMove(PointerEventData eventData)
{
ProcessEvent(eventData, PointerMoved);
}
public void OnPointerUp(PointerEventData eventData)
{
ProcessEvent(eventData, PointerReleased);
}
public void OnPointerDown(PointerEventData eventData)
{
ProcessEvent(eventData, PointerPressed);
}
public void OnPointerExit(PointerEventData eventData)
{
ProcessEvent(eventData, PointerExited);
}
public void OnPointerEnter(PointerEventData eventData)
{
ProcessEvent(eventData, PointerEntered);
}
void OnValidate()
{
if (m_rivePanelTextureRenderer == null)
{
m_rivePanelTextureRenderer = GetComponent();
}
}
}
}