/** * 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 "CubismMotion.hpp" #include #include "CubismFramework.hpp" #include "CubismMotionInternal.hpp" #include "CubismMotionJson.hpp" #include "CubismMotionQueueManager.hpp" #include "CubismMotionQueueEntry.hpp" #include "Math/CubismMath.hpp" #include "Type/csmVector.hpp" #include "Id/CubismIdManager.hpp" namespace Live2D { namespace Cubism { namespace Framework { namespace { const csmChar* EffectNameEyeBlink = "EyeBlink"; const csmChar* EffectNameLipSync = "LipSync"; const csmChar* TargetNameModel = "Model"; const csmChar* TargetNameParameter = "Parameter"; const csmChar* TargetNamePartOpacity = "PartOpacity"; // Id const csmChar* IdNameOpacity = "Opacity"; /** * Cubism SDK R2 以前のモーションを再現させるなら true 、アニメータのモーションを正しく再現するなら false 。 */ const csmBool UseOldBeziersCurveMotion = false; CubismMotionPoint LerpPoints(const CubismMotionPoint a, const CubismMotionPoint b, const csmFloat32 t) { CubismMotionPoint result; result.Time = a.Time + ((b.Time - a.Time) * t); result.Value = a.Value + ((b.Value - a.Value) * t); return result; } csmFloat32 LinearEvaluate(const CubismMotionPoint* points, const csmFloat32 time) { csmFloat32 t = (time - points[0].Time) / (points[1].Time - points[0].Time); if (t < 0.0f) { t = 0.0f; } return points[0].Value + ((points[1].Value - points[0].Value) * t); } csmFloat32 BezierEvaluate(const CubismMotionPoint* points, const csmFloat32 time) { csmFloat32 t = (time - points[0].Time) / (points[3].Time - points[0].Time); if (t < 0.0f) { t = 0.0f; } const CubismMotionPoint p01 = LerpPoints(points[0], points[1], t); const CubismMotionPoint p12 = LerpPoints(points[1], points[2], t); const CubismMotionPoint p23 = LerpPoints(points[2], points[3], t); const CubismMotionPoint p012 = LerpPoints(p01, p12, t); const CubismMotionPoint p123 = LerpPoints(p12, p23, t); return LerpPoints(p012, p123, t).Value; } csmFloat32 BezierEvaluateBinarySearch(const CubismMotionPoint* points, const csmFloat32 time) { const csmFloat32 x_error = 0.01f; const csmFloat32 x = time; csmFloat32 x1 = points[0].Time; csmFloat32 x2 = points[3].Time; csmFloat32 cx1 = points[1].Time; csmFloat32 cx2 = points[2].Time; csmFloat32 ta = 0.0f; csmFloat32 tb = 1.0f; csmFloat32 t = 0.0f; int i = 0; for (csmBool var33 = true; i < 20; ++i) { if (x < x1 + x_error) { t = ta; break; } if (x2 - x_error < x) { t = tb; break; } csmFloat32 centerx = (cx1 + cx2) * 0.5f; cx1 = (x1 + cx1) * 0.5f; cx2 = (x2 + cx2) * 0.5f; csmFloat32 ctrlx12 = (cx1 + centerx) * 0.5f; csmFloat32 ctrlx21 = (cx2 + centerx) * 0.5f; centerx = (ctrlx12 + ctrlx21) * 0.5f; if (x < centerx) { tb = (ta + tb) * 0.5f; if (centerx - x_error < x) { t = tb; break; } x2 = centerx; cx2 = ctrlx12; } else { ta = (ta + tb) * 0.5f; if (x < centerx + x_error) { t = ta; break; } x1 = centerx; cx1 = ctrlx21; } } if (i == 20) { t = (ta + tb) * 0.5f; } if (t < 0.0f) { t = 0.0f; } if (t > 1.0f) { t = 1.0f; } const CubismMotionPoint p01 = LerpPoints(points[0], points[1], t); const CubismMotionPoint p12 = LerpPoints(points[1], points[2], t); const CubismMotionPoint p23 = LerpPoints(points[2], points[3], t); const CubismMotionPoint p012 = LerpPoints(p01, p12, t); const CubismMotionPoint p123 = LerpPoints(p12, p23, t); return LerpPoints(p012, p123, t).Value; } csmFloat32 BezierEvaluateCardanoInterpretation(const CubismMotionPoint* points, const csmFloat32 time) { const csmFloat32 x = time; csmFloat32 x1 = points[0].Time; csmFloat32 x2 = points[3].Time; csmFloat32 cx1 = points[1].Time; csmFloat32 cx2 = points[2].Time; csmFloat32 a = x2 - 3.0f * cx2 + 3.0f * cx1 - x1; csmFloat32 b = 3.0f * cx2 - 6.0f * cx1 + 3.0f * x1; csmFloat32 c = 3.0f * cx1 - 3.0f * x1; csmFloat32 d = x1 - x; csmFloat32 t = CubismMath::CardanoAlgorithmForBezier(a, b, c, d); const CubismMotionPoint p01 = LerpPoints(points[0], points[1], t); const CubismMotionPoint p12 = LerpPoints(points[1], points[2], t); const CubismMotionPoint p23 = LerpPoints(points[2], points[3], t); const CubismMotionPoint p012 = LerpPoints(p01, p12, t); const CubismMotionPoint p123 = LerpPoints(p12, p23, t); return LerpPoints(p012, p123, t).Value; } csmFloat32 SteppedEvaluate(const CubismMotionPoint* points, const csmFloat32 time) { return points[0].Value; } csmFloat32 InverseSteppedEvaluate(const CubismMotionPoint* points, const csmFloat32 time) { return points[1].Value; } csmFloat32 EvaluateCurve(const CubismMotionData* motionData, const csmInt32 index, csmFloat32 time) { // Find segment to evaluate. const CubismMotionCurve& curve = motionData->Curves[index]; csmInt32 target = -1; const csmInt32 totalSegmentCount = curve.BaseSegmentIndex + curve.SegmentCount; csmInt32 pointPosition = 0; for (csmInt32 i = curve.BaseSegmentIndex; i < totalSegmentCount; ++i) { // Get first point of next segment. pointPosition = motionData->Segments[i].BasePointIndex + (motionData->Segments[i].SegmentType == CubismMotionSegmentType_Bezier ? 3 : 1); // Break if time lies within current segment. if (motionData->Points[pointPosition].Time > time) { target = i; break; } } if (target == -1) { return motionData->Points[pointPosition].Value; } const CubismMotionSegment& segment = motionData->Segments[target]; return segment.Evaluate(&motionData->Points[segment.BasePointIndex], time); } } CubismMotion::CubismMotion() : _sourceFrameRate(30.0f) , _loopDurationSeconds(-1.0f) , _isLoop(false) // trueから false へデフォルトを変更 , _isLoopFadeIn(true) // ループ時にフェードインが有効かどうかのフラグ , _lastWeight(0.0f) , _motionData(NULL) , _modelCurveIdEyeBlink(NULL) , _modelCurveIdLipSync(NULL) { } CubismMotion::~CubismMotion() { CSM_DELETE(_motionData); } CubismMotion* CubismMotion::Create(const csmByte* buffer, csmSizeInt size, FinishedMotionCallback onFinishedMotionHandler) { CubismMotion* ret = CSM_NEW CubismMotion(); ret->Parse(buffer, size); ret->_sourceFrameRate = ret->_motionData->Fps; ret->_loopDurationSeconds = ret->_motionData->Duration; ret->_onFinishedMotion = onFinishedMotionHandler; // NOTE: Editorではループありのモーション書き出しは非対応 // ret->_loop = (ret->_motionData->Loop > 0); return ret; } csmFloat32 CubismMotion::GetDuration() { return _isLoop ? -1.0f : _loopDurationSeconds; } void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSeconds, csmFloat32 fadeWeight, CubismMotionQueueEntry* motionQueueEntry) { if (_modelCurveIdEyeBlink == NULL) { _modelCurveIdEyeBlink = CubismFramework::GetIdManager()->GetId(EffectNameEyeBlink); } if (_modelCurveIdLipSync == NULL) { _modelCurveIdLipSync = CubismFramework::GetIdManager()->GetId(EffectNameLipSync); } csmFloat32 timeOffsetSeconds = userTimeSeconds - motionQueueEntry->GetStartTime(); if (timeOffsetSeconds < 0.0f) { timeOffsetSeconds = 0.0f; // エラー回避 } csmFloat32 lipSyncValue = FLT_MAX; csmFloat32 eyeBlinkValue = FLT_MAX; //まばたき、リップシンクのうちモーションの適用を検出するためのビット(maxFlagCount個まで const csmInt32 MaxTargetSize = 64; csmUint64 lipSyncFlags = 0ULL; csmUint64 eyeBlinkFlags = 0ULL; //瞬き、リップシンクのターゲット数が上限を超えている場合 if (_eyeBlinkParameterIds.GetSize() > MaxTargetSize) { CubismLogDebug("too many eye blink targets : %d", _eyeBlinkParameterIds.GetSize()); } if (_lipSyncParameterIds.GetSize() > MaxTargetSize) { CubismLogDebug("too many lip sync targets : %d", _lipSyncParameterIds.GetSize()); } const csmFloat32 tmpFadeIn = (_fadeInSeconds <= 0.0f) ? 1.0f : CubismMath::GetEasingSine((userTimeSeconds - motionQueueEntry->GetFadeInStartTime()) / _fadeInSeconds); const csmFloat32 tmpFadeOut = (_fadeOutSeconds <= 0.0f || motionQueueEntry->GetEndTime() < 0.0f) ? 1.0f : CubismMath::GetEasingSine((motionQueueEntry->GetEndTime() - userTimeSeconds) / _fadeOutSeconds); csmFloat32 value; csmInt32 c, parameterIndex; // 'Repeat' time as necessary. csmFloat32 time = timeOffsetSeconds; if (_isLoop) { while (time > _motionData->Duration) { time -= _motionData->Duration; } } csmVector& curves = _motionData->Curves; // Evaluate model curves. for (c = 0; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_Model; ++c) { // Evaluate curve and call handler. value = EvaluateCurve(_motionData, c, time); if (curves[c].Id == _modelCurveIdEyeBlink) { eyeBlinkValue = value; } else if (curves[c].Id == _modelCurveIdLipSync) { lipSyncValue = value; } } csmInt32 parameterMotionCurveCount = 0; for (; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_Parameter; ++c) { parameterMotionCurveCount++; // Find parameter index. parameterIndex = model->GetParameterIndex(curves[c].Id); // Skip curve evaluation if no value in sink. if (parameterIndex == -1) { continue; } const csmFloat32 sourceValue = model->GetParameterValue(parameterIndex); // Evaluate curve and apply value. value = EvaluateCurve(_motionData, c, time); if (eyeBlinkValue != FLT_MAX) { for (csmUint32 i = 0; i < _eyeBlinkParameterIds.GetSize() && i < MaxTargetSize; ++i) { if (_eyeBlinkParameterIds[i] == curves[c].Id) { value *= eyeBlinkValue; eyeBlinkFlags |= 1ULL << i; break; } } } if (lipSyncValue != FLT_MAX) { for (csmUint32 i = 0; i < _lipSyncParameterIds.GetSize() && i < MaxTargetSize; ++i) { if (_lipSyncParameterIds[i] == curves[c].Id) { value += lipSyncValue; lipSyncFlags |= 1ULL << i; break; } } } csmFloat32 v; // パラメータごとのフェード if (curves[c].FadeInTime < 0.0f && curves[c].FadeOutTime < 0.0f) { //モーションのフェードを適用 v = sourceValue + (value - sourceValue) * fadeWeight; } else { // パラメータに対してフェードインかフェードアウトが設定してある場合はそちらを適用 csmFloat32 fin; csmFloat32 fout; if (curves[c].FadeInTime < 0.0f) { fin = tmpFadeIn; } else { fin = curves[c].FadeInTime == 0.0f ? 1.0f : CubismMath::GetEasingSine((userTimeSeconds - motionQueueEntry->GetFadeInStartTime()) / curves[c].FadeInTime); } if (curves[c].FadeOutTime < 0.0f) { fout = tmpFadeOut; } else { fout = (curves[c].FadeOutTime == 0.0f || motionQueueEntry->GetEndTime() < 0.0f) ? 1.0f : CubismMath::GetEasingSine((motionQueueEntry->GetEndTime() - userTimeSeconds) / curves[c].FadeOutTime ); } const csmFloat32 paramWeight = _weight * fin * fout; // パラメータごとのフェードを適用 v = sourceValue + (value - sourceValue) * paramWeight; } model->SetParameterValue(parameterIndex, v); } { if (eyeBlinkValue != FLT_MAX) { for (csmUint32 i = 0; i < _eyeBlinkParameterIds.GetSize() && i < MaxTargetSize; ++i) { const csmFloat32 sourceValue = model->GetParameterValue(_eyeBlinkParameterIds[i]); //モーションでの上書きがあった時にはまばたきは適用しない if ((eyeBlinkFlags >> i) & 0x01) { continue; } const csmFloat32 v = sourceValue + (eyeBlinkValue - sourceValue) * fadeWeight; model->SetParameterValue(_eyeBlinkParameterIds[i], v); } } if (lipSyncValue != FLT_MAX) { for (csmUint32 i = 0; i < _lipSyncParameterIds.GetSize() && i < MaxTargetSize; ++i) { const csmFloat32 sourceValue = model->GetParameterValue(_lipSyncParameterIds[i]); //モーションでの上書きがあった時にはリップシンクは適用しない if ((lipSyncFlags >> i) & 0x01) { continue; } const csmFloat32 v = sourceValue + (lipSyncValue - sourceValue) * fadeWeight; model->SetParameterValue(_lipSyncParameterIds[i], v); } } } for (; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_PartOpacity; ++c) { // Find parameter index. parameterIndex = model->GetParameterIndex(curves[c].Id); // Skip curve evaluation if no value in sink. if (parameterIndex == -1) { continue; } // Evaluate curve and apply value. value = EvaluateCurve(_motionData, c, time); model->SetParameterValue(parameterIndex, value); } if (timeOffsetSeconds >= _motionData->Duration) { if (_isLoop) { motionQueueEntry->SetStartTime(userTimeSeconds); //最初の状態へ if (_isLoopFadeIn) { //ループ中でループ用フェードインが有効のときは、フェードイン設定し直し motionQueueEntry->SetFadeInStartTime(userTimeSeconds); } } else { if (this->_onFinishedMotion != NULL) { this->_onFinishedMotion(this); } motionQueueEntry->IsFinished(true); } } _lastWeight = fadeWeight; } void CubismMotion::Parse(const csmByte* motionJson, const csmSizeInt size) { _motionData = CSM_NEW CubismMotionData; CubismMotionJson* json = CSM_NEW CubismMotionJson(motionJson, size); _motionData->Duration = json->GetMotionDuration(); _motionData->Loop = json->IsMotionLoop(); _motionData->CurveCount = json->GetMotionCurveCount(); _motionData->Fps = json->GetMotionFps(); _motionData->EventCount = json->GetEventCount(); csmBool areBeziersRestructed = json->GetEvaluationOptionFlag( EvaluationOptionFlag_AreBeziersRistricted ); if (json->IsExistMotionFadeInTime()) { _fadeInSeconds = (json->GetMotionFadeInTime() < 0.0f) ? 1.0f : json->GetMotionFadeInTime(); } else { _fadeInSeconds = 1.0f; } if (json->IsExistMotionFadeOutTime()) { _fadeOutSeconds = (json->GetMotionFadeOutTime() < 0.0f) ? 1.0f : json->GetMotionFadeOutTime(); } else { _fadeOutSeconds = 1.0f; } _motionData->Curves.UpdateSize(_motionData->CurveCount, CubismMotionCurve(), true); _motionData->Segments.UpdateSize(json->GetMotionTotalSegmentCount(), CubismMotionSegment(), true); _motionData->Points.UpdateSize(json->GetMotionTotalPointCount(), CubismMotionPoint(), true); _motionData->Events.UpdateSize(_motionData->EventCount, CubismMotionEvent(), true); csmInt32 totalPointCount = 0; csmInt32 totalSegmentCount = 0; // Curves for (csmInt32 curveCount = 0; curveCount < _motionData->CurveCount; ++curveCount) { if (strcmp(json->GetMotionCurveTarget(curveCount), TargetNameModel) == 0) { _motionData->Curves[curveCount].Type = CubismMotionCurveTarget_Model; } else if (strcmp(json->GetMotionCurveTarget(curveCount), TargetNameParameter) == 0) { _motionData->Curves[curveCount].Type = CubismMotionCurveTarget_Parameter; } else if (strcmp(json->GetMotionCurveTarget(curveCount), TargetNamePartOpacity) == 0) { _motionData->Curves[curveCount].Type = CubismMotionCurveTarget_PartOpacity; } else { CubismLogWarning("Warning : Unable to get segment type from Curve! The number of \"CurveCount\" may be incorrect!"); } _motionData->Curves[curveCount].Id = json->GetMotionCurveId(curveCount); _motionData->Curves[curveCount].BaseSegmentIndex = totalSegmentCount; _motionData->Curves[curveCount].FadeInTime = (json->IsExistMotionCurveFadeInTime(curveCount)) ? json->GetMotionCurveFadeInTime(curveCount) : -1.0f ; _motionData->Curves[curveCount].FadeOutTime = (json->IsExistMotionCurveFadeOutTime(curveCount)) ? json->GetMotionCurveFadeOutTime(curveCount) : -1.0f; // Segments for (csmInt32 segmentPosition = 0; segmentPosition < json->GetMotionCurveSegmentCount(curveCount);) { if (segmentPosition == 0) { _motionData->Segments[totalSegmentCount].BasePointIndex = totalPointCount; _motionData->Points[totalPointCount].Time = json->GetMotionCurveSegment(curveCount, segmentPosition); _motionData->Points[totalPointCount].Value = json->GetMotionCurveSegment(curveCount, segmentPosition + 1); totalPointCount += 1; segmentPosition += 2; } else { _motionData->Segments[totalSegmentCount].BasePointIndex = totalPointCount - 1; } const csmInt32 segment = static_cast(json->GetMotionCurveSegment(curveCount, segmentPosition)); switch (segment) { case CubismMotionSegmentType_Linear: { _motionData->Segments[totalSegmentCount].SegmentType = CubismMotionSegmentType_Linear; _motionData->Segments[totalSegmentCount].Evaluate = LinearEvaluate; _motionData->Points[totalPointCount].Time = json->GetMotionCurveSegment(curveCount, (segmentPosition + 1)); _motionData->Points[totalPointCount].Value = json->GetMotionCurveSegment(curveCount, (segmentPosition + 2)); totalPointCount += 1; segmentPosition += 3; break; } case CubismMotionSegmentType_Bezier: { _motionData->Segments[totalSegmentCount].SegmentType = CubismMotionSegmentType_Bezier; if (areBeziersRestructed || UseOldBeziersCurveMotion) { _motionData->Segments[totalSegmentCount].Evaluate = BezierEvaluate; } else { _motionData->Segments[totalSegmentCount].Evaluate = BezierEvaluateCardanoInterpretation; } _motionData->Points[totalPointCount].Time = json->GetMotionCurveSegment(curveCount, (segmentPosition + 1)); _motionData->Points[totalPointCount].Value = json->GetMotionCurveSegment(curveCount, (segmentPosition + 2)); _motionData->Points[totalPointCount + 1].Time = json->GetMotionCurveSegment(curveCount, (segmentPosition + 3)); _motionData->Points[totalPointCount + 1].Value = json->GetMotionCurveSegment(curveCount, (segmentPosition + 4)); _motionData->Points[totalPointCount + 2].Time = json->GetMotionCurveSegment(curveCount, (segmentPosition + 5)); _motionData->Points[totalPointCount + 2].Value = json->GetMotionCurveSegment(curveCount, (segmentPosition + 6)); totalPointCount += 3; segmentPosition += 7; break; } case CubismMotionSegmentType_Stepped: { _motionData->Segments[totalSegmentCount].SegmentType = CubismMotionSegmentType_Stepped; _motionData->Segments[totalSegmentCount].Evaluate = SteppedEvaluate; _motionData->Points[totalPointCount].Time = json->GetMotionCurveSegment(curveCount, (segmentPosition + 1)); _motionData->Points[totalPointCount].Value = json->GetMotionCurveSegment(curveCount, (segmentPosition + 2)); totalPointCount += 1; segmentPosition += 3; break; } case CubismMotionSegmentType_InverseStepped: { _motionData->Segments[totalSegmentCount].SegmentType = CubismMotionSegmentType_InverseStepped; _motionData->Segments[totalSegmentCount].Evaluate = InverseSteppedEvaluate; _motionData->Points[totalPointCount].Time = json->GetMotionCurveSegment(curveCount, (segmentPosition + 1)); _motionData->Points[totalPointCount].Value = json->GetMotionCurveSegment(curveCount, (segmentPosition + 2)); totalPointCount += 1; segmentPosition += 3; break; } default: { CSM_ASSERT(0); break; } } ++_motionData->Curves[curveCount].SegmentCount; ++totalSegmentCount; } } for (csmInt32 userdatacount = 0; userdatacount < json->GetEventCount(); ++userdatacount) { _motionData->Events[userdatacount].FireTime = json->GetEventTime(userdatacount); _motionData->Events[userdatacount].Value = json->GetEventValue(userdatacount); } CSM_DELETE(json); } void CubismMotion::SetParameterFadeInTime(CubismIdHandle parameterId, csmFloat32 value) { csmVector& curves = _motionData->Curves; for (csmInt16 i = 0; i < _motionData->CurveCount; ++i) { if (parameterId == curves[i].Id) { curves[i].FadeInTime = value; return; } } } void CubismMotion::SetParameterFadeOutTime(CubismIdHandle parameterId, csmFloat32 value) { csmVector& curves = _motionData->Curves; for (csmInt16 i = 0; i < _motionData->CurveCount; ++i) { if (parameterId == curves[i].Id) { curves[i].FadeOutTime = value; return; } } } csmFloat32 CubismMotion::GetParameterFadeInTime(CubismIdHandle parameterId) const { csmVector& curves = _motionData->Curves; for (csmInt16 i = 0; i < _motionData->CurveCount; ++i) { if (parameterId == curves[i].Id) { return curves[i].FadeInTime; } } return -1; } csmFloat32 CubismMotion::GetParameterFadeOutTime(CubismIdHandle parameterId) const { csmVector& curves = _motionData->Curves; for (csmInt16 i = 0; i < _motionData->CurveCount; ++i) { if (parameterId == curves[i].Id) { return curves[i].FadeOutTime; } } return -1; } void CubismMotion::IsLoop(csmBool loop) { this->_isLoop = loop; } csmBool CubismMotion::IsLoop() const { return this->_isLoop; } void CubismMotion::IsLoopFadeIn(csmBool loopFadeIn) { this->_isLoopFadeIn = loopFadeIn; } csmBool CubismMotion::IsLoopFadeIn() const { return this->_isLoopFadeIn; } csmFloat32 CubismMotion::GetLoopDuration() { return _loopDurationSeconds; } void CubismMotion::SetEffectIds(const csmVector& eyeBlinkParameterIds, const csmVector& lipSyncParameterIds) { _eyeBlinkParameterIds = eyeBlinkParameterIds; _lipSyncParameterIds = lipSyncParameterIds; } const csmVector& CubismMotion::GetFiredEvent(csmFloat32 beforeCheckTimeSeconds, csmFloat32 motionTimeSeconds) { _firedEventValues.UpdateSize(0); /// イベントの発火チェック for (csmInt32 u = 0; u < _motionData->EventCount; ++u) { if ((_motionData->Events[u].FireTime >beforeCheckTimeSeconds) && (_motionData->Events[u].FireTime <= motionTimeSeconds)) { _firedEventValues.PushBack(&_motionData->Events[u].Value); } } return _firedEventValues; } csmBool CubismMotion::IsExistOpacity() const { for (csmInt32 i = 0; i < _motionData->CurveCount; i++) { CubismMotionCurve curve = _motionData->Curves[i]; if (curve.Type != CubismMotionCurveTarget_Model) { continue; } if (strcmp(curve.Id->GetString().GetRawString(), IdNameOpacity) == 0) { return true; } } return false; } csmInt32 CubismMotion::GetOpacityIndex() const { if (IsExistOpacity()) { for (csmInt32 i = 0; i < _motionData->CurveCount; i++) { CubismMotionCurve curve = _motionData->Curves[i]; if (curve.Type != CubismMotionCurveTarget_Model) { continue; } if (strcmp(curve.Id->GetString().GetRawString(), IdNameOpacity) == 0) { return i; } } } return -1; } CubismIdHandle CubismMotion::GetOpacityId(csmInt32 index) { if (index != -1) { CubismMotionCurve curve = _motionData->Curves[index]; if (curve.Type == CubismMotionCurveTarget_Model) { if (strcmp(curve.Id->GetString().GetRawString(), IdNameOpacity) == 0) { return CubismFramework::GetIdManager()->GetId(curve.Id->GetString().GetRawString()); } } } return NULL; } csmFloat32 CubismMotion::GetOpacityValue(csmFloat32 motionTimeSeconds) const { if (motionTimeSeconds >= 0.0f) { csmInt32 index = GetOpacityIndex(); if (index != -1) { csmInt32 baseSegmentIndex = _motionData->Curves[index].BaseSegmentIndex; csmInt32 basePointIndex = _motionData->Segments[baseSegmentIndex].BasePointIndex; csmFloat32 fps = 1.0f / _motionData->Fps; csmFloat32 pointTime = -1.0f; csmInt32 segmentPosition = 0; CubismMotionSegmentType segmentType = static_cast(_motionData->Segments[index + segmentPosition].SegmentType); for (segmentPosition = 0; segmentPosition < _motionData->Curves[index].SegmentCount; segmentPosition++) { if (segmentPosition == 0) { if (motionTimeSeconds == 0.0f) { return LinearEvaluate(&_motionData->Points[basePointIndex], motionTimeSeconds); } segmentPosition += 2; } segmentType = static_cast(_motionData->Segments[index + segmentPosition].SegmentType); basePointIndex = _motionData->Segments[index + segmentPosition].BasePointIndex; pointTime = _motionData->Points[basePointIndex].Time; switch (segmentType) { case Live2D::Cubism::Framework::CubismMotionSegmentType_Linear: segmentPosition += 3; break; case Live2D::Cubism::Framework::CubismMotionSegmentType_Bezier: basePointIndex += 3; segmentPosition += 7; break; case Live2D::Cubism::Framework::CubismMotionSegmentType_Stepped: segmentPosition += 3; break; case Live2D::Cubism::Framework::CubismMotionSegmentType_InverseStepped: segmentPosition += 3; break; default: CSM_ASSERT(0); break; } if (pointTime + fps < motionTimeSeconds) { continue; } switch (segmentType) { case Live2D::Cubism::Framework::CubismMotionSegmentType_Linear: return LinearEvaluate(&_motionData->Points[basePointIndex], motionTimeSeconds); case Live2D::Cubism::Framework::CubismMotionSegmentType_Bezier: return BezierEvaluateCardanoInterpretation(&_motionData->Points[basePointIndex - 3], motionTimeSeconds); case Live2D::Cubism::Framework::CubismMotionSegmentType_Stepped: return SteppedEvaluate(&_motionData->Points[basePointIndex], motionTimeSeconds); case Live2D::Cubism::Framework::CubismMotionSegmentType_InverseStepped: return InverseSteppedEvaluate(&_motionData->Points[basePointIndex], motionTimeSeconds); default: CSM_ASSERT(0); break; } } } } return 1.0f; } }}}