#if RIVE_USING_HDRP
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.SceneManagement;
using Rive.Utils;
namespace Rive.Components.HDRP
{
///
/// This class is responsible for handling the rendering of Rive objects in the High Definition Render Pipeline.
///
public class HighDefinitionRenderPipelineHandler : MonoBehaviour, IRenderPipelineHandler
{
[SerializeField] private CustomPassInjectionPoint m_injectionPoint = CustomPassInjectionPoint.AfterOpaqueDepthAndNormal;
[Tooltip("The camera that will render the Rive content. If not provided, the main camera will be used.")]
[SerializeField] private Camera m_renderCamera;
private Dictionary m_rtHandleCache = new Dictionary();
private bool m_registeredWithOrchestrator = false;
///
/// The camera that will render the Rive content.
///
public Camera RenderCamera
{
get => m_renderCamera;
set
{
m_renderCamera = value;
if (m_customPassVolume != null)
{
m_customPassVolume.targetCamera = m_renderCamera;
}
}
}
private List m_activeRenderPasses = new List();
private RiveCustomPass m_riveCustomPass;
private CustomPassVolume m_customPassVolume;
private const string DEFAULT_RENDER_TEXTURE_NAME = "URP Rive Render Texture";
private RTHandleSystem m_RTHandleSystem = new RTHandleSystem();
private Coroutine m_setNewRenderCameraCoroutine;
///
/// The rive renderers that are currently being rendered by the custom pass.
///
public IEnumerable ActiveRenderers => m_activeRenderPasses;
protected virtual void Awake()
{
m_RTHandleSystem.Initialize(Screen.width, Screen.height);
}
protected virtual void OnEnable()
{
SceneManager.activeSceneChanged += ChangedActiveScene;
TrySubscribeToOrchestrator();
}
protected virtual void OnDisable()
{
SceneManager.activeSceneChanged -= ChangedActiveScene;
UnsubscribeFromOrchestrator();
}
private void TrySubscribeToOrchestrator()
{
if (m_registeredWithOrchestrator || !isActiveAndEnabled || m_activeRenderPasses.Count == 0)
{
return;
}
var orchestrator = Orchestrator.Instance;
if (orchestrator != null)
{
orchestrator.OnPostRenderPreparation += ValidateRenderCamera;
m_registeredWithOrchestrator = true;
}
}
private void UnsubscribeFromOrchestrator()
{
if (!m_registeredWithOrchestrator)
{
return;
}
var orchestrator = Orchestrator.Instance;
if (orchestrator != null)
{
orchestrator.OnPostRenderPreparation -= ValidateRenderCamera;
}
m_registeredWithOrchestrator = false;
}
private void ValidateRenderCamera()
{
if (m_renderCamera != null && !m_renderCamera.isActiveAndEnabled)
{
Camera newCamera = CameraHelper.GetRenderCameraInScene();
if (newCamera != null)
{
RenderCamera = newCamera;
}
}
}
private void ChangedActiveScene(Scene arg0, Scene arg1)
{
// If the scene changes and there's no main camera, we need to wait until there's one
if (m_renderCamera == null && m_setNewRenderCameraCoroutine == null)
{
m_setNewRenderCameraCoroutine = StartCoroutine(SetNewRenderCamera());
}
}
private IEnumerator SetNewRenderCamera()
{
// Wait until there's a main camera
Camera camera = null;
while (camera == null)
{
camera = Camera.main;
if (camera == null)
{
yield return null;
}
}
RenderCamera = camera;
m_setNewRenderCameraCoroutine = null;
}
protected virtual void Start()
{
if (m_renderCamera == null)
{
RenderCamera = Camera.main;
}
m_customPassVolume = gameObject.AddComponent();
m_customPassVolume.injectionPoint = m_injectionPoint;
// We only want one camera to render the Rive content.
m_customPassVolume.targetCamera = RenderCamera;
m_riveCustomPass = new RiveCustomPass(this);
m_customPassVolume.customPasses.Add(m_riveCustomPass);
}
public void Register(IRenderer renderer)
{
if (renderer == null)
{
DebugLogger.Instance.LogWarning("Cannot register a null renderer.");
return;
}
if (m_activeRenderPasses.Contains(renderer))
{
DebugLogger.Instance.LogWarning("Renderer is already registered.");
return;
}
m_activeRenderPasses.Add(renderer);
TrySubscribeToOrchestrator();
}
public void Unregister(IRenderer renderer)
{
if (renderer == null)
{
DebugLogger.Instance.LogWarning("Cannot unregister a null renderer.");
return;
}
if (!m_activeRenderPasses.Contains(renderer))
{
// Teardown paths can attempt to unregister the same renderer more than once.
// We keep unregister idempotent and quiet to match URP/BuiltIn handlers.
return;
}
m_activeRenderPasses.Remove(renderer);
if (m_activeRenderPasses.Count == 0)
{
UnsubscribeFromOrchestrator();
}
}
public UnityEngine.RenderTexture AllocateRenderTexture(int width, int height)
{
var descriptor = TextureHelper.Descriptor(width, height);
return m_RTHandleSystem.Alloc(width, height, colorFormat: descriptor.graphicsFormat, enableRandomWrite: descriptor.enableRandomWrite, name: DEFAULT_RENDER_TEXTURE_NAME);
}
public void ReleaseRenderTexture(RenderTexture renderTexture)
{
if (m_RTHandleSystem == null)
{
return;
}
if (renderTexture == null)
{
return;
}
if (m_rtHandleCache.Remove(renderTexture, out var rtHandle))
{
rtHandle.Release();
}
}
public RenderTexture ResizeRenderTexture(RenderTexture renderTexture, int width, int height)
{
if (m_RTHandleSystem == null)
{
DebugLogger.Instance.LogWarning("Cannot resize render texture. RTHandleSystem is null.");
return null;
}
if (renderTexture == null)
{
DebugLogger.Instance.LogWarning("Cannot resize a null render texture.");
return null;
}
if (m_rtHandleCache.TryGetValue(renderTexture, out var rtHandle))
{
rtHandle.Release();
m_rtHandleCache.Remove(renderTexture);
}
return AllocateRenderTexture(width, height);
}
public void SetRendererTexture(IRenderer renderer, RenderTexture renderTexture)
{
if (renderer == null)
{
DebugLogger.Instance.LogError("Cannot set texture on a null renderer.");
return;
}
Renderer riveRenderer = renderer as Renderer;
if (riveRenderer == null)
{
DebugLogger.Instance.LogError("Cannot set texture on a non-Rive renderer.");
return;
}
riveRenderer.RenderQueue.UpdateTexture(renderTexture);
}
public bool IsRendererRegistered(IRenderer renderer)
{
return m_activeRenderPasses.Contains(renderer);
}
private void Cleanup()
{
UnsubscribeFromOrchestrator();
// Remove the custom pass from the volume and destroy the volume
if (m_customPassVolume != null)
{
if (m_riveCustomPass != null)
{
m_customPassVolume.customPasses.Remove(m_riveCustomPass);
m_riveCustomPass = null;
}
Destroy(m_customPassVolume);
m_customPassVolume = null;
}
m_RTHandleSystem?.Dispose();
// Stop any running coroutines
if (m_setNewRenderCameraCoroutine != null)
{
StopCoroutine(m_setNewRenderCameraCoroutine);
m_setNewRenderCameraCoroutine = null;
}
}
private void OnDestroy()
{
Cleanup();
}
}
}
#endif