#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