/**
* 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.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Live2D.Cubism.Rendering.Masking
{
///
/// Texture for rendering masks.
///
[CreateAssetMenu(menuName = "Live2D Cubism/Mask Texture")]
public sealed class CubismMaskTexture : ScriptableObject, ICubismMaskCommandSource
{
#region Conversion
///
/// Converts a to a .
///
/// Value to convert.
public static implicit operator Texture(CubismMaskTexture value)
{
return value.RenderTexture;
}
#endregion
///
/// The global mask texture.
///
public static CubismMaskTexture GlobalMaskTexture
{
get { return Resources.Load("Live2D/Cubism/GlobalMaskTexture"); }
}
///
/// backing field.
///
[SerializeField, HideInInspector]
private int _size = 1024;
///
/// Texture size in pixels.
///
public int Size
{
get { return _size; }
set
{
// Return early if same value given.
if (value == _size)
{
return;
}
// Fail silently if not power-of-two.
if (!value.IsPowerOfTwo())
{
return;
}
// Apply changes.
_size = value;
RefreshRenderTexture();
}
}
///
/// Channel count.
///
public int Channels
{
get { return 4; }
}
///
/// backing field.
///
[SerializeField, HideInInspector]
private int _subdivisions = 3;
///
/// Subdivision level.
///
public int Subdivisions
{
get { return _subdivisions; }
set
{
if (value == _subdivisions)
{
return;
}
// Apply changes.
_subdivisions = value;
RefreshRenderTexture();
}
}
///
/// Tile pool 'allocator'.
///
private CubismMaskTilePool TilePool { get; set; }
///
/// backing field.
///
private RenderTexture _renderTexture;
///
/// to draw on.
///
private RenderTexture RenderTexture
{
get
{
if (_renderTexture == null)
{
RefreshRenderTexture();
}
return _renderTexture;
}
set { _renderTexture = value; }
}
///
/// Sources.
///
private List Sources { get; set; }
///
/// True if instance is revived.
///
private bool IsRevived
{
get { return TilePool != null; }
}
///
/// True if instance contains any sources.
///
private bool ContainsSources
{
get { return Sources != null && Sources.Count > 0; }
}
#region Interface For ICubismMaskSources
///
/// Add source of masks for drawing.
///
public void AddSource(ICubismMaskTextureCommandSource source)
{
// Make sure instance is valid.
TryRevive();
// Initialize container if necessary.
if (Sources == null)
{
Sources = new List();
}
// Return early if source already exists.
else if (Sources.FindIndex(i => i.Source == source) != -1)
{
return;
}
// Register source.
var item = new SourcesItem
{
Source = source,
Tiles = TilePool.AcquireTiles(source.GetNecessaryTileCount())
};
Sources.Add(item);
// Apply tiles to source.
source.SetTiles(item.Tiles);
}
///
/// Remove source of masks
///
public void RemoveSource(ICubismMaskTextureCommandSource source)
{
// Return early if empty.
if (!ContainsSources)
{
return;
}
var itemIndex = Sources.FindIndex(i => i.Source == source);
// Return if source is invalid.
if (itemIndex == -1)
{
return;
}
// Return tiles and deregister source.
TilePool.ReturnTiles(Sources[itemIndex].Tiles);
Sources.RemoveAt(itemIndex);
}
#endregion
private void TryRevive()
{
// Return early if already revived.
if (IsRevived)
{
return;
}
RefreshRenderTexture();
}
private void ReinitializeSources()
{
// Reallocate tiles if sources exist.
if (ContainsSources)
{
for (var i = 0; i < Sources.Count; ++i)
{
var source = Sources[i];
source.Tiles = TilePool.AcquireTiles(source.Source.GetNecessaryTileCount());
source.Source.SetTiles(source.Tiles);
Sources[i] = source;
}
}
}
private void RefreshRenderTexture()
{
// Recreate render texture.
RenderTexture = new RenderTexture(Size, Size, 0, RenderTextureFormat.ARGB32);
// Recreate allocator.
TilePool = new CubismMaskTilePool(Subdivisions, Channels);
// Reinitialize sources.
ReinitializeSources();
}
#region Unity Event Handling
///
/// Initializes instance.
///
// ReSharper disable once UnusedMember.Local
private void OnEnable()
{
CubismMaskCommandBuffer.AddSource(this);
}
///
/// Finalizes instance.
///
// ReSharper disable once UnusedMember.Local
private void OnDestroy()
{
CubismMaskCommandBuffer.RemoveSource(this);
}
#endregion
#region ICubismMaskCommandSource
///
/// Called to enqueue source.
///
/// Buffer to enqueue in.
void ICubismMaskCommandSource.AddToCommandBuffer(CommandBuffer buffer)
{
// Return early if empty.
if (!ContainsSources)
{
return;
}
// Enqueue render target.
buffer.SetRenderTarget(RenderTexture);
buffer.ClearRenderTarget(false, true, Color.clear);
// Enqueue sources.
for (var i = 0; i < Sources.Count; ++i)
{
Sources[i].Source.AddToCommandBuffer(buffer);
}
}
#endregion
#region Source Item
///
/// Source of masks and its tiles
///
private struct SourcesItem
{
///
/// SourcesItem instance.
///
public ICubismMaskTextureCommandSource Source;
///
/// Tiles assigned to the instance.
///
public CubismMaskTile[] Tiles;
}
#endregion
}
}