From 3716800b98911235074833134eed22fd8939544f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 14 Oct 2015 18:16:13 -0700 Subject: [PATCH 01/44] Moved AnimPose class into it's own set of files --- libraries/animation/src/AnimPose.cpp | 56 ++++++++++++++++++++++++ libraries/animation/src/AnimPose.h | 47 ++++++++++++++++++++ libraries/animation/src/AnimSkeleton.cpp | 44 ------------------- libraries/animation/src/AnimSkeleton.h | 28 +----------- 4 files changed, 104 insertions(+), 71 deletions(-) create mode 100644 libraries/animation/src/AnimPose.cpp create mode 100644 libraries/animation/src/AnimPose.h diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp new file mode 100644 index 0000000000..bae34509ae --- /dev/null +++ b/libraries/animation/src/AnimPose.cpp @@ -0,0 +1,56 @@ +// +// AnimPose.cpp +// +// Created by Anthony J. Thibault on 10/14/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AnimPose.h" +#include "GLMHelpers.h" + +const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), + glm::quat(), + glm::vec3(0.0f)); + +AnimPose::AnimPose(const glm::mat4& mat) { + scale = extractScale(mat); + rot = glm::normalize(glm::quat_cast(mat)); + trans = extractTranslation(mat); +} + +glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { + return trans + (rot * (scale * rhs)); +} + +glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { + return *this * rhs; +} + +// really slow +glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const { + glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); + glm::mat3 mat(xAxis, yAxis, zAxis); + glm::mat3 transInvMat = glm::inverse(glm::transpose(mat)); + return transInvMat * rhs; +} + +AnimPose AnimPose::operator*(const AnimPose& rhs) const { + return AnimPose(static_cast(*this) * static_cast(rhs)); +} + +AnimPose AnimPose::inverse() const { + return AnimPose(glm::inverse(static_cast(*this))); +} + +AnimPose::operator glm::mat4() const { + glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); + return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), + glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); +} diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h new file mode 100644 index 0000000000..852d84ec1b --- /dev/null +++ b/libraries/animation/src/AnimPose.h @@ -0,0 +1,47 @@ +// +// AnimPose.h +// +// Created by Anthony J. Thibault on 10/14/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AnimPose +#define hifi_AnimPose + +#include +#include +#include +#include +#include + +struct AnimPose { + AnimPose() {} + explicit AnimPose(const glm::mat4& mat); + AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} + static const AnimPose identity; + + glm::vec3 xformPoint(const glm::vec3& rhs) const; + glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow + + glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint + AnimPose operator*(const AnimPose& rhs) const; + + AnimPose inverse() const; + operator glm::mat4() const; + + glm::vec3 scale; + glm::quat rot; + glm::vec3 trans; +}; + +inline QDebug operator<<(QDebug debug, const AnimPose& pose) { + debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")"; + return debug; +} + +using AnimPoseVec = std::vector; + +#endif diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index e43a55150c..0db7473c9c 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,50 +16,6 @@ #include "AnimationLogging.h" -const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), - glm::quat(), - glm::vec3(0.0f)); - -AnimPose::AnimPose(const glm::mat4& mat) { - scale = extractScale(mat); - rot = glm::normalize(glm::quat_cast(mat)); - trans = extractTranslation(mat); -} - -glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { - return trans + (rot * (scale * rhs)); -} - -glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { - return *this * rhs; -} - -// really slow -glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const { - glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); - glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); - glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); - glm::mat3 mat(xAxis, yAxis, zAxis); - glm::mat3 transInvMat = glm::inverse(glm::transpose(mat)); - return transInvMat * rhs; -} - -AnimPose AnimPose::operator*(const AnimPose& rhs) const { - return AnimPose(static_cast(*this) * static_cast(rhs)); -} - -AnimPose AnimPose::inverse() const { - return AnimPose(glm::inverse(static_cast(*this))); -} - -AnimPose::operator glm::mat4() const { - glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); - glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); - glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); - return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), - glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); -} - AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // convert to std::vector of joints std::vector joints; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index d59719df73..9dda313528 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -16,33 +16,7 @@ #include #include - -struct AnimPose { - AnimPose() {} - explicit AnimPose(const glm::mat4& mat); - AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} - static const AnimPose identity; - - glm::vec3 xformPoint(const glm::vec3& rhs) const; - glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow - - glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint - AnimPose operator*(const AnimPose& rhs) const; - - AnimPose inverse() const; - operator glm::mat4() const; - - glm::vec3 scale; - glm::quat rot; - glm::vec3 trans; -}; - -inline QDebug operator<<(QDebug debug, const AnimPose& pose) { - debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")"; - return debug; -} - -using AnimPoseVec = std::vector; +#include "AnimPose.h" class AnimSkeleton { public: From 2b4788929f6cb7e8c5b5944e69415a0b9d73ac42 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 15 Oct 2015 17:57:06 -0700 Subject: [PATCH 02/44] AnimBlendLinear: Untested implementation of sync flag. Move accumulateTime into AnimUtil. --- .../defaultAvatar_full/avatar-animation.json | 2 + libraries/animation/src/AnimBlendLinear.cpp | 86 ++++++++++++++++--- libraries/animation/src/AnimBlendLinear.h | 15 +++- libraries/animation/src/AnimClip.cpp | 38 +------- libraries/animation/src/AnimClip.h | 4 +- libraries/animation/src/AnimNode.h | 4 +- libraries/animation/src/AnimNodeLoader.cpp | 3 +- libraries/animation/src/AnimUtil.cpp | 39 +++++++++ libraries/animation/src/AnimUtil.h | 6 +- 9 files changed, 141 insertions(+), 56 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 682e0be1bf..5b042a09b1 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -190,6 +190,7 @@ "type": "blendLinear", "data": { "alpha": 0.0, + "sync": false, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -339,6 +340,7 @@ "type": "blendLinear", "data": { "alpha": 0.0, + "sync": false, "alphaVar": "leftHandGrabBlend" }, "children": [ diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index bc95565f6f..1e65ba2b36 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -12,10 +12,12 @@ #include "GLMHelpers.h" #include "AnimationLogging.h" #include "AnimUtil.h" +#include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync) : AnimNode(AnimNode::Type::BlendLinear, id), - _alpha(alpha) { + _alpha(alpha), + _sync(sync) { } @@ -34,24 +36,19 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo } else if (_children.size() == 1) { _poses = _children[0]->evaluate(animVars, dt, triggersOut); } else { + float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); size_t prevPoseIndex = glm::floor(clampedAlpha); size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - if (prevPoseIndex == nextPoseIndex) { - // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); - } else { - // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); - if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { - _poses.resize(prevPoses.size()); - - ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); - } + float prevPoseDeltaTime = dt; + float nextPoseDeltaTime = dt; + if (_sync) { + setSyncFrameAndComputeDeltaTime(dt, prevPoseIndex, nextPoseIndex, &prevPoseDeltaTime, &nextPoseDeltaTime, triggersOut); } + + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevPoseDeltaTime, nextPoseDeltaTime); } return _poses; } @@ -60,3 +57,64 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { return _poses; } + +void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevPoseDeltaTime, float nextPoseDeltaTime) { + if (prevPoseIndex == nextPoseIndex) { + // this can happen if alpha is on an integer boundary + _poses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + } else { + // need to eval and blend between two children. + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextPoseDeltaTime, triggersOut); + + if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { + _poses.resize(prevPoses.size()); + + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } + } +} + +void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, + float* prevPoseDeltaTime, float* nextPoseDeltaTime, + Triggers& triggersOut) { + std::vector offsets(_children.size(), 0.0f); + std::vector timeScales(_children.size(), 1.0f); + + float lengthSum = 0.0f; + for (size_t i = 0; i < _children.size(); i++) { + // abort if we find a child that is NOT a clipNode. + if (_children[i]->getType() != AnimNode::Type::Clip) { + // TODO: FIXME: make sync this work for other node types. + *prevPoseDeltaTime = dt; + *nextPoseDeltaTime = dt; + return; + } + auto clipNode = std::dynamic_pointer_cast(_children[i]); + assert(clipNode); + if (clipNode) { + lengthSum += clipNode->getEndFrame() - clipNode->getStartFrame(); + } + } + + float averageLength = lengthSum / (float)_children.size(); + + auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); + float prevTimeScale = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) / averageLength; + float prevOffset = prevClipNode->getStartFrame(); + prevClipNode->setCurrentFrame(prevOffset + prevTimeScale / _syncFrame); + + auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); + float nextTimeScale = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) / averageLength; + float nextOffset = nextClipNode->getStartFrame(); + nextClipNode->setCurrentFrame(nextOffset + nextTimeScale / _syncFrame); + + const bool LOOP_FLAG = true; + _syncFrame = ::accumulateTime(0.0f, averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); + + *prevPoseDeltaTime = prevTimeScale; + *nextPoseDeltaTime = nextTimeScale; +} + diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 56acd5c2f7..d8b4f52fb6 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -22,12 +22,15 @@ // between 0 and n - 1. This alpha can be used to linearly interpolate between // the closest two children poses. This can be used to sweep through a series // of animation poses. +// +// The sync flag is used to synchronize between child animations of different lengths. +// Typically used to synchronize blending between walk and run cycles. class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha); + AnimBlendLinear(const QString& id, float alpha, bool sync); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; @@ -38,9 +41,19 @@ protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevPoseDeltaTime, float nextPoseDeltaTime); + void setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, + float* prevPoseDeltaTime, float* nextPoseDeltaTime, + Triggers& triggersOut); + AnimPoseVec _poses; float _alpha; + bool _sync; + float _syncFrame = 0.0f; + float _timeScale = 1.0f; // TODO: HOOK THIS UP TO AN ANIMVAR. QString _alphaVar; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 251cb0047a..97f46a1b68 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -35,7 +35,9 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, _endFrame = animVars.lookup(_endFrameVar, _endFrame); _timeScale = animVars.lookup(_timeScaleVar, _timeScale); _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); - _frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt, triggersOut); + float frame = animVars.lookup(_frameVar, _frame); + + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); // poll network anim to see if it's finished loading yet. if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { @@ -78,39 +80,7 @@ void AnimClip::setCurrentFrameInternal(float frame) { // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _frame = accumulateTime(frame * _timeScale, dt, triggers); -} - -float AnimClip::accumulateTime(float frame, float dt, Triggers& triggersOut) const { - const float startFrame = std::min(_startFrame, _endFrame); - if (startFrame == _endFrame) { - // when startFrame >= endFrame - frame = _endFrame; - } else if (_timeScale > 0.0f) { - // accumulate time, keeping track of loops and end of animation events. - const float FRAMES_PER_SECOND = 30.0f; - float framesRemaining = (dt * _timeScale) * FRAMES_PER_SECOND; - while (framesRemaining > 0.0f) { - float framesTillEnd = _endFrame - _frame; - if (framesRemaining >= framesTillEnd) { - if (_loopFlag) { - // anim loop - triggersOut.push_back(_id + "OnLoop"); - framesRemaining -= framesTillEnd; - frame = startFrame; - } else { - // anim end - triggersOut.push_back(_id + "OnDone"); - frame = _endFrame; - framesRemaining = 0.0f; - } - } else { - frame += framesRemaining; - framesRemaining = 0.0f; - } - } - } - return frame; + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame * _timeScale, dt, _loopFlag, _id, triggers); } void AnimClip::copyFromNetworkAnim() { diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 3a76870c98..b7cd2861fa 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,12 +36,14 @@ public: void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; } void setFrameVar(const QString& frameVar) { _frameVar = frameVar; } + float getStartFrame() const { return _startFrame; } + float getEndFrame() const { return _endFrame; } + protected: void loadURL(const QString& url); virtual void setCurrentFrameInternal(float frame) override; - float accumulateTime(float frame, float dt, Triggers& triggersOut) const; void copyFromNetworkAnim(); // for AnimDebugDraw rendering diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 9a21526409..315ee357dd 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -75,10 +75,10 @@ public: return evaluate(animVars, dt, triggersOut); } -protected: - void setCurrentFrame(float frame); +protected: + virtual void setCurrentFrameInternal(float frame) {} virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; } diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 147025f1cf..2acbeed734 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -221,10 +221,11 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); + READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); - auto node = std::make_shared(id, alpha); + auto node = std::make_shared(id, alpha, sync); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index 81b294e66c..c8efa85df9 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -11,6 +11,9 @@ #include "AnimUtil.h" #include "GLMHelpers.h" +// TODO: use restrict keyword +// TODO: excellent candidate for simd vectorization. + void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { for (size_t i = 0; i < numPoses; i++) { const AnimPose& aPose = a[i]; @@ -20,3 +23,39 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A result[i].trans = lerp(aPose.trans, bPose.trans, alpha); } } + +float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag, + const QString& id, AnimNode::Triggers& triggersOut) { + + float frame = currentFrame; + const float clampedStartFrame = std::min(startFrame, endFrame); + if (clampedStartFrame == endFrame) { + // when clampedStartFrame >= endFrame + frame = endFrame; + } else if (timeScale > 0.0f) { + // accumulate time, keeping track of loops and end of animation events. + const float FRAMES_PER_SECOND = 30.0f; + float framesRemaining = (dt * timeScale) * FRAMES_PER_SECOND; + while (framesRemaining > 0.0f) { + float framesTillEnd = endFrame - frame; + if (framesRemaining >= framesTillEnd) { + if (loopFlag) { + // anim loop + triggersOut.push_back(id + "OnLoop"); + framesRemaining -= framesTillEnd; + frame = clampedStartFrame; + } else { + // anim end + triggersOut.push_back(id + "OnDone"); + frame = endFrame; + framesRemaining = 0.0f; + } + } else { + frame += framesRemaining; + framesRemaining = 0.0f; + } + } + } + return frame; +} + diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 23c02b6183..6d394be882 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -13,12 +13,12 @@ #include "AnimNode.h" -// TODO: use restrict keyword -// TODO: excellent candidate for simd vectorization. - // this is where the magic happens void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); +float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag, + const QString& id, AnimNode::Triggers& triggersOut); + #endif From 9b9bd7fe269ce0eb22390ede7554fd3e70f1981e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 09:29:59 -0700 Subject: [PATCH 03/44] AnimNodeLoader: Fix for crash condition When a node with children had an error loading, it would lead to a nullptr dereference. --- libraries/animation/src/AnimNodeLoader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 2acbeed734..4222364629 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -160,6 +160,9 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr assert((int)type >= 0 && type < AnimNode::Type::NumTypes); auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl); + if (!node) { + return nullptr; + } auto childrenValue = jsonObj.value("children"); if (!childrenValue.isArray()) { From 8e7e94c50102e554efb5c7da532690d1bcfd79a9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 09:31:14 -0700 Subject: [PATCH 04/44] AnimTests: now compile and pass again. --- tests/animation/src/AnimTests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index a08288000e..200a6c0c3c 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -30,8 +30,8 @@ void AnimTests::cleanupTestCase() { } void AnimTests::testClipInternalState() { - std::string id = "my anim clip"; - std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + QString id = "my anim clip"; + QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 20.0f; float timeScale = 1.1f; @@ -55,8 +55,8 @@ static float framesToSec(float secs) { } void AnimTests::testClipEvaulate() { - std::string id = "myClipNode"; - std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + QString id = "myClipNode"; + QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; float timeScale = 1.0f; @@ -90,8 +90,8 @@ void AnimTests::testClipEvaulate() { } void AnimTests::testClipEvaulateWithVars() { - std::string id = "myClipNode"; - std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + QString id = "myClipNode"; + QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; float timeScale = 1.0f; @@ -126,9 +126,9 @@ void AnimTests::testClipEvaulateWithVars() { } void AnimTests::testLoader() { - auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json"); + auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/0c54500f480fd7314a5aeb147c45a8a707edcc2e/test.json"); // NOTE: This will warn about missing "test01.fbx", "test02.fbx", etc. if the resource loading code doesn't handle relative pathnames! - // However, the test will proceed. + // However, the test will proceed. AnimNodeLoader loader(url); const int timeout = 1000; From 073cec41c41c77546824f2fda5780c8a28c86df5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 12:11:46 -0700 Subject: [PATCH 05/44] AnimClip & accumulateTime smoother looping anims Looping animations should have an extra frame of interpolation between the start and end frames. --- libraries/animation/src/AnimClip.cpp | 11 ++++++----- libraries/animation/src/AnimUtil.cpp | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 97f46a1b68..613194164f 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -47,16 +47,17 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, } if (_anim.size()) { - int frameCount = _anim.size(); - int prevIndex = (int)glm::floor(_frame); - int nextIndex = (int)glm::ceil(_frame); - if (_loopFlag && nextIndex >= frameCount) { - nextIndex = 0; + int nextIndex; + if (_loopFlag && _frame >= _endFrame) { + nextIndex = (int)glm::ceil(_startFrame); + } else { + nextIndex = (int)glm::ceil(_frame); } // It can be quite possible for the user to set _startFrame and _endFrame to // values before or past valid ranges. We clamp the frames here. + int frameCount = _anim.size(); prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index c8efa85df9..e9e5ea95de 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -29,8 +29,7 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu float frame = currentFrame; const float clampedStartFrame = std::min(startFrame, endFrame); - if (clampedStartFrame == endFrame) { - // when clampedStartFrame >= endFrame + if (fabsf(clampedStartFrame - endFrame) < 1.0f) { frame = endFrame; } else if (timeScale > 0.0f) { // accumulate time, keeping track of loops and end of animation events. @@ -38,6 +37,10 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu float framesRemaining = (dt * timeScale) * FRAMES_PER_SECOND; while (framesRemaining > 0.0f) { float framesTillEnd = endFrame - frame; + // when looping, add one frame between start and end. + if (loopFlag) { + framesTillEnd += 1.0f; + } if (framesRemaining >= framesTillEnd) { if (loopFlag) { // anim loop From 15f3894001086b2ad856c242079f9d0f52887d96 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 12:13:31 -0700 Subject: [PATCH 06/44] AnimTests: added tests for accumulateTime --- tests/animation/src/AnimTests.cpp | 89 ++++++++++++++++++++++++++++++- tests/animation/src/AnimTests.h | 3 ++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 200a6c0c3c..1b5bb4739a 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -13,6 +13,7 @@ #include "AnimBlendLinear.h" #include "AnimationLogging.h" #include "AnimVariant.h" +#include "AnimUtil.h" #include <../QTestExtensions.h> @@ -73,8 +74,8 @@ void AnimTests::testClipEvaulate() { // does it loop? triggers.clear(); - clip.evaluate(vars, framesToSec(11.0f), triggers); - QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); + clip.evaluate(vars, framesToSec(12.0f), triggers); + QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); // Note: frame 3 and not 4, because extra frame between start and end. // did we receive a loop trigger? QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end()); @@ -238,3 +239,87 @@ void AnimTests::testVariant() { QVERIFY(m[1].z == -7.0f); QVERIFY(m[3].w == 16.0f); } + +void AnimTests::testAccumulateTime() { + + float startFrame = 0.0f; + float endFrame = 10.0f; + float timeScale = 1.0f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); + + startFrame = 5.0f; + endFrame = 15.0f; + timeScale = 1.0f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); + + startFrame = 0.0f; + endFrame = 10.0f; + timeScale = 0.5f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); + + startFrame = 5.0f; + endFrame = 15.0f; + timeScale = 2.0f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); +} + +void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const { + + float dt = (1.0f / 30.0f) / timeScale; // sec + QString id = "testNode"; + AnimNode::Triggers triggers; + bool loopFlag = false; + + float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 1.0f); + QVERIFY(triggers.empty()); + triggers.clear(); + + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 2.0f); + QVERIFY(triggers.empty()); + triggers.clear(); + + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 3.0f); + QVERIFY(triggers.empty()); + triggers.clear(); + + // test onDone trigger and frame clamping. + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); + triggers.clear(); + + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); + triggers.clear(); + + // test onLoop trigger and looping frame logic + loopFlag = true; + + // should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames. + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame); + QVERIFY(triggers.empty()); + triggers.clear(); + + // now we should hit loop trigger + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); + triggers.clear(); + + // should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start. + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame + 0.5f); + QVERIFY(triggers.empty()); + triggers.clear(); + + // now we should hit loop trigger + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 0.5f); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); + triggers.clear(); +} diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index e667444657..7bd05369c7 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -15,6 +15,8 @@ class AnimTests : public QObject { Q_OBJECT +public: + void testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const; private slots: void initTestCase(); void cleanupTestCase(); @@ -23,6 +25,7 @@ private slots: void testClipEvaulateWithVars(); void testLoader(); void testVariant(); + void testAccumulateTime(); }; #endif // hifi_AnimTests_h From 0c36180e2fd8acefb4c49379ef88bcdf3eac3781 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 12:17:01 -0700 Subject: [PATCH 07/44] avatar-animation.json: Adjustment to sidestep endFrame, to prevent stalling at end --- .../resources/meshes/defaultAvatar_full/avatar-animation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 5b042a09b1..be1abbc820 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -579,7 +579,7 @@ "data": { "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", "startFrame": 0.0, - "endFrame": 31.0, + "endFrame": 30.0, "timeScale": 1.0, "loopFlag": true }, @@ -591,7 +591,7 @@ "data": { "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", "startFrame": 0.0, - "endFrame": 31.0, + "endFrame": 30.0, "timeScale": 1.0, "loopFlag": true }, From 11f2d29bf80ce439ac3c697d7cdaffceea03f7f5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 20 Oct 2015 10:36:37 -0700 Subject: [PATCH 08/44] AnimBlendLinear: bugfixes for sync flag added timeScale --- .../defaultAvatar_full/avatar-animation.json | 37 +++++++++-- libraries/animation/src/AnimBlendLinear.cpp | 61 +++++++++++-------- libraries/animation/src/AnimBlendLinear.h | 17 +++--- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimClip.h | 3 + libraries/animation/src/AnimNodeLoader.cpp | 8 ++- 6 files changed, 87 insertions(+), 41 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index be1abbc820..ea4ef63d16 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -191,6 +191,7 @@ "data": { "alpha": 0.0, "sync": false, + "timeScale": 1.0, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -341,6 +342,7 @@ "data": { "alpha": 0.0, "sync": false, + "timeScale": 1.0, "alphaVar": "leftHandGrabBlend" }, "children": [ @@ -525,16 +527,39 @@ }, { "id": "walkFwd", - "type": "clip", + "type": "blendLinear", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", - "startFrame": 0.0, - "endFrame": 35.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true, "timeScaleVar": "walkTimeScale" }, - "children": [] + "children": [ + { + "id": "walkFwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx", + "startFrame": 0.0, + "endFrame": 39.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkFwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", + "startFrame": 0.0, + "endFrame": 35.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "walkBwd", diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 1e65ba2b36..1f9026de9d 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,10 +14,11 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale) : AnimNode(AnimNode::Type::BlendLinear, id), _alpha(alpha), - _sync(sync) { + _sync(sync), + _timeScale(timeScale) { } @@ -28,6 +29,7 @@ AnimBlendLinear::~AnimBlendLinear() { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); + _timeScale = animVars.lookup(_timeScaleVar, _timeScale); if (_children.size() == 0) { for (auto&& pose : _poses) { @@ -42,13 +44,11 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - float prevPoseDeltaTime = dt; - float nextPoseDeltaTime = dt; if (_sync) { - setSyncFrameAndComputeDeltaTime(dt, prevPoseIndex, nextPoseIndex, &prevPoseDeltaTime, &nextPoseDeltaTime, triggersOut); + setSyncAndAccumulateTime(dt, prevPoseIndex, nextPoseIndex, triggersOut); } - evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevPoseDeltaTime, nextPoseDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; } @@ -59,15 +59,14 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { } void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, - size_t prevPoseIndex, size_t nextPoseIndex, - float prevPoseDeltaTime, float nextPoseDeltaTime) { + size_t prevPoseIndex, size_t nextPoseIndex, float dt) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextPoseDeltaTime, triggersOut); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); @@ -77,9 +76,7 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, T } } -void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, - float* prevPoseDeltaTime, float* nextPoseDeltaTime, - Triggers& triggersOut) { +void AnimBlendLinear::setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut) { std::vector offsets(_children.size(), 0.0f); std::vector timeScales(_children.size(), 1.0f); @@ -88,33 +85,45 @@ void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseI // abort if we find a child that is NOT a clipNode. if (_children[i]->getType() != AnimNode::Type::Clip) { // TODO: FIXME: make sync this work for other node types. - *prevPoseDeltaTime = dt; - *nextPoseDeltaTime = dt; return; } auto clipNode = std::dynamic_pointer_cast(_children[i]); assert(clipNode); if (clipNode) { - lengthSum += clipNode->getEndFrame() - clipNode->getStartFrame(); + lengthSum += (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; } } - float averageLength = lengthSum / (float)_children.size(); + _averageLength = lengthSum / (float)_children.size(); + + float progress = (_syncFrame / _averageLength); auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); - float prevTimeScale = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) / averageLength; + float prevLength = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float prevOffset = prevClipNode->getStartFrame(); - prevClipNode->setCurrentFrame(prevOffset + prevTimeScale / _syncFrame); + float prevFrame = prevOffset + (progress * prevLength); + float prevTimeScale = _timeScale * (_averageLength / prevLength); + prevClipNode->setTimeScale(prevTimeScale); + prevClipNode->setCurrentFrame(prevFrame); auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); - float nextTimeScale = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) / averageLength; + float nextLength = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; float nextOffset = nextClipNode->getStartFrame(); - nextClipNode->setCurrentFrame(nextOffset + nextTimeScale / _syncFrame); + float nextFrame = nextOffset + (progress * nextLength); + float nextTimeScale = _timeScale * (_averageLength / nextLength); + nextClipNode->setTimeScale(nextTimeScale); + nextClipNode->setCurrentFrame(nextFrame); + const float START_FRAME = 0.0f; const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(0.0f, averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); - - *prevPoseDeltaTime = prevTimeScale; - *nextPoseDeltaTime = nextTimeScale; + _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); } +void AnimBlendLinear::setCurrentFrameInternal(float frame) { + // because dt is 0, we should not encounter any triggers + const float dt = 0.0f; + Triggers triggers; + const float START_FRAME = 0.0f; + const bool LOOP_FLAG = true; + _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, frame, dt, LOOP_FLAG, _id, triggers); +} diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index d8b4f52fb6..7def6be91d 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -30,32 +30,35 @@ class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha, bool sync); + AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, - size_t prevPoseIndex, size_t nextPoseIndex, - float prevPoseDeltaTime, float nextPoseDeltaTime); - void setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, - float* prevPoseDeltaTime, float* nextPoseDeltaTime, - Triggers& triggersOut); + size_t prevPoseIndex, size_t nextPoseIndex, float dt); + void setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut); + + virtual void setCurrentFrameInternal(float frame) override; AnimPoseVec _poses; float _alpha; bool _sync; + float _timeScale; + float _syncFrame = 0.0f; - float _timeScale = 1.0f; // TODO: HOOK THIS UP TO AN ANIMVAR. + float _averageLength = 0.0f; // average length of child animations in frames. QString _alphaVar; + QString _timeScaleVar; // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 613194164f..8f50874ed3 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -81,7 +81,7 @@ void AnimClip::setCurrentFrameInternal(float frame) { // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame * _timeScale, dt, _loopFlag, _id, triggers); + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggers); } void AnimClip::copyFromNetworkAnim() { diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index b7cd2861fa..36867e622b 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -39,6 +39,9 @@ public: float getStartFrame() const { return _startFrame; } float getEndFrame() const { return _endFrame; } + void setTimeScale(float timeScale) { _timeScale = timeScale; } + float getTimeScale() const { return _timeScale; } + protected: void loadURL(const QString& url); diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 4222364629..f9ebf2a630 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -225,15 +225,21 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); + READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); + READ_OPTIONAL_STRING(timeScaleVar, jsonObj); - auto node = std::make_shared(id, alpha, sync); + auto node = std::make_shared(id, alpha, sync, timeScale); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); } + if (!timeScaleVar.isEmpty()) { + node->setTimeScaleVar(timeScaleVar); + } + return node; } From 5cd2786c1d4e71a28e47dac550ee2a24b7835d57 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 20 Oct 2015 16:37:05 -0700 Subject: [PATCH 09/44] First pass at Rig timeScaling and blending between slow, walk and run. --- .../defaultAvatar_full/avatar-animation.json | 17 ++++++- libraries/animation/src/Rig.cpp | 44 +++++++++++++++++-- libraries/animation/src/Rig.h | 4 ++ libraries/shared/src/GLMHelpers.h | 5 +++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index ea4ef63d16..e75e806df6 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -4,7 +4,7 @@ "id": "ikOverlay", "type": "overlay", "data": { - "alpha": 1.0, + "alpha": 0.0, "boneSet": "fullBody" }, "children": [ @@ -532,7 +532,8 @@ "alpha": 0.0, "sync": true, "timeScale": 1.0, - "timeScaleVar": "walkTimeScale" + "timeScaleVar": "walkTimeScale", + "alphaVar": "walkAlpha" }, "children": [ { @@ -558,6 +559,18 @@ "loopFlag": true }, "children": [] + }, + { + "id": "walkFwdRun", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx", + "startFrame": 0.0, + "endFrame": 21.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] } ] }, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 25f28a3f64..82cc19f18c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -408,6 +408,41 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return _jointStates[jointIndex].getTransform(); } +void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut) { + + // filter speed using a moving average. + _averageForwardSpeed.updateAverage(speed); + speed = _averageForwardSpeed.getAverage(); + + const int NUM_FWD_SPEEDS = 3; + float FWD_SPEEDS[NUM_FWD_SPEEDS] = { 0.3f, 1.4f, 2.7f }; // m/s + + // first calculate alpha by lerping between speeds. + float alpha = 0.0f; + if (speed <= FWD_SPEEDS[0]) { + alpha = 0.0f; + } else if (speed > FWD_SPEEDS[NUM_FWD_SPEEDS - 1]) { + alpha = (float)(NUM_FWD_SPEEDS - 1); + } else { + for (int i = 0; i < NUM_FWD_SPEEDS - 1; i++) { + if (FWD_SPEEDS[i] < speed && speed < FWD_SPEEDS[i + 1]) { + alpha = (float)i + ((speed - FWD_SPEEDS[i]) / (FWD_SPEEDS[i + 1] - FWD_SPEEDS[i])); + break; + } + } + } + + // now keeping the alpha fixed compute the timeScale. + // NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear. + int prevIndex = glm::floor(alpha); + int nextIndex = glm::ceil(alpha); + float animSpeed = lerp(FWD_SPEEDS[prevIndex], FWD_SPEEDS[nextIndex], (float)glm::fract(alpha)); + float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed); + + *alphaOut = alpha; + *timeScaleOut = timeScale; +} + void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -435,10 +470,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // sine wave LFO var for testing. static float t = 0.0f; - _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); + _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); - const float ANIM_WALK_SPEED = 1.4f; // m/s - _animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); + float walkAlpha, walkTimeScale; + calcWalkForwardAlphaAndTimeScale(glm::length(localVel), &walkAlpha, &walkTimeScale); + + _animVars.set("walkTimeScale", walkTimeScale); + _animVars.set("walkAlpha", walkAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 71c27e7213..c6f21deba6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -42,6 +42,7 @@ #include "AnimNode.h" #include "AnimNodeLoader.h" +#include "SimpleMovingAverage.h" class AnimationHandle; typedef std::shared_ptr AnimationHandlePointer; @@ -206,6 +207,7 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); + void calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut); QVector _jointStates; int _rootJointIndex = -1; @@ -240,6 +242,8 @@ public: float _desiredStateAge = 0.0f; float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; + + SimpleMovingAverage _averageForwardSpeed{ 25 }; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 7bdd3bf2de..9c1bbe23a4 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -183,6 +183,11 @@ T toNormalizedDeviceScale(const T& value, const T& size) { #define PITCH(euler) euler.x #define ROLL(euler) euler.z +// float - linear interpolate +inline float lerp(float x, float y, float a) { + return x * (1.0f - a) + (y * a); +} + // vec2 lerp - linear interpolate template glm::detail::tvec2 lerp(const glm::detail::tvec2& x, const glm::detail::tvec2& y, T a) { From c83af43d0e93d1b6f27417240abbc3326d975c32 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 21 Oct 2015 17:34:09 -0700 Subject: [PATCH 10/44] Added strafe and backward blending --- .../defaultAvatar_full/avatar-animation.json | 118 ++++++++++++++---- libraries/animation/src/Rig.cpp | 53 +++++--- libraries/animation/src/Rig.h | 3 +- 3 files changed, 133 insertions(+), 41 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index e75e806df6..723176c17e 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -4,7 +4,7 @@ "id": "ikOverlay", "type": "overlay", "data": { - "alpha": 0.0, + "alpha": 1.0, "boneSet": "fullBody" }, "children": [ @@ -532,8 +532,8 @@ "alpha": 0.0, "sync": true, "timeScale": 1.0, - "timeScaleVar": "walkTimeScale", - "alphaVar": "walkAlpha" + "timeScaleVar": "moveForwardTimeScale", + "alphaVar": "moveForwardAlpha" }, "children": [ { @@ -576,16 +576,40 @@ }, { "id": "walkBwd", - "type": "clip", + "type": "blendLinear", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", - "startFrame": 0.0, - "endFrame": 37.0, + "alpha": 1.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true, - "timeScaleVar": "walkTimeScale" + "timeScaleVar": "moveBackwardTimeScale", + "alphaVar": "moveBackwardAlpha" }, - "children": [] + "children": [ + { + "id": "walkBwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx", + "startFrame": 0.0, + "endFrame": 38.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkBwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", + "startFrame": 0.0, + "endFrame": 36.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "turnLeft", @@ -613,27 +637,77 @@ }, { "id": "strafeLeft", - "type": "clip", + "type": "blendLinear", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", - "startFrame": 0.0, - "endFrame": 30.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true + "timeScaleVar": "moveLateralTimeScale", + "alphaVar": "moveLateralAlpha" }, - "children": [] + "children": [ + { + "id": "strafeLeftShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafeLeftNormal", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "strafeRight", - "type": "clip", + "type": "blendLinear", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", - "startFrame": 0.0, - "endFrame": 30.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true + "timeScaleVar": "moveLateralTimeScale", + "alphaVar": "moveLateralAlpha" }, - "children": [] + "children": [ + { + "id": "strafeRightShort", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafeRightNormal", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] } ] } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9179e7675f..cf1d1bc703 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -408,25 +408,20 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return _jointStates[jointIndex].getTransform(); } -void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut) { +void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const { - // filter speed using a moving average. - _averageForwardSpeed.updateAverage(speed); - speed = _averageForwardSpeed.getAverage(); - - const int NUM_FWD_SPEEDS = 3; - float FWD_SPEEDS[NUM_FWD_SPEEDS] = { 0.3f, 1.4f, 2.7f }; // m/s + assert(referenceSpeeds.size() > 0); // first calculate alpha by lerping between speeds. float alpha = 0.0f; - if (speed <= FWD_SPEEDS[0]) { + if (speed <= referenceSpeeds.front()) { alpha = 0.0f; - } else if (speed > FWD_SPEEDS[NUM_FWD_SPEEDS - 1]) { - alpha = (float)(NUM_FWD_SPEEDS - 1); + } else if (speed > referenceSpeeds.back()) { + alpha = (float)(referenceSpeeds.size() - 1); } else { - for (int i = 0; i < NUM_FWD_SPEEDS - 1; i++) { - if (FWD_SPEEDS[i] < speed && speed < FWD_SPEEDS[i + 1]) { - alpha = (float)i + ((speed - FWD_SPEEDS[i]) / (FWD_SPEEDS[i + 1] - FWD_SPEEDS[i])); + for (size_t i = 0; i < referenceSpeeds.size() - 1; i++) { + if (referenceSpeeds[i] < speed && speed < referenceSpeeds[i + 1]) { + alpha = (float)i + ((speed - referenceSpeeds[i]) / (referenceSpeeds[i + 1] - referenceSpeeds[i])); break; } } @@ -436,13 +431,18 @@ void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* // NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear. int prevIndex = glm::floor(alpha); int nextIndex = glm::ceil(alpha); - float animSpeed = lerp(FWD_SPEEDS[prevIndex], FWD_SPEEDS[nextIndex], (float)glm::fract(alpha)); + float animSpeed = lerp(referenceSpeeds[prevIndex], referenceSpeeds[nextIndex], (float)glm::fract(alpha)); float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed); *alphaOut = alpha; *timeScaleOut = timeScale; } +// animation reference speeds. +static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 2.5f }; // m/s +static const std::vector BACKWARD_SPEEDS = { 0.45f, 1.4f }; // m/s +static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s + void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -468,15 +468,32 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + // filter speeds using a simple moving average. + _averageForwardSpeed.updateAverage(forwardSpeed); + _averageLateralSpeed.updateAverage(lateralSpeed); + // sine wave LFO var for testing. static float t = 0.0f; _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); - float walkAlpha, walkTimeScale; - calcWalkForwardAlphaAndTimeScale(glm::length(localVel), &walkAlpha, &walkTimeScale); + float moveForwardAlpha = 0.0f; + float moveForwardTimeScale = 1.0f; + float moveBackwardAlpha = 0.0f; + float moveBackwardTimeScale = 1.0f; + float moveLateralAlpha = 0.0f; + float moveLateralTimeScale = 1.0f; - _animVars.set("walkTimeScale", walkTimeScale); - _animVars.set("walkAlpha", walkAlpha); + // calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds. + calcAnimAlphaAndTimeScale(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha, &moveForwardTimeScale); + calcAnimAlphaAndTimeScale(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha, &moveBackwardTimeScale); + calcAnimAlphaAndTimeScale(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha, &moveLateralTimeScale); + + _animVars.set("moveFowardTimeScale", moveForwardTimeScale); + _animVars.set("moveForwardAlpha", moveForwardAlpha); + _animVars.set("moveBackwardTimeScale", moveBackwardTimeScale); + _animVars.set("moveBackwardAlpha", moveBackwardAlpha); + _animVars.set("moveLateralTimeScale", moveLateralTimeScale); + _animVars.set("moveLateralAlpha", moveLateralAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 93692eb00c..d1031c017a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,7 +208,7 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); - void calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut); + void calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const; QVector _jointStates; int _rootJointIndex = -1; @@ -245,6 +245,7 @@ public: float _rightHandOverlayAlpha = 0.0f; SimpleMovingAverage _averageForwardSpeed{ 25 }; + SimpleMovingAverage _averageLateralSpeed{ 25 }; }; #endif /* defined(__hifi__Rig__) */ From a66f31bb206ba7623179e9c4e0391bf5c870122a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 22 Oct 2015 16:33:31 -0700 Subject: [PATCH 11/44] Added AnimBlendLinearMove node AnimBlendLinearMove is now in use by forward, backward and strafe movements. Tuned rig moving average speeds to be more sensitive. --- .../defaultAvatar_full/avatar-animation.json | 42 +++--- .../animation/src/AnimBlendLinearMove.cpp | 126 ++++++++++++++++++ libraries/animation/src/AnimBlendLinearMove.h | 77 +++++++++++ libraries/animation/src/AnimNode.h | 1 + libraries/animation/src/AnimNodeLoader.cpp | 60 +++++++-- libraries/animation/src/Rig.cpp | 33 ++--- libraries/animation/src/Rig.h | 6 +- 7 files changed, 290 insertions(+), 55 deletions(-) create mode 100644 libraries/animation/src/AnimBlendLinearMove.cpp create mode 100644 libraries/animation/src/AnimBlendLinearMove.h diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 723176c17e..46c5cd9567 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -527,13 +527,13 @@ }, { "id": "walkFwd", - "type": "blendLinear", + "type": "blendLinearMove", "data": { "alpha": 0.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveForwardTimeScale", - "alphaVar": "moveForwardAlpha" + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.5, 1.4, 4.5], + "alphaVar": "moveForwardAlpha", + "desiredSpeedVar": "moveForwardSpeed" }, "children": [ { @@ -576,13 +576,13 @@ }, { "id": "walkBwd", - "type": "blendLinear", + "type": "blendLinearMove", "data": { - "alpha": 1.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveBackwardTimeScale", - "alphaVar": "moveBackwardAlpha" + "alpha": 0.0, + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.6, 1.45], + "alphaVar": "moveBackwardAlpha", + "desiredSpeedVar": "moveBackwardSpeed" }, "children": [ { @@ -637,13 +637,13 @@ }, { "id": "strafeLeft", - "type": "blendLinear", + "type": "blendLinearMove", "data": { "alpha": 0.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveLateralTimeScale", - "alphaVar": "moveLateralAlpha" + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.2, 0.65], + "alphaVar": "moveLateralAlpha", + "desiredSpeedVar": "moveLateralSpeed" }, "children": [ { @@ -674,13 +674,13 @@ }, { "id": "strafeRight", - "type": "blendLinear", + "type": "blendLinearMove", "data": { "alpha": 0.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveLateralTimeScale", - "alphaVar": "moveLateralAlpha" + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.2, 0.65], + "alphaVar": "moveLateralAlpha", + "desiredSpeedVar": "moveLateralSpeed" }, "children": [ { diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp new file mode 100644 index 0000000000..072dc28f57 --- /dev/null +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -0,0 +1,126 @@ +// +// AnimBlendLinearMove.cpp +// +// Created by Anthony J. Thibault on 10/22/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AnimBlendLinearMove.h" +#include "GLMHelpers.h" +#include "AnimationLogging.h" +#include "AnimUtil.h" +#include "AnimClip.h" + +AnimBlendLinearMove::AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector& characteristicSpeeds) : + AnimNode(AnimNode::Type::BlendLinearMove, id), + _alpha(alpha), + _desiredSpeed(desiredSpeed), + _characteristicSpeeds(characteristicSpeeds) { + +} + +AnimBlendLinearMove::~AnimBlendLinearMove() { + +} + +const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { + + assert(_children.size() == _characteristicSpeeds.size()); + + _alpha = animVars.lookup(_alphaVar, _alpha); + _desiredSpeed = animVars.lookup(_desiredSpeedVar, _desiredSpeed); + + if (_children.size() == 0) { + for (auto&& pose : _poses) { + pose = AnimPose::identity; + } + } else if (_children.size() == 1) { + const float alpha = 0.0f; + const int prevPoseIndex = 0; + const int nextPoseIndex = 0; + float prevDeltaTime, nextDeltaTime; + setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + } else { + + float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); + size_t prevPoseIndex = glm::floor(clampedAlpha); + size_t nextPoseIndex = glm::ceil(clampedAlpha); + float alpha = glm::fract(clampedAlpha); + float prevDeltaTime, nextDeltaTime; + setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + } + return _poses; +} + +// for AnimDebugDraw rendering +const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const { + return _poses; +} + +void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevDeltaTime, float nextDeltaTime) { + if (prevPoseIndex == nextPoseIndex) { + // this can happen if alpha is on an integer boundary + _poses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); + } else { + // need to eval and blend between two children. + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextDeltaTime, triggersOut); + + if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { + _poses.resize(prevPoses.size()); + + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } + } +} + +void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex, + float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) { + + const float FRAMES_PER_SECOND = 30.0f; + auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); + assert(prevClipNode); + auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); + assert(nextClipNode); + + float v0 = _characteristicSpeeds[prevPoseIndex]; + float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; + float v1 = _characteristicSpeeds[nextPoseIndex]; + float n1 = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; + + // rate of change in phase space, necessary to achive desired speed. + float omega = (_desiredSpeed * FRAMES_PER_SECOND) / ((1.0f - alpha) * v0 * n0 + alpha * v1 * n1); + + float f0 = prevClipNode->getStartFrame() + _phase * n0; + prevClipNode->setCurrentFrame(f0); + + float f1 = nextClipNode->getStartFrame() + _phase * n1; + nextClipNode->setCurrentFrame(f1); + + // integrate phase forward in time. + _phase += omega * dt; + + // detect loop trigger events + if (_phase >= 1.0f) { + triggersOut.push_back(_id + "Loop"); + _phase = glm::fract(_phase); + } + + *prevDeltaTimeOut = omega * dt * (n0 / FRAMES_PER_SECOND); + *nextDeltaTimeOut = omega * dt * (n1 / FRAMES_PER_SECOND); +} + +void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { + assert(_children.size() > 0); + auto clipNode = std::dynamic_pointer_cast(_children.front()); + assert(clipNode); + const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; + _phase = fmodf(frame, NUM_FRAMES); +} diff --git a/libraries/animation/src/AnimBlendLinearMove.h b/libraries/animation/src/AnimBlendLinearMove.h new file mode 100644 index 0000000000..4e04ce29cb --- /dev/null +++ b/libraries/animation/src/AnimBlendLinearMove.h @@ -0,0 +1,77 @@ +// +// AnimBlendLinearMove.h +// +// Created by Anthony J. Thibault on 10/22/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AnimBlendLinearMove_h +#define hifi_AnimBlendLinearMove_h + +#include "AnimNode.h" + +// Synced linear blend between two AnimNodes, where the playback speed of +// the animation is timeScaled to match movement speed. +// +// Each child animation is associated with a chracteristic speed. +// This defines the speed of that animation when played at the normal playback rate, 30 frames per second. +// +// The user also specifies a desired speed. This desired speed is used to timescale +// the animation to achive the desired movement velocity. +// +// Blending is determined by the alpha parameter. +// If the number of children is 2, then the alpha parameters should be between +// 0 and 1. The first animation will have a (1 - alpha) factor, and the second +// will have factor of alpha. +// +// This node supports more then 2 children. In this case the alpha should be +// between 0 and n - 1. This alpha can be used to linearly interpolate between +// the closest two children poses. This can be used to sweep through a series +// of animation poses. + +class AnimBlendLinearMove : public AnimNode { +public: + friend class AnimTests; + + AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector& characteristicSpeeds); + virtual ~AnimBlendLinearMove() override; + + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; + + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; } + +protected: + // for AnimDebugDraw rendering + virtual const AnimPoseVec& getPosesInternal() const override; + + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevDeltaTime, float nextDeltaTime); + + void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex, + float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut); + + virtual void setCurrentFrameInternal(float frame) override; + + AnimPoseVec _poses; + + float _alpha; + float _desiredSpeed; + + float _phase = 0.0f; + + QString _alphaVar; + QString _desiredSpeedVar; + + std::vector _characteristicSpeeds; + + // no copies + AnimBlendLinearMove(const AnimBlendLinearMove&) = delete; + AnimBlendLinearMove& operator=(const AnimBlendLinearMove&) = delete; +}; + +#endif // hifi_AnimBlendLinearMove_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 315ee357dd..ac3365c272 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -38,6 +38,7 @@ public: enum class Type { Clip = 0, BlendLinear, + BlendLinearMove, Overlay, StateMachine, Manipulator, diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index f9ebf2a630..38296923e3 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -16,6 +16,7 @@ #include "AnimNode.h" #include "AnimClip.h" #include "AnimBlendLinear.h" +#include "AnimBlendLinearMove.h" #include "AnimationLogging.h" #include "AnimOverlay.h" #include "AnimNodeLoader.h" @@ -29,6 +30,7 @@ using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& json // factory functions static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); @@ -36,17 +38,14 @@ static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, c // called after children have been loaded // returns node on success, nullptr on failure. -static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); -static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static const char* animNodeTypeToString(AnimNode::Type type) { switch (type) { case AnimNode::Type::Clip: return "clip"; case AnimNode::Type::BlendLinear: return "blendLinear"; + case AnimNode::Type::BlendLinearMove: return "blendLinearMove"; case AnimNode::Type::Overlay: return "overlay"; case AnimNode::Type::StateMachine: return "stateMachine"; case AnimNode::Type::Manipulator: return "manipulator"; @@ -60,6 +59,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { switch (type) { case AnimNode::Type::Clip: return loadClipNode; case AnimNode::Type::BlendLinear: return loadBlendLinearNode; + case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode; case AnimNode::Type::Overlay: return loadOverlayNode; case AnimNode::Type::StateMachine: return loadStateMachineNode; case AnimNode::Type::Manipulator: return loadManipulatorNode; @@ -71,12 +71,13 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { switch (type) { - case AnimNode::Type::Clip: return processClipNode; - case AnimNode::Type::BlendLinear: return processBlendLinearNode; - case AnimNode::Type::Overlay: return processOverlayNode; + case AnimNode::Type::Clip: return processDoNothing; + case AnimNode::Type::BlendLinear: return processDoNothing; + case AnimNode::Type::BlendLinearMove: return processDoNothing; + case AnimNode::Type::Overlay: return processDoNothing; case AnimNode::Type::StateMachine: return processStateMachineNode; - case AnimNode::Type::Manipulator: return processManipulatorNode; - case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode; + case AnimNode::Type::Manipulator: return processDoNothing; + case AnimNode::Type::InverseKinematics: return processDoNothing; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -243,6 +244,45 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q return node; } +static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { + + READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); + READ_FLOAT(desiredSpeed, jsonObj, id, jsonUrl, nullptr); + + std::vector characteristicSpeeds; + auto speedsValue = jsonObj.value("characteristicSpeeds"); + if (!speedsValue.isArray()) { + qCCritical(animation) << "AnimNodeLoader, bad array \"characteristicSpeeds\" in blendLinearMove node, id =" << id << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + auto speedsArray = speedsValue.toArray(); + for (const auto& speedValue : speedsArray) { + if (!speedValue.isDouble()) { + qCCritical(animation) << "AnimNodeLoader, bad number in \"characteristicSpeeds\", id =" << id << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + float speedVal = (float)speedValue.toDouble(); + characteristicSpeeds.push_back(speedVal); + }; + + READ_OPTIONAL_STRING(alphaVar, jsonObj); + READ_OPTIONAL_STRING(desiredSpeedVar, jsonObj); + + auto node = std::make_shared(id, alpha, desiredSpeed, characteristicSpeeds); + + if (!alphaVar.isEmpty()) { + node->setAlphaVar(alphaVar); + } + + if (!desiredSpeedVar.isEmpty()) { + node->setDesiredSpeedVar(desiredSpeedVar); + } + + return node; +} + + static const char* boneSetStrings[AnimOverlay::NumBoneSets] = { "fullBody", "upperBody", diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index cf1d1bc703..7f9908faaf 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -408,11 +408,11 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return _jointStates[jointIndex].getTransform(); } -void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const { +void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { assert(referenceSpeeds.size() > 0); - // first calculate alpha by lerping between speeds. + // calculate alpha from linear combination of referenceSpeeds. float alpha = 0.0f; if (speed <= referenceSpeeds.front()) { alpha = 0.0f; @@ -427,20 +427,12 @@ void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector& refer } } - // now keeping the alpha fixed compute the timeScale. - // NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear. - int prevIndex = glm::floor(alpha); - int nextIndex = glm::ceil(alpha); - float animSpeed = lerp(referenceSpeeds[prevIndex], referenceSpeeds[nextIndex], (float)glm::fract(alpha)); - float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed); - *alphaOut = alpha; - *timeScaleOut = timeScale; } // animation reference speeds. -static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 2.5f }; // m/s -static const std::vector BACKWARD_SPEEDS = { 0.45f, 1.4f }; // m/s +static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s +static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { @@ -477,22 +469,21 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); float moveForwardAlpha = 0.0f; - float moveForwardTimeScale = 1.0f; float moveBackwardAlpha = 0.0f; - float moveBackwardTimeScale = 1.0f; float moveLateralAlpha = 0.0f; - float moveLateralTimeScale = 1.0f; // calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds. - calcAnimAlphaAndTimeScale(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha, &moveForwardTimeScale); - calcAnimAlphaAndTimeScale(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha, &moveBackwardTimeScale); - calcAnimAlphaAndTimeScale(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha, &moveLateralTimeScale); + calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha); + calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha); + calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha); - _animVars.set("moveFowardTimeScale", moveForwardTimeScale); + _animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage()); _animVars.set("moveForwardAlpha", moveForwardAlpha); - _animVars.set("moveBackwardTimeScale", moveBackwardTimeScale); + + _animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage()); _animVars.set("moveBackwardAlpha", moveBackwardAlpha); - _animVars.set("moveLateralTimeScale", moveLateralTimeScale); + + _animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage())); _animVars.set("moveLateralAlpha", moveLateralAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index d1031c017a..861eba40b5 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,7 +208,7 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); - void calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const; + void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; QVector _jointStates; int _rootJointIndex = -1; @@ -244,8 +244,8 @@ public: float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; - SimpleMovingAverage _averageForwardSpeed{ 25 }; - SimpleMovingAverage _averageLateralSpeed{ 25 }; + SimpleMovingAverage _averageForwardSpeed{ 10 }; + SimpleMovingAverage _averageLateralSpeed{ 10 }; }; #endif /* defined(__hifi__Rig__) */ From 86891704159f0154d48070a66a2ad3c8c55a84e6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 22 Oct 2015 16:40:53 -0700 Subject: [PATCH 12/44] Removed sync and timescale from AnimBlendLinear node. AnimBlendLinearMove will now be used instead. --- .../defaultAvatar_full/avatar-animation.json | 4 -- libraries/animation/src/AnimBlendLinear.cpp | 63 +------------------ libraries/animation/src/AnimBlendLinear.h | 15 +---- libraries/animation/src/AnimNodeLoader.cpp | 9 +-- 4 files changed, 4 insertions(+), 87 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 46c5cd9567..c6c67a21d5 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -190,8 +190,6 @@ "type": "blendLinear", "data": { "alpha": 0.0, - "sync": false, - "timeScale": 1.0, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -341,8 +339,6 @@ "type": "blendLinear", "data": { "alpha": 0.0, - "sync": false, - "timeScale": 1.0, "alphaVar": "leftHandGrabBlend" }, "children": [ diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 1f9026de9d..52c440a14e 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,11 +14,9 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : AnimNode(AnimNode::Type::BlendLinear, id), - _alpha(alpha), - _sync(sync), - _timeScale(timeScale) { + _alpha(alpha) { } @@ -29,7 +27,6 @@ AnimBlendLinear::~AnimBlendLinear() { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); - _timeScale = animVars.lookup(_timeScaleVar, _timeScale); if (_children.size() == 0) { for (auto&& pose : _poses) { @@ -44,10 +41,6 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - if (_sync) { - setSyncAndAccumulateTime(dt, prevPoseIndex, nextPoseIndex, triggersOut); - } - evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; @@ -75,55 +68,3 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, T } } } - -void AnimBlendLinear::setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut) { - std::vector offsets(_children.size(), 0.0f); - std::vector timeScales(_children.size(), 1.0f); - - float lengthSum = 0.0f; - for (size_t i = 0; i < _children.size(); i++) { - // abort if we find a child that is NOT a clipNode. - if (_children[i]->getType() != AnimNode::Type::Clip) { - // TODO: FIXME: make sync this work for other node types. - return; - } - auto clipNode = std::dynamic_pointer_cast(_children[i]); - assert(clipNode); - if (clipNode) { - lengthSum += (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; - } - } - - _averageLength = lengthSum / (float)_children.size(); - - float progress = (_syncFrame / _averageLength); - - auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); - float prevLength = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; - float prevOffset = prevClipNode->getStartFrame(); - float prevFrame = prevOffset + (progress * prevLength); - float prevTimeScale = _timeScale * (_averageLength / prevLength); - prevClipNode->setTimeScale(prevTimeScale); - prevClipNode->setCurrentFrame(prevFrame); - - auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); - float nextLength = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; - float nextOffset = nextClipNode->getStartFrame(); - float nextFrame = nextOffset + (progress * nextLength); - float nextTimeScale = _timeScale * (_averageLength / nextLength); - nextClipNode->setTimeScale(nextTimeScale); - nextClipNode->setCurrentFrame(nextFrame); - - const float START_FRAME = 0.0f; - const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); -} - -void AnimBlendLinear::setCurrentFrameInternal(float frame) { - // because dt is 0, we should not encounter any triggers - const float dt = 0.0f; - Triggers triggers; - const float START_FRAME = 0.0f; - const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, frame, dt, LOOP_FLAG, _id, triggers); -} diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 7def6be91d..2478f9b473 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -22,21 +22,17 @@ // between 0 and n - 1. This alpha can be used to linearly interpolate between // the closest two children poses. This can be used to sweep through a series // of animation poses. -// -// The sync flag is used to synchronize between child animations of different lengths. -// Typically used to synchronize blending between walk and run cycles. class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale); + AnimBlendLinear(const QString& id, float alpha); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } - void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } protected: // for AnimDebugDraw rendering @@ -44,21 +40,12 @@ protected: void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float dt); - void setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut); - - virtual void setCurrentFrameInternal(float frame) override; AnimPoseVec _poses; float _alpha; - bool _sync; - float _timeScale; - - float _syncFrame = 0.0f; - float _averageLength = 0.0f; // average length of child animations in frames. QString _alphaVar; - QString _timeScaleVar; // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 38296923e3..2a52e04e1d 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -225,22 +225,15 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); - READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); - READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); - READ_OPTIONAL_STRING(timeScaleVar, jsonObj); - auto node = std::make_shared(id, alpha, sync, timeScale); + auto node = std::make_shared(id, alpha); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); } - if (!timeScaleVar.isEmpty()) { - node->setTimeScaleVar(timeScaleVar); - } - return node; } From e639d5313944f7b4689c4454f1a9b3f652cc897c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 23 Oct 2015 08:55:38 -0700 Subject: [PATCH 13/44] Use #include<> for headers not in current working directory. --- libraries/animation/src/AnimBlendLinearMove.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 072dc28f57..d8985f8b72 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -9,7 +9,7 @@ // #include "AnimBlendLinearMove.h" -#include "GLMHelpers.h" +#include #include "AnimationLogging.h" #include "AnimUtil.h" #include "AnimClip.h" From 06de0878020e1be466acef54dfd52b20219cbf89 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 23 Oct 2015 18:27:20 -0700 Subject: [PATCH 14/44] Filter out velocities computed from position delta if dt is small While in the HMD, updates can occur with very small deltaTime values. These this makes the position delta method of computing a velocity very susceptible to noise and precision errors. --- libraries/animation/src/Rig.cpp | 11 ++++++++++- libraries/animation/src/Rig.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7f9908faaf..bc47116c30 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -443,8 +443,14 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // but some modes (e.g., hmd standing) update position without updating velocity. // It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...) // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 workingVelocity = _lastVelocity; glm::vec3 positionDelta = worldPosition - _lastPosition; - glm::vec3 workingVelocity = positionDelta / deltaTime; + + // don't trust position delta if deltaTime is 'small'. + const float SMALL_DELTA_TIME = 0.006f; // 6 ms + if (deltaTime > SMALL_DELTA_TIME) { + workingVelocity = positionDelta / deltaTime; + } #if !WANT_DEBUG // But for smoothest (non-hmd standing) results, go ahead and use velocity: @@ -453,9 +459,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } #endif + _lastVelocity = workingVelocity; + if (_enableAnimGraph) { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; + float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 861eba40b5..42c59fac44 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -228,6 +228,7 @@ public: bool _enableAnimGraph = false; glm::vec3 _lastFront; glm::vec3 _lastPosition; + glm::vec3 _lastVelocity; std::shared_ptr _animNode; std::shared_ptr _animSkeleton; From f9dc05c98919f4287cefc247711ec9d572af797c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 27 Oct 2015 18:59:02 -0700 Subject: [PATCH 15/44] Rig::computeMotionAnimationState better filtering of small dt velocities. Only update _lastVelocity when dt is sufficiently large. --- libraries/animation/src/Rig.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index bc47116c30..6afe362146 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -446,7 +446,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos glm::vec3 workingVelocity = _lastVelocity; glm::vec3 positionDelta = worldPosition - _lastPosition; - // don't trust position delta if deltaTime is 'small'. + // Don't trust position delta if deltaTime is 'small'. + // NOTE: This is mostly just a work around for an issue in oculus 0.7 runtime, where + // Application::idle() is being called more frequently and with smaller dt's then expected. const float SMALL_DELTA_TIME = 0.006f; // 6 ms if (deltaTime > SMALL_DELTA_TIME) { workingVelocity = positionDelta / deltaTime; @@ -459,7 +461,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } #endif - _lastVelocity = workingVelocity; + if (deltaTime > SMALL_DELTA_TIME) { + _lastVelocity = workingVelocity; + } if (_enableAnimGraph) { From 8e83943efd4840f25166c2d12ffa482f38938d12 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 28 Oct 2015 15:43:36 -0700 Subject: [PATCH 16/44] Don't play other avatar's collision sounds. https://app.asana.com/0/32622044445063/61104546898252 --- libraries/physics/src/PhysicsEngine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index d6772f8d36..0efa0c0f39 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -352,16 +352,16 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { glm::vec3 velocityChange = (motionStateA ? motionStateA->getObjectLinearVelocityChange() : glm::vec3(0.0f)) + (motionStateB ? motionStateB->getObjectLinearVelocityChange() : glm::vec3(0.0f)); - if (motionStateA && motionStateA->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (motionStateA) { QUuid idA = motionStateA->getObjectID(); QUuid idB; - if (motionStateB && motionStateB->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (motionStateB) { idB = motionStateB->getObjectID(); } glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset; glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB); _collisionEvents.push_back(Collision(type, idA, idB, position, penetration, velocityChange)); - } else if (motionStateB && motionStateB->getType() == MOTIONSTATE_TYPE_ENTITY) { + } else if (motionStateB) { QUuid idB = motionStateB->getObjectID(); glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset; // NOTE: we're flipping the order of A and B (so that the first objectID is never NULL) From 792fed0090a7f633e92e7912d0297b8d9134e4be Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 22:15:26 -0700 Subject: [PATCH 17/44] We weren't setting type for avatar motion states. --- interface/src/avatar/AvatarMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index cabe545f5a..acd9a45aab 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -18,6 +18,7 @@ AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); + _type = MOTIONSTATE_TYPE_AVATAR; if (_shape) { _mass = 100.0f; // HACK } From ce1d0fe07c1d89e3bff075a2e31455d5e58688a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 30 Oct 2015 11:25:54 -0700 Subject: [PATCH 18/44] Git rid of unused (un-emitted and intended to be removed long ago) script signal. --- libraries/entities/src/EntityScriptingInterface.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d764cd7bab..156f16cf46 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -152,7 +152,6 @@ public slots: Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); signals: - void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void canAdjustLocksChanged(bool canAdjustLocks); From 00be6b3e3a12ee2de08360b484fd73fe3a2d8879 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 30 Oct 2015 17:32:26 -0700 Subject: [PATCH 19/44] Add support for NOT conditional in the route json --- .../src/controllers/UserInputMapper.cpp | 19 ++++++++++++++++++- .../impl/conditionals/EndpointConditional.h | 8 ++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index d33e215797..6462103290 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -825,12 +825,29 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) return std::make_shared(children); } else if (value.isString()) { // Support "when" : "GamePad.RB" - auto input = findDeviceInput(value.toString()); + auto conditionalToken = value.toString(); + + // Detect for modifier case (Not...) + QString conditionalModifier; + const QString JSON_CONDITIONAL_MODIFIER_NOT("!"); + if (conditionalToken.startsWith(JSON_CONDITIONAL_MODIFIER_NOT)) { + conditionalModifier = JSON_CONDITIONAL_MODIFIER_NOT; + conditionalToken = conditionalToken.right(conditionalToken.size() - conditionalModifier.size()); + } + + auto input = findDeviceInput(conditionalToken); auto endpoint = endpointFor(input); if (!endpoint) { return Conditional::Pointer(); } + if (!conditionalModifier.isEmpty()) { + if (conditionalModifier == JSON_CONDITIONAL_MODIFIER_NOT) { + return std::make_shared(endpoint); + } + } + + // Default and conditional behavior return std::make_shared(endpoint); } diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 1e4205afc7..8f0499077e 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -23,5 +23,13 @@ private: Endpoint::Pointer _endpoint; }; +class NotEndpointConditional : public Conditional { +public: + NotEndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} + virtual bool satisfied() override { return _endpoint && _endpoint->value() == 0.0; } +private: + Endpoint::Pointer _endpoint; +}; + } #endif From fc63fa6f7b963acc1c6c481a35009c8a5b9e939b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 1 Nov 2015 18:55:43 -0800 Subject: [PATCH 20/44] Update comments, and don't continue through collisions after finding one for our avatar. --- interface/src/avatar/AvatarManager.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b0da8faeca..4b9bfd21a3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -286,10 +286,10 @@ void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStat void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents) { for (Collision collision : collisionEvents) { - // TODO: Current physics uses null idA or idB for non-entities. The plan is to handle MOTIONSTATE_TYPE_AVATAR, - // and then MOTIONSTATE_TYPE_MYAVATAR. As it is, this code only covers the case of my avatar (in which case one - // if the ids will be null), and the behavior for other avatars is not specified. This has to be fleshed - // out as soon as we use the new motionstates. + // TODO: The plan is to handle MOTIONSTATE_TYPE_AVATAR, and then MOTIONSTATE_TYPE_MYAVATAR. As it is, other + // people's avatars will have an id that doesn't match any entities, and one's own avatar will have + // an id of null. Thus this code handles any collision in which one of the participating objects is + // my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.) if (collision.idA.isNull() || collision.idB.isNull()) { MyAvatar* myAvatar = getMyAvatar(); const QString& collisionSoundURL = myAvatar->getCollisionSoundURL(); @@ -299,9 +299,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); if (!isSound) { - // TODO: When the new motion states are used, we'll probably break from the whole loop as soon as we hit our own avatar - // (regardless of isSound), because other users should inject for their own avatars. - continue; + return; // No sense iterating for others. We only have one avatar. } // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. const float energy = velocityChange * velocityChange; @@ -314,7 +312,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition()); myAvatar->collisionWithEntity(collision); - } + return; } } } } From b067abe2d9f74a93afda0fe858b2a71adccf991c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 2 Nov 2015 09:58:23 -0800 Subject: [PATCH 21/44] Added name to basketball hoop in reset scripts --- unpublishedScripts/hiddenEntityReset.js | 1 + unpublishedScripts/masterReset.js | 1 + 2 files changed, 2 insertions(+) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 43d23e4b3c..015157fd26 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -944,6 +944,7 @@ var hoop = Entities.addEntity({ type: "Model", modelURL: hoopURL, + name: "Basketball Hoop", position: position, rotation: rotation, shapeType: 'compound', diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 74eb3c85ac..2e7a7ee2e7 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -921,6 +921,7 @@ MasterReset = function() { var hoop = Entities.addEntity({ type: "Model", + name: "Basketball Hoop", modelURL: hoopURL, position: position, rotation: rotation, From 3019886344f9f6101f10d682b7d81ac8d04a63ac Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 2 Nov 2015 11:08:06 -0800 Subject: [PATCH 22/44] Removing basketball hoop from reset scripts Hoop will be saved in SVO instead --- unpublishedScripts/hiddenEntityReset.js | 44 ------------------------- unpublishedScripts/masterReset.js | 43 +----------------------- 2 files changed, 1 insertion(+), 86 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 015157fd26..f56c705ce4 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -114,7 +114,6 @@ createTargets(); createTargetResetter(); - createBasketballHoop(); createBasketballRack(); createBasketballResetter(); @@ -130,14 +129,11 @@ z: 503.49 }); - createSprayCan({ x: 549.7, y: 495.6, z: 503.91 }); - - } function deleteAllToys() { @@ -930,46 +926,6 @@ }); } - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - name: "Basketball Hoop", - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 2e7a7ee2e7..b138db163d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -10,7 +10,7 @@ //per script -/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */ +/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */ var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js"); Script.include(utilitiesScript); @@ -87,7 +87,6 @@ MasterReset = function() { createTargets(); createTargetResetter(); - createBasketballHoop(); createBasketballRack(); createBasketballResetter(); @@ -908,46 +907,6 @@ MasterReset = function() { }); } - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - name: "Basketball Hoop", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; From b94258f9536dab302db52651d54b8e94b5ddfb59 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 11:55:50 -0800 Subject: [PATCH 23/44] add example of overriding default rightClick behavior --- examples/controllers/rightClickExample.js | 10 ++++++++++ .../controllers/src/controllers/UserInputMapper.cpp | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js new file mode 100644 index 0000000000..c3e6ea8f3d --- /dev/null +++ b/examples/controllers/rightClickExample.js @@ -0,0 +1,10 @@ +var MAPPING_NAME = "com.highfidelity.rightClickExample"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); +}); +Controller.enableMapping(MAPPING_NAME); + +Script.scriptEnding.connect(function () { + Controller.disableMapping(MAPPING_NAME); +}); \ No newline at end of file diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 1a0fae8158..3b256066f8 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -676,7 +676,7 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { Locker locker(_lock); - qCDebug(controllers) << "Attempting to enable mapping " << mappingName; + qCDebug(controllers) << "Attempting to " << (enable ? "enable" : "disable") << " mapping " << mappingName; auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; From 46bedf7667db02fc0bdf74fd43efc1ff68eabd66 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 13:35:49 -0800 Subject: [PATCH 24/44] more tweaks to hydra mappings --- interface/resources/controllers/hydra.json | 14 ++++---------- interface/resources/controllers/standard.json | 6 +++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index 20d954932a..0cdeba1b8d 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -13,17 +13,11 @@ { "from": "Hydra.RB", "to": "Standard.RB" }, { "from": "Hydra.RS", "to": "Standard.RS" }, - { "from": "Hydra.L0", "to": "Standard.Back" }, - { "from": "Hydra.L1", "to": "Standard.DL" }, - { "from": "Hydra.L2", "to": "Standard.DD" }, - { "from": "Hydra.L3", "to": "Standard.DR" }, - { "from": "Hydra.L4", "to": "Standard.DU" }, + { "from": "Hydra.L1", "to": "Standard.LeftPrimaryThumb" }, + { "from": "Hydra.L2", "to": "Standard.LeftSecondaryThumb" }, - { "from": "Hydra.R0", "to": "Standard.Start" }, - { "from": "Hydra.R1", "to": "Standard.X" }, - { "from": "Hydra.R2", "to": "Standard.A" }, - { "from": "Hydra.R3", "to": "Standard.B" }, - { "from": "Hydra.R4", "to": "Standard.Y" }, + { "from": "Hydra.R1", "to": "Standard.RightPrimaryThumb" }, + { "from": "Hydra.R2", "to": "Standard.RightSecondaryThumb" }, { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, { "from": "Hydra.RightHand", "to": "Standard.RightHand" } diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index b08abcbaad..d312913a23 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -2,9 +2,8 @@ "name": "Standard to Action", "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, - { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", + { "from": "Standard.LX", "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw", "filters": @@ -14,8 +13,9 @@ ] }, + { "from": "Standard.LX", "to": "Actions.Yaw" }, - { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RX", "to": "Actions.TranslateX" }, { "from": "Standard.RY", "filters": "invert", "to": "Actions.TranslateY" }, From d359aa3b654a6235da3e651bdfb05b5383be2982 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 13:41:16 -0800 Subject: [PATCH 25/44] more tweaks --- interface/resources/controllers/hydra.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index 0cdeba1b8d..b517d1bad5 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -13,11 +13,12 @@ { "from": "Hydra.RB", "to": "Standard.RB" }, { "from": "Hydra.RS", "to": "Standard.RS" }, - { "from": "Hydra.L1", "to": "Standard.LeftPrimaryThumb" }, - { "from": "Hydra.L2", "to": "Standard.LeftSecondaryThumb" }, + { "from": [ "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" }, + { "from": [ "Hydra.L1", "Hydra.L2" ], "to": "Standard.LeftSecondaryThumb" }, + + { "from": [ "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" }, + { "from": [ "Hydra.R1", "Hydra.R2" ], "to": "Standard.RightSecondaryThumb" }, - { "from": "Hydra.R1", "to": "Standard.RightPrimaryThumb" }, - { "from": "Hydra.R2", "to": "Standard.RightSecondaryThumb" }, { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, { "from": "Hydra.RightHand", "to": "Standard.RightHand" } From 310a5c9bfbbea205fa5a86c7937c6c85e40aeaf9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 13:42:12 -0800 Subject: [PATCH 26/44] don't initialize SMI sdk unless we need it --- interface/src/devices/EyeTracker.cpp | 30 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index b7a0a8c299..2cfc2ecd75 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -118,18 +118,6 @@ void EyeTracker::init() { qCWarning(interfaceapp) << "Eye Tracker: Already initialized"; return; } - -#ifdef HAVE_IVIEWHMD - int result = smi_setCallback(eyeTrackerCallback); - if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); - QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); - } else { - _isInitialized = true; - } - - connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted())); -#endif } #ifdef HAVE_IVIEWHMD @@ -140,6 +128,10 @@ int EyeTracker::startStreaming(bool simulate) { #ifdef HAVE_IVIEWHMD void EyeTracker::onStreamStarted() { + if (!_isInitialized) { + return; + } + int result = _startStreamingWatcher.result(); _isStreaming = (result == SMI_RET_SUCCESS); @@ -171,6 +163,20 @@ void EyeTracker::onStreamStarted() { #endif void EyeTracker::setEnabled(bool enabled, bool simulate) { + if (enabled && !_isInitialized) { +#ifdef HAVE_IVIEWHMD + int result = smi_setCallback(eyeTrackerCallback); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); + QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + } else { + _isInitialized = true; + } + + connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted())); +#endif + } + if (!_isInitialized) { return; } From c2687b4998ad47c4b15dd6ba951e30786ea5562f Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 2 Nov 2015 14:17:07 -0800 Subject: [PATCH 27/44] Cleaning the feature not conditional do the true design --- .../src/controllers/UserInputMapper.cpp | 9 ++++----- .../impl/conditionals/EndpointConditional.h | 8 -------- .../impl/conditionals/NotConditional.cpp | 20 ++++++++++++------- .../impl/conditionals/NotConditional.h | 14 +++++++++++++ 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 5edc3390a6..a5d447d479 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -26,6 +26,7 @@ #include "Logging.h" #include "impl/conditionals/AndConditional.h" +#include "impl/conditionals/NotConditional.h" #include "impl/conditionals/EndpointConditional.h" #include "impl/conditionals/ScriptConditional.h" @@ -838,18 +839,16 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) auto input = findDeviceInput(conditionalToken); auto endpoint = endpointFor(input); - if (!endpoint) { - return Conditional::Pointer(); - } + auto conditional = std::make_shared(endpoint); if (!conditionalModifier.isEmpty()) { if (conditionalModifier == JSON_CONDITIONAL_MODIFIER_NOT) { - return std::make_shared(endpoint); + return std::make_shared(conditional); } } // Default and conditional behavior - return std::make_shared(endpoint); + return conditional; } return Conditional::parse(value); diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 36fd3e5daa..0ba1347087 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -23,13 +23,5 @@ private: Endpoint::Pointer _endpoint; }; -class NotEndpointConditional : public Conditional { -public: - NotEndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} - virtual bool satisfied() override { return _endpoint && _endpoint->value() == 0.0; } -private: - Endpoint::Pointer _endpoint; -}; - } #endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp index e30b060985..813c8ebfad 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp @@ -6,10 +6,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// NOTE: we don't need to include this header unless/until we add additional symbols. -// By removing this header we prevent these warnings on windows: -// -// warning LNK4221: This object file does not define any previously undefined public symbols, -// so it will not be used by any link operation that consumes this library -// -//#include "NotConditional.h" + +#include "NotConditional.h" + +using namespace controller; + +bool NotConditional::satisfied() { + if (_operand) { + return (!_operand->satisfied()); + } else { + return false; + } +} + diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h index 3acda07106..6b19cf9505 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h @@ -12,5 +12,19 @@ #include "../Conditional.h" +namespace controller { + + class NotConditional : public Conditional { + public: + using Pointer = std::shared_ptr; + + NotConditional(Conditional::Pointer operand) : _operand(operand) { } + + virtual bool satisfied() override; + + private: + Conditional::Pointer _operand; + }; +} #endif From 181901ed1930f11f25c2f3762ab43747501f838b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 14:42:28 -0800 Subject: [PATCH 28/44] distance grab works better with hmd on --- examples/controllers/handControllerGrab.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9f7d579f10..b882777048 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -446,7 +446,7 @@ function MyController(hand) { this.currentObjectPosition = grabbedProperties.position; this.currentObjectRotation = grabbedProperties.rotation; this.currentObjectTime = now; - this.handPreviousPosition = handControllerPosition; + this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); this.handPreviousRotation = handRotation; this.actionID = NULL_ACTION_ID; @@ -523,11 +523,10 @@ function MyController(hand) { this.currentAvatarOrientation = currentOrientation; // how far did hand move this timestep? - var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); - this.handPreviousPosition = handControllerPosition; + var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); + this.handRelativePreviousPosition = handToAvatar; // magnify the hand movement but not the change from avatar movement & rotation - handMoved = Vec3.subtract(handMoved, avatarDeltaPosition); handMoved = Vec3.subtract(handMoved, handMovementFromTurning); var superHandMoved = Vec3.multiply(handMoved, radius); From 0211e00c86bc49a91c5d5944f8c87df4d8014752 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 2 Nov 2015 15:01:59 -0800 Subject: [PATCH 29/44] Cleaning the feature not conditional do the true design --- libraries/controllers/src/controllers/UserInputMapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index a5d447d479..192404b85a 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -839,6 +839,9 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) auto input = findDeviceInput(conditionalToken); auto endpoint = endpointFor(input); + if (!endpoint) { + return Conditional::Pointer(); + } auto conditional = std::make_shared(endpoint); if (!conditionalModifier.isEmpty()) { From f532607ccab1b94c150c6f207dbf49557c0148ad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 15:06:38 -0800 Subject: [PATCH 30/44] add touch and other gestures to standard --- .../src/controllers/StandardController.cpp | 22 +++++++++++++++++-- .../src/controllers/StandardControls.h | 17 ++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 31c023b1ea..5b68f38374 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -55,6 +55,9 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(LS, "LS"), makePair(RS, "RS"), + makePair(LS, "LS_TOUCH"), + makePair(RS, "RS_TOUCH"), + // Center buttons makePair(START, "Start"), makePair(BACK, "Back"), @@ -69,26 +72,41 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(LT, "LT"), makePair(RT, "RT"), - // Finger abstractions makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), + makePair(LEFT_THUMB_UP, "LeftThumbUp"), makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), + makePair(RIGHT_THUMB_UP, "RightThumbUp"), + + makePair(LEFT_PRIMARY_THUMB_TOUCH, "LeftPrimaryThumbTouch"), + makePair(LEFT_SECONDARY_THUMB_TOUCH, "LeftSecondaryThumbTouch"), + makePair(RIGHT_PRIMARY_THUMB_TOUCH, "RightPrimaryThumbTouch"), + makePair(RIGHT_SECONDARY_THUMB_TOUCH, "RightSecondaryThumbTouch"), + + makePair(LEFT_INDEX_POINT, "LeftIndexPoint"), + makePair(RIGHT_INDEX_POINT, "RightIndexPoint"), makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"), makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"), makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"), makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"), + makePair(LEFT_PRIMARY_INDEX_TOUCH, "LeftPrimaryIndexTouch"), + makePair(LEFT_SECONDARY_INDEX_TOUCH, "LeftSecondaryIndexTouch"), + makePair(RIGHT_PRIMARY_INDEX_TOUCH, "RightPrimaryIndexTouch"), + makePair(RIGHT_SECONDARY_INDEX_TOUCH, "RightSecondaryIndexTouch"), + makePair(LEFT_GRIP, "LeftGrip"), + makePair(LEFT_GRIP_TOUCH, "LeftGripTouch"), makePair(RIGHT_GRIP, "RightGrip"), + makePair(RIGHT_GRIP_TOUCH, "RightGripTouch"), // Poses makePair(LEFT_HAND, "LeftHand"), makePair(RIGHT_HAND, "RightHand"), - // Aliases, PlayStation style names makePair(LB, "L1"), makePair(RB, "R1"), diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index d5063f6034..bbd33c5cb3 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -39,16 +39,33 @@ namespace controller { // These don't map to SDL types LEFT_PRIMARY_THUMB, LEFT_SECONDARY_THUMB, + LEFT_PRIMARY_THUMB_TOUCH, + LEFT_SECONDARY_THUMB_TOUCH, + LS_TOUCH, + LEFT_THUMB_UP, + RIGHT_PRIMARY_THUMB, RIGHT_SECONDARY_THUMB, + RIGHT_PRIMARY_THUMB_TOUCH, + RIGHT_SECONDARY_THUMB_TOUCH, + RS_TOUCH, + RIGHT_THUMB_UP, LEFT_PRIMARY_INDEX, LEFT_SECONDARY_INDEX, + LEFT_PRIMARY_INDEX_TOUCH, + LEFT_SECONDARY_INDEX_TOUCH, + LEFT_INDEX_POINT, RIGHT_PRIMARY_INDEX, RIGHT_SECONDARY_INDEX, + RIGHT_PRIMARY_INDEX_TOUCH, + RIGHT_SECONDARY_INDEX_TOUCH, + RIGHT_INDEX_POINT, LEFT_GRIP, + LEFT_GRIP_TOUCH, RIGHT_GRIP, + RIGHT_GRIP_TOUCH, NUM_STANDARD_BUTTONS }; From 02288e84e8017943f2507d4be87dc11985f39712 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 15:35:30 -0800 Subject: [PATCH 31/44] CR feedback --- libraries/controllers/src/controllers/StandardController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 5b68f38374..fadbeee326 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -55,8 +55,8 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(LS, "LS"), makePair(RS, "RS"), - makePair(LS, "LS_TOUCH"), - makePair(RS, "RS_TOUCH"), + makePair(LS_TOUCH, "LSTouch"), + makePair(RS_TOUCH, "RSTouch"), // Center buttons makePair(START, "Start"), From b77945d430d4bd09e8dff43e6bef56a3d7a6cc47 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Mon, 2 Nov 2015 15:50:27 -0800 Subject: [PATCH 32/44] Vive - fix input drifting Conflicts: libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp --- interface/resources/controllers/vive.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 6a3c562e44..c46bcd9402 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,15 +1,15 @@ { "name": "Vive to Standard", "channels": [ - { "from": "Vive.LY", "filters": [ "invert", { "type": "deadZone", "min": 0.7 } ], "to": "Standard.LY" }, - { "from": "Vive.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" }, + { "from": "Vive.LY", "when": "Vive.LS", "filters": "invert", "to": "Standard.LY" }, + { "from": "Vive.LX", "when": "Vive.LS", "to": "Standard.LX" }, { "from": "Vive.LT", "to": "Standard.LT" }, { "from": "Vive.LB", "to": "Standard.LB" }, { "from": "Vive.LS", "to": "Standard.LS" }, - { "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" }, - { "from": "Vive.RX", "to": "Standard.RX" }, + { "from": "Vive.RY", "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" }, + { "from": "Vive.RX", "when": "Vive.RS", "to": "Standard.RX" }, { "from": "Vive.RT", "to": "Standard.RT" }, { "from": "Vive.RB", "to": "Standard.RB" }, From 0a5f43bba78732ad3d31bc031bcf21c5fde4dbad Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 16:16:13 -0800 Subject: [PATCH 33/44] fix up boolean test --- examples/controllers/handControllerGrab.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b882777048..74c830616a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -18,7 +18,7 @@ Script.include("../libraries/utils.js"); // // add lines where the hand ray picking is happening // -var WANT_DEBUG = false; +var WANT_DEBUG = true; // // these tune time-averaging and "on" value for analog trigger @@ -352,14 +352,14 @@ function MyController(hand) { var intersection = Entities.findRayIntersection(pickRayBacked, true); - if (intersection.intersects && intersection.properties.locked === 0) { + if (intersection.intersects && !intersection.properties.locked) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; //this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - if (grabbableData["turnOffOppositeBeam"] === true) { + if (grabbableData["turnOffOppositeBeam"]) { if (this.hand === RIGHT_HAND) { disabledHand = LEFT_HAND; } else { @@ -369,7 +369,7 @@ function MyController(hand) { disabledHand = 'none'; } - if (grabbableData.grabbable === false) { + if (!grabbableData.grabbable) { this.grabbedEntity = null; continue; } @@ -391,7 +391,7 @@ function MyController(hand) { this.grabbedEntity = null; } else { // the hand is far from the intersected object. go into distance-holding mode - if (intersection.properties.collisionsWillMove === 1) { + if (intersection.properties.collisionsWillMove) { this.setState(STATE_DISTANCE_HOLDING); } else { this.setState(STATE_FAR_GRABBING_NON_COLLIDING); @@ -409,7 +409,7 @@ function MyController(hand) { for (i = 0; i < nearbyEntities.length; i++) { var grabbableDataForCandidate = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (grabbableDataForCandidate.grabbable === false) { + if (!grabbableDataForCandidate.grabbable) { continue; } var propsForCandidate = @@ -427,7 +427,7 @@ function MyController(hand) { } if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } else if (props.locked === 0) { + } else if (!props.locked) { this.setState(STATE_NEAR_GRABBING); } } @@ -569,7 +569,7 @@ function MyController(hand) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); var turnOffOtherHand = grabbableData["turnOffOtherHand"]; - if (turnOffOtherHand === true) { + if (turnOffOtherHand) { //don't activate the second hand grab because the script is handling the second hand logic return; } @@ -782,11 +782,11 @@ function MyController(hand) { // we haven't been touched before, but either right or left is touching us now _this.allTouchedIDs[id] = true; _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === true) { + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { // we have been touched before and are still being touched // continue touch _this.continueTouch(id); - } else if (_this.allTouchedIDs[id] === true) { + } else if (_this.allTouchedIDs[id]) { delete _this.allTouchedIDs[id]; _this.stopTouch(id); From fe2c0effd04099c4c6cd256cb74af6b40f0667ef Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 16:17:46 -0800 Subject: [PATCH 34/44] oops left debug on --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 74c830616a..30c99f43f8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -18,7 +18,7 @@ Script.include("../libraries/utils.js"); // // add lines where the hand ray picking is happening // -var WANT_DEBUG = true; +var WANT_DEBUG = false; // // these tune time-averaging and "on" value for analog trigger From 764ecba2a8bb337f0131a42e0de1f70a756a1db8 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 21:55:35 -0800 Subject: [PATCH 35/44] Adding diagnostics to track down the 'black rectangles of death' --- interface/src/avatar/Avatar.cpp | 29 ++++++++++++++++++++++++----- libraries/gpu/src/gpu/Batch.cpp | 22 ++++++++++++++++++++++ libraries/gpu/src/gpu/Batch.h | 28 ++++++++++++++++++++++++++++ libraries/gpu/src/gpu/GLBackend.cpp | 22 ++++++++++++++++++++++ libraries/gpu/src/gpu/GLBackend.h | 3 +++ 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ae3aec3572..675a71a2f4 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -73,6 +73,7 @@ namespace render { avatarPtr->setDisplayingLookatTarget(renderLookAtTarget); if (avatarPtr->isInitialized() && args) { + PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload"); avatarPtr->render(args, qApp->getCamera()->getPosition()); } } @@ -334,6 +335,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } auto& batch = *renderArgs->_batch; + PROFILE_RANGE_BATCH(batch, __FUNCTION__); if (glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get(); @@ -360,6 +362,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } if (havePosition && haveRotation) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":leftHandPointer"); Transform pointerTransform; pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); @@ -383,6 +386,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } if (havePosition && haveRotation) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":rightHandPointer"); Transform pointerTransform; pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); @@ -455,6 +459,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } @@ -464,6 +469,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { static const float INDICATOR_RADIUS = 0.03f; static const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + INDICATOR_OFFSET, _position.z); + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderFocusIndicator"); Transform transform; transform.setTranslation(position); transform.postScale(INDICATOR_RADIUS); @@ -472,6 +478,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { // If the avatar is looking at me, indicate that they are if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderLookingAtMe"); const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; const float LOOKING_AT_ME_ALPHA_START = 0.8f; const float LOOKING_AT_ME_DURATION = 0.5f; // seconds @@ -517,6 +524,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float MIN_VOICE_SPHERE_DISTANCE = 12.0f; if (Menu::getInstance()->isOptionChecked(MenuOption::BlueSpeechSphere) && distanceToTarget > MIN_VOICE_SPHERE_DISTANCE) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderVoiceSphere"); // render voice intensity sphere for avatars that are farther away const float MAX_SPHERE_ANGLE = 10.0f * RADIANS_PER_DEGREE; @@ -684,6 +692,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) { glm::vec2 texCoordBottomRight(1.0f, 1.0f); gpu::Batch& batch = *renderArgs->_batch; + PROFILE_RANGE_BATCH(batch, __FUNCTION__); batch.setResourceTexture(0, _billboardTexture->getGPUTexture()); DependencyManager::get()->bindSimpleProgram(batch, true); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, @@ -766,6 +775,8 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, cons } void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const { + PROFILE_RANGE_BATCH(batch, __FUNCTION__); + bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return @@ -816,17 +827,24 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co // Test on extent above insures abs(height) > 0.0f textTransform.postScale(1.0f / height); batch.setModelTransform(textTransform); - - DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true); - DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, - bevelDistance, backgroundColor); + + { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); + DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true); + DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, + bevelDistance, backgroundColor); + } + // Render actual name QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); // Render text slightly in front to avoid z-fighting textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize())); batch.setModelTransform(textTransform); - renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); + { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); + renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); + } } } @@ -1089,6 +1107,7 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g points << p1a << p1b << p2a << p1b << p2a << p2b; } + PROFILE_RANGE_BATCH(batch, __FUNCTION__); // TODO: this is really inefficient constantly recreating these vertices buffers. It would be // better if the avatars cached these buffers for each of the joints they are rendering geometryCache->updateVertices(_jointConesID, points, color); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index c3e186a630..2e3ea7fc36 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -22,6 +22,14 @@ ProfileRange::ProfileRange(const char *name) { ProfileRange::~ProfileRange() { nvtxRangePop(); } + +ProfileRangeBatch::ProfileRangeBatch(gpu::Batch& batch, const char *name) : _batch(batch) { + _batch.pushProfileRange(name); +} + +ProfileRangeBatch::~ProfileRangeBatch() { + _batch.popProfileRange(); +} #endif #define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); @@ -391,3 +399,17 @@ QDebug& operator<<(QDebug& debug, const Batch::CacheState& cacheState) { return debug; } + +// Debugging +void Batch::pushProfileRange(const char* name) { +#if defined(NSIGHT_FOUND) + ADD_COMMAND(pushProfileRange); + _params.push_back(_profileRanges.cache(name)); +#endif +} + +void Batch::popProfileRange() { +#if defined(NSIGHT_FOUND) + ADD_COMMAND(popProfileRange); +#endif +} diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index deb70f7a68..8397f92da6 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -229,6 +229,10 @@ public: // Reset the stage caches and states void resetStages(); + // Debugging + void pushProfileRange(const char* name); + void popProfileRange(); + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API @@ -324,6 +328,9 @@ public: COMMAND_glColor4f, + COMMAND_pushProfileRange, + COMMAND_popProfileRange, + NUM_COMMANDS, }; typedef std::vector Commands; @@ -389,6 +396,7 @@ public: typedef Cache::Vector PipelineCaches; typedef Cache::Vector FramebufferCaches; typedef Cache::Vector QueryCaches; + typedef Cache::Vector ProfileRangeCaches; typedef Cache>::Vector LambdaCache; // Cache Data in a byte array if too big to fit in Param @@ -416,6 +424,7 @@ public: FramebufferCaches _framebuffers; QueryCaches _queries; LambdaCache _lambdas; + ProfileRangeCaches _profileRanges; NamedBatchDataMap _namedData; @@ -429,6 +438,25 @@ protected: } +#if defined(NSIGHT_FOUND) + +class ProfileRangeBatch { +public: + ProfileRangeBatch(gpu::Batch& batch, const char *name); + ~ProfileRangeBatch(); + +private: + gpu::Batch& _batch; +}; + +#define PROFILE_RANGE_BATCH(batch, name) ProfileRangeBatch profileRangeThis(batch, name); + +#else + +#define PROFILE_RANGE_BATCH(batch, name) + +#endif + QDebug& operator<<(QDebug& debug, const gpu::Batch::CacheState& cacheState); #endif diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 19edbaee5f..e49a3ba6c0 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -15,6 +15,11 @@ #include #include +#if defined(NSIGHT_FOUND) +#include "nvToolsExt.h" +#endif + + using namespace gpu; GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = @@ -69,6 +74,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUniformMatrix4fv), (&::gpu::GLBackend::do_glColor4f), + + (&::gpu::GLBackend::do_pushProfileRange), + (&::gpu::GLBackend::do_popProfileRange), }; void GLBackend::init() { @@ -710,3 +718,17 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) { } (void) CHECK_GL_ERROR(); } + + +void GLBackend::do_pushProfileRange(Batch& batch, uint32 paramOffset) { +#if defined(NSIGHT_FOUND) + auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + nvtxRangePush(name.c_str()); +#endif +} + +void GLBackend::do_popProfileRange(Batch& batch, uint32 paramOffset) { +#if defined(NSIGHT_FOUND) + nvtxRangePop(); +#endif +} diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 6c359d81f1..9f1e17205c 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -479,6 +479,9 @@ protected: void do_glColor4f(Batch& batch, uint32 paramOffset); + void do_pushProfileRange(Batch& batch, uint32 paramOffset); + void do_popProfileRange(Batch& batch, uint32 paramOffset); + typedef void (GLBackend::*CommandCall)(Batch&, uint32); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; }; From eeabc9b093384731431df63c8efaaf30ef8ade57 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 23:49:03 -0800 Subject: [PATCH 36/44] Removing iviewhmd due to broken production builds --- interface/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fecdfe64fb..930bdbd7ce 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD") +set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK") if(WIN32) list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient") From 27b239176f32cc82b9dfc95baa07a11050800ef1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 3 Nov 2015 10:16:56 -0800 Subject: [PATCH 37/44] fix grab --- examples/controllers/handControllerGrab.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 30c99f43f8..edb9fe05bd 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -369,7 +369,7 @@ function MyController(hand) { disabledHand = 'none'; } - if (!grabbableData.grabbable) { + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { this.grabbedEntity = null; continue; } @@ -379,7 +379,6 @@ function MyController(hand) { } if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); } else { From 73d7fd4648208cf8c201105129b009ed55164ddb Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 3 Nov 2015 19:19:12 +0100 Subject: [PATCH 38/44] Fix: Vec3.UP was pointed to Vectors::UNIT_X This fixes the scripting property Vec3.UP to point to Vectors::UP which is equal to {X: 0, Y: 1, Z: 0}. --- libraries/script-engine/src/Vec3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 17a5afd09a..71b6cf3872 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -84,7 +84,7 @@ private: const glm::vec3& TWO() { return Vectors::TWO; } const glm::vec3& HALF() { return Vectors::HALF; } const glm::vec3& RIGHT() { return Vectors::RIGHT; } - const glm::vec3& UP() { return Vectors::UNIT_X; } + const glm::vec3& UP() { return Vectors::UP; } const glm::vec3& FRONT() { return Vectors::FRONT; } }; From 12ff9734e05391cfe3124cd3f24c6fe4a1ffbf8c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 3 Nov 2015 11:26:49 -0800 Subject: [PATCH 39/44] init _bodySensorMatrix in ctor --- interface/src/avatar/MyAvatar.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 00870c62c6..4376aa6d79 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -121,6 +121,12 @@ MyAvatar::MyAvatar(RigPointer rig) : connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation); _characterController.setEnabled(true); + + _hmdSensorMatrix = glm::mat4(); + _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); + _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); + _bodySensorMatrix = deriveBodyFromHMDSensor(); + updateSensorToWorldMatrix(); } MyAvatar::~MyAvatar() { From 4c076d8b128d1984dca520ec5c8bba28ca56a333 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 3 Nov 2015 11:34:05 -0800 Subject: [PATCH 40/44] Disabling billboard rendering, source of the evil black rectangles --- interface/src/avatar/Avatar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 675a71a2f4..b979334383 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -661,6 +661,9 @@ void Avatar::updateJointMappings() { } void Avatar::renderBillboard(RenderArgs* renderArgs) { + // FIXME disabling the billboard because it doesn't appear to work reliably + // the billboard is ending up with a random texture and position. + return; if (_billboard.isEmpty()) { return; } From e160e2a7ae88a1d7ca632fa6c15ad69196811987 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 3 Nov 2015 11:37:30 -0800 Subject: [PATCH 41/44] the minimal fix --- interface/src/avatar/MyAvatar.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4376aa6d79..a5b39ef6e9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -122,11 +122,7 @@ MyAvatar::MyAvatar(RigPointer rig) : this, &MyAvatar::goToLocation); _characterController.setEnabled(true); - _hmdSensorMatrix = glm::mat4(); - _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); - _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); _bodySensorMatrix = deriveBodyFromHMDSensor(); - updateSensorToWorldMatrix(); } MyAvatar::~MyAvatar() { From b83b24b096a35d217d944844229405f6bf64628c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 3 Nov 2015 13:42:18 -0800 Subject: [PATCH 42/44] remove strange test file --- examples/tests/timerStressTest.js | 68 ------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 examples/tests/timerStressTest.js diff --git a/examples/tests/timerStressTest.js b/examples/tests/timerStressTest.js deleted file mode 100644 index a1cf76c5a6..0000000000 --- a/examples/tests/timerStressTest.js +++ /dev/null @@ -1,68 +0,0 @@ -// createBoxes.js -// part of bubblewand -// -// Created by James B. Pollack @imgntn -- 09/07/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Loads a wand model and attaches the bubble wand behavior. -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - - - -Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js"); -Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js"); - -var bubbleModel = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx?' + randInt(0, 10000);; -//var scriptURL'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?'+randInt(0,10000); - -//for local testing -//var scriptURL = "http://localhost:8080/scripts/setRecurringTimeout.js?" + randInt(0, 10000); - - -var scriptURL='http://hifi-public.s3.amazonaws.com/james/debug/timeouts/setRecurringTimeout.js?'+ randInt(0, 10000); -//create the wand in front of the avatar - -var boxes=[]; -var TEST_ENTITY_NAME = "TimerScript"; - - -var TOTAL_ENTITIES = 100; -for (var i = 0; i < TOTAL_ENTITIES; i++) { - var box = Entities.addEntity({ - type: "Box", - name: TEST_ENTITY_NAME, - position: { - x: randInt(0, 100) - 50 + MyAvatar.position.x, - y: randInt(0, 100) - 50 + MyAvatar.position.x, - z: randInt(0, 100) - 50 + MyAvatar.position.x, - }, - dimensions: { - x: 1, - y: 1, - z: 1, - }, - color: { - red: 255, - green: 0, - blue: 0, - }, - //must be enabled to be grabbable in the physics engine - collisionsWillMove: true, - shapeType: 'box', - lifetime:60, - script: scriptURL - }); - boxes.push(box) -} - - -function cleanup() { - while (boxes.length > 0) { - Entities.deleteEntity(boxes.pop()); - - } -} - - -Script.scriptEnding.connect(cleanup); \ No newline at end of file From d544b7a645a779d95caed858b36b1440a5f9dc5a Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Tue, 3 Nov 2015 13:59:15 -0800 Subject: [PATCH 43/44] fix bug: avatar can't walking when HMD at rest --- interface/src/avatar/MyAvatar.cpp | 37 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a5b39ef6e9..741a82fdb8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -347,23 +347,6 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } void MyAvatar::updateHMDFollowVelocity() { - bool isMoving; - if (_lastIsMoving) { - const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec - isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; - } else { - const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec - isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; - } - - bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; - _lastIsMoving = isMoving; - - bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); - if (hmdIsAtRest || justStartedMoving) { - _isFollowingHMD = true; - } - // compute offset to body's target position (in sensor-frame) auto sensorBodyMatrix = deriveBodyFromHMDSensor(); _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); @@ -372,13 +355,29 @@ void MyAvatar::updateHMDFollowVelocity() { // don't pull the body DOWN to match the target (allow animation system to squat) truncatedOffset.y = 0.0f; } + float truncatedOffsetDistance = glm::length(truncatedOffset); + + bool isMoving; + if (_lastIsMoving) { + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; + } else { + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; + } + bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; + _lastIsMoving = isMoving; + bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); + const float MIN_HMD_HIP_SHIFT = 0.05f; + if (justStartedMoving || (hmdIsAtRest && truncatedOffsetDistance > MIN_HMD_HIP_SHIFT)) { + _isFollowingHMD = true; + } bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); if (!needNewFollowSpeed) { // check to see if offset has exceeded its threshold - float distance = glm::length(truncatedOffset); const float MAX_HMD_HIP_SHIFT = 0.2f; - if (distance > MAX_HMD_HIP_SHIFT) { + if (truncatedOffsetDistance > MAX_HMD_HIP_SHIFT) { _isFollowingHMD = true; needNewFollowSpeed = true; } From 8346ce27b322aa2d422045b6a4a97d7a0d309ab3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 3 Nov 2015 14:22:41 -0800 Subject: [PATCH 44/44] allow a non-colliding grab of locked entities --- examples/controllers/handControllerGrab.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index edb9fe05bd..936a00db07 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -291,7 +291,7 @@ function MyController(hand) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { + this.triggerSqueezed = function() { var triggerValue = this.rawTriggerValue; return triggerValue > TRIGGER_ON_VALUE; }; @@ -352,7 +352,7 @@ function MyController(hand) { var intersection = Entities.findRayIntersection(pickRayBacked, true); - if (intersection.intersects && !intersection.properties.locked) { + if (intersection.intersects) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; @@ -381,7 +381,7 @@ function MyController(hand) { // the hand is very close to the intersected object. go into close-grabbing mode. if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } else { + } else if (!intersection.properties.locked) { this.setState(STATE_NEAR_GRABBING); } } else { @@ -390,7 +390,8 @@ function MyController(hand) { this.grabbedEntity = null; } else { // the hand is far from the intersected object. go into distance-holding mode - if (intersection.properties.collisionsWillMove) { + if (intersection.properties.collisionsWillMove + && !intersection.properties.locked) { this.setState(STATE_DISTANCE_HOLDING); } else { this.setState(STATE_FAR_GRABBING_NON_COLLIDING); @@ -502,7 +503,7 @@ function MyController(hand) { // How far did the avatar turn this timestep? // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. + // that returns the minimum quaternion between two quaternions. var currentOrientation = MyAvatar.orientation; if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { var negativeCurrentOrientation = {