live2D (sdk가 6.5 미지원으로 sprite로 대응)
This commit is contained in:
919
Assets/Live2D/Cubism/Rendering/CubismRendererUsingBlendMode.cs
Normal file
919
Assets/Live2D/Cubism/Rendering/CubismRendererUsingBlendMode.cs
Normal file
@@ -0,0 +1,919 @@
|
||||
/**
|
||||
* 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;
|
||||
using Live2D.Cubism.Rendering.Util;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
|
||||
|
||||
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 CubismRenderer
|
||||
{
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// High precision mask tile for mask rendering.
|
||||
/// </summary>
|
||||
private static Vector4 HighPrecisionMaskTile = new Vector4(
|
||||
0, // Channel R
|
||||
0, // Column
|
||||
0, // Row
|
||||
1 // Size
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Vertices for offscreen rendering.
|
||||
/// </summary>
|
||||
internal static Vector3[] OffscreenVertices = new Vector3[]
|
||||
{
|
||||
new Vector3(-1, -1, 0),
|
||||
new Vector3(1, -1, 0),
|
||||
new Vector3(1, 1, 0),
|
||||
new Vector3(-1, 1, 0)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// UVs for offscreen rendering.
|
||||
/// </summary>
|
||||
internal static Vector2[] OffscreenUVs = new Vector2[]
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, 1),
|
||||
new Vector2(0, 1)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Triangle indices for offscreen rendering.
|
||||
/// </summary>
|
||||
internal static int[] OffscreenTriangle = new int[] { 0, 1, 2, 0, 2, 3 };
|
||||
|
||||
/// <summary>
|
||||
/// Masks used by this renderer.
|
||||
/// </summary>
|
||||
[SerializeField, HideInInspector]
|
||||
private CubismRenderer[] _masks;
|
||||
|
||||
/// <summary>
|
||||
/// Transform values for mask rendering.
|
||||
/// </summary>
|
||||
private Vector4 _maskTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Bounds that contain all masks.
|
||||
/// </summary>
|
||||
private Bounds _maskBounds;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Whether <see cref="_masks"/> have null.
|
||||
/// </summary>
|
||||
private bool _haveMasksNull;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Index of the draw object.
|
||||
/// </summary>
|
||||
public int DrawObjectUnmanagedIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="CubismOffscreen"/>.
|
||||
/// </summary>
|
||||
public CubismOffscreen Offscreen { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Color blend types.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public BlendTypes.ColorBlend ColorBlendType;
|
||||
|
||||
/// <summary>
|
||||
/// Alpha blend types.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public BlendTypes.AlphaBlend AlphaBlendType;
|
||||
|
||||
/// <summary>
|
||||
/// Type of draw object this renderer is used for.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public CubismModelTypes.DrawObjectType DrawObjectType;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is the last draw object in the model.
|
||||
/// </summary>
|
||||
internal bool IsLastDrawObjectInModel;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="OffscreenFrameBuffer"/>'s backing field.
|
||||
/// </summary>
|
||||
private RenderTexture _offscreenFrameBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Offscreen render texture used for rendering.
|
||||
/// </summary>
|
||||
public RenderTexture OffscreenFrameBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_offscreenFrameBuffer
|
||||
&& RenderController.CurrentFrameBuffer
|
||||
&& (RenderController.OffscreenRenderers?.Length ?? 0) > 0)
|
||||
{
|
||||
_offscreenFrameBuffer = CubismOffscreenRenderTextureManager.GetInstance().GetOffscreenRenderTexture(
|
||||
RenderController.CurrentFrameBuffer);
|
||||
}
|
||||
|
||||
return _offscreenFrameBuffer;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_offscreenFrameBuffer != null)
|
||||
{
|
||||
CubismOffscreenRenderTextureManager.GetInstance().StopUsingRenderTexture(RenderController, _offscreenFrameBuffer);
|
||||
_offscreenFrameBuffer = null;
|
||||
}
|
||||
_offscreenFrameBuffer = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="OffsetScale"/>'s backing field.
|
||||
/// </summary>
|
||||
[SerializeField, HideInInspector]
|
||||
private Vector4 _offsetScale = new Vector4(0, 0, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Quaternion"/>'s backing field.
|
||||
/// </summary>
|
||||
[SerializeField, HideInInspector]
|
||||
private Vector4 _quaternion = Vector4.zero;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ZOffset"/>'s backing field.
|
||||
/// </summary>
|
||||
[SerializeField, HideInInspector]
|
||||
private float _zOffset = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Offscreen mesh used for rendering.
|
||||
/// </summary>
|
||||
private Mesh _offscreenMesh;
|
||||
|
||||
/// <summary>
|
||||
/// Previous offscreen unmanaged index.
|
||||
/// </summary>
|
||||
private int _previousOffscreenUnmanagedIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to skip rendering for this draw object.
|
||||
/// </summary>
|
||||
public bool SkipRendering
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Interface For CubismRenderController
|
||||
|
||||
/// <summary>
|
||||
/// Sets draw object's render order.
|
||||
/// </summary>
|
||||
/// <param name="newRenderOrder"></param>
|
||||
internal void SetDrawObjectRenderOrder(int newRenderOrder)
|
||||
{
|
||||
if (RenderOrder == newRenderOrder) return;
|
||||
|
||||
RenderOrder = newRenderOrder;
|
||||
|
||||
ApplySorting();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sorting direction at the last sorting.
|
||||
/// </summary>
|
||||
internal Vector3 LastDirection;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the direction has been updated since the last sorting.
|
||||
/// </summary>
|
||||
/// <returns>True if the direction has changed, false otherwise.</returns>
|
||||
internal bool DidUpdateDirectionFromLastSorted(Vector3 cameraPosition)
|
||||
{
|
||||
return LastDirection != (transform.position - cameraPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distance from the active camera.
|
||||
/// </summary>
|
||||
internal float DistanceToCamera;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance by projecting the vector from camera to renderer onto the camera's forward direction.
|
||||
/// </summary>
|
||||
/// <param name="cameraPosition">Position of the camera.</param>
|
||||
/// <param name="cameraForward">Forward direction of the camera (normalized vector).</param>
|
||||
internal void CalculateDistanceToCamera(Vector3 cameraPosition, Vector3 cameraForward)
|
||||
{
|
||||
// Vector from cameraPosition to transform.position
|
||||
var directionToRenderer = transform.position - cameraPosition;
|
||||
|
||||
LastDirection = directionToRenderer;
|
||||
|
||||
// Project the vector onto the camera's forward direction
|
||||
var projection = Vector3.Project(directionToRenderer, cameraForward);
|
||||
|
||||
// The magnitude of the projected vector is the distance along the camera's forward direction
|
||||
// Formula: projection = dot(directionToRenderer, cameraForward) * cameraForward
|
||||
// Distance = |projection| = |dot(directionToRenderer, cameraForward)|
|
||||
DistanceToCamera = projection.magnitude;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PropertyBlock"/> backing field.
|
||||
/// </summary>
|
||||
private MaterialPropertyBlock _propertyBlock;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="MaterialPropertyBlock"/>
|
||||
/// </summary>
|
||||
private MaterialPropertyBlock PropertyBlock
|
||||
{
|
||||
get
|
||||
{
|
||||
// Lazily initialize.
|
||||
if (_propertyBlock == null)
|
||||
{
|
||||
_propertyBlock = new MaterialPropertyBlock();
|
||||
}
|
||||
|
||||
|
||||
return _propertyBlock;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies common rendering texture for rendering.
|
||||
/// </summary>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
private void ApplyBlendedRenderTexture(CubismRenderPassFeature.CubismRenderPass.PassData passData)
|
||||
{
|
||||
if (!RenderController?.CurrentFrameBuffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var property = PropertyBlock;
|
||||
|
||||
MeshRenderer.GetPropertyBlock(property);
|
||||
|
||||
// Write property.
|
||||
property.SetTexture(CubismShaderVariables.RenderTexture, passData.CommonTemporaryTextureHandle);
|
||||
|
||||
MeshRenderer.SetPropertyBlock(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds this renderer to the command buffer for rendering.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
public void DrawObject(CommandBuffer buffer, CubismRenderPassFeature.CubismRenderPass.PassData passData)//, RenderTexture frameBuffer)
|
||||
{
|
||||
if (!MeshRenderer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
// Set offscreen frame buffer.
|
||||
SetOffscreen(buffer, passData);
|
||||
break;
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
// Draw a drawable.
|
||||
DrawDrawable(buffer, passData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies transform properties to the material property block.
|
||||
/// </summary>
|
||||
private void ApplyTransform()
|
||||
{
|
||||
var property = PropertyBlock;
|
||||
MeshRenderer.GetPropertyBlock(property);
|
||||
|
||||
// Set offset and scale from transform.
|
||||
var offsetScale = _offsetScale;
|
||||
|
||||
offsetScale.Set(RenderController.transform.localPosition.x + transform.localPosition.x, RenderController.transform.localPosition.y + transform.localPosition.y,
|
||||
RenderController.transform.localScale.x * transform.localScale.x, RenderController.transform.localScale.y * transform.localScale.y);
|
||||
_offsetScale = offsetScale;
|
||||
// Write property.
|
||||
property.SetVector(CubismShaderVariables.OffsetScale, _offsetScale);
|
||||
|
||||
// Set rotation from transform.
|
||||
var combinedRotation = RenderController.transform.localRotation * transform.localRotation;
|
||||
_quaternion.Set(combinedRotation.x, combinedRotation.y, combinedRotation.z, combinedRotation.w);
|
||||
|
||||
// Write property.
|
||||
property.SetVector(CubismShaderVariables.RotationQuaternion, _quaternion);
|
||||
|
||||
// Set z offset from transform.
|
||||
_zOffset = RenderController.transform.localPosition.z + transform.localPosition.z;
|
||||
// Write property.
|
||||
property.SetFloat(CubismShaderVariables.ZOffset, _zOffset);
|
||||
|
||||
MeshRenderer.SetPropertyBlock(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the offscreen frame buffer for rendering.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
private void SetOffscreen(CommandBuffer buffer, CubismRenderPassFeature.CubismRenderPass.PassData passData)
|
||||
{
|
||||
var currentOffscreenUnmanagedIndex = RenderController.CurrentOffscreenUnmanagedIndex;
|
||||
SubmitDrawToParentOffscreen(ref passData, ref currentOffscreenUnmanagedIndex,
|
||||
buffer, this);
|
||||
|
||||
// Ready the render target to the offscreen frame buffer.
|
||||
OffscreenFrameBuffer = CubismOffscreenRenderTextureManager.GetInstance().GetOffscreenRenderTexture(passData.CommonRenderingTextureHandle);
|
||||
|
||||
// Set current frame buffer to offscreen frame buffer.
|
||||
buffer.SetRenderTarget(OffscreenFrameBuffer);
|
||||
// Clear the offscreen frame buffer.
|
||||
buffer.ClearRenderTarget(false, true, Color.clear);
|
||||
|
||||
// Set up for drawing to offscreen.
|
||||
RenderController.CurrentFrameBuffer = OffscreenFrameBuffer;
|
||||
RenderController.CurrentOffscreenUnmanagedIndex = Offscreen.UnmanagedIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounds that can contain all masks.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Bounds GetMaskBounds()
|
||||
{
|
||||
// If there are no masks, return empty bounds.
|
||||
if (_masks == null
|
||||
|| _masks.Length < 1)
|
||||
{
|
||||
return new Bounds();
|
||||
}
|
||||
|
||||
var min = _masks[0]?.Mesh?.bounds.min ?? Vector3.zero;
|
||||
var max = _masks[0]?.Mesh?.bounds.max ?? Vector3.zero;
|
||||
|
||||
|
||||
for (var i = 1; i < _masks.Length; ++i)
|
||||
{
|
||||
// Skip if the mask is null.
|
||||
if (!_masks[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var boundsI = _masks[i].Mesh.bounds;
|
||||
|
||||
|
||||
if (boundsI.min.x < min.x)
|
||||
{
|
||||
min.x = boundsI.min.x;
|
||||
}
|
||||
|
||||
if (boundsI.max.x > max.x)
|
||||
{
|
||||
max.x = boundsI.max.x;
|
||||
}
|
||||
|
||||
|
||||
if (boundsI.min.y < min.y)
|
||||
{
|
||||
min.y = boundsI.min.y;
|
||||
}
|
||||
|
||||
if (boundsI.max.y > max.y)
|
||||
{
|
||||
max.y = boundsI.max.y;
|
||||
}
|
||||
}
|
||||
|
||||
var bounds = _maskBounds;
|
||||
bounds.SetMinMax(min, max);
|
||||
_maskBounds = bounds;
|
||||
|
||||
return _maskBounds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the transform values for mask rendering based on mask bounds.
|
||||
/// </summary>
|
||||
private void CalcMaskTransform()
|
||||
{
|
||||
// Compute bounds and scale.
|
||||
var bounds = GetMaskBounds();
|
||||
var scale = (bounds.size.x > bounds.size.y)
|
||||
? bounds.size.x
|
||||
: bounds.size.y;
|
||||
|
||||
// Compute mask transform.
|
||||
var maskTransform = _maskTransform;
|
||||
maskTransform.Set(
|
||||
bounds.center.x, // Offset X
|
||||
bounds.center.y, // Offset Y
|
||||
1.0f / scale, // Scale
|
||||
0 // Dummy, unused.
|
||||
);
|
||||
_maskTransform = maskTransform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mask.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
internal void DrawMasks(CommandBuffer buffer, CubismRenderPassFeature.CubismRenderPass.PassData passData)
|
||||
{
|
||||
// If the model doesn't have masks, return.
|
||||
if (!RenderController.HasMask)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// If the masks have null, try to initialize them.
|
||||
if (_haveMasksNull)
|
||||
{
|
||||
TryInitializeMasks();
|
||||
_haveMasksNull = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_masks?.Length > 0)
|
||||
{
|
||||
var maskTexture = passData.MaskTextureHandle;
|
||||
|
||||
buffer.SetRenderTarget(maskTexture);
|
||||
buffer.ClearRenderTarget(true, true, Color.clear);
|
||||
|
||||
// Calculate mask transform.
|
||||
CalcMaskTransform();
|
||||
|
||||
// Draw the masks.
|
||||
for (var maskIndex = 0; maskIndex < _masks.Length; maskIndex++)
|
||||
{
|
||||
var mask = _masks[maskIndex];
|
||||
|
||||
if (!mask)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
_haveMasksNull = true;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
mask.PropertyBlock.SetTexture(CubismShaderVariables.MainTexture, mask.MainTexture);
|
||||
mask.PropertyBlock.SetVector(CubismShaderVariables.MaskTile, HighPrecisionMaskTile);
|
||||
mask.PropertyBlock.SetVector(CubismShaderVariables.MaskTransform, _maskTransform);
|
||||
|
||||
// Draw the mesh with the material.
|
||||
buffer.DrawMesh(
|
||||
mask.Mesh,
|
||||
Matrix4x4.identity,
|
||||
mask.Drawable.IsDoubleSided
|
||||
? CubismBuiltinMaterials.Mask
|
||||
: CubismBuiltinMaterials.MaskCulling,
|
||||
0,
|
||||
0,
|
||||
mask.PropertyBlock);
|
||||
break;
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
mask.PropertyBlock.SetTexture(CubismShaderVariables.MainTexture, mask.MainTexture);
|
||||
mask.ApplyTransform();
|
||||
|
||||
// Draw the mesh with the material.
|
||||
buffer.DrawMesh(
|
||||
mask.Mesh,
|
||||
Matrix4x4.identity,
|
||||
CubismBuiltinMaterials.OffscreenMask,
|
||||
0,
|
||||
0,
|
||||
mask.PropertyBlock);
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning("Unknown DrawObjectType.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyMask(maskTexture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits the offscreen frame buffer and draws to the parent offscreen if necessary.
|
||||
/// </summary>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
/// <param name="currentOffscreenUnmanagedIndex">Current offscreen's unmanaged index.</param>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="targetRenderer">Current target rendering object.</param>
|
||||
private void SubmitDrawToParentOffscreen(ref CubismRenderPassFeature.CubismRenderPass.PassData passData, ref int currentOffscreenUnmanagedIndex,
|
||||
CommandBuffer buffer, CubismRenderer targetRenderer)
|
||||
{
|
||||
if (passData == null
|
||||
|| RenderController.CurrentFrameBuffer == (RenderTexture)passData.CommonRenderingTextureHandle
|
||||
|| currentOffscreenUnmanagedIndex == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current offscreen renderer.
|
||||
CubismRenderer offscreenRenderer = null;
|
||||
for (var offscreenRendererIndex = 0; offscreenRendererIndex < RenderController.OffscreenRenderers.Length; offscreenRendererIndex++)
|
||||
{
|
||||
if (RenderController.OffscreenRenderers[offscreenRendererIndex].Offscreen.UnmanagedIndex !=
|
||||
currentOffscreenUnmanagedIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
offscreenRenderer = RenderController.OffscreenRenderers[offscreenRendererIndex];
|
||||
break;
|
||||
}
|
||||
var currentOwnerIndex = offscreenRenderer?.Offscreen.OwnerIndex ?? -1;
|
||||
|
||||
if (currentOwnerIndex == -1)
|
||||
{
|
||||
// Fail silently if the offscreen renderer is not found.
|
||||
return;
|
||||
}
|
||||
|
||||
var targetParentIndex = -1;
|
||||
RenderTexture previousOffscreen = null;
|
||||
switch (targetRenderer.DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
targetParentIndex = targetRenderer.Drawable.ParentPartIndex;
|
||||
break;
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
// Check target renderer's offscreen owner type.
|
||||
targetParentIndex = RenderController.Model.Parts[targetRenderer.Offscreen.OwnerIndex].UnmanagedParentIndex;
|
||||
break;
|
||||
default:
|
||||
// If the draw object type is not drawable or offscreen, return.
|
||||
return;
|
||||
}
|
||||
|
||||
// If targetRenderer isn't child of the current offscreen renderer.
|
||||
while (targetParentIndex != -1)
|
||||
{
|
||||
// If the target parent index is the same as the current owner index, return.
|
||||
if (targetParentIndex == RenderController.Model.Parts[currentOwnerIndex].UnmanagedIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
targetParentIndex = RenderController.Model.Parts[targetParentIndex].UnmanagedParentIndex;
|
||||
}
|
||||
var parentIndex = RenderController.Model.Parts[currentOwnerIndex].UnmanagedParentIndex;
|
||||
|
||||
// Find the offscreen renderer with the previous offscreen unmanaged index.
|
||||
while (parentIndex != -1)
|
||||
{
|
||||
var part = RenderController.Model.Parts[parentIndex];
|
||||
if (part.OffscreenIndex != -1)
|
||||
{
|
||||
for (var offscreenRendererIndex = 0; offscreenRendererIndex < RenderController.OffscreenRenderers.Length; offscreenRendererIndex++)
|
||||
{
|
||||
var element = RenderController.OffscreenRenderers[offscreenRendererIndex];
|
||||
|
||||
if (!element.isActiveAndEnabled
|
||||
|| !element.MeshRenderer.enabled
|
||||
|| element.Offscreen.UnmanagedIndex != part.OffscreenIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
previousOffscreen = RenderController.OffscreenRenderers[offscreenRendererIndex].OffscreenFrameBuffer;
|
||||
_previousOffscreenUnmanagedIndex = part.OffscreenIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parentIndex = RenderController.Model.Parts[parentIndex].UnmanagedParentIndex;
|
||||
|
||||
if (!previousOffscreen)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to get the root part offscreen if there is no parent offscreen.
|
||||
if (!previousOffscreen && RenderController.HasRootPartOffscreen)
|
||||
{
|
||||
var offscreenRenderers = RenderController.OffscreenRenderers;
|
||||
|
||||
for (var offscreenIndex = 0; offscreenIndex < offscreenRenderers.Length; offscreenIndex++)
|
||||
{
|
||||
if (offscreenRenderers[offscreenIndex].Offscreen.UnmanagedIndex != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
previousOffscreen = offscreenRenderers[offscreenIndex].OffscreenFrameBuffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no parent offscreen, use the common rendering texture.
|
||||
if (!previousOffscreen)
|
||||
{
|
||||
previousOffscreen = passData.CommonRenderingTextureHandle;
|
||||
}
|
||||
|
||||
if (previousOffscreen == offscreenRenderer?.OffscreenFrameBuffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If It can copy the parent offscreen, copy it to the current offscreen renderer.
|
||||
DrawOffscreen(buffer, previousOffscreen, offscreenRenderer, passData);
|
||||
|
||||
offscreenRenderer.OffscreenFrameBuffer = null;
|
||||
RenderController.CurrentFrameBuffer = previousOffscreen;
|
||||
currentOffscreenUnmanagedIndex = _previousOffscreenUnmanagedIndex;
|
||||
|
||||
// If the current offscreen is the parent of the target renderer, draw to the parent offscreen.
|
||||
SubmitDrawToParentOffscreen(ref passData, ref currentOffscreenUnmanagedIndex,
|
||||
buffer, targetRenderer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a drawable.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
private void DrawDrawable(CommandBuffer buffer, CubismRenderPassFeature.CubismRenderPass.PassData passData)
|
||||
{
|
||||
if (!RenderController)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (RenderController.CurrentOffscreenUnmanagedIndex != -1)
|
||||
{
|
||||
var currentOffscreenOwnerUnmanagedIndex = RenderController.CurrentOffscreenUnmanagedIndex;
|
||||
SubmitDrawToParentOffscreen(ref passData, ref currentOffscreenOwnerUnmanagedIndex,
|
||||
buffer, this);
|
||||
|
||||
RenderController.CurrentOffscreenUnmanagedIndex = currentOffscreenOwnerUnmanagedIndex;
|
||||
}
|
||||
|
||||
// Mask rendering.
|
||||
DrawMasks(buffer, passData);
|
||||
|
||||
// Set property block.
|
||||
ApplyMainTexture();
|
||||
ApplyBlendedRenderTexture(passData);
|
||||
ApplyScreenColor();
|
||||
ApplyMultiplyColor();
|
||||
ApplyVertexColors();
|
||||
ApplyTransform();
|
||||
|
||||
// In the case of color blending before Cubism 5.2.
|
||||
if ((ColorBlendType == BlendTypes.ColorBlend.Normal
|
||||
&& AlphaBlendType == BlendTypes.AlphaBlend.Over)
|
||||
|| ColorBlendType == BlendTypes.ColorBlend.Add
|
||||
|| ColorBlendType == BlendTypes.ColorBlend.Multiply)
|
||||
{
|
||||
// If the current frame buffer is different from the common rendering texture, update it.
|
||||
// HACK: Assumes that the size has already been corrected in the `SetOffscreen()` function for cases where drawing occurs to the offscreen.
|
||||
if (!RenderController.CurrentFrameBuffer
|
||||
|| RenderController.CurrentFrameBuffer.width != ((RenderTexture)passData.CameraDepthTextureHandle).width
|
||||
|| RenderController.CurrentFrameBuffer.height != ((RenderTexture)passData.CameraDepthTextureHandle).height)
|
||||
{
|
||||
RenderController.CurrentFrameBuffer = passData.CommonRenderingTextureHandle;
|
||||
}
|
||||
|
||||
// Set render target with depth buffer for proper depth testing
|
||||
buffer.SetRenderTarget(RenderController.CurrentFrameBuffer, passData.CameraDepthTextureHandle);
|
||||
|
||||
// Draw the mesh with the material.
|
||||
buffer.DrawMesh(Mesh, Matrix4x4.identity, DrawMaterial ?? Material, 0, 0, PropertyBlock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Blit to temporary texture.
|
||||
buffer.Blit(RenderController.CurrentFrameBuffer, passData.CommonTemporaryTextureHandle);
|
||||
|
||||
// Set temporary render target.
|
||||
buffer.SetRenderTarget(RenderController.CurrentFrameBuffer, passData.CameraDepthTextureHandle);
|
||||
|
||||
// Draw the mesh with the material.
|
||||
buffer.DrawMesh(Mesh, Matrix4x4.identity, DrawMaterial ?? Material, 0, 0, PropertyBlock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the current drawable to the parent offscreen.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="previousOffscreen">Previous offscreen render texture.</param>
|
||||
/// <param name="currentOffscreenRenderer">Current offscreen renderer.</param>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
internal void DrawOffscreen(CommandBuffer buffer, RenderTexture previousOffscreen, CubismRenderer currentOffscreenRenderer, CubismRenderPassFeature.CubismRenderPass.PassData passData)
|
||||
{
|
||||
if (previousOffscreen == null
|
||||
|| currentOffscreenRenderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw the mesh with the material.
|
||||
currentOffscreenRenderer.DrawOffscreenMesh(buffer, previousOffscreen, passData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mesh for offscreen rendering.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Command buffer to record draw commands.</param>
|
||||
/// <param name="previousOffscreen">Previous offscreen render texture.</param>
|
||||
/// <param name="passData">Pass data containing render controllers and camera data.</param>
|
||||
internal void DrawOffscreenMesh(CommandBuffer buffer, RenderTexture previousOffscreen, CubismRenderPassFeature.CubismRenderPass.PassData passData)
|
||||
{
|
||||
// Mask rendering.
|
||||
DrawMasks(buffer, passData);
|
||||
|
||||
ApplyPropertyForOffscreen(previousOffscreen);
|
||||
|
||||
// In the case of color blending before Cubism 5.2.
|
||||
if ((ColorBlendType == BlendTypes.ColorBlend.Normal
|
||||
&& AlphaBlendType == BlendTypes.AlphaBlend.Over)
|
||||
|| ColorBlendType == BlendTypes.ColorBlend.Add
|
||||
|| ColorBlendType == BlendTypes.ColorBlend.Multiply)
|
||||
{
|
||||
buffer.SetRenderTarget(previousOffscreen, passData.CameraDepthTextureHandle);
|
||||
|
||||
// Draw the mesh with the material.
|
||||
buffer.DrawMesh(Mesh, Matrix4x4.identity, DrawMaterial ?? Material, 0, 0, PropertyBlock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set temporary render target.
|
||||
buffer.SetRenderTarget(passData.CommonTemporaryTextureHandle, passData.CameraDepthTextureHandle);
|
||||
// Clear the render target.
|
||||
buffer.ClearRenderTarget(false, true, Color.clear);
|
||||
|
||||
// Draw the mesh with the material.
|
||||
buffer.DrawMesh(Mesh, Matrix4x4.identity, DrawMaterial ?? Material, 0, 0, PropertyBlock);
|
||||
|
||||
// Blit to previous offscreen.
|
||||
buffer.Blit(passData.CommonTemporaryTextureHandle, previousOffscreen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies properties for offscreen rendering.
|
||||
/// </summary>
|
||||
/// <param name="previousOffscreen">Previous offscreen render texture.</param>
|
||||
private void ApplyPropertyForOffscreen(RenderTexture previousOffscreen)
|
||||
{
|
||||
var property = PropertyBlock;
|
||||
MeshRenderer.GetPropertyBlock(property);
|
||||
|
||||
// Write property.
|
||||
property.SetTexture(CubismShaderVariables.MainTexture, OffscreenFrameBuffer);
|
||||
property.SetTexture(CubismShaderVariables.RenderTexture, previousOffscreen);
|
||||
property.SetColor(CubismShaderVariables.MultiplyColor, MultiplyColor);
|
||||
property.SetColor(CubismShaderVariables.ScreenColor, ScreenColor);
|
||||
property.SetFloat(CubismShaderVariables.OffscreenOpacity, Offscreen.Opacity);
|
||||
|
||||
var reversedZ = SystemInfo.usesReversedZBuffer ? CubismRenderPassFeature.GEqual : CubismRenderPassFeature.LEqual;
|
||||
property.SetInt(CubismShaderVariables.ReversedZ, reversedZ);
|
||||
|
||||
MeshRenderer.SetPropertyBlock(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies mask texture for rendering.
|
||||
/// </summary>
|
||||
/// <param name="maskTextureHandle">Texture handle for the mask texture.</param>
|
||||
private void ApplyMask(TextureHandle maskTextureHandle)
|
||||
{
|
||||
MeshRenderer.GetPropertyBlock(PropertyBlock);
|
||||
|
||||
// Write property.
|
||||
PropertyBlock.SetTexture(CubismShaderVariables.MaskTexture, maskTextureHandle);
|
||||
if (DrawObjectType == CubismModelTypes.DrawObjectType.Drawable)
|
||||
{
|
||||
PropertyBlock.SetVector(CubismShaderVariables.MaskTile, HighPrecisionMaskTile);
|
||||
PropertyBlock.SetVector(CubismShaderVariables.MaskTransform, _maskTransform);
|
||||
}
|
||||
|
||||
MeshRenderer.SetPropertyBlock(PropertyBlock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes masks if possible.
|
||||
/// </summary>
|
||||
private void TryInitializeMasks()
|
||||
{
|
||||
if (!RenderController.HasMask)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CubismDrawable[] maskDrawables = null;
|
||||
switch (DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
_masks = new CubismRenderer[Drawable.Masks.Length];
|
||||
maskDrawables = Drawable.Masks;
|
||||
break;
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
_masks = new CubismRenderer[Offscreen.Masks.Length];
|
||||
maskDrawables = Offscreen.Masks;
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning($"{name} has unknown DrawObjectType.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var maskIndex = 0; maskIndex < maskDrawables.Length; maskIndex++)
|
||||
{
|
||||
for (var drawableRendererIndex = 0; drawableRendererIndex < RenderController.DrawableRenderers.Length; drawableRendererIndex++)
|
||||
{
|
||||
if (RenderController.DrawableRenderers[drawableRendererIndex].Drawable.UnmanagedIndex != maskDrawables[maskIndex].UnmanagedIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_masks[maskIndex] = RenderController.DrawableRenderers[drawableRendererIndex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the draw object based on its type.
|
||||
/// </summary>
|
||||
private void InitializeDrawObject()
|
||||
{
|
||||
switch (DrawObjectType)
|
||||
{
|
||||
case CubismModelTypes.DrawObjectType.Drawable:
|
||||
{
|
||||
Drawable = GetComponent<CubismDrawable>();
|
||||
DrawObjectUnmanagedIndex = Drawable.UnmanagedIndex;
|
||||
break;
|
||||
}
|
||||
case CubismModelTypes.DrawObjectType.Offscreen:
|
||||
{
|
||||
Offscreen = GetComponent<CubismOffscreen>();
|
||||
DrawObjectUnmanagedIndex = Offscreen.UnmanagedIndex;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DrawObjectUnmanagedIndex = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after all <see cref="CubismRenderer"/> are initialized.
|
||||
/// </summary>
|
||||
public void OnAfterAllRendererInitialize()
|
||||
{
|
||||
TryInitializeMasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user