/** * 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. */ #include "CubismPose.hpp" #include "Id/CubismIdManager.hpp" using namespace Live2D::Cubism::Framework; namespace Live2D { namespace Cubism { namespace Framework { namespace { const csmFloat32 Epsilon= 0.001f; const csmFloat32 DefaultFadeInSeconds = 0.5f; // Pose.jsonのタグ const csmChar* FadeIn = "FadeInTime"; const csmChar* Link = "Link"; const csmChar* Groups = "Groups"; const csmChar* Id = "Id"; } CubismPose::PartData::PartData() { } CubismPose::PartData::~PartData() { } CubismPose::PartData::PartData(const PartData& v) : ParameterIndex(0) , PartIndex(0) { PartId = v.PartId; for (csmVector::const_iterator ite = v.Link.Begin(); ite != v.Link.End(); ++ite) { Link.PushBack(*ite); } } CubismPose::PartData& CubismPose::PartData::operator=(const PartData& v) { PartId = v.PartId; for (csmVector::const_iterator ite = v.Link.Begin(); ite != v.Link.End(); ++ite) { Link.PushBack(*ite); } return (*this); } void CubismPose::PartData::Initialize(CubismModel* model) { ParameterIndex = model->GetParameterIndex(PartId); PartIndex = model->GetPartIndex(PartId); model->SetParameterValue(ParameterIndex, 1); } CubismPose::CubismPose() : _fadeTimeSeconds(DefaultFadeInSeconds) , _lastModel(NULL) { } CubismPose::~CubismPose() { } CubismPose* CubismPose::Create(const csmByte* pose3json, csmSizeInt size) { Utils::CubismJson* json = Utils::CubismJson::Create(pose3json, size); if (!json) { return NULL; } CubismPose* ret = CSM_NEW CubismPose(); Utils::Value& root = json->GetRoot(); // フェード時間の指定 if (!root[FadeIn].IsNull()) { ret->_fadeTimeSeconds = root[FadeIn].ToFloat(DefaultFadeInSeconds); if (ret->_fadeTimeSeconds < 0.0f) { ret->_fadeTimeSeconds = DefaultFadeInSeconds; } } // パーツグループ Utils::Value& poseListInfo = root[Groups]; const csmInt32 poseCount = poseListInfo.GetSize(); for (csmInt32 poseIndex = 0; poseIndex < poseCount; ++poseIndex) { Utils::Value& idListInfo = poseListInfo[poseIndex]; const csmInt32 idCount = idListInfo.GetSize(); csmInt32 groupCount = 0; for (csmInt32 groupIndex = 0; groupIndex < idCount; ++groupIndex) { Utils::Value& partInfo = idListInfo[groupIndex]; PartData partData; const CubismIdHandle parameterId = CubismFramework::GetIdManager()->GetId(partInfo[Id].GetRawString()); partData.PartId = parameterId; // リンクするパーツの設定 if (!partInfo[Link].IsNull()) { Utils::Value& linkListInfo = partInfo[Link]; const csmInt32 linkCount = linkListInfo.GetSize(); for (csmInt32 linkIndex = 0; linkIndex < linkCount; ++linkIndex) { PartData linkPart; const CubismIdHandle linkId = CubismFramework::GetIdManager()->GetId(linkListInfo[linkIndex].GetString()); linkPart.PartId = linkId; partData.Link.PushBack(linkPart); } } ret->_partGroups.PushBack(partData); ++groupCount; } ret->_partGroupCounts.PushBack(groupCount); } Utils::CubismJson::Delete(json); return ret; } void CubismPose::Delete(CubismPose* pose) { CSM_DELETE_SELF(CubismPose, pose); } void CubismPose::Reset(CubismModel* model) { csmInt32 beginIndex = 0; for (csmUint32 i = 0; i < _partGroupCounts.GetSize(); ++i) { const csmInt32 groupCount = _partGroupCounts[i]; for (csmInt32 j = beginIndex; j < beginIndex + groupCount; ++j) { _partGroups[j].Initialize(model); const csmInt32 partsIndex = _partGroups[j].PartIndex; const csmInt32 paramIndex = _partGroups[j].ParameterIndex; if (partsIndex < 0) { continue; } model->SetPartOpacity( partsIndex, (j == beginIndex ? 1.0f : 0.0f)); model->SetParameterValue( paramIndex, (j == beginIndex ? 1.0f : 0.0f)); for (csmUint32 k = 0; k < _partGroups[j].Link.GetSize(); ++k) { _partGroups[j].Link[k].Initialize(model); } } beginIndex += groupCount; } } void CubismPose::CopyPartOpacities(CubismModel* model) { for (csmUint32 groupIndex = 0; groupIndex < _partGroups.GetSize(); ++groupIndex) { PartData& partData = _partGroups[groupIndex]; if (partData.Link.GetSize() == 0) { continue; // 連動するパラメータはない } const csmInt32 partIndex = _partGroups[groupIndex].PartIndex; const csmFloat32 opacity = model->GetPartOpacity(partIndex); for (csmUint32 linkIndex = 0; linkIndex < partData.Link.GetSize(); ++linkIndex) { PartData& linkPart = partData.Link[linkIndex]; const csmInt32 linkPartIndex = linkPart.PartIndex; if (linkPartIndex < 0) { continue; } model->SetPartOpacity(linkPartIndex, opacity); } } } void CubismPose::DoFade(CubismModel* model, csmFloat32 deltaTimeSeconds, csmInt32 beginIndex, csmInt32 partGroupCount) { csmInt32 visiblePartIndex = -1; csmFloat32 newOpacity = 1.0f; const csmFloat32 Phi = 0.5f; const csmFloat32 BackOpacityThreshold = 0.15f; // 現在、表示状態になっているパーツを取得 for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i) { const csmInt32 partIndex = _partGroups[i].PartIndex; const csmInt32 paramIndex = _partGroups[i].ParameterIndex; if (model->GetParameterValue(paramIndex) > Epsilon) { if (visiblePartIndex >= 0) { break; } visiblePartIndex = i; if (_fadeTimeSeconds == 0.0f) { newOpacity = 1.0f; continue; } newOpacity = model->GetPartOpacity(partIndex); // 新しい不透明度を計算 newOpacity += (deltaTimeSeconds / _fadeTimeSeconds); if (newOpacity > 1.0f) { newOpacity = 1.0f; } } } if (visiblePartIndex < 0) { visiblePartIndex = 0; newOpacity = 1.0f; } // 表示パーツ、非表示パーツの不透明度を設定する for (csmInt32 i = beginIndex; i < beginIndex + partGroupCount; ++i) { const csmInt32 partsIndex = _partGroups[i].PartIndex; // 表示パーツの設定 if (visiblePartIndex == i) { model->SetPartOpacity(partsIndex, newOpacity); // 先に設定 } // 非表示パーツの設定 else { csmFloat32 opacity = model->GetPartOpacity(partsIndex); csmFloat32 a1; // 計算によって求められる不透明度 if (newOpacity < Phi) { a1 = newOpacity * (Phi - 1) / Phi + 1.0f; // (0,1),(phi,phi)を通る直線式 } else { a1 = (1 - newOpacity) * Phi / (1.0f - Phi); // (1,0),(phi,phi)を通る直線式 } // 背景の見える割合を制限する場合 const csmFloat32 backOpacity = (1.0f - a1) * (1.0f - newOpacity); if (backOpacity > BackOpacityThreshold) { a1 = 1.0f - BackOpacityThreshold / (1.0f - newOpacity); } if (opacity > a1) { opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる } model->SetPartOpacity(partsIndex, opacity); } } } void CubismPose::UpdateParameters(CubismModel* model, csmFloat32 deltaTimeSeconds) { // 前回のモデルと同じではないときは初期化が必要 if (model != _lastModel) { // パラメータインデックスの初期化 Reset(model); } _lastModel = model; // 設定から時間を変更すると、経過時間がマイナスになることがあるので、経過時間0として対応。 if (deltaTimeSeconds < 0.0f) { deltaTimeSeconds = 0.0f; } csmInt32 beginIndex = 0; for (csmUint32 i = 0; i < _partGroupCounts.GetSize(); i++) { const csmInt32 partGroupCount = _partGroupCounts[i]; DoFade(model, deltaTimeSeconds, beginIndex, partGroupCount); beginIndex += partGroupCount; } CopyPartOpacities(model); } }}}