diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 3800a330bb..aba74806c0 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1416,6 +1416,10 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) {
}
}
+void MyAvatar::setDebugDrawAnimPoseName(QString poseName) {
+ _debugDrawAnimPoseName.set(poseName);
+}
+
void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) {
if (isEnabled) {
const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f);
@@ -3086,15 +3090,26 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
}
if (_enableDebugDrawAnimPose && animSkeleton) {
- // build absolute AnimPoseVec from rig
+
AnimPoseVec absPoses;
const Rig& rig = _skeletonModel->getRig();
- absPoses.reserve(rig.getJointStateCount());
- for (int i = 0; i < rig.getJointStateCount(); i++) {
- absPoses.push_back(AnimPose(rig.getJointTransform(i)));
+ const glm::vec4 CYAN(0.1f, 0.6f, 0.6f, 1.0f);
+
+ QString name = _debugDrawAnimPoseName.get();
+ if (name.isEmpty()) {
+ // build absolute AnimPoseVec from rig transforms. i.e. the same that are used for rendering.
+ absPoses.reserve(rig.getJointStateCount());
+ for (int i = 0; i < rig.getJointStateCount(); i++) {
+ absPoses.push_back(AnimPose(rig.getJointTransform(i)));
+ }
+ AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, CYAN);
+ } else {
+ AnimNode::ConstPointer node = rig.findAnimNodeByName(name);
+ if (node) {
+ rig.buildAbsoluteRigPoses(node->getPoses(), absPoses);
+ AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, CYAN);
+ }
}
- glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f);
- AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan);
}
}
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index d60c023caa..f31e6e524e 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -2018,12 +2018,20 @@ public slots:
void setEnableDebugDrawDefaultPose(bool isEnabled);
/**jsdoc
- * Displays animation debug graphics.
+ * Displays animation debug graphics. By default it shows the animation poses used for rendering.
+ * However, the property MyAvatar.setDebugDrawAnimPoseName can be used to draw a specific animation node.
* @function MyAvatar.setEnableDebugDrawAnimPose
* @param {boolean} enabled - true
to show the debug graphics, false
to hide.
*/
void setEnableDebugDrawAnimPose(bool isEnabled);
+ /**jsdoc
+ * If set it determines which animation debug graphics to draw, when MyAvatar.setEnableDebugDrawAnimPose is set to true.
+ * @function MyAvatar.setDebugDrawAnimPoseName
+ * @param {boolean} enabled - true
to show the debug graphics, false
to hide.
+ */
+ void setDebugDrawAnimPoseName(QString poseName);
+
/**jsdoc
* Displays position debug graphics.
* @function MyAvatar.setEnableDebugDrawPosition
@@ -2666,6 +2674,8 @@ private:
bool _enableDebugDrawIKChains { false };
bool _enableDebugDrawDetailedCollision { false };
+ ThreadSafeValueCache _debugDrawAnimPoseName;
+
mutable bool _cauterizationNeedsUpdate { false }; // do we need to scan children and update their "cauterized" state?
AudioListenerMode _audioListenerMode;
diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp
index 1a922e507d..e5edb46f69 100644
--- a/libraries/animation/src/AnimClip.cpp
+++ b/libraries/animation/src/AnimClip.cpp
@@ -10,10 +10,13 @@
#include "AnimClip.h"
+#include
+
#include "GLMHelpers.h"
#include "AnimationLogging.h"
#include "AnimUtil.h"
+
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
AnimNode(AnimNode::Type::Clip, id),
_startFrame(startFrame),
@@ -107,6 +110,19 @@ static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, cons
return jointIndexMap;
}
+#ifdef USE_CUSTOM_ASSERT
+#undef ASSERT
+#define ASSERT(x) \
+ do { \
+ if (!(x)) { \
+ int* bad_ptr = 0; \
+ *bad_ptr = 0x0badf00d; \
+ } \
+ } while (0)
+#else
+#define ASSERT assert
+#endif
+
void AnimClip::copyFromNetworkAnim() {
assert(_networkAnim && _networkAnim->isLoaded() && _skeleton);
_anim.clear();
@@ -165,11 +181,14 @@ void AnimClip::copyFromNetworkAnim() {
for (int frame = 0; frame < animFrameCount; frame++) {
const HFMAnimationFrame& animFrame = animModel.animationFrames[frame];
+ ASSERT(frame >= 0 && frame < (int)animModel.animationFrames.size());
// extract the full rotations from the animFrame (including pre and post rotations from the animModel).
std::vector animRotations;
animRotations.reserve(animJointCount);
for (int i = 0; i < animJointCount; i++) {
+ ASSERT(i >= 0 && i < (int)animModel.joints.size());
+ ASSERT(i >= 0 && i < (int)animFrame.rotations.size());
animRotations.push_back(animModel.joints[i].preRotation * animFrame.rotations[i] * animModel.joints[i].postRotation);
}
@@ -180,10 +199,12 @@ void AnimClip::copyFromNetworkAnim() {
std::vector avatarRotations;
avatarRotations.reserve(avatarJointCount);
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
+ ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarToAnimJointIndexMap.size());
int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex];
if (animJointIndex >= 0) {
// This joint is in both animation and avatar.
// Set the absolute rotation directly
+ ASSERT(animJointIndex >= 0 && animJointIndex < (int)animRotations.size());
avatarRotations.push_back(animRotations[animJointIndex]);
} else {
// This joint is NOT in the animation at all.
@@ -192,6 +213,7 @@ void AnimClip::copyFromNetworkAnim() {
glm::quat avatarParentAbsoluteRot;
int avatarParentJointIndex = avatarSkeleton->getParentIndex(avatarJointIndex);
if (avatarParentJointIndex >= 0) {
+ ASSERT(avatarParentJointIndex >= 0 && avatarParentJointIndex < (int)avatarRotations.size());
avatarParentAbsoluteRot = avatarRotations[avatarParentJointIndex];
}
avatarRotations.push_back(avatarParentAbsoluteRot * avatarRelativeDefaultRot);
@@ -201,6 +223,7 @@ void AnimClip::copyFromNetworkAnim() {
// convert avatar rotations into relative frame
avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations);
+ ASSERT(frame >= 0 && frame < (int)_anim.size());
_anim[frame].reserve(avatarJointCount);
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex);
@@ -209,12 +232,15 @@ void AnimClip::copyFromNetworkAnim() {
glm::vec3 relativeScale = avatarDefaultPose.scale();
glm::vec3 relativeTranslation;
+ ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarToAnimJointIndexMap.size());
int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex];
if (animJointIndex >= 0) {
// This joint is in both animation and avatar.
+ ASSERT(animJointIndex >= 0 && animJointIndex < (int)animFrame.translations.size());
const glm::vec3& animTrans = animFrame.translations[animJointIndex];
// retarget translation from animation to avatar
+ ASSERT(animJointIndex >= 0 && animJointIndex < (int)animModel.animationFrames[0].translations.size());
const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex];
relativeTranslation = avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans);
} else {
@@ -224,6 +250,7 @@ void AnimClip::copyFromNetworkAnim() {
}
// build the final pose
+ ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarRotations.size());
_anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation));
}
}
diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h
index 31e10ca2d5..ec5f2b7375 100644
--- a/libraries/animation/src/AnimNode.h
+++ b/libraries/animation/src/AnimNode.h
@@ -98,6 +98,8 @@ public:
return result;
}
+ const AnimPoseVec& getPoses() const { return getPosesInternal(); }
+
protected:
virtual void setCurrentFrameInternal(float frame) {}
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index 633a505d14..a58b8745d7 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -2232,6 +2232,14 @@ void Rig::initAnimGraph(const QUrl& url) {
}
}
+AnimNode::ConstPointer Rig::findAnimNodeByName(const QString& name) const {
+ if (_animNode) {
+ return _animNode->findByName(name);
+ } else {
+ return nullptr;
+ }
+}
+
bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const {
if (_animSkeleton && _rootJointIndex >= 0) {
modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteDefaultPose(_rootJointIndex).trans();
@@ -2258,7 +2266,7 @@ void Rig::applyOverridePoses() {
}
}
-void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) {
+void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) const {
DETAILED_PERFORMANCE_TIMER("buildAbsolute");
if (!_animSkeleton) {
return;
diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h
index 786d14200e..02fa965f64 100644
--- a/libraries/animation/src/Rig.h
+++ b/libraries/animation/src/Rig.h
@@ -196,6 +196,7 @@ public:
void initAnimGraph(const QUrl& url);
AnimNode::ConstPointer getAnimNode() const { return _animNode; }
+ AnimNode::ConstPointer findAnimNodeByName(const QString& name) const;
AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList);
void removeAnimationStateHandler(QScriptValue handler);
@@ -243,7 +244,7 @@ public:
Flow& getFlow() { return _internalFlow; }
float getUnscaledEyeHeight() const;
-
+ void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) const;
signals:
void onLoadComplete();
@@ -252,7 +253,6 @@ protected:
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
void updateAnimationStateHandlers();
void applyOverridePoses();
- void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut);
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,