diff --git a/interface/resources/avatar/animations/teleport.fbx b/interface/resources/avatar/animations/teleport.fbx new file mode 100644 index 0000000000..99c950ced6 Binary files /dev/null and b/interface/resources/avatar/animations/teleport.fbx differ diff --git a/interface/resources/avatar/network-animation.json b/interface/resources/avatar/network-animation.json index 0ba4ea465c..d1046d2c6b 100644 --- a/interface/resources/avatar/network-animation.json +++ b/interface/resources/avatar/network-animation.json @@ -1,70 +1,138 @@ { "version": "1.1", "root": { - "id": "userAnimStateMachine", + "id": "networkAnimStateMachine", "type": "stateMachine", "data": { - "currentState": "idleAnim", + "currentState": "transitAnimStateMachine", "states": [ { - "id": "idleAnim", + "id": "transitAnimStateMachine", "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "postTransitAnim", "state": "postTransitAnim" }, - { "var": "preTransitAnim", "state": "preTransitAnim" } + { "var": "userNetworkAnimA", "state": "userNetworkAnimA" }, + { "var": "userNetworkAnimB", "state": "userNetworkAnimB" } ] }, { - "id": "preTransitAnim", + "id": "userNetworkAnimA", "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "idleAnim", "state": "idleAnim" }, - { "var": "transitAnim", "state": "transitAnim" } + { "var": "transitAnimStateMachine", "state": "transitAnimStateMachine" }, + { "var": "userNetworkAnimB", "state": "userNetworkAnimB" } ] }, { - "id": "transitAnim", + "id": "userNetworkAnimB", "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "preTransitAnim", "state": "preTransitAnim" }, - { "var": "postTransitAnim", "state": "postTransitAnim" } - ] - }, - { - "id": "postTransitAnim", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "transitAnim", "state": "transitAnim" }, - { "var": "idleAnim", "state": "idleAnim" } - ] - }, - { - "id": "userAnimA", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "idleAnim", "state": "idleAnim" }, - { "var": "userAnimB", "state": "userAnimB" } - ] - }, - { - "id": "userAnimB", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "idleAnim", "state": "idleAnim" }, - { "var": "userAnimA", "state": "userAnimA" } + { "var": "transitAnimStateMachine", "state": "transitAnimStateMachine" }, + { "var": "userNetworkAnimA", "state": "userNetworkAnimA" } ] } ] }, "children": [ { - "id": "idleAnim", + "id": "transitAnimStateMachine", + "type": "stateMachine", + "data": { + "currentState": "idleAnim", + "states": [ + { + "id": "idleAnim", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "postTransitAnim", "state": "postTransitAnim" }, + { "var": "preTransitAnim", "state": "preTransitAnim" } + ] + }, + { + "id": "preTransitAnim", + "interpTarget": 4, + "interpDuration": 4, + "transitions": [ + { "var": "idleAnim", "state": "idleAnim" }, + { "var": "transitAnim", "state": "transitAnim" } + ] + }, + { + "id": "transitAnim", + "interpTarget": 2, + "interpDuration": 2, + "transitions": [ + { "var": "preTransitAnim", "state": "preTransitAnim" }, + { "var": "postTransitAnim", "state": "postTransitAnim" } + ] + }, + { + "id": "postTransitAnim", + "interpTarget": 4, + "interpDuration": 4, + "transitions": [ + { "var": "transitAnim", "state": "transitAnim" }, + { "var": "idleAnim", "state": "idleAnim" } + ] + } + ] + }, + "children" : [ + { + "id": "idleAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "preTransitAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/teleport.fbx", + "startFrame": 0.0, + "endFrame": 10.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "transitAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/teleport.fbx", + "startFrame": 11.0, + "endFrame": 18.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "postTransitAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/teleport.fbx", + "startFrame": 19.0, + "endFrame": 44.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + } + ] + }, + { + "id": "userNetworkAnimA", "type": "clip", "data": { "url": "qrc:///avatar/animations/idle.fbx", @@ -76,55 +144,7 @@ "children": [] }, { - "id": "preTransitAnim", - "type": "clip", - "data": { - "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx", - "startFrame": 0.0, - "endFrame": 10.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "transitAnim", - "type": "clip", - "data": { - "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx", - "startFrame": 11.0, - "endFrame": 11.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "postTransitAnim", - "type": "clip", - "data": { - "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx", - "startFrame": 22.0, - "endFrame": 49.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "userAnimA", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/idle.fbx", - "startFrame": 0.0, - "endFrame": 90.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "userAnimB", + "id": "userNetworkAnimB", "type": "clip", "data": { "url": "qrc:///avatar/animations/idle.fbx", diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index aa57336a3c..906e3a01fa 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -127,16 +127,16 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { switch (status) { case AvatarTransit::Status::STARTED: - _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("preTransitAnim"); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("preTransitAnim"); break; case AvatarTransit::Status::START_TRANSIT: - _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("transitAnim"); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("transitAnim"); break; case AvatarTransit::Status::END_TRANSIT: - _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("postTransitAnim"); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("postTransitAnim"); break; case AvatarTransit::Status::ENDED: - _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("idleAnim"); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("idleAnim"); break; case AvatarTransit::Status::PRE_TRANSIT: break; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c2f909dd24..87f03f8bac 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -135,30 +135,6 @@ void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firs _animVars.set("userAnimB", clipNodeEnum == UserAnimState::B); } -void Rig::triggerNetworkAnimation(const QString& animName) { - _networkVars.set("idleAnim", false); - _networkVars.set("preTransitAnim", false); - _networkVars.set("transitAnim", false); - _networkVars.set("postTransitAnim", false); - _sendNetworkNode = true; - - if (animName == "idleAnim") { - _networkVars.set("idleAnim", true); - _networkAnimState.clipNodeEnum = NetworkAnimState::Idle; - _sendNetworkNode = false; - } else if (animName == "preTransitAnim") { - _networkVars.set("preTransitAnim", true); - _networkAnimState.clipNodeEnum = NetworkAnimState::PreTransit; - } else if (animName == "transitAnim") { - _networkVars.set("transitAnim", true); - _networkAnimState.clipNodeEnum = NetworkAnimState::Transit; - } else if (animName == "postTransitAnim") { - _networkVars.set("postTransitAnim", true); - _networkAnimState.clipNodeEnum = NetworkAnimState::PostTransit; - } - -} - void Rig::restoreAnimation() { if (_userAnimState.clipNodeEnum != UserAnimState::None) { _userAnimState.clipNodeEnum = UserAnimState::None; @@ -170,13 +146,87 @@ void Rig::restoreAnimation() { } } -void Rig::restoreNetworkAnimation() { - if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) { - _networkAnimState.clipNodeEnum = NetworkAnimState::Idle; +void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { + + NetworkAnimState::ClipNodeEnum clipNodeEnum = NetworkAnimState::None; + if (_networkAnimState.clipNodeEnum == NetworkAnimState::None || _networkAnimState.clipNodeEnum == NetworkAnimState::B) { + clipNodeEnum = NetworkAnimState::A; + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::A) { + clipNodeEnum = NetworkAnimState::B; + } + + if (_networkNode) { + // find an unused AnimClip clipNode + std::shared_ptr clip; + if (clipNodeEnum == NetworkAnimState::A) { + clip = std::dynamic_pointer_cast(_networkNode->findByName("userNetworkAnimA")); + } else { + clip = std::dynamic_pointer_cast(_networkNode->findByName("userNetworkAnimB")); + } + if (clip) { + // set parameters + clip->setLoopFlag(loop); + clip->setStartFrame(firstFrame); + clip->setEndFrame(lastFrame); + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; + clip->setTimeScale(timeScale); + clip->loadURL(url); + } + } + + // store current user anim state. + _networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; + + // notify the userAnimStateMachine the desired state. + _networkVars.set("transitAnimStateMachine", false); + _networkVars.set("userNetworkAnimA", clipNodeEnum == NetworkAnimState::A); + _networkVars.set("userNetworkAnimB", clipNodeEnum == NetworkAnimState::B); + if (!_computeNetworkAnimation) { + _networkAnimState.blendTime = 0.0f; + _computeNetworkAnimation = true; + } +} + +void Rig::triggerNetworkRole(const QString& role) { + _networkVars.set("transitAnimStateMachine", false); + _networkVars.set("idleAnim", false); + _networkVars.set("userNetworkAnimA", false); + _networkVars.set("userNetworkAnimB", false); + _networkVars.set("preTransitAnim", false); + _networkVars.set("preTransitAnim", false); + _networkVars.set("transitAnim", false); + _networkVars.set("postTransitAnim", false); + _computeNetworkAnimation = true; + if (role == "idleAnim") { _networkVars.set("idleAnim", true); - _networkVars.set("preTransitAnim", false); - _networkVars.set("transitAnim", false); - _networkVars.set("postTransitAnim", false); + _networkAnimState.clipNodeEnum = NetworkAnimState::None; + _computeNetworkAnimation = false; + _networkAnimState.blendTime = 0.0f; + } else if (role == "preTransitAnim") { + _networkVars.set("preTransitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::PreTransit; + _networkAnimState.blendTime = 0.0f; + } else if (role == "transitAnim") { + _networkVars.set("transitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::Transit; + } else if (role == "postTransitAnim") { + _networkVars.set("postTransitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::PostTransit; + } + +} + +void Rig::restoreNetworkAnimation() { + if (_networkAnimState.clipNodeEnum != NetworkAnimState::None) { + if (_computeNetworkAnimation) { + _networkAnimState.blendTime = 0.0f; + _computeNetworkAnimation = false; + } + _networkAnimState.clipNodeEnum = NetworkAnimState::None; + _networkVars.set("transitAnimStateMachine", true); + _networkVars.set("userNetworkAnimA", false); + _networkVars.set("userNetworkAnimB", false); } } @@ -1131,24 +1181,19 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons AnimVariantMap networkTriggersOut; _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); if (_networkNode) { - _networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut); - const float NETWORK_ANIMATION_BLEND_FRAMES = 6.0f; + // Manually blending networkPoseSet with internalPoseSet. float alpha = 1.0f; - std::shared_ptr clip; - if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) { - clip = std::dynamic_pointer_cast(_networkNode->findByName("preTransitAnim")); - if (clip) { - alpha = (clip->getFrame() - clip->getStartFrame()) / NETWORK_ANIMATION_BLEND_FRAMES; - } - } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) { - clip = std::dynamic_pointer_cast(_networkNode->findByName("postTransitAnim")); - if (clip) { - alpha = (clip->getEndFrame() - clip->getFrame()) / NETWORK_ANIMATION_BLEND_FRAMES; - } - } + const float FRAMES_PER_SECOND = 30.0f; + const float TOTAL_BLEND_FRAMES = 6.0f; + const float TOTAL_BLEND_TIME = TOTAL_BLEND_FRAMES / FRAMES_PER_SECOND; + _sendNetworkNode = _computeNetworkAnimation || _networkAnimState.blendTime < TOTAL_BLEND_TIME; if (_sendNetworkNode) { + _networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut); + _networkAnimState.blendTime += deltaTime; + alpha = _computeNetworkAnimation ? (_networkAnimState.blendTime / TOTAL_BLEND_TIME) : (1.0f - (_networkAnimState.blendTime / TOTAL_BLEND_TIME)); + alpha = glm::clamp(alpha, 0.0f, 1.0f); for (size_t i = 0; i < _networkPoseSet._relativePoses.size(); i++) { - _networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], (alpha > 1.0f ? 1.0f : alpha)); + _networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], alpha); } } } @@ -1840,16 +1885,16 @@ void Rig::initAnimGraph(const QUrl& url) { return; } _networkNode->setSkeleton(sharedSkeletonPtr); - if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) { + if (_networkAnimState.clipNodeEnum != NetworkAnimState::None) { // restore the user animation we had before reset. NetworkAnimState origState = _networkAnimState; - _networkAnimState = { NetworkAnimState::Idle, "", 30.0f, false, 0.0f, 0.0f }; + _networkAnimState = { NetworkAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) { - triggerNetworkAnimation("preTransitAnim"); + triggerNetworkRole("preTransitAnim"); } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::Transit) { - triggerNetworkAnimation("transitAnim"); + triggerNetworkRole("transitAnim"); } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) { - triggerNetworkAnimation("postTransitAnim"); + triggerNetworkRole("postTransitAnim"); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 345f335f88..41c25a3c3e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -114,8 +114,10 @@ public: void destroyAnimGraph(); void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - void triggerNetworkAnimation(const QString& animName); void restoreAnimation(); + + void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void triggerNetworkRole(const QString& role); void restoreNetworkAnimation(); QStringList getAnimationRoles() const; @@ -327,12 +329,14 @@ protected: struct NetworkAnimState { enum ClipNodeEnum { - Idle = 0, + None = 0, PreTransit, Transit, - PostTransit + PostTransit, + A, + B }; - NetworkAnimState() : clipNodeEnum(NetworkAnimState::Idle) {} + NetworkAnimState() : clipNodeEnum(NetworkAnimState::None) {} NetworkAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) : clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {} @@ -342,6 +346,7 @@ protected: bool loop; float firstFrame; float lastFrame; + float blendTime; }; struct UserAnimState { @@ -411,6 +416,7 @@ protected: int _rigId; bool _headEnabled { false }; + bool _computeNetworkAnimation { false }; bool _sendNetworkNode { false }; AnimContext _lastContext; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 48593de212..36c6ed6c50 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -341,10 +341,10 @@ const glm::vec3 START_LOCATION(6270, 211, 6000); // Avatar Transit Constants const float AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE = 1.0f; const float AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE = 30.0f; -const int AVATAR_TRANSIT_FRAME_COUNT = 11; +const int AVATAR_TRANSIT_FRAME_COUNT = 5; const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; const float AVATAR_TRANSIT_ABORT_DISTANCE = 0.1f; -const bool AVATAR_TRANSIT_DISTANCE_BASED = true; +const bool AVATAR_TRANSIT_DISTANCE_BASED = false; const float AVATAR_TRANSIT_FRAMES_PER_SECOND = 30.0f; const float AVATAR_PRE_TRANSIT_FRAME_COUNT = 10.0f; const float AVATAR_POST_TRANSIT_FRAME_COUNT = 27.0f;