/** * 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 { /// /// Cubism pose controller. /// public sealed class CubismPoseController : MonoBehaviour, ICubismUpdatable { #region variable /// /// Default visible pose index. /// [SerializeField] public int defaultPoseIndex = 0; /// /// Back opacity threshold. /// private const float BackOpacityThreshold = 0.15f; /// /// Cubism model cache. /// private CubismModel _model; /// /// Model has update controller component. /// [HideInInspector] public bool HasUpdateController { get; set; } /// /// Pose data. /// private CubismPoseData[][] _poseData; #endregion #region Function /// /// update hidden part opacity. /// public void Refresh() { _model = this.FindCubismModel(); // Fail silently... if (_model == null) { return; } var tags = _model .Parts .GetComponentsMany(); 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(); 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() != null); } /// /// update hidden part opacity. /// 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; } } } } /// /// Copy opacity to linked parts. /// 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; } } } } } /// /// Save parts opacity. /// 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; } } } /// /// Called by cubism update controller. Order to invoke OnLateUpdate. /// public int ExecutionOrder { get { return CubismUpdateExecutionOrder.CubismPoseController; } } /// /// Called by cubism update controller. Needs to invoke OnLateUpdate on Editing. /// public bool NeedsUpdateOnEditing { get { return false; } } /// /// Called by cubism update manager. Updates controller. /// public void OnLateUpdate() { // Fail silently... if (!enabled || _model == null || _poseData == null) { return; } DoFade(); CopyPartOpacities(); SavePartOpacities(); } #endregion #region Unity Event Handling /// /// Called by Unity. Makes sure cache is initialized. /// private void OnEnable() { Refresh(); } /// /// Called by Unity. /// private void LateUpdate() { if(!HasUpdateController) { OnLateUpdate(); } } #endregion } }