#include "AnimationState.h" #include "WorldClock.h" #include "../model/DisplayData.h" #include "../model/AnimationConfig.h" #include "../model/AnimationData.h" #include "../armature/Armature.h" #include "../armature/Bone.h" #include "../armature/Slot.h" #include "../armature/Constraint.h" #include "../event/EventObject.h" #include "../event/IEventDispatcher.h" #include "TimelineState.h" DRAGONBONES_NAMESPACE_BEGIN void AnimationState::_onClear() { for (const auto timeline : _boneTimelines) { timeline->returnToPool(); } for (const auto timeline : _slotTimelines) { timeline->returnToPool(); } for (const auto timeline : _constraintTimelines) { timeline->returnToPool(); } for (const auto& pair : _bonePoses) { pair.second->returnToPool(); } if (_actionTimeline != nullptr) { _actionTimeline->returnToPool(); } if (_zOrderTimeline != nullptr) { _zOrderTimeline->returnToPool(); } actionEnabled = false; additiveBlending = false; displayControl = false; resetToPose = false; playTimes = 1; layer = 0; timeScale = 1.0f; weight = 1.0f; autoFadeOutTime = 0.0f; fadeTotalTime = 0.0f; name = ""; group = ""; _timelineDirty = 2; _playheadState = 0; _fadeState = -1; _subFadeState = -1; _position = 0.0f; _duration = 0.0f; _fadeTime = 0.0f; _time = 0.0f; _fadeProgress = 0.0f; _weightResult = 0.0f; _boneMask.clear(); _boneTimelines.clear(); _slotTimelines.clear(); _constraintTimelines.clear(); _poseTimelines.clear(); _bonePoses.clear(); _animationData = nullptr; _armature = nullptr; _actionTimeline = nullptr; _zOrderTimeline = nullptr; } void AnimationState::_updateTimelines() { { // Update constraint timelines. hlookup::string_map> constraintTimelines; for (const auto timeline : _constraintTimelines) // Create constraint timelines map. { constraintTimelines[timeline->constraint->getName()].push_back(timeline); } for (const auto constraint : _armature->_constraints) { const auto& timelineName = constraint->getName(); const auto timelineDatas = _animationData->getConstraintTimelines(timelineName); const auto iterator = constraintTimelines.find(timelineName); if (iterator != constraintTimelines.end()) // Remove constraint timeline from map. { constraintTimelines.erase(iterator); } else // Create new constraint timeline. { if (timelineDatas != nullptr) { for (const auto timelineData : *timelineDatas) { switch (timelineData->type) { case TimelineType::IKConstraint: { const auto timeline = BaseObject::borrowObject(); timeline->constraint = constraint; timeline->init(_armature, this, timelineData); _constraintTimelines.push_back(timeline); break; } default: break; } } } else if (resetToPose) // Pose timeline. { const auto timeline = BaseObject::borrowObject(); timeline->constraint = constraint; timeline->init(_armature, this, nullptr); _constraintTimelines.push_back(timeline); _poseTimelines.push_back(std::make_pair(timeline, BaseTimelineType::Constraint)); } } } } } void AnimationState::_updateBoneAndSlotTimelines() { { // Update bone timelines. hlookup::string_map> boneTimelines; for (const auto timeline : _boneTimelines) // Create bone timelines map. { boneTimelines[timeline->bone->getName()].push_back(timeline); } for (const auto bone : _armature->getBones()) { const auto& timelineName = bone->getName(); if (!containsBoneMask(timelineName)) { continue; } const auto timelineDatas = _animationData->getBoneTimelines(timelineName); const auto iterator = boneTimelines.find(timelineName); if (iterator != boneTimelines.end()) // Remove bone timeline from map. { boneTimelines.erase(iterator); } else // Create new bone timeline. { const auto bonePose = _bonePoses.find(timelineName) != _bonePoses.end() ? _bonePoses[timelineName] : (_bonePoses[timelineName] = BaseObject::borrowObject()); if (timelineDatas != nullptr) { for (const auto timelineData : *timelineDatas) { switch (timelineData->type) { case TimelineType::BoneAll: { const auto timeline = BaseObject::borrowObject(); timeline->bone = bone; timeline->bonePose = bonePose; timeline->init(_armature, this, timelineData); _boneTimelines.push_back(timeline); break; } case TimelineType::BoneTranslate: { const auto timeline = BaseObject::borrowObject(); timeline->bone = bone; timeline->bonePose = bonePose; timeline->init(_armature, this, timelineData); _boneTimelines.push_back(timeline); break; } case TimelineType::BoneRotate: { const auto timeline = BaseObject::borrowObject(); timeline->bone = bone; timeline->bonePose = bonePose; timeline->init(_armature, this, timelineData); _boneTimelines.push_back(timeline); break; } case TimelineType::BoneScale: { const auto timeline = BaseObject::borrowObject(); timeline->bone = bone; timeline->bonePose = bonePose; timeline->init(_armature, this, timelineData); _boneTimelines.push_back(timeline); break; } default: break; } } } else if (resetToPose) // Pose timeline. { const auto timeline = BaseObject::borrowObject(); timeline->bone = bone; timeline->bonePose = bonePose; timeline->init(_armature, this, nullptr); _boneTimelines.push_back(timeline); _poseTimelines.push_back(std::make_pair(timeline, BaseTimelineType::Bone)); } } } for (const auto& pair : boneTimelines) // Remove bone timelines. { for (const auto timeline : pair.second) { _boneTimelines.erase(std::find(_boneTimelines.begin(), _boneTimelines.end(), timeline)); timeline->returnToPool(); } } } { // Update slot timelines. hlookup::string_map> slotTimelines; std::vector ffdFlags; for (const auto timeline : _slotTimelines) // Create slot timelines map. { slotTimelines[timeline->slot->getName()].push_back(timeline); } for (const auto slot : _armature->getSlots()) { const auto& boneName = slot->getParent()->getName(); if (!containsBoneMask(boneName)) { continue; } const auto& timelineName = slot->getName(); const auto timelineDatas = _animationData->getSlotTimelines(timelineName); const auto iterator = slotTimelines.find(timelineName); if (iterator != slotTimelines.end()) // Remove slot timeline from map. { slotTimelines.erase(iterator); } else // Create new slot timeline. { auto displayIndexFlag = false; auto colorFlag = false; ffdFlags.clear(); if (timelineDatas != nullptr) { for (const auto timelineData : *timelineDatas) { switch (timelineData->type) { case TimelineType::SlotDisplay: { const auto timeline = BaseObject::borrowObject(); timeline->slot = slot; timeline->init(_armature, this, timelineData); _slotTimelines.push_back(timeline); displayIndexFlag = true; break; } case TimelineType::SlotColor: { const auto timeline = BaseObject::borrowObject(); timeline->slot = slot; timeline->init(_armature, this, timelineData); _slotTimelines.push_back(timeline); colorFlag = true; break; } case TimelineType::SlotDeform: { const auto timeline = BaseObject::borrowObject(); timeline->slot = slot; timeline->init(_armature, this, timelineData); _slotTimelines.push_back(timeline); ffdFlags.push_back(timeline->vertexOffset); break; } default: break; } } } if (resetToPose) // Pose timeline. { if (!displayIndexFlag) { const auto timeline = BaseObject::borrowObject(); timeline->slot = slot; timeline->init(_armature, this, nullptr); _slotTimelines.push_back(timeline); _poseTimelines.push_back(std::make_pair(timeline, BaseTimelineType::Slot)); } if (!colorFlag) { const auto timeline = BaseObject::borrowObject(); timeline->slot = slot; timeline->init(_armature, this, nullptr); _slotTimelines.push_back(timeline); _poseTimelines.push_back(std::make_pair(timeline, BaseTimelineType::Slot)); } if (slot->getRawDisplayDatas() != nullptr) { for (const auto displayData : *(slot->getRawDisplayDatas())) { if (displayData != nullptr && displayData->type == DisplayType::Mesh) { const auto meshOffset = static_cast(displayData)->vertices.offset; if (std::find(ffdFlags.cbegin(), ffdFlags.cend(), meshOffset) == ffdFlags.cend()) { const auto timeline = BaseObject::borrowObject(); timeline->vertexOffset = meshOffset; timeline->slot = slot; timeline->init(_armature, this, nullptr); _slotTimelines.push_back(timeline); _poseTimelines.push_back(std::make_pair(timeline, BaseTimelineType::Slot)); } } } } } } } for (const auto& pair : slotTimelines) // Remove slot timelines. { for (const auto timeline : pair.second) { _slotTimelines.erase(std::find(_slotTimelines.begin(), _slotTimelines.end(), timeline)); timeline->returnToPool(); } } } } void AnimationState::_advanceFadeTime(float passedTime) { const auto isFadeOut = _fadeState > 0; if (_subFadeState < 0) { _subFadeState = 0; const auto eventType = isFadeOut ? EventObject::FADE_OUT : EventObject::FADE_IN; if (_armature->getProxy()->hasDBEventListener(eventType)) { const auto eventObject = BaseObject::borrowObject(); eventObject->type = eventType; eventObject->armature = _armature; eventObject->animationState = this; _armature->_dragonBones->bufferEvent(eventObject); } } if (passedTime < 0.0f) { passedTime = -passedTime; } _fadeTime += passedTime; if (_fadeTime >= fadeTotalTime) { _subFadeState = 1; _fadeProgress = isFadeOut ? 0.0f : 1.0f; } else if (_fadeTime > 0.0f) { _fadeProgress = isFadeOut ? (1.0f - _fadeTime / fadeTotalTime) : (_fadeTime / fadeTotalTime); } else { _fadeProgress = isFadeOut ? 1.0f : 0.0f; } if (_subFadeState > 0) { if (!isFadeOut) { _playheadState |= 1; // x1 _fadeState = 0; } const auto eventType = isFadeOut ? EventObject::FADE_OUT_COMPLETE : EventObject::FADE_IN_COMPLETE; if (_armature->getProxy()->hasDBEventListener(eventType)) { const auto eventObject = BaseObject::borrowObject(); eventObject->type = eventType; eventObject->armature = _armature; eventObject->animationState = this; _armature->_dragonBones->bufferEvent(eventObject); } } } void AnimationState::init(Armature* parmature, AnimationData* panimationData, AnimationConfig* animationConfig) { if (_armature != nullptr) { return; } _armature = parmature; _animationData = panimationData; // resetToPose = animationConfig->resetToPose; additiveBlending = animationConfig->additiveBlending; displayControl = animationConfig->displayControl; actionEnabled = animationConfig->actionEnabled; layer = animationConfig->layer; playTimes = animationConfig->playTimes; timeScale = animationConfig->timeScale; fadeTotalTime = animationConfig->fadeInTime; autoFadeOutTime = animationConfig->autoFadeOutTime; weight = animationConfig->weight; name = !animationConfig->name.empty() ? animationConfig->name : animationConfig->animation; group = animationConfig->group; if (animationConfig->pauseFadeIn) { _playheadState = 2; // 10 } else { _playheadState = 3; // 11 } if (animationConfig->duration < 0.0f) { _position = 0.0f; _duration = _animationData->duration; if (animationConfig->position != 0.0f) { if (timeScale >= 0.0f) { _time = animationConfig->position; } else { _time = animationConfig->position - _duration; } } else { _time = 0.0f; } } else { _position = animationConfig->position; _duration = animationConfig->duration; _time = 0.0f; } if (timeScale < 0.0f && _time == 0.0f) { _time = -0.000001f; // Can not cross last frame event. } if (fadeTotalTime <= 0.0f) { _fadeProgress = 0.999999f; } if (!animationConfig->boneMask.empty()) { _boneMask.resize(animationConfig->boneMask.size()); for (std::size_t i = 0, l = _boneMask.size(); i < l; ++i) { _boneMask[i] = animationConfig->boneMask[i]; } } _actionTimeline = BaseObject::borrowObject(); _actionTimeline->init(_armature, this, _animationData->actionTimeline); // _actionTimeline->currentTime = _time; if (_actionTimeline->currentTime < 0.0f) { _actionTimeline->currentTime = _duration - _actionTimeline->currentTime; } if (_animationData->zOrderTimeline != nullptr) { _zOrderTimeline = BaseObject::borrowObject(); _zOrderTimeline->init(_armature, this, _animationData->zOrderTimeline); } } void AnimationState::advanceTime(float passedTime, float cacheFrameRate) { // Update fade time. if (_fadeState != 0 || _subFadeState != 0) { _advanceFadeTime(passedTime); } // Update time. if (_playheadState == 3) // 11 { if (timeScale != 1.0f) { passedTime *= timeScale; } _time += passedTime; } if (_timelineDirty != 0) { if (_timelineDirty == 2) { _updateTimelines(); } _timelineDirty = 0; _updateBoneAndSlotTimelines(); } if (weight == 0.0f) { return; } const auto isCacheEnabled = _fadeState == 0 && cacheFrameRate > 0.0f; auto isUpdateTimeline = true; auto isUpdateBoneTimeline = true; auto time = _time; _weightResult = weight * _fadeProgress; if (_actionTimeline->playState <= 0) { _actionTimeline->update(time); // Update main timeline. } if (isCacheEnabled) // Cache time internval. { const auto internval = cacheFrameRate * 2.0f; _actionTimeline->currentTime = (unsigned)(_actionTimeline->currentTime * internval) / internval; } if (_zOrderTimeline != nullptr && _zOrderTimeline->playState <= 0) // Update zOrder timeline. { _zOrderTimeline->update(time); } if (isCacheEnabled) // Update cache. { const auto cacheFrameIndex = (unsigned)(_actionTimeline->currentTime * cacheFrameRate); // uint if ((unsigned)_armature->_cacheFrameIndex == cacheFrameIndex) // Same cache. { isUpdateTimeline = false; isUpdateBoneTimeline = false; } else { _armature->_cacheFrameIndex = cacheFrameIndex; if (_animationData->cachedFrames[cacheFrameIndex]) // Cached. { isUpdateBoneTimeline = false; } else // Cache. { _animationData->cachedFrames[cacheFrameIndex] = true; } } } if (isUpdateTimeline) { if (isUpdateBoneTimeline) // Update bone timelines. { for (std::size_t i = 0, l = _boneTimelines.size(); i < l; ++i) { const auto timeline = _boneTimelines[i]; if (timeline->playState <= 0) { timeline->update(time); } if (i == l - 1 || timeline->bone != _boneTimelines[i + 1]->bone) { const auto state = timeline->bone->_blendState.update(_weightResult, layer); if (state != 0) { timeline->blend(state); } } } } if (displayControl) { for (std::size_t i = 0, l = _slotTimelines.size(); i < l; ++i) { const auto timeline = _slotTimelines[i]; const auto& displayController = timeline->slot->displayController; if (displayController.empty() || displayController == name || displayController == group) { if (timeline->playState <= 0) { timeline->update(time); } } } } for (std::size_t i = 0, l = _constraintTimelines.size(); i < l; ++i) { const auto timeline = _constraintTimelines[i]; if (timeline->playState <= 0) { timeline->update(time); } } } if (_fadeState == 0) { if (_subFadeState > 0) { _subFadeState = 0; if (!_poseTimelines.empty()) { for (const auto& pair : _poseTimelines) { const auto timeline = pair.first; if (pair.second == BaseTimelineType::Bone) { _boneTimelines.erase(std::find(_boneTimelines.begin(), _boneTimelines.end(), timeline)); } else if (pair.second == BaseTimelineType::Slot) { _slotTimelines.erase(std::find(_slotTimelines.begin(), _slotTimelines.end(), timeline)); } else if (pair.second == BaseTimelineType::Constraint) { _constraintTimelines.erase( std::find(_constraintTimelines.begin(), _constraintTimelines.end(), timeline)); } timeline->returnToPool(); } _poseTimelines.clear(); } } if (_actionTimeline->playState > 0) { if (autoFadeOutTime >= 0.0f) // Auto fade out. { fadeOut(autoFadeOutTime); } } } } void AnimationState::play() { _playheadState = 3; // 11 } void AnimationState::stop() { _playheadState &= 1; // 0x } void AnimationState::fadeOut(float fadeOutTime, bool pausePlayhead) { if (fadeOutTime < 0.0f) { fadeOutTime = 0.0f; } if (pausePlayhead) { _playheadState &= 2; // x0 } if (_fadeState > 0) { if (fadeOutTime > fadeTotalTime - _fadeTime) // If the animation is already in fade out, the new fade out will be ignored. { return; } } else { _fadeState = 1; _subFadeState = -1; if (fadeOutTime <= 0.0f || _fadeProgress <= 0.0f) { _fadeProgress = 0.000001f; // Modify fade progress to different value. } for (const auto timeline : _boneTimelines) { timeline->fadeOut(); } for (const auto timeline : _slotTimelines) { timeline->fadeOut(); } } displayControl = false; // fadeTotalTime = _fadeProgress > 0.000001f ? fadeOutTime / _fadeProgress : 0.0f; _fadeTime = fadeTotalTime * (1.0f - _fadeProgress); } bool AnimationState::containsBoneMask(std::string_view boneName) const { return _boneMask.empty() || std::find(_boneMask.cbegin(), _boneMask.cend(), boneName) != _boneMask.cend(); } void AnimationState::addBoneMask(std::string_view boneName, bool recursive) { const auto currentBone = _armature->getBone(boneName); if (currentBone == nullptr) { return; } if (std::find(_boneMask.cbegin(), _boneMask.cend(), boneName) == _boneMask.cend()) { _boneMask.push_back(std::string{boneName}); } if (recursive) // Add recursive mixing. { for (const auto bone : _armature->getBones()) { if (std::find(_boneMask.cbegin(), _boneMask.cend(), bone->getName()) == _boneMask.cend() && currentBone->contains(bone)) { _boneMask.push_back(std::string{bone->getName()}); } } } _timelineDirty = 1; } void AnimationState::removeBoneMask(std::string_view boneName, bool recursive) { { auto iterator = std::find(_boneMask.begin(), _boneMask.end(), boneName); if (iterator != _boneMask.cend()) // Remove mixing. { _boneMask.erase(iterator); } } if (recursive) { const auto currentBone = _armature->getBone(boneName); if (currentBone != nullptr) { const auto& bones = _armature->getBones(); if (!_boneMask.empty()) // Remove recursive mixing. { for (const auto bone : bones) { auto iterator = std::find(_boneMask.begin(), _boneMask.end(), bone->getName()); if (iterator != _boneMask.end() && currentBone->contains(bone)) { _boneMask.erase(iterator); } } } else // Add unrecursive mixing. { for (const auto bone : bones) { if (bone == currentBone) { continue; } if (!currentBone->contains(bone)) { _boneMask.push_back(std::string{bone->getName()}); } } } } } _timelineDirty = 1; } void AnimationState::removeAllBoneMask() { _boneMask.clear(); _timelineDirty = 1; } bool AnimationState::isPlaying() const { return (_playheadState & 2) != 0 && _actionTimeline->playState <= 0; } bool AnimationState::isCompleted() const { return _actionTimeline->playState > 0; } unsigned AnimationState::getCurrentPlayTimes() const { return _actionTimeline->currentPlayTimes; } float AnimationState::getCurrentTime() const { return _actionTimeline->currentTime; } void AnimationState::setCurrentTime(float value) { const auto currentPlayTimes = _actionTimeline->currentPlayTimes - (_actionTimeline->playState > 0 ? 1 : 0); if (value < 0.0f || _duration < value) { value = fmod(value, _duration) + currentPlayTimes * _duration; if (value < 0.0f) { value += _duration; } } if (playTimes > 0 && (unsigned)currentPlayTimes == playTimes - 1 && value == _duration) { value = _duration - 0.000001f; } if (_time == value) { return; } _time = value; _actionTimeline->setCurrentTime(_time); if (_zOrderTimeline != nullptr) { _zOrderTimeline->playState = -1; } for (const auto timeline : _boneTimelines) { timeline->playState = -1; } for (const auto timeline : _slotTimelines) { timeline->playState = -1; } } void BonePose::_onClear() { current.identity(); delta.identity(); result.identity(); } int BlendState::update(float weight, int p_layer) { if (dirty) { if (leftWeight > 0.0f) { if (layer != p_layer) { if (layerWeight >= leftWeight) { leftWeight = 0.0f; return 0; } else { layer = p_layer; leftWeight -= layerWeight; layerWeight = 0.0f; } } } else { return 0; } weight *= leftWeight; layerWeight += weight; blendWeight = weight; return 2; } dirty = true; layer = p_layer; layerWeight = weight; leftWeight = 1.0f; blendWeight = weight; return 1; } void BlendState::clear() { dirty = false; layer = 0; leftWeight = 0.0f; layerWeight = 0.0f; blendWeight = 0.0f; } DRAGONBONES_NAMESPACE_END