123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657 |
- /**
- * 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 System;
- using UnityEngine;
- using Object = UnityEngine.Object;
- namespace Live2D.Cubism.Rendering
- {
- /// <summary>
- /// Controls rendering of a <see cref="CubismModel"/>.
- /// </summary>
- [ExecuteInEditMode, CubismDontMoveOnReimport]
- public sealed class CubismRenderController : MonoBehaviour, ICubismUpdatable
- {
- /// <summary>
- /// Model opacity.
- /// </summary>
- /// <remarks>
- /// This is turned into a field to be available to <see cref="AnimationClip"/>s...
- /// </remarks>
- [SerializeField, HideInInspector]
- public float Opacity = 1f;
- /// <summary>
- /// <see cref="LastOpacity"/> backing field.
- /// </summary>
- [SerializeField, HideInInspector]
- private float _lastOpacity;
- /// <summary>
- /// Last model opacity.
- /// </summary>
- private float LastOpacity
- {
- get { return _lastOpacity; }
- set { _lastOpacity = value; }
- }
- /// <summary>
- /// Sorting layer name.
- /// </summary>
- public string SortingLayer
- {
- get
- {
- return UnityEngine.SortingLayer.IDToName(SortingLayerId);
- }
- set
- {
- SortingLayerId = UnityEngine.SortingLayer.NameToID(value);
- }
- }
- /// <summary>
- /// <see cref="SortingLayerId"/> backing field.
- /// </summary>
- [SerializeField, HideInInspector]
- private int _sortingLayerId;
- /// <summary>
- /// Sorting layer Id.
- /// </summary>
- public int SortingLayerId
- {
- get
- {
- return _sortingLayerId;
- }
- set
- {
- if (value == _sortingLayerId)
- {
- return;
- }
- _sortingLayerId = value;
- // Apply sorting layer.
- var renderers = Renderers;
- for (var i = 0; i < renderers.Length; ++i)
- {
- renderers[i].OnControllerSortingLayerDidChange(_sortingLayerId);
- }
- }
- }
- /// <summary>
- /// <see cref="SortingMode"/> backing field.
- /// </summary>
- [SerializeField, HideInInspector]
- private CubismSortingMode _sortingMode;
- /// <summary>
- /// <see cref="CubismDrawable"/> sorting.
- /// </summary>
- public CubismSortingMode SortingMode
- {
- get
- {
- return _sortingMode;
- }
- set
- {
- // Return early if same value given.
- if (value == _sortingMode)
- {
- return;
- }
- _sortingMode = value;
- // Flip sorting.
- var renderers = Renderers;
- for (var i = 0; i < renderers.Length; ++i)
- {
- renderers[i].OnControllerSortingModeDidChange(_sortingMode);
- }
- }
- }
- /// <summary>
- /// Order in sorting layer.
- /// </summary>
- [SerializeField, HideInInspector]
- private int _sortingOrder;
- /// <summary>
- /// Order in sorting layer.
- /// </summary>
- public int SortingOrder
- {
- get
- {
- return _sortingOrder;
- }
- set
- {
- // Return early in case same value given.
- if (value == _sortingOrder)
- {
- return;
- }
- _sortingOrder = value;
- // Apply new sorting order.
- var renderers = Renderers;
- for (var i = 0; i < renderers.Length; ++i)
- {
- renderers[i].OnControllerSortingOrderDidChange(SortingOrder);
- }
- }
- }
- /// <summary>
- /// [Optional] Camera to face.
- /// </summary>
- [SerializeField]
- public Camera CameraToFace;
- /// <summary>
- /// <see cref="DrawOrderHandler"/> backing field.
- /// </summary>
- [SerializeField, HideInInspector]
- private Object _drawOrderHandler;
- /// <summary>
- /// Draw order handler proxy object.
- /// </summary>
- public Object DrawOrderHandler
- {
- get { return _drawOrderHandler; }
- set { _drawOrderHandler = value.ToNullUnlessImplementsInterface<ICubismDrawOrderHandler>(); }
- }
- /// <summary>
- /// <see cref="DrawOrderHandlerInterface"/> backing field.
- /// </summary>
- [NonSerialized]
- private ICubismDrawOrderHandler _drawOrderHandlerInterface;
- /// <summary>
- /// Listener for draw order changes.
- /// </summary>
- private ICubismDrawOrderHandler DrawOrderHandlerInterface
- {
- get
- {
- if (_drawOrderHandlerInterface == null)
- {
- _drawOrderHandlerInterface = DrawOrderHandler.GetInterface<ICubismDrawOrderHandler>();
- }
- return _drawOrderHandlerInterface;
- }
- }
- /// <summary>
- /// <see cref="OpacityHandler"/> backing field.
- /// </summary>
- [SerializeField, HideInInspector]
- private Object _opacityHandler;
- /// <summary>
- /// Opacity handler proxy object.
- /// </summary>
- public Object OpacityHandler
- {
- get { return _opacityHandler; }
- set { _opacityHandler = value.ToNullUnlessImplementsInterface<ICubismOpacityHandler>(); }
- }
- /// <summary>
- /// <see cref="OpacityHandler"/> backing field.
- /// </summary>
- private ICubismOpacityHandler _opacityHandlerInterface;
- /// <summary>
- /// Listener for opacity changes.
- /// </summary>
- private ICubismOpacityHandler OpacityHandlerInterface
- {
- get
- {
- if (_opacityHandlerInterface == null)
- {
- _opacityHandlerInterface = OpacityHandler.GetInterface<ICubismOpacityHandler>();
- }
- return _opacityHandlerInterface;
- }
- }
- /// <summary>
- /// The value to offset the <see cref="CubismDrawable"/>s by.
- /// </summary>
- /// <remarks>
- /// You only need to adjust this value when using perspective cameras.
- /// </remarks>
- [SerializeField, HideInInspector]
- public float _depthOffset = 0.00001f;
- /// <summary>
- /// Depth offset used when sorting by depth.
- /// </summary>
- public float DepthOffset
- {
- get { return _depthOffset; }
- set
- {
- // Return if same value given.
- if (Mathf.Abs(value - _depthOffset) < Mathf.Epsilon)
- {
- return;
- }
- // Store value.
- _depthOffset = value;
- // Apply it.
- var renderers = Renderers;
- for (var i = 0; i < renderers.Length; ++i)
- {
- renderers[i].OnControllerDepthOffsetDidChange(_depthOffset);
- }
- }
- }
- /// <summary>
- /// Model the controller belongs to.
- /// </summary>
- private CubismModel Model
- {
- get { return this.FindCubismModel(); }
- }
- /// <summary>
- /// <see cref="DrawablesRootTransform"/> backing field.
- /// </summary>
- private Transform _drawablesRootTransform;
- /// <summary>
- /// Root transform of all <see cref="CubismDrawable"/>s of the model.
- /// </summary>
- private Transform DrawablesRootTransform
- {
- get
- {
- if (_drawablesRootTransform == null)
- {
- _drawablesRootTransform = Model.Drawables[0].transform.parent;
- }
- return _drawablesRootTransform;
- }
- }
- /// <summary>
- /// <see cref="Renderers"/>s backing field.
- /// </summary>
- [NonSerialized]
- private CubismRenderer[] _renderers;
- /// <summary>
- /// <see cref="CubismRenderer"/>s.
- /// </summary>
- public CubismRenderer[] Renderers
- {
- get
- {
- if (_renderers== null)
- {
- _renderers = Model.Drawables.GetComponentsMany<CubismRenderer>();
- }
- return _renderers;
- }
- private set { _renderers = value; }
- }
- /// <summary>
- /// Model has update controller component.
- /// </summary>
- [HideInInspector]
- public bool HasUpdateController { get; set; }
- /// <summary>
- /// Makes sure all <see cref="CubismDrawable"/>s have <see cref="CubismRenderer"/>s attached to them.
- /// </summary>
- private void TryInitializeRenderers()
- {
- // Try get renderers.
- var renderers = Renderers;
- // Create renderers if necesssary.
- if (renderers == null || renderers.Length == 0)
- {
- // Create renders and apply it to backing field...
- var drawables = this
- .FindCubismModel()
- .Drawables;
- renderers = drawables.AddComponentEach<CubismRenderer>();
- // Store renderers.
- Renderers = renderers;
- }
- // Make sure renderers are initialized.
- for (var i = 0; i < renderers.Length; ++i)
- {
- renderers[i].TryInitialize();
- }
- // Initialize sorting layer.
- // We set the backing field here directly because we pull the sorting layer directly from the renderer.
- _sortingLayerId = renderers[0]
- .MeshRenderer
- .sortingLayerID;
- }
- /// <summary>
- /// Updates opacity if necessary.
- /// </summary>
- private void UpdateOpacity()
- {
- // Return if same value given.
- if (Mathf.Abs(Opacity - LastOpacity) < Mathf.Epsilon)
- {
- return;
- }
- // Store value.
- Opacity = Mathf.Clamp(Opacity, 0f, 1f);
- LastOpacity = Opacity;
- // Apply opacity.
- var applyOpacityToRenderers = (OpacityHandlerInterface == null || Opacity > (1f - Mathf.Epsilon));
- if (applyOpacityToRenderers)
- {
- var renderers = Renderers;
- for (var i = 0; i < renderers.Length; ++i)
- {
- renderers[i].OnModelOpacityDidChange(Opacity);
- }
- }
- // Call handler.
- if (OpacityHandlerInterface != null)
- {
- OpacityHandlerInterface.OnOpacityDidChange(this, Opacity);
- }
- }
- /// <summary>
- /// Called by cubism update controller. Order to invoke OnLateUpdate.
- /// </summary>
- public int ExecutionOrder
- {
- get { return CubismUpdateExecutionOrder.CubismRenderController; }
- }
- /// <summary>
- /// Called by cubism update controller. Needs to invoke OnLateUpdate on Editing.
- /// </summary>
- public bool NeedsUpdateOnEditing
- {
- get { return true; }
- }
- /// <summary>
- /// Called by cubism update controller. Applies billboarding.
- /// </summary>
- public void OnLateUpdate()
- {
- // Fail silently...
- if(!enabled)
- {
- return;
- }
- // Update opacity if necessary.
- UpdateOpacity();
- // Return early in case no camera is to be faced.
- if (CameraToFace == null)
- {
- return;
- }
- // Face camera.
- DrawablesRootTransform.rotation = (Quaternion.LookRotation(CameraToFace.transform.forward, Vector3.up));
- }
- #region Unity Event Handling
- /// <summary>
- /// Called by Unity.
- /// </summary>
- private void Start()
- {
- // Get cubism update controller.
- HasUpdateController = (GetComponent<CubismUpdateController>() != null);
- }
- /// <summary>
- /// Called by Unity. Enables listening to render data updates.
- /// </summary>
- private void OnEnable()
- {
- // Fail silently.
- if (Model == null)
- {
- return;
- }
- // Make sure renderers are available.
- TryInitializeRenderers();
- // Register listener.
- Model.OnDynamicDrawableData += OnDynamicDrawableData;
- }
- /// <summary>
- /// Called by Unity. Disables listening to render data updates.
- /// </summary>
- private void OnDisable()
- {
- // Fail silently.
- if (Model == null)
- {
- return;
- }
- // Deregister listener.
- Model.OnDynamicDrawableData -= OnDynamicDrawableData;
- }
- #endregion
- #region Cubism Event Handling
- /// <summary>
- /// Called by Unity.
- /// </summary>
- private void LateUpdate()
- {
- if(!HasUpdateController)
- {
- OnLateUpdate();
- }
- }
- /// <summary>
- /// Called whenever new render data is available.
- /// </summary>
- /// <param name="sender">Model with new render data.</param>
- /// <param name="data">New render data.</param>
- private void OnDynamicDrawableData(CubismModel sender, CubismDynamicDrawableData[] data)
- {
- // Get drawables.
- var drawables = sender.Drawables;
- var renderers = Renderers;
- // Handle render data changes.
- for (var i = 0; i < data.Length; ++i)
- {
- // Controls whether mesh buffers are to be swapped.
- var swapMeshes = false;
- // Update visibility if last SwapInfo flag is true.
- renderers[i].UpdateVisibility();
- // Update render order if last SwapInfo flags is true.
- renderers[i].UpdateRenderOrder();
- // Skip completely non-dirty data.
- if (!data[i].IsAnyDirty)
- {
- continue;
- }
- // Update visibility.
- if (data[i].IsVisibilityDirty)
- {
- renderers[i].OnDrawableVisiblityDidChange(data[i].IsVisible);
- swapMeshes = true;
- }
- // Update render order.
- if (data[i].IsRenderOrderDirty)
- {
- renderers[i].OnDrawableRenderOrderDidChange(data[i].RenderOrder);
- swapMeshes = true;
- }
- // Update opacity.
- if (data[i].IsOpacityDirty)
- {
- renderers[i].OnDrawableOpacityDidChange(data[i].Opacity);
- swapMeshes = true;
- }
- // Update vertex positions.
- if (data[i].AreVertexPositionsDirty)
- {
- renderers[i].OnDrawableVertexPositionsDidChange(data[i].VertexPositions);
- swapMeshes = true;
- }
- // Swap buffers if necessary.
- // [INV] Swapping only half of the meshes might improve performance even. Would that be visually feasible?
- if (swapMeshes)
- {
- renderers[i].SwapMeshes();
- }
- }
- // Pass draw order changes to handler (if available).
- var drawOrderHandler = DrawOrderHandlerInterface;
- if (drawOrderHandler != null)
- {
- for (var i = 0; i < data.Length; ++i)
- {
- if (data[i].IsDrawOrderDirty)
- {
- drawOrderHandler.OnDrawOrderDidChange(this, drawables[i], data[i].DrawOrder);
- }
- }
- }
- }
- #endregion
- }
- }
|