| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 | /** * 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 System;using UnityEngine;namespace Live2D.Cubism.Framework.Pose{    /// <summary>    /// Cubism pose controller.    /// </summary>    public sealed class CubismPoseController : MonoBehaviour, ICubismUpdatable    {        #region variable        /// <summary>        /// Default visible pose index.        /// </summary>        [SerializeField]        public int defaultPoseIndex = 0;        /// <summary>        /// Back opacity threshold.        /// </summary>        private const float BackOpacityThreshold = 0.15f;        /// <summary>        /// Cubism model cache.        /// </summary>        private CubismModel _model;        /// <summary>        /// Model has update controller component.        /// </summary>        [HideInInspector]        public bool HasUpdateController { get; set; }        /// <summary>        /// Pose data.        /// </summary>        private CubismPoseData[][] _poseData;        #endregion        #region Function        /// <summary>        /// update hidden part opacity.        /// </summary>        public void Refresh()        {            _model = this.FindCubismModel();            // Fail silently...            if (_model == null)            {                return;            }            var tags = _model                .Parts                .GetComponentsMany<CubismPosePart>();            for(var i = 0; i < tags.Length; ++i)            {                var groupIndex = tags[i].GroupIndex;                var partIndex = tags[i].PartIndex;                if(_poseData == null || _poseData.Length <= groupIndex)                {                    Array.Resize(ref _poseData, groupIndex + 1);                }                if(_poseData[groupIndex] == null || _poseData[groupIndex].Length <= partIndex)                {                    Array.Resize(ref _poseData[groupIndex], partIndex + 1);                }                _poseData[groupIndex][partIndex].PosePart = tags[i];                _poseData[groupIndex][partIndex].Part= tags[i].GetComponent<CubismPart>();                defaultPoseIndex = (defaultPoseIndex < 0) ? 0 : defaultPoseIndex;                if (partIndex != defaultPoseIndex)                {                    _poseData[groupIndex][partIndex].Part.Opacity = 0.0f;                }                _poseData[groupIndex][partIndex].Opacity = _poseData[groupIndex][partIndex].Part.Opacity;                if(tags[i].Link == null || tags[i].Link.Length == 0)                {                    continue;                }                _poseData[groupIndex][partIndex].LinkParts = new CubismPart[tags[i].Link.Length];                for(var j = 0; j < tags[i].Link.Length; ++j)                {                    var linkId = tags[i].Link[j];                    _poseData[groupIndex][partIndex].LinkParts[j] = _model.Parts.FindById(linkId);                }            }            // Get cubism update controller.            HasUpdateController = (GetComponent<CubismUpdateController>() != null);        }        /// <summary>        /// update hidden part opacity.        /// </summary>        private void DoFade()        {            for(var groupIndex = 0; groupIndex < _poseData.Length; ++groupIndex)            {                var appearPartsGroupIndex = -1;                var appearPartsGroupOpacity = 1.0f;                // Find appear parts group index and opacity.                for (var i = 0; i < _poseData[groupIndex].Length; ++i)                {                    var part = _poseData[groupIndex][i].Part;                    if(part.Opacity > _poseData[groupIndex][i].Opacity)                    {                        appearPartsGroupIndex = i;                        appearPartsGroupOpacity = part.Opacity;                        break;                    }                }                // Fail silently...                if(appearPartsGroupIndex < 0)                {                    return;                }                // Delay disappearing parts groups disappear.                for (var i = 0; i < _poseData[groupIndex].Length; ++i)                {                    // Fail silently...                    if(i == appearPartsGroupIndex)                    {                        continue;                    }                    var part = _poseData[groupIndex][i].Part;                    var delayedOpacity = part.Opacity;                    var backOpacity = (1.0f - delayedOpacity) * (1.0f - appearPartsGroupOpacity);                    // When restricting the visible proportion of the background                    if (backOpacity > BackOpacityThreshold)                    {                        delayedOpacity = 1.0f - BackOpacityThreshold / (1.0f - appearPartsGroupOpacity);                    }                    // Overwrite the opacity if it's greater than the delayed opacity.                    if (part.Opacity > delayedOpacity)                    {                        part.Opacity = delayedOpacity;                    }                }            }        }        /// <summary>        /// Copy opacity to linked parts.        /// </summary>        private void CopyPartOpacities()        {            for(var groupIndex = 0; groupIndex < _poseData.Length; ++groupIndex)            {                for (var partIndex = 0; partIndex < _poseData[groupIndex].Length; ++partIndex)                {                    var linkParts = _poseData[groupIndex][partIndex].LinkParts;                    if(linkParts == null)                    {                        continue;                    }                    var opacity = _poseData[groupIndex][partIndex].Part.Opacity;                    for (var linkIndex = 0; linkIndex < linkParts.Length; ++linkIndex)                    {                        var linkPart = linkParts[linkIndex];                        if(linkPart != null)                        {                            linkPart.Opacity = opacity;                        }                    }                }            }        }        /// <summary>        /// Save parts opacity.        /// </summary>        private void SavePartOpacities()        {            for(var groupIndex = 0; groupIndex < _poseData.Length; ++groupIndex)            {                for (var partIndex = 0; partIndex < _poseData[groupIndex].Length; ++partIndex)                {                    _poseData[groupIndex][partIndex].Opacity = _poseData[groupIndex][partIndex].Part.Opacity;                }            }        }        /// <summary>        /// Called by cubism update controller. Order to invoke OnLateUpdate.        /// </summary>        public int ExecutionOrder        {            get { return CubismUpdateExecutionOrder.CubismPoseController; }        }        /// <summary>        /// Called by cubism update controller. Needs to invoke OnLateUpdate on Editing.        /// </summary>        public bool NeedsUpdateOnEditing        {            get { return false; }        }        /// <summary>        /// Called by cubism update manager. Updates controller.        /// </summary>        public void OnLateUpdate()        {            // Fail silently...            if (!enabled || _model == null || _poseData == null)            {               return;            }            DoFade();            CopyPartOpacities();            SavePartOpacities();        }        #endregion        #region Unity Event Handling        /// <summary>        /// Called by Unity. Makes sure cache is initialized.        /// </summary>        private void OnEnable()        {            Refresh();        }        /// <summary>        /// Called by Unity.        /// </summary>        private void LateUpdate()        {            if(!HasUpdateController)            {                OnLateUpdate();            }        }        #endregion    }}
 |