| 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    }}
 |