live2D (sdk가 6.5 미지원으로 sprite로 대응)
This commit is contained in:
874
Assets/Live2D/Cubism/Rendering/URP/CubismRenderPassFeature.cs
Normal file
874
Assets/Live2D/Cubism/Rendering/URP/CubismRenderPassFeature.cs
Normal file
@@ -0,0 +1,874 @@
|
||||
/**
|
||||
* Copyright(c) Live2D Inc. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by the Live2D Open Software license
|
||||
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||
*/
|
||||
|
||||
|
||||
using Live2D.Cubism.Core;
|
||||
using Live2D.Cubism.Rendering.URP.RenderingInterceptor;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace Live2D.Cubism.Rendering.URP
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for rendering events `CubismRenderingInterceptorsManager.GetInstance().OnPreRendering` and `CubismRenderingInterceptorsManager.GetInstance().OnPostRendering`.
|
||||
/// </summary>
|
||||
public struct CubismRenderedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Render pass data.
|
||||
/// </summary>
|
||||
public CubismRenderPassFeature.CubismRenderPass.PassData PassData;
|
||||
/// <summary>
|
||||
/// Command buffer for rendering.
|
||||
/// </summary>
|
||||
public CommandBuffer CommandBuffer;
|
||||
/// <summary>
|
||||
/// Color buffer render texture.
|
||||
/// </summary>
|
||||
public RenderTexture ColorBuffer;
|
||||
/// <summary>
|
||||
/// Depth buffer texture handle.
|
||||
/// </summary>
|
||||
public TextureHandle DepthBuffer;
|
||||
/// <summary>
|
||||
/// Sorting mode of the draw object's render controller.
|
||||
/// </summary>
|
||||
public CubismSortingMode SortingMode;
|
||||
/// <summary>
|
||||
/// Group sorting order.
|
||||
/// </summary>
|
||||
public int GroupSortingOrder;
|
||||
/// <summary>
|
||||
/// Sorting order of the draw object.
|
||||
/// NOTE: If SortingMode is set to sort by depth, multiple draw objects may have the same sorting order.
|
||||
/// </summary>
|
||||
public int SortingOrder;
|
||||
/// <summary>
|
||||
/// The drawable being rendered.
|
||||
/// </summary>
|
||||
public CubismDrawable Drawable;
|
||||
/// <summary>
|
||||
/// Distance to the camera.
|
||||
/// NOTE: If SortingMode is set to sort by order, multiple draw objects may have the same distance.
|
||||
/// </summary>
|
||||
public float Distance;
|
||||
/// <summary>
|
||||
/// Distance to the previous draw object in the sorted order.
|
||||
/// </summary>
|
||||
public float? PreviousDistance;
|
||||
/// <summary>
|
||||
/// Distance to the next draw object in the sorted order.
|
||||
/// </summary>
|
||||
public float? NextDistance;
|
||||
/// <summary>
|
||||
/// Position of the camera.
|
||||
/// </summary>
|
||||
public Vector3 CameraPos;
|
||||
/// <summary>
|
||||
/// Forward direction of the camera.
|
||||
/// </summary>
|
||||
public Vector3 CameraForward;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scriptable renderer feature for rendering Cubism models in URP.
|
||||
/// </summary>
|
||||
public class CubismRenderPassFeature : ScriptableRendererFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Greater than or equal to comparison value on Z Test.
|
||||
/// </summary>
|
||||
internal static readonly int GEqual = 7;
|
||||
|
||||
/// <summary>
|
||||
/// Less than or equal to comparison value on Z Test.
|
||||
/// </summary>
|
||||
internal static readonly int LEqual = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Scriptable render pass for rendering Cubism models.
|
||||
/// </summary>
|
||||
public class CubismRenderPass : ScriptableRenderPass
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure that groups renderers by their sorting index.
|
||||
/// </summary>
|
||||
private struct RendererGroupData
|
||||
{
|
||||
/// <summary>
|
||||
/// Sorting index for the renderer group.
|
||||
/// </summary>
|
||||
public int SortingIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Array of render controllers in this group.
|
||||
/// </summary>
|
||||
public CubismRenderController[] RenderControllers;
|
||||
|
||||
/// <summary>
|
||||
/// Array of renderers in this group.
|
||||
/// </summary>
|
||||
public CubismRenderer[] Renderers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command buffer used for rendering operations.
|
||||
/// </summary>
|
||||
private static CommandBuffer _commandBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Array of renderer groups sorted by their sorting index.
|
||||
/// </summary>
|
||||
private static RendererGroupData[] _sortedRendererGroupDataArray;
|
||||
|
||||
/// <summary>
|
||||
/// Mesh used for blitting render textures.
|
||||
/// </summary>
|
||||
private static Mesh _blitRenderTextureMesh;
|
||||
|
||||
/// <summary>
|
||||
/// Material used for blitting render textures.
|
||||
/// </summary>
|
||||
private static Material _blitRenderTextureMaterial;
|
||||
|
||||
/// <summary>
|
||||
/// This class stores the data needed by the RenderGraph pass.
|
||||
/// It is passed as a parameter to the delegate function that executes the RenderGraph pass.
|
||||
/// </summary>
|
||||
public class PassData
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of render controllers to be rendered.
|
||||
/// </summary>
|
||||
public CubismRenderController[] RenderControllers;
|
||||
|
||||
/// <summary>
|
||||
/// Array of render controller groups.
|
||||
/// </summary>
|
||||
public CubismRenderControllerGroup.RenderControllerGroupData[] RenderControllerGroupDaraArray;
|
||||
|
||||
/// <summary>
|
||||
/// Camera data for the current rendering pass.
|
||||
/// </summary>
|
||||
public UniversalCameraData CameraData;
|
||||
|
||||
/// <summary>
|
||||
/// Resource data for the current rendering pass.
|
||||
/// </summary>
|
||||
public UniversalResourceData ResourceData;
|
||||
|
||||
/// <summary>
|
||||
/// Texture handle for mask rendering.
|
||||
/// </summary>
|
||||
public TextureHandle MaskTextureHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Texture handle for the camera render target.
|
||||
/// </summary>
|
||||
public TextureHandle CameraTextureHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Texture handle for the camera depth buffer.
|
||||
/// </summary>
|
||||
public TextureHandle CameraDepthTextureHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Texture handle for common rendering operations.
|
||||
/// </summary>
|
||||
public TextureHandle CommonRenderingTextureHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Texture handle for temporary rendering operations.
|
||||
/// </summary>
|
||||
public TextureHandle CommonTemporaryTextureHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up and fills the renderer group.
|
||||
/// </summary>
|
||||
/// <param name="targetIndex">Target index in the sorted renderer groups array.</param>
|
||||
/// <param name="target">Added target renderer group.</param>
|
||||
/// <param name="source">Source render controller group.</param>
|
||||
/// <param name="cameraPos">Position of the camera</param>
|
||||
/// <param name="data">Pass data containing render controller groups and camera data</param>
|
||||
private static void SetUpRendererGroup(int targetIndex, RendererGroupData target, CubismRenderControllerGroup.RenderControllerGroupData source, Vector3 cameraPos, PassData data)
|
||||
{
|
||||
var previousCount = 0;
|
||||
for (var renderControllerIndex = 0; renderControllerIndex < source.Controllers.Length; renderControllerIndex++)
|
||||
{
|
||||
var controller = source.Controllers[renderControllerIndex];
|
||||
|
||||
controller.CurrentFrameBuffer = data.CommonRenderingTextureHandle;
|
||||
|
||||
if (!controller || controller.Renderers == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy the renderers from the controller to the sorted array.
|
||||
for (var rendererIndex = 0; rendererIndex < controller.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
target.Renderers[previousCount + rendererIndex] = controller.Renderers[rendererIndex];
|
||||
target.Renderers[previousCount + rendererIndex].IsLastDrawObjectInModel = false;
|
||||
}
|
||||
|
||||
// Update the previous count for the next controller
|
||||
previousCount += controller.Renderers.Length;
|
||||
}
|
||||
|
||||
// Sort the renderers by their sorting order.
|
||||
SortBySortingOrder(target.Renderers, cameraPos, data.CameraData.camera.transform.forward);
|
||||
|
||||
// Assign the filled target to the sorted renderer groups array.
|
||||
_sortedRendererGroupDataArray[targetIndex] = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the renderer groups based on their sorting order.
|
||||
/// </summary>
|
||||
/// <param name="data">Pass data containing render controller groups.</param>
|
||||
private static void SortingRendererGroups(PassData data)
|
||||
{
|
||||
var didChangeSortingRenderControllerGroup = CubismRenderControllerGroup.GetInstance().DidChangeSortingRenderControllerGroup;
|
||||
if (data.RenderControllerGroupDaraArray == null
|
||||
|| data.RenderControllerGroupDaraArray.Length < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through each render controller group.
|
||||
for (var groupsIndex = 0; groupsIndex < data.RenderControllerGroupDaraArray.Length; groupsIndex++)
|
||||
{
|
||||
var group = data.RenderControllerGroupDaraArray[groupsIndex];
|
||||
var rendererCount = 0;
|
||||
var didChangeSortingOrder = false;
|
||||
|
||||
for (var i = 0; i < group.Controllers.Length; i++)
|
||||
{
|
||||
var controller = group.Controllers[i];
|
||||
|
||||
// Skip if controller is null or has no renderers
|
||||
if (!controller || controller.Renderers == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update sorting state from camera position.
|
||||
controller.UpdateDidChangeSortingFromZ(data.CameraData.worldSpaceCameraPos);
|
||||
|
||||
// Update offscreen render order if it has changed.
|
||||
if (controller.DidChangeDrawableRenderOrder)
|
||||
{
|
||||
for (var offscreenIndex = 0; offscreenIndex < controller.OffscreenRenderers?.Length; offscreenIndex++)
|
||||
{
|
||||
var offscreenRenderer = controller.OffscreenRenderers[offscreenIndex];
|
||||
offscreenRenderer.SetDrawObjectRenderOrder(controller.Model.AllDrawObjectsRenderOrder[controller.DrawableRenderers.Length + offscreenRenderer.Offscreen.UnmanagedIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any controller has changed its drawable render order.
|
||||
didChangeSortingOrder |=
|
||||
controller.DidChangeSorting
|
||||
|| controller.DidChangeDrawableRenderOrder
|
||||
|| didChangeSortingRenderControllerGroup;
|
||||
|
||||
rendererCount += controller.Renderers.Length;
|
||||
}
|
||||
|
||||
// If nothing changed, no need to sort again.
|
||||
if (!didChangeSortingOrder)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create an empty array if it doesn't exist or its size doesn't match the number of groups.
|
||||
if (_sortedRendererGroupDataArray == null
|
||||
|| _sortedRendererGroupDataArray.Length > data.RenderControllerGroupDaraArray.Length)
|
||||
{
|
||||
_sortedRendererGroupDataArray = Array.Empty<RendererGroupData>();
|
||||
}
|
||||
|
||||
var hasGroupFound = false;
|
||||
for (var targetIndex = 0; targetIndex < _sortedRendererGroupDataArray.Length; targetIndex++)
|
||||
{
|
||||
var target = _sortedRendererGroupDataArray[targetIndex];
|
||||
|
||||
if (target.Renderers == null
|
||||
|| target.SortingIndex != group.SortingGroupIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (target.Renderers.Length != rendererCount)
|
||||
{
|
||||
// Resize the renderers array to fit all renderers in the group.
|
||||
Array.Resize(ref target.Renderers, rendererCount);
|
||||
}
|
||||
|
||||
var hasControllersNull = false;
|
||||
for (var controllerIndex = 0; controllerIndex < target.RenderControllers.Length; controllerIndex++)
|
||||
{
|
||||
if (target.RenderControllers[controllerIndex])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasControllersNull = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasControllersNull
|
||||
|| didChangeSortingRenderControllerGroup
|
||||
|| target.RenderControllers.Length != data.RenderControllerGroupDaraArray[groupsIndex].Controllers.Length)
|
||||
{
|
||||
target.RenderControllers = data.RenderControllerGroupDaraArray[groupsIndex].Controllers;
|
||||
}
|
||||
|
||||
// Set up and fill the renderer group.
|
||||
SetUpRendererGroup(targetIndex, target, group, data.CameraData.worldSpaceCameraPos, data);
|
||||
|
||||
hasGroupFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize the renderer group if necessary
|
||||
if (hasGroupFound)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Resize the sorted renderer groups array to add a new group.
|
||||
Array.Resize(ref _sortedRendererGroupDataArray, _sortedRendererGroupDataArray.Length + 1);
|
||||
var newRendererGroup = new RendererGroupData
|
||||
{
|
||||
SortingIndex = group.SortingGroupIndex,
|
||||
RenderControllers = data.RenderControllerGroupDaraArray[groupsIndex].Controllers,
|
||||
Renderers = new CubismRenderer[rendererCount]
|
||||
};
|
||||
|
||||
// Set up and fill the renderer group.
|
||||
SetUpRendererGroup(_sortedRendererGroupDataArray.Length - 1, newRendererGroup, group, data.CameraData.worldSpaceCameraPos,data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the renderers by their sorting order.
|
||||
/// Calculates the distance to camera for each renderer and sorts them accordingly.
|
||||
/// </summary>
|
||||
/// <param name="renderers">Array of renderers to sort</param>
|
||||
/// <param name="cameraPosition">Position of the camera</param>
|
||||
/// <param name="cameraForward">Forward direction of the camera (normalized vector)</param>
|
||||
private static void SortBySortingOrder(CubismRenderer[] renderers, Vector3 cameraPosition, Vector3 cameraForward)
|
||||
{
|
||||
for (var index = 0; index < renderers.Length; index++)
|
||||
{
|
||||
renderers[index].CalculateDistanceToCamera(cameraPosition, cameraForward);
|
||||
}
|
||||
|
||||
Array.Sort(renderers, CompareBySortingOrder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two renderers by their sorting order.
|
||||
/// </summary>
|
||||
/// <param name="a">First renderer to compare</param>
|
||||
/// <param name="b">Second renderer to compare</param>
|
||||
/// <returns>Negative value if a should be rendered before b, positive if after, zero if equal.</returns>
|
||||
private static int CompareBySortingOrder(CubismRenderer a, CubismRenderer b)
|
||||
{
|
||||
// Fail-safe check
|
||||
if (!a || !b
|
||||
|| !a.MeshRenderer || !b.MeshRenderer
|
||||
|| !a.RenderController || !b.RenderController)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var result = a.MeshRenderer.sortingOrder - b.MeshRenderer.sortingOrder;
|
||||
|
||||
if (result != 0
|
||||
|| !(a.SortingMode.SortByDepth() && b.SortingMode.SortByDepth()))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// If sorting order is the same, compare by local Z position.
|
||||
var sortValue = (b.DistanceToCamera / b.RenderController.DepthOffset) - (a.DistanceToCamera / a.RenderController.DepthOffset);
|
||||
result = sortValue >= 0.0f
|
||||
? Mathf.CeilToInt(sortValue)
|
||||
: Mathf.FloorToInt(sortValue);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks and sets the skip rendering flag for each renderer.
|
||||
/// </summary>
|
||||
private static void CheckRenderingSkip()
|
||||
{
|
||||
// Reset skip rendering flag for each renderer.
|
||||
for (var groupIndex = 0; groupIndex < _sortedRendererGroupDataArray.Length; groupIndex++)
|
||||
{
|
||||
var rendererGroup = _sortedRendererGroupDataArray[groupIndex];
|
||||
|
||||
for (var rendererIndex = 0; rendererIndex < rendererGroup.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
rendererGroup.Renderers[rendererIndex].SkipRendering = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check and set skip rendering flag for each renderer.
|
||||
for (var groupIndex = 0; groupIndex < _sortedRendererGroupDataArray.Length; groupIndex++)
|
||||
{
|
||||
var rendererGroup = _sortedRendererGroupDataArray[groupIndex];
|
||||
|
||||
for (var rendererIndex = 0; rendererIndex < rendererGroup.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
var renderer = rendererGroup.Renderers[rendererIndex];
|
||||
|
||||
if (!renderer
|
||||
|| renderer.SkipRendering)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
renderer.SkipRendering |= !renderer.RenderController
|
||||
|| !renderer.RenderController.gameObject.activeSelf
|
||||
|| !renderer.RenderController.enabled
|
||||
|| !renderer.gameObject.activeSelf
|
||||
|| !renderer.MeshRenderer
|
||||
|| !renderer.MeshRenderer.enabled;
|
||||
|
||||
switch (renderer.DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
renderer.SkipRendering |= renderer.Opacity <= 0.0f;
|
||||
break;
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
renderer.SkipRendering |= renderer.Offscreen.Opacity <= 0.0f;
|
||||
|
||||
if (!renderer.SkipRendering)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip rendering for all child drawables and offscreens of the offscreen part.
|
||||
var parts = renderer.RenderController?.Model?.Parts;
|
||||
CubismPart part = null;
|
||||
for (var partIndex = 0; partIndex < parts?.Length; partIndex++)
|
||||
{
|
||||
if (parts[partIndex].UnmanagedIndex != renderer.Offscreen.OwnerIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
part = parts[partIndex];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!part)
|
||||
{
|
||||
renderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip rendering for all child drawables and offscreens of the offscreen part.
|
||||
for (var drawablesIndex = 0; drawablesIndex < part.AllChildDrawables?.Length; drawablesIndex++)
|
||||
{
|
||||
var childDrawable = part.AllChildDrawables[drawablesIndex];
|
||||
|
||||
for (var targetRendererIndex = 0; targetRendererIndex < renderer.RenderController.Renderers.Length; targetRendererIndex++)
|
||||
{
|
||||
var targetRenderer = renderer.RenderController.Renderers[targetRendererIndex];
|
||||
|
||||
if (targetRenderer.DrawObjectType != CubismModelTypes.DrawObjectType.Drawable
|
||||
|| targetRenderer.Drawable.UnmanagedIndex != childDrawable.UnmanagedIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
targetRenderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var offscreensIndex = 0; offscreensIndex < part.AllChildOffscreens?.Length; offscreensIndex++)
|
||||
{
|
||||
var childOffscreen = part.AllChildOffscreens[offscreensIndex];
|
||||
|
||||
for (var j = 0; j < renderer.RenderController.Renderers.Length; j++)
|
||||
{
|
||||
var targetRenderer = renderer.RenderController.Renderers[j];
|
||||
|
||||
if (targetRenderer.DrawObjectType != CubismModelTypes.DrawObjectType.Offscreen
|
||||
|| targetRenderer.Offscreen.UnmanagedIndex != childOffscreen.UnmanagedIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
targetRenderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
renderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i= 0; i < _sortedRendererGroupDataArray.Length; i++)
|
||||
{
|
||||
var target = _sortedRendererGroupDataArray[i];
|
||||
|
||||
// Reset the last draw object flag for each renderer.
|
||||
for (var rendererIndex = 0; rendererIndex < target.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
var targetRenderer = target.Renderers[rendererIndex];
|
||||
targetRenderer.IsLastDrawObjectInModel = false;
|
||||
}
|
||||
|
||||
for (var controllerIndex = 0; controllerIndex < target.RenderControllers.Length; controllerIndex++)
|
||||
{
|
||||
var controller = target.RenderControllers[controllerIndex];
|
||||
|
||||
if (!controller
|
||||
|| !controller.enabled
|
||||
|| !controller.gameObject.activeSelf)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var lastIndex = target.Renderers.Length - 1;
|
||||
while (lastIndex >= 0)
|
||||
{
|
||||
var targetRenderer = target.Renderers[lastIndex];
|
||||
|
||||
// Find the last draw renderer in the sorted array.
|
||||
if (!targetRenderer.SkipRendering
|
||||
&& targetRenderer.RenderController == controller)
|
||||
{
|
||||
// Mark it as the last draw object in the model.
|
||||
targetRenderer.IsLastDrawObjectInModel = true;
|
||||
break;
|
||||
}
|
||||
|
||||
lastIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the objects using the provided command buffer and pass data.
|
||||
/// </summary>
|
||||
/// <param name="commandBuffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="data">Pass data containing render controllers and camera data.</param>
|
||||
private static void DrawObjects(CommandBuffer commandBuffer, PassData data)
|
||||
{
|
||||
// Clear offscreen render textures at the beginning of the draw call.
|
||||
CubismOffscreenRenderTextureManager.GetInstance().ClearRenderTextures(_commandBuffer);
|
||||
|
||||
// Check and set skip rendering flags for each renderer.
|
||||
CheckRenderingSkip();
|
||||
|
||||
for (var groupIndex = 0; groupIndex < _sortedRendererGroupDataArray?.Length; groupIndex++)
|
||||
{
|
||||
var rendererGroup = _sortedRendererGroupDataArray[groupIndex];
|
||||
|
||||
if (rendererGroup.Renderers == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Calculate distance to camera for each renderer for CubismRenderInterceptorsManager events.
|
||||
// HACK: Unity may mix scene camera and game camera information, so recalculate the distance each time in the Editor.
|
||||
for (var rendererIndex = 0; rendererIndex < rendererGroup.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
var target = rendererGroup.Renderers[rendererIndex];
|
||||
|
||||
if (!target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
target.CalculateDistanceToCamera(data.CameraData.worldSpaceCameraPos, data.CameraData.camera.transform.forward);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (var rendererIndex = 0; rendererIndex < rendererGroup.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
var renderer = rendererGroup.Renderers[rendererIndex];
|
||||
|
||||
// Skip if renderer is null or inactive
|
||||
if (!renderer
|
||||
|| renderer.SkipRendering)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var previousRenderer = rendererIndex > 0
|
||||
? rendererGroup.Renderers[rendererIndex - 1]
|
||||
: null;
|
||||
var nextRenderer = rendererIndex < rendererGroup.Renderers.Length - 1
|
||||
? rendererGroup.Renderers[rendererIndex + 1]
|
||||
: null;
|
||||
|
||||
var args = new CubismRenderedEventArgs()
|
||||
{
|
||||
PassData = data,
|
||||
CommandBuffer = commandBuffer,
|
||||
ColorBuffer = renderer.RenderController.CurrentFrameBuffer,
|
||||
DepthBuffer = data.CameraDepthTextureHandle,
|
||||
SortingOrder = renderer.MeshRenderer.sortingOrder,
|
||||
Drawable = renderer.Drawable,
|
||||
SortingMode = renderer.RenderController.SortingMode,
|
||||
GroupSortingOrder = rendererGroup.SortingIndex,
|
||||
Distance = renderer.DistanceToCamera,
|
||||
NextDistance = nextRenderer != null ? nextRenderer.DistanceToCamera : null,
|
||||
PreviousDistance = previousRenderer != null ? previousRenderer.DistanceToCamera : null,
|
||||
CameraPos = data.CameraData.worldSpaceCameraPos,
|
||||
CameraForward = data.CameraData.camera.transform.forward
|
||||
};
|
||||
|
||||
// Pre-rendering event.
|
||||
CubismRenderingInterceptorsManager.GetInstance().OnPreRendering(args);
|
||||
|
||||
// Draw the object.
|
||||
renderer.DrawObject(commandBuffer, data);
|
||||
|
||||
// Post-rendering event.
|
||||
CubismRenderingInterceptorsManager.GetInstance().OnPostRendering(args);
|
||||
|
||||
if (renderer.IsLastDrawObjectInModel)
|
||||
{
|
||||
renderer.RenderController.SubmitDrawOffscreen(commandBuffer, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Blit the result back to the camera texture if needed.
|
||||
if (CubismRenderControllerGroup.GetInstance().IsCopiedToCameraTexture)
|
||||
{
|
||||
_commandBuffer.SetRenderTarget(data.CameraTextureHandle, data.CameraDepthTextureHandle);
|
||||
|
||||
// Draw the full-screen quad to blit the common rendering texture to the camera texture.
|
||||
_blitRenderTextureMaterial.SetTexture(CubismShaderVariables.MainTexture, data.CommonRenderingTextureHandle);
|
||||
|
||||
// Check for reversed Z buffer.
|
||||
var reversedZ = SystemInfo.usesReversedZBuffer ? GEqual : LEqual;
|
||||
_blitRenderTextureMaterial.SetInt(CubismShaderVariables.ReversedZ, reversedZ);
|
||||
|
||||
// Draw the full-screen quad.
|
||||
_commandBuffer.DrawMesh(_blitRenderTextureMesh, Matrix4x4.identity, _blitRenderTextureMaterial);
|
||||
|
||||
// Clear the common rendering texture for the next group.
|
||||
_commandBuffer.SetRenderTarget(data.CommonRenderingTextureHandle);
|
||||
_commandBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the flag after processing.
|
||||
for (var i = 0; i < data.RenderControllers?.Length; i++)
|
||||
{
|
||||
var controller = data.RenderControllers[i];
|
||||
if (!controller || !controller.enabled || !controller.gameObject.activeSelf)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
controller.DidChangeSorting = false;
|
||||
controller.DidChangeDrawableRenderOrder = false;
|
||||
}
|
||||
|
||||
CubismRenderControllerGroup.GetInstance().DidChangeSortingRenderControllerGroup = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ExecutePass is the function that executes the render pass.
|
||||
/// This static method is passed as the RenderFunc delegate to the RenderGraph render pass.
|
||||
/// It is used to execute draw commands.
|
||||
/// </summary>
|
||||
/// <param name="data">Pass data containing render controllers, camera data, and texture handles.</param>
|
||||
/// <param name="context">Render graph context for executing render commands.</param>
|
||||
private static void ExecutePass(PassData data, UnsafeGraphContext context)
|
||||
{
|
||||
// Check if we have any render controllers to process
|
||||
if (data.RenderControllers == null || data.RenderControllers.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_commandBuffer == null)
|
||||
{
|
||||
_commandBuffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
}
|
||||
|
||||
if (!_blitRenderTextureMesh)
|
||||
{
|
||||
_blitRenderTextureMesh = new Mesh
|
||||
{
|
||||
vertices = CubismRenderer.OffscreenVertices,
|
||||
uv = CubismRenderer.OffscreenUVs,
|
||||
triangles = CubismRenderer.OffscreenTriangle
|
||||
};
|
||||
|
||||
_blitRenderTextureMesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
if (!_blitRenderTextureMaterial)
|
||||
{
|
||||
_blitRenderTextureMaterial = new Material(CubismBuiltinMaterials.UnlitBlit);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// HACK: In the editor, Scene view camera may not have the latest texture data.
|
||||
if (data.CameraData.isSceneViewCamera)
|
||||
{
|
||||
_commandBuffer.Blit(data.CameraTextureHandle, data.CommonRenderingTextureHandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sort the renderers by their sorting order.
|
||||
SortingRendererGroups(data);
|
||||
|
||||
// Set render target with both color and depth buffers for proper depth testing
|
||||
_commandBuffer.SetRenderTarget(data.CommonRenderingTextureHandle, data.CameraDepthTextureHandle);
|
||||
_commandBuffer.ClearRenderTarget(false, true, Color.clear);
|
||||
|
||||
// Draw the objects.
|
||||
DrawObjects(_commandBuffer, data);
|
||||
|
||||
// Blit the result back to the camera texture.
|
||||
if (!CubismRenderControllerGroup.GetInstance().IsCopiedToCameraTexture)
|
||||
{
|
||||
_commandBuffer.SetRenderTarget(data.CameraTextureHandle, data.CameraDepthTextureHandle);
|
||||
|
||||
// Draw the full-screen quad to blit the common rendering texture to the camera texture.
|
||||
_blitRenderTextureMaterial.SetTexture(CubismShaderVariables.MainTexture, data.CommonRenderingTextureHandle);
|
||||
|
||||
// Check for reversed Z buffer.
|
||||
var reversedZ = SystemInfo.usesReversedZBuffer? GEqual : LEqual;
|
||||
_blitRenderTextureMaterial.SetInt(CubismShaderVariables.ReversedZ, reversedZ);
|
||||
|
||||
// Draw the full-screen quad.
|
||||
_commandBuffer.DrawMesh(_blitRenderTextureMesh, Matrix4x4.identity, _blitRenderTextureMaterial);
|
||||
}
|
||||
|
||||
// Clear the common rendering texture for the next frame.
|
||||
_commandBuffer.SetRenderTarget(data.CommonRenderingTextureHandle);
|
||||
_commandBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by the render graph to record render passes.
|
||||
/// RecordRenderGraph is where the RenderGraph handle can be accessed, through which render passes can be added to the graph.
|
||||
/// FrameData is a context container through which URP resources can be accessed and managed.
|
||||
/// </summary>
|
||||
/// <param name="renderGraph">Render graph to add render passes to.</param>
|
||||
/// <param name="frameData">Context container providing access to URP resources and camera data.</param>
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||||
{
|
||||
const string renderCustomPass = "Cubism URP Render Pass";
|
||||
|
||||
// Get render controllers - this should work in both play mode and edit mode
|
||||
var renderControllers = CubismRenderControllerGroup.GetInstance().RenderControllers;
|
||||
|
||||
// Skip if no render controllers are available
|
||||
if (renderControllers == null || renderControllers.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This adds a raster render pass to the graph, specifying the name and the data type that will be passed to the ExecutePass function.
|
||||
using (var builder = renderGraph.AddUnsafePass<PassData>(renderCustomPass, out var passData))
|
||||
{
|
||||
// Set up pass data
|
||||
passData.RenderControllers = renderControllers;
|
||||
|
||||
var renderControllerGroups = CubismRenderControllerGroup.GetInstance().GroupDataArray;
|
||||
passData.RenderControllerGroupDaraArray = renderControllerGroups;
|
||||
|
||||
var cameraData = frameData.Get<UniversalCameraData>();
|
||||
var resourceData = frameData.Get<UniversalResourceData>();
|
||||
|
||||
passData.CameraData = cameraData;
|
||||
passData.ResourceData = resourceData;
|
||||
|
||||
var descriptor = resourceData.activeColorTexture.GetDescriptor(renderGraph);
|
||||
descriptor.wrapMode = TextureWrapMode.Repeat;
|
||||
descriptor.filterMode = FilterMode.Point;
|
||||
|
||||
descriptor.name = "CommonTexture";
|
||||
passData.CommonRenderingTextureHandle = renderGraph.CreateTexture(descriptor);
|
||||
builder.UseTexture(passData.CommonRenderingTextureHandle, AccessFlags.ReadWrite);
|
||||
|
||||
descriptor.name = "TempTexture";
|
||||
passData.CommonTemporaryTextureHandle = renderGraph.CreateTexture(descriptor);
|
||||
builder.UseTexture(passData.CommonTemporaryTextureHandle, AccessFlags.ReadWrite);
|
||||
|
||||
descriptor.name = "MaskTexture";
|
||||
var maskTextureHandle = renderGraph.CreateTexture(descriptor);
|
||||
builder.UseTexture(maskTextureHandle, AccessFlags.ReadWrite);
|
||||
passData.MaskTextureHandle = maskTextureHandle;
|
||||
|
||||
// This sets the render target of the pass to the active color texture. Change it to your own render target as needed.
|
||||
passData.CameraTextureHandle = resourceData.activeColorTexture;
|
||||
builder.UseTexture(passData.CameraTextureHandle, AccessFlags.ReadWrite);
|
||||
|
||||
// Set up depth buffer for proper depth testing with other Unity objects
|
||||
passData.CameraDepthTextureHandle = resourceData.activeDepthTexture;
|
||||
builder.UseTexture(passData.CameraDepthTextureHandle);
|
||||
|
||||
// Assigns the ExecutePass function to the render pass delegate. This will be called by the render graph when executing the pass.
|
||||
builder.SetRenderFunc((PassData data, UnsafeGraphContext context) => ExecutePass(data, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scriptable render pass that will be injected into the renderer.
|
||||
/// </summary>
|
||||
private CubismRenderPass _mScriptablePass;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the scriptable render pass and configures its injection point.
|
||||
/// </summary>
|
||||
public override void Create()
|
||||
{
|
||||
_mScriptablePass = new CubismRenderPass
|
||||
{
|
||||
// Configures where the render pass should be injected.
|
||||
renderPassEvent = RenderPassEvent.BeforeRenderingTransparents
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects one or multiple render passes into the renderer.
|
||||
/// This method is called when setting up the renderer once per-camera.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The scriptable renderer to add passes to.</param>
|
||||
/// <param name="renderingData">Rendering state information.</param>
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Skip adding the pass for scene view and game view cameras in the editor.
|
||||
if (!(renderingData.cameraData.cameraType == CameraType.Game
|
||||
|| renderingData.cameraData.cameraType == CameraType.SceneView))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
renderer.EnqueuePass(_mScriptablePass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3927fd6f4d7d83045b64b4cc46dcc3d7
|
||||
BIN
Assets/Live2D/Cubism/Rendering/URP/CubismURPRenderer.asset
LFS
Normal file
BIN
Assets/Live2D/Cubism/Rendering/URP/CubismURPRenderer.asset
LFS
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f003999a57a1da40950cb24efcbacdc
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7754a173d6a71648a081287a1a8d77b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,466 @@
|
||||
/**
|
||||
* Copyright(c) Live2D Inc. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by the Live2D Open Software license
|
||||
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Live2D.Cubism.Core;
|
||||
using Live2D.Cubism.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Live2D.Cubism.Rendering.URP.RenderingInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for rendering interceptors.
|
||||
/// Place custom rendering logic by inheriting from this class.
|
||||
/// </summary>
|
||||
public class CubismRenderingInterceptController : MonoBehaviour, ICubismUpdatable, ICubismRenderingInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Timing for invoking.
|
||||
/// </summary>
|
||||
public enum InvokeTiming
|
||||
{
|
||||
PreRendering,
|
||||
PostRendering,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timing for invoking setting.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public InvokeTiming InvokeTimingSetting;
|
||||
|
||||
/// <summary>
|
||||
/// Modes for invoking.
|
||||
/// </summary>
|
||||
public enum InterceptingMode
|
||||
{
|
||||
SortingOrder,
|
||||
ZDepth,
|
||||
Drawable,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mode for invoking setting.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public InterceptingMode Mode;
|
||||
|
||||
/// <summary>
|
||||
/// Group sorting order for the interceptor.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public int GroupSortingOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Sorting order for the interceptor.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public int SortingOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Target art mesh for the interceptor.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
protected CubismDrawable _targetArtMesh;
|
||||
|
||||
/// <summary>
|
||||
/// If attached <see cref="CubismRenderController"/>, reference.
|
||||
/// </summary>
|
||||
protected CubismRenderController _renderController;
|
||||
|
||||
/// <summary>
|
||||
/// Renderer reference.
|
||||
/// </summary>
|
||||
protected Renderer _renderer;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="_rederer"/>'s material reference.
|
||||
/// </summary>
|
||||
protected Material _material;
|
||||
|
||||
/// <summary>
|
||||
/// Camera draw status.
|
||||
/// </summary>
|
||||
private struct CameraDrawStatus
|
||||
{
|
||||
public Camera Camera;
|
||||
public int LastRenderedPassCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Camera draw status array.
|
||||
/// </summary>
|
||||
private CameraDrawStatus[] _cameraDrawStatus;
|
||||
|
||||
/// <summary>
|
||||
/// Render pass counter.
|
||||
/// </summary>
|
||||
private int _renderPassCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Execution order of the interceptor.
|
||||
/// </summary>
|
||||
public virtual int ExecutionOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return CubismUpdateExecutionOrder.CubismPhysicsController + 10;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this interceptor needs to be updated during editing.
|
||||
/// </summary>
|
||||
public virtual bool NeedsUpdateOnEditing
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a <see cref="CubismUpdateController"/> is attached to the same GameObject.
|
||||
/// </summary>
|
||||
public bool HasUpdateController
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity when the component is enabled.
|
||||
/// </summary>
|
||||
public void OnEnable()
|
||||
{
|
||||
_renderController = GetComponent<CubismRenderController>();
|
||||
|
||||
if (!_renderController)
|
||||
{
|
||||
_renderer = GetComponent<Renderer>();
|
||||
|
||||
_renderer.enabled = false;
|
||||
_material = _renderer.material;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
_material = _renderer.sharedMaterial;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
_cameraDrawStatus = Array.Empty<CameraDrawStatus>();
|
||||
|
||||
CubismRenderingInterceptorsManager.GetInstance().AddInterceptors(this);
|
||||
|
||||
RenderPipelineManager.beginContextRendering += OnBeginContextRendering;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity when the component starts.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
HasUpdateController = TryGetComponent<CubismUpdateController>(out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity when the component is disabled.
|
||||
/// </summary>
|
||||
public void OnDisable()
|
||||
{
|
||||
CubismRenderingInterceptorsManager.GetInstance().RemoveInterceptors(this);
|
||||
|
||||
RenderPipelineManager.beginContextRendering -= OnBeginContextRendering;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity at the beginning of context rendering.
|
||||
/// </summary>
|
||||
private void OnBeginContextRendering(ScriptableRenderContext context, List<Camera> cameras)
|
||||
{
|
||||
_renderPassCounter++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity during LateUpdate.
|
||||
/// </summary>
|
||||
public void LateUpdate()
|
||||
{
|
||||
// Skip if an update controller is present.
|
||||
if (HasUpdateController)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnLateUpdate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called during LateUpdate if no <see cref="CubismUpdateController"/> is present.
|
||||
/// </summary>
|
||||
public virtual void OnLateUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before rendering for each pass.
|
||||
/// </summary>
|
||||
/// <param name="args"> Event arguments. </param>
|
||||
void ICubismRenderingInterceptor.OnPreRenderingForPass(CubismRenderedEventArgs args)
|
||||
{
|
||||
OnPreRendering(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before rendering for each pass.
|
||||
/// </summary>
|
||||
/// <param name="args"> Event arguments. </param>
|
||||
protected virtual void OnPreRendering(CubismRenderedEventArgs args)
|
||||
{
|
||||
// Skip if not invoking pre-rendering.
|
||||
if (InvokeTimingSetting != InvokeTiming.PreRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryDraw(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after rendering for each pass.
|
||||
/// </summary>
|
||||
/// <param name="args"> Event arguments. </param>
|
||||
void ICubismRenderingInterceptor.OnPostRenderingForPass(CubismRenderedEventArgs args)
|
||||
{
|
||||
OnPostRendering(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after rendering for each pass.
|
||||
/// </summary>
|
||||
/// <param name="args"> Event arguments. </param>
|
||||
protected virtual void OnPostRendering(CubismRenderedEventArgs args)
|
||||
{
|
||||
// Skip if not invoking post-rendering.
|
||||
if (InvokeTimingSetting != InvokeTiming.PostRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryDraw(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to draw the object based on the interceptor's settings.
|
||||
/// </summary>
|
||||
/// <param name="args"> Event arguments. </param>
|
||||
private void TryDraw(CubismRenderedEventArgs args)
|
||||
{
|
||||
var currentCamera = args.PassData.CameraData.camera;
|
||||
|
||||
// Find or create camera draw status.
|
||||
var statusIndex = Array.FindIndex(_cameraDrawStatus, status => status.Camera == currentCamera);
|
||||
|
||||
// Create a new entry if not found.
|
||||
if (statusIndex < 0)
|
||||
{
|
||||
// Create a new camera draw status entry.
|
||||
Array.Resize(ref _cameraDrawStatus, _cameraDrawStatus.Length + 1);
|
||||
statusIndex = _cameraDrawStatus.Length - 1;
|
||||
|
||||
// Initialize the new entry.
|
||||
_cameraDrawStatus[statusIndex] = new CameraDrawStatus
|
||||
{
|
||||
Camera = currentCamera,
|
||||
LastRenderedPassCount = -1,
|
||||
};
|
||||
}
|
||||
|
||||
// Get the current rendered frame count.
|
||||
if (_cameraDrawStatus[statusIndex].LastRenderedPassCount == _renderPassCounter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var doDraw = true;
|
||||
switch (Mode)
|
||||
{
|
||||
case InterceptingMode.SortingOrder:
|
||||
// Decide whether to draw based on sorting order comparisons.
|
||||
if (args.GroupSortingOrder != GroupSortingOrder || args.SortingOrder < SortingOrder)
|
||||
{
|
||||
doDraw = false;
|
||||
}
|
||||
break;
|
||||
case InterceptingMode.ZDepth:
|
||||
// Calculate the distance from the camera to the renderer.
|
||||
var directionToRenderer = transform.position - args.CameraPos;
|
||||
var projection = Vector3.Project(directionToRenderer, args.CameraForward);
|
||||
var distance = projection.magnitude;
|
||||
|
||||
// Determine whether to draw based on distance comparisons.
|
||||
var canDraw = (args.Distance < distance || args.NextDistance > distance) &&
|
||||
!(distance > args.Distance && distance > args.NextDistance &&
|
||||
distance > args.PreviousDistance);
|
||||
|
||||
// Decide whether to draw.
|
||||
if (args.PreviousDistance != null
|
||||
&& args.NextDistance != null
|
||||
&& canDraw)
|
||||
{
|
||||
doDraw = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (distance < args.Distance)
|
||||
{
|
||||
doDraw = false;
|
||||
}
|
||||
break;
|
||||
case InterceptingMode.Drawable:
|
||||
if (!_targetArtMesh || args.Drawable != _targetArtMesh)
|
||||
{
|
||||
doDraw = false;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// Skip drawing for unknown modes.
|
||||
break;
|
||||
}
|
||||
|
||||
if (!doDraw)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_renderController)
|
||||
{
|
||||
// Draw the object.
|
||||
args.CommandBuffer.DrawRenderer(_renderer, _material);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var index = 0; index < _renderController.SortedRenderers.Length; index++)
|
||||
{
|
||||
var targetRenderer = _renderController.SortedRenderers[index];
|
||||
|
||||
// Check whether to skip rendering.
|
||||
CheckSkipRendering(targetRenderer);
|
||||
|
||||
if (targetRenderer.SkipRendering)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw the object.
|
||||
targetRenderer.DrawObject(args.CommandBuffer, args.PassData);
|
||||
}
|
||||
|
||||
// Submit offscreen draws.
|
||||
_renderController.SubmitDrawOffscreen(args.CommandBuffer, args.PassData);
|
||||
}
|
||||
|
||||
// Mark as drawn.
|
||||
_cameraDrawStatus[statusIndex].LastRenderedPassCount = _renderPassCounter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether to skip rendering for the given renderer.
|
||||
/// </summary>
|
||||
/// <param name="targetRenderer"> The target renderer. </param>
|
||||
private void CheckSkipRendering(CubismRenderer targetRenderer)
|
||||
{
|
||||
targetRenderer.SkipRendering |= !targetRenderer
|
||||
|| !targetRenderer.gameObject.activeSelf
|
||||
|| !targetRenderer.MeshRenderer
|
||||
|| !targetRenderer.MeshRenderer.enabled;
|
||||
|
||||
switch (targetRenderer.DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
targetRenderer.SkipRendering |= targetRenderer.Opacity <= 0.0f;
|
||||
break;
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
targetRenderer.SkipRendering |= targetRenderer.Offscreen.Opacity <= 0.0f;
|
||||
|
||||
if (!targetRenderer.SkipRendering)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip rendering for all child drawables and offscreens of the offscreen part.
|
||||
var parts = targetRenderer.RenderController.Model.Parts;
|
||||
CubismPart part = null;
|
||||
for (var partIndex = 0; partIndex < parts.Length; partIndex++)
|
||||
{
|
||||
if (parts[partIndex].UnmanagedIndex != targetRenderer.Offscreen.OwnerIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
part = parts[partIndex];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!part)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip rendering for all child drawables and offscreens of the offscreen part.
|
||||
for (var drawablesIndex = 0; drawablesIndex < part.AllChildDrawables?.Length; drawablesIndex++)
|
||||
{
|
||||
var childDrawable = part.AllChildDrawables[drawablesIndex];
|
||||
|
||||
for (var rendererIndex = 0; rendererIndex < targetRenderer.RenderController.Renderers.Length; rendererIndex++)
|
||||
{
|
||||
var checkedRenderer = targetRenderer.RenderController.Renderers[rendererIndex];
|
||||
|
||||
if (checkedRenderer.DrawObjectType != CubismModelTypes.DrawObjectType.Drawable
|
||||
|| checkedRenderer.Drawable.UnmanagedIndex != childDrawable.UnmanagedIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
checkedRenderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var offscreensIndex = 0; offscreensIndex < part.AllChildOffscreens?.Length; offscreensIndex++)
|
||||
{
|
||||
var childOffscreen = part.AllChildOffscreens[offscreensIndex];
|
||||
|
||||
for (var j = 0; j < targetRenderer.RenderController.Renderers.Length; j++)
|
||||
{
|
||||
var checkedRenderer = targetRenderer.RenderController.Renderers[j];
|
||||
|
||||
if (checkedRenderer.DrawObjectType != CubismModelTypes.DrawObjectType.Offscreen
|
||||
|| checkedRenderer.Offscreen.UnmanagedIndex != childOffscreen.UnmanagedIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
checkedRenderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
targetRenderer.SkipRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42ee021d862afdb4dbc221ac3f20ced9
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* Copyright(c) Live2D Inc. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by the Live2D Open Software license
|
||||
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Live2D.Cubism.Rendering.URP.RenderingInterceptor
|
||||
{
|
||||
public class CubismRenderingInterceptorsManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton instance.
|
||||
/// </summary>
|
||||
private static CubismRenderingInterceptorsManager _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the <see cref="CubismRenderingInterceptorsManager"/>.
|
||||
/// </summary>
|
||||
public static CubismRenderingInterceptorsManager GetInstance()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new CubismRenderingInterceptorsManager();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Interceptors"/>'s backing field.
|
||||
/// </summary>
|
||||
private ICubismRenderingInterceptor[] _interceptors;
|
||||
|
||||
/// <summary>
|
||||
/// Registered rendering interceptors.
|
||||
/// </summary>
|
||||
public ICubismRenderingInterceptor[] Interceptors
|
||||
{
|
||||
get { return _interceptors; }
|
||||
set { _interceptors = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor to enforce singleton pattern.
|
||||
/// </summary>
|
||||
private CubismRenderingInterceptorsManager()
|
||||
{
|
||||
_interceptors = Array.Empty<ICubismRenderingInterceptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given interceptor to the manager.
|
||||
/// </summary>
|
||||
/// <param name="interceptor"> The interceptor to add. </param>
|
||||
public void AddInterceptors(ICubismRenderingInterceptor interceptor)
|
||||
{
|
||||
RemoveInterceptors(interceptor);
|
||||
|
||||
for (var interceptorIndex = 0; interceptorIndex < Interceptors.Length; interceptorIndex++)
|
||||
{
|
||||
var current = Interceptors[interceptorIndex];
|
||||
if (interceptor != current)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.LogWarning($"This component is already registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
Array.Resize(ref _interceptors, Interceptors.Length + 1);
|
||||
|
||||
Interceptors[^1] = interceptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given interceptor from the manager.
|
||||
/// </summary>
|
||||
/// <param name="interceptor"> The interceptor to remove. </param>
|
||||
public void RemoveInterceptors(ICubismRenderingInterceptor interceptor)
|
||||
{
|
||||
var targetIndex = -1;
|
||||
for (var interceptorIndex = 0; interceptorIndex < Interceptors.Length; interceptorIndex++)
|
||||
{
|
||||
if (Interceptors[interceptorIndex] != interceptor)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
targetIndex = interceptorIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
if (targetIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var interceptorIndex = targetIndex; interceptorIndex < Interceptors.Length - 1; interceptorIndex++)
|
||||
{
|
||||
Interceptors[interceptorIndex] = Interceptors[interceptorIndex + 1];
|
||||
}
|
||||
|
||||
Array.Resize(ref _interceptors, Interceptors.Length - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reorders the given interceptor to the new index.
|
||||
/// </summary>
|
||||
/// <param name="interceptor"> The interceptor to reorder. </param>
|
||||
/// <param name="newIndex"> The new index to move the interceptor to. </param>
|
||||
public void ReorderIndices(ICubismRenderingInterceptor interceptor, int newIndex)
|
||||
{
|
||||
// Find the current index of the interceptor.
|
||||
var currentIndex = Array.IndexOf(Interceptors, interceptor);
|
||||
|
||||
if (currentIndex < 0
|
||||
|| newIndex < 0
|
||||
|| newIndex >= Interceptors.Length
|
||||
|| currentIndex == newIndex)
|
||||
{
|
||||
// Return silently.
|
||||
return;
|
||||
}
|
||||
|
||||
var temp = Interceptors[currentIndex];
|
||||
if (currentIndex < newIndex)
|
||||
{
|
||||
// Shift elements to the left.
|
||||
for (var i = currentIndex; i < newIndex; i++)
|
||||
{
|
||||
Interceptors[i] = Interceptors[i + 1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shift elements to the right.
|
||||
for (var i = currentIndex; i > newIndex; i--)
|
||||
{
|
||||
Interceptors[i] = Interceptors[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Place the interceptor at the new index.
|
||||
Interceptors[newIndex] = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before rendering.
|
||||
/// </summary>
|
||||
/// <param name="args"> Rendering event arguments. </param>
|
||||
public void OnPreRendering(CubismRenderedEventArgs args)
|
||||
{
|
||||
for (var index = 0; index < Interceptors.Length; index++)
|
||||
{
|
||||
Interceptors[index].OnPreRenderingForPass(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after rendering.
|
||||
/// </summary>
|
||||
/// <param name="args"> Rendering event arguments. </param>
|
||||
public void OnPostRendering(CubismRenderedEventArgs args)
|
||||
{
|
||||
for (var index = 0; index < Interceptors.Length; index++)
|
||||
{
|
||||
Interceptors[index].OnPostRenderingForPass(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89f91d617d8871840a61c1510182383a
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright(c) Live2D Inc. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by the Live2D Open Software license
|
||||
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
|
||||
*/
|
||||
|
||||
|
||||
namespace Live2D.Cubism.Rendering.URP.RenderingInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Rendering interceptor interface.
|
||||
/// </summary>
|
||||
public interface ICubismRenderingInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Called by CubismRenderingInterceptorsManager before rendering for Drawables and Offscreens.
|
||||
/// </summary>
|
||||
/// <param name="args"> Rendering event arguments. </param>
|
||||
internal void OnPreRenderingForPass(CubismRenderedEventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Called by CubismRenderingInterceptorsManager after rendering for Drawables and Offscreens.
|
||||
/// </summary>
|
||||
/// <param name="args"> Rendering event arguments. </param>
|
||||
internal void OnPostRenderingForPass(CubismRenderedEventArgs args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e62e03fd9d651f14b9028f4e5b8d4009
|
||||
Reference in New Issue
Block a user