Files
StoryGame_Unity/Assets/Live2D/Cubism/Rendering/CubismRenderControllerUsingBlendMode.cs

498 lines
18 KiB
C#

/**
* 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.Framework;
using Live2D.Cubism.Rendering.URP;
using System;
using System.Collections.Generic;
using Live2D.Cubism.Rendering.URP.RenderingInterceptor;
using UnityEngine;
using UnityEngine.Rendering;
namespace Live2D.Cubism.Rendering
{
/// <summary>
/// Partial class that describes processing related to the rendering method added in Cubism 5.3 and later.
/// </summary>
public partial class CubismRenderController
{
#region Values
/// <summary>
/// Material property block for the model.
/// </summary>
private MaterialPropertyBlock _properties;
/// <summary>
/// Whether the model has a root part offscreen.
/// </summary>
internal bool HasRootPartOffscreen = true;
/// <summary>
/// <see cref="HasMask"/>'s backing field.
/// </summary>
[SerializeField, HideInInspector]
private bool _hasMask;
/// <summary>
/// Is the model using masks?
/// </summary>
public bool HasMask
{
get
{
return _hasMask;
}
set
{
_hasMask = value;
}
}
/// <summary>
/// Current frame buffer used for rendering.
/// Offscreen rendering uses this to render the model.
/// </summary>
public RenderTexture CurrentFrameBuffer { get; set; }
/// <summary>
/// Index of the current offscreen owner in the unmanaged array.
/// </summary>
public int CurrentOffscreenUnmanagedIndex { get; set; }
#region Sorting
/// <summary>
/// <see cref="GroupedSortingIndex"/>'s backing field.
/// </summary>
[SerializeField, HideInInspector]
private int _groupedSortingIndex;
/// <summary>
/// Sorting index for grouped rendering.
/// </summary>
public int GroupedSortingIndex
{
get
{
return _groupedSortingIndex;
}
set
{
// Remove from the common rendering controller's groups.
CubismRenderControllerGroup.GetInstance().RemoveRenderControllerFromGroups(this, true);
_groupedSortingIndex = value;
// Re-add to the common rendering controller's groups.
CubismRenderControllerGroup.GetInstance().AddRenderControllerGroups(this);
}
}
/// <summary>
/// Whether the render order of the <see cref="CubismDrawable"/>s did change.
/// </summary>
internal bool DidChangeDrawableRenderOrder;
/// <summary>
/// Whether the sorting order of the <see cref="CubismRenderer"/>s did change.
/// </summary>
internal bool DidChangeSorting;
[NonSerialized]
private CubismRenderer[] _sortedRenderers;
/// <summary>
/// Sorted <see cref="Renderers"/>s for using <see cref="ICubismRenderingInterceptor"/>.
/// </summary>
public CubismRenderer[] SortedRenderers
{
get
{
if (_sortedRenderers == null)
{
SortRenderers();
}
return _sortedRenderers;
}
private set { _sortedRenderers = value; }
}
/// <summary>
/// Sorts the <see cref="Renderers"/> by their draw object render order.
/// </summary>
private void SortRenderers()
{
if (Renderers == null)
{
return;
}
for (var i = 0; i < Renderers.Length; i++)
{
if (Renderers[i].DrawObjectType != CubismModelTypes.DrawObjectType.Offscreen)
{
continue;
}
Renderers[i].SetDrawObjectRenderOrder(
Model.AllDrawObjectsRenderOrder[
DrawableRenderers.Length + Renderers[i].Offscreen.UnmanagedIndex]);
}
var renderers = new List<CubismRenderer>(Renderers);
SortBySortingOrder(renderers);
_sortedRenderers = renderers.ToArray();
}
/// <summary>
/// Sorts the given renderers by their sorting order.
/// </summary>
/// <param name="renderers"> Renderers to sort. </param>
private void SortBySortingOrder(List<CubismRenderer> renderers)
{
renderers.Sort(CompareBySortingOrder);
}
/// <summary>
/// Compares two <see cref="CubismRenderer"/>s by their sorting order.
/// </summary>
/// <param name="a"> First renderer. </param>
/// <param name="b"> Second renderer. </param>
/// <returns></returns>
private int CompareBySortingOrder(CubismRenderer a, CubismRenderer b)
{
return a.MeshRenderer.sortingOrder - b.MeshRenderer.sortingOrder;
}
#endregion
/// <summary>
/// <see cref="DrawableRenderers"/>'s backing field.
/// </summary>
[NonSerialized]
private CubismRenderer[] _drawableRenderers;
/// <summary>
/// Drawable <see cref="CubismRenderer"/>s.
/// </summary>
public CubismRenderer[] DrawableRenderers
{
get
{
if (_drawableRenderers == null)
{
_drawableRenderers = Model.Drawables.GetComponentsMany<CubismRenderer>();
}
return _drawableRenderers;
}
private set { _drawableRenderers = value; }
}
/// <summary>
/// <see cref="OffscreenRenderers"/>'s backing field.
/// </summary>
private CubismRenderer[] _offscreenRenderers;
/// <summary>
/// Offscreen <see cref="CubismRenderer"/>s.
/// </summary>
public CubismRenderer[] OffscreenRenderers
{
get
{
if (_offscreenRenderers == null && Model?.Offscreens != null)
{
_offscreenRenderers = Model.Offscreens.GetComponentsMany<CubismRenderer>();
}
return _offscreenRenderers;
}
private set { _offscreenRenderers = value; }
}
#endregion
/// <summary>
/// Initializes the render controller.
/// </summary>
/// <param name="renderers">Array of renderers to initialize.</param>
private void TryInitializeRenderers(CubismRenderer[] renderers)
{
// HACK: It doesn't work correctly when placed inside a function.
if (renderers != null && renderers.Length != 0)
{
return;
}
// Initialize renderers.
renderers = Array.Empty<CubismRenderer>();
// Create renders and apply it to backing field...
var drawables = Model.Drawables;
var drawableRenderers = drawables.AddComponentEach<CubismRenderer>();
Array.Resize(ref renderers, drawableRenderers.Length);
Array.Copy(drawableRenderers, renderers, drawableRenderers.Length);
for (var index = 0; index < renderers.Length; index++)
{
var targetRenderer = renderers[index];
targetRenderer.DrawObjectType = CubismModelTypes.DrawObjectType.Drawable;
targetRenderer.Drawable = drawables[index];
if (!HasRootPartOffscreen)
{
continue;
}
HasRootPartOffscreen = CheckHasRootPartOffscreen(targetRenderer);
}
// Store drawable renderers.
DrawableRenderers = drawableRenderers;
var offscreens = Model.Offscreens;
if (offscreens != null)
{
var offscreenRenderers = offscreens.AddComponentEach<CubismRenderer>();
Array.Resize(ref renderers, renderers.Length + offscreenRenderers.Length);
Array.Copy(offscreenRenderers, 0, renderers, renderers.Length - offscreenRenderers.Length, offscreenRenderers.Length);
for (var index = drawableRenderers.Length; index < renderers.Length; ++index)
{
var targetRenderer = renderers[index];
targetRenderer.DrawObjectType = CubismModelTypes.DrawObjectType.Offscreen;
targetRenderer.Offscreen = offscreens[index - drawableRenderers.Length];
if (!HasRootPartOffscreen)
{
continue;
}
HasRootPartOffscreen = CheckHasRootPartOffscreen(targetRenderer);
}
// Store offscreen renderers.
OffscreenRenderers = offscreenRenderers;
}
// Store renderers.
Renderers = renderers;
}
/// <summary>
/// Called after all <see cref="CubismRenderer"/>s are initialized.
/// </summary>
/// <param name="renderers"></param>
private void OnAfterRenderersInitialize(CubismRenderer[] renderers)
{
// Set the render order and call `OnAfterAllRendererInitialize` method for each renderer.
for (var i = 0; i < renderers.Length; i++)
{
var initRenderer = renderers[i];
switch (initRenderer.DrawObjectType)
{
case CubismModelTypes.DrawObjectType.Drawable:
initRenderer.SetDrawObjectRenderOrder(Model.AllDrawObjectsRenderOrder[initRenderer.Drawable.UnmanagedIndex]);
break;
case CubismModelTypes.DrawObjectType.Offscreen:
initRenderer.SetDrawObjectRenderOrder(Model.AllDrawObjectsRenderOrder[DrawableRenderers.Length + initRenderer.Offscreen.UnmanagedIndex]);
break;
default:
Debug.LogWarning($"Unknown draw object type: {initRenderer.DrawObjectType} for renderer: {initRenderer.name}");
break;
}
initRenderer.OnAfterAllRendererInitialize();
}
}
/// <summary>
/// Does the model have an offscreen for the root part?
/// </summary>
/// <param name="targetRenderer">The <see cref="CubismRenderer"/> to check.</param>
/// <returns>True if it has one, false if it does not.</returns>
private bool CheckHasRootPartOffscreen(CubismRenderer targetRenderer)
{
var parentPartIndex = -1;
switch (targetRenderer.DrawObjectType)
{
case CubismModelTypes.DrawObjectType.Drawable:
parentPartIndex = targetRenderer.Drawable.ParentPartIndex;
break;
case CubismModelTypes.DrawObjectType.Offscreen:
parentPartIndex = Model.Parts[targetRenderer.Offscreen.OwnerIndex].UnmanagedParentIndex;
break;
default:
Debug.LogWarning($"Unknown draw object type: {targetRenderer.DrawObjectType} for renderer: {targetRenderer.name}");
break;
}
CubismPart part = null;
while (parentPartIndex > 0)
{
for (var partIndex = 0; partIndex < Model.Parts.Length; partIndex++)
{
if (Model.Parts[partIndex].UnmanagedIndex != parentPartIndex)
{
continue;
}
// Found the part.
part = Model.Parts[partIndex];
break;
}
parentPartIndex = part?.UnmanagedParentIndex ?? -1;
}
return parentPartIndex == 0 && Model.Parts[parentPartIndex].OffscreenIndex > -1;
}
/// <summary>
/// Submits drawing offscreen for all offscreen renderers.
/// </summary>
/// <param name="commandBuffer">Command buffer to record draw commands.</param>
/// <param name="passData">Pass data containing render controllers and camera data.</param>
internal void SubmitDrawOffscreen(CommandBuffer commandBuffer, CubismRenderPassFeature.CubismRenderPass.PassData passData)
{
if (!IsInitialized
|| !Model
|| OffscreenRenderers == null)
{
return;
}
while (CurrentFrameBuffer != (RenderTexture)passData.CommonRenderingTextureHandle)
{
CubismRenderer offscreenRenderer = null;
if (CurrentOffscreenUnmanagedIndex == -1 && HasRootPartOffscreen)
{
for (var offscreenIndex = 0; offscreenIndex < OffscreenRenderers.Length; offscreenIndex++)
{
if (OffscreenRenderers[offscreenIndex].Offscreen.UnmanagedIndex != 0)
{
continue;
}
offscreenRenderer = OffscreenRenderers[offscreenIndex];
break;
}
if (!offscreenRenderer)
{
break;
}
offscreenRenderer.DrawOffscreen(commandBuffer, passData.CommonRenderingTextureHandle, offscreenRenderer, passData);
offscreenRenderer.OffscreenFrameBuffer = null;
CurrentFrameBuffer = passData.CommonRenderingTextureHandle;
CurrentOffscreenUnmanagedIndex = -1;
break;
}
for (var offscreenRendererIndex = 0; offscreenRendererIndex < OffscreenRenderers.Length; offscreenRendererIndex++)
{
if (OffscreenRenderers[offscreenRendererIndex].Offscreen.UnmanagedIndex != CurrentOffscreenUnmanagedIndex)
{
continue;
}
offscreenRenderer = OffscreenRenderers[offscreenRendererIndex];
break;
}
RenderTexture previousOffscreen = null;
var currentOwnerIndex = offscreenRenderer?.Offscreen?.OwnerIndex ?? -1;
var parentIndex = -1;
if (currentOwnerIndex != -1)
{
parentIndex = Model.Parts[currentOwnerIndex]?.UnmanagedParentIndex ?? -1;
}
var previousIndex = -1;
// Find the offscreen renderer with the previous offscreen unmanaged index.
while (parentIndex != -1)
{
var part = Model.Parts[parentIndex];
if (part && part.OffscreenIndex != -1)
{
for (var offscreenRendererIndex = 0; offscreenRendererIndex < OffscreenRenderers.Length; offscreenRendererIndex++)
{
var element = OffscreenRenderers[offscreenRendererIndex];
if (!element.isActiveAndEnabled
|| !element.MeshRenderer.enabled)
{
continue;
}
if (element.Offscreen.UnmanagedIndex != part?.OffscreenIndex)
{
continue;
}
previousOffscreen = OffscreenRenderers[offscreenRendererIndex].OffscreenFrameBuffer;
previousIndex = part.OffscreenIndex;
break;
}
}
parentIndex = Model.Parts[parentIndex]?.UnmanagedParentIndex ?? -1;
if (!previousOffscreen)
{
continue;
}
break;
}
// Try to get the root part offscreen if there is no parent offscreen.
if (!previousOffscreen && HasRootPartOffscreen)
{
for (var offscreenIndex = 0; offscreenIndex < OffscreenRenderers.Length; offscreenIndex++)
{
if (OffscreenRenderers[offscreenIndex].Offscreen.UnmanagedIndex != 0)
{
continue;
}
previousOffscreen = OffscreenRenderers[offscreenIndex].OffscreenFrameBuffer;
break;
}
}
// If there is no previous offscreen, or it is the same as the current offscreen, use the common rendering texture.
if (!previousOffscreen
|| previousOffscreen == offscreenRenderer?.OffscreenFrameBuffer)
{
previousOffscreen = passData.CommonRenderingTextureHandle;
}
// If It can copy the parent offscreen, copy it to the current offscreen renderer.
offscreenRenderer?.DrawOffscreen(commandBuffer, previousOffscreen, offscreenRenderer, passData);
if (offscreenRenderer)
{
offscreenRenderer.OffscreenFrameBuffer = null;
}
CurrentFrameBuffer = previousOffscreen;
CurrentOffscreenUnmanagedIndex = previousIndex;
}
}
}
}