diff --git a/interface/src/ui/AnimStats.cpp b/interface/src/ui/AnimStats.cpp index 47116ea281..2a355e48d1 100644 --- a/interface/src/ui/AnimStats.cpp +++ b/interface/src/ui/AnimStats.cpp @@ -118,7 +118,7 @@ void AnimStats::updateStats(bool force) { auto prevIter = _prevDebugAlphaMap.find(key); if (prevIter != _prevDebugAlphaMap.end()) { - float prevAlpha = std::get<0>(iter.second); + float prevAlpha = std::get<0>(prevIter->second); if (prevAlpha != alpha) { // change detected: reset timer _animAlphaValueChangedTimers[key] = now; diff --git a/libraries/animation/src/AnimBlendDirectional.cpp b/libraries/animation/src/AnimBlendDirectional.cpp new file mode 100644 index 0000000000..4e7c67f276 --- /dev/null +++ b/libraries/animation/src/AnimBlendDirectional.cpp @@ -0,0 +1,148 @@ +// +// AnimBlendDirectional.cpp +// +// Created by Anthony J. Thibault on Augest 30 2019. +// Copyright (c) 2019 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 "AnimBlendDirectional.h" +#include "GLMHelpers.h" +#include "AnimationLogging.h" +#include "AnimUtil.h" + +AnimBlendDirectional::AnimBlendDirectional(const QString& id, glm::vec3 alpha, const QString& centerId, + const QString& upId, const QString& downId, const QString& leftId, const QString& rightId, + const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId) : + AnimNode(AnimNode::Type::BlendDirectional, id), + _alpha(alpha), + _centerId(centerId), + _upId(upId), + _downId(downId), + _leftId(leftId), + _rightId(rightId), + _upLeftId(upLeftId), + _upRightId(upRightId), + _downLeftId(downLeftId), + _downRightId(downRightId) { + +} + +AnimBlendDirectional::~AnimBlendDirectional() { + +} + +const AnimPoseVec& AnimBlendDirectional::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) { + + // lookupRaw don't transform the vector. + _alpha = animVars.lookupRaw(_alphaVar, _alpha); + float parentDebugAlpha = context.getDebugAlpha(_id); + + if (_children.size() == 9) { + + // try to keep the order the same as quadrants, for _childIndices. + // +---+---+ + // | 1 | 0 | + // +---+---+ + // | 2 | 3 | + // +---+---+ + + std::array indices; + glm::vec2 alpha = _alpha; + if (_alpha.x > 0.0f) { + if (_alpha.y > 0.0f) { + // quadrant 0 + indices = {{_childIndices[0][2], _childIndices[0][1], _childIndices[1][1], _childIndices[1][2]}}; + } else { + // quadrant 3 + indices = {{_childIndices[1][2], _childIndices[1][1], _childIndices[2][1], _childIndices[2][2]}}; + // shift alpha up so both alpha.x and alpha.y are in the (0, 1) range. + alpha.y += 1.0f; + } + } else { + if (_alpha.y > 0.0f) { + // quadrant 1 + indices = {{_childIndices[0][1], _childIndices[0][0], _childIndices[1][0], _childIndices[1][1]}}; + // shift alpha right so both alpha.x and alpha.y are in the (0, 1) range. + alpha.x += 1.0f; + } else { + // quadrant 2 + indices = {{_childIndices[1][1], _childIndices[1][0], _childIndices[2][0], _childIndices[2][1]}}; + // shift alpha up and right so both alpha.x and alpha.y are in the (0, 1) range. + alpha.x += 1.0f; + alpha.y += 1.0f; + } + } + std::array alphas = {{ + alpha.x * alpha.y, + (1.0f - alpha.x) * alpha.y, + (1.0f - alpha.x) * (1.0f - alpha.y), + alpha.x * (1.0f - alpha.y) + }}; + + // evaluate children + std::array poseVecs; + for (int i = 0; i < 4; i++) { + poseVecs[i] = _children[indices[i]]->evaluate(animVars, context, dt, triggersOut); + } + + // blend children + size_t minSize = INT_MAX; + for (int i = 0; i < 4; i++) { + if (poseVecs[i].size() < minSize) { + minSize = poseVecs[i].size(); + } + } + _poses.resize(minSize); + blend4(minSize, &poseVecs[0][0], &poseVecs[1][0], &poseVecs[2][0], &poseVecs[3][0], &alphas[0], &_poses[0]); + + // animation stack debug stats + for (int i = 0; i < 9; i++) { + context.setDebugAlpha(_children[i]->getID(), 0.0f, _children[i]->getType()); + } + for (int i = 0; i < 4; i++) { + context.setDebugAlpha(_children[indices[i]]->getID(), alphas[i] * parentDebugAlpha, _children[indices[i]]->getType()); + } + + } else { + for (auto&& pose : _poses) { + pose = AnimPose::identity; + } + } + + return _poses; +} + +// for AnimDebugDraw rendering +const AnimPoseVec& AnimBlendDirectional::getPosesInternal() const { + return _poses; +} + +bool AnimBlendDirectional::lookupChildIds() { + _childIndices[0][0] = findChildIndexByName(_upLeftId); + _childIndices[0][1] = findChildIndexByName(_upId); + _childIndices[0][2] = findChildIndexByName(_upRightId); + + _childIndices[1][0] = findChildIndexByName(_leftId); + _childIndices[1][1] = findChildIndexByName(_centerId); + _childIndices[1][2] = findChildIndexByName(_rightId); + + _childIndices[2][0] = findChildIndexByName(_downLeftId); + _childIndices[2][1] = findChildIndexByName(_downId); + _childIndices[2][2] = findChildIndexByName(_downRightId); + + // manditory children + // TODO: currently all are manditory. + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (_childIndices[i][j] == -1) { + qDebug(animation) << "Error in AnimBlendDirectional::lookupChildIds() could not find child[" << i << "," << j << "]"; + return false; + } + } + } + + return true; +} diff --git a/libraries/animation/src/AnimBlendDirectional.h b/libraries/animation/src/AnimBlendDirectional.h new file mode 100644 index 0000000000..9053012a79 --- /dev/null +++ b/libraries/animation/src/AnimBlendDirectional.h @@ -0,0 +1,59 @@ +// +// AnimBlendDirectonal.h +// +// Created by Anthony J. Thibault on Augest 30 2019. +// Copyright (c) 2019 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_AnimBlendDirectional_h +#define hifi_AnimBlendDirectional_h + +#include "AnimNode.h" + +// blend between up to nine AnimNodes. + +class AnimBlendDirectional : public AnimNode { +public: + friend class AnimTests; + + AnimBlendDirectional(const QString& id, glm::vec3 alpha, const QString& centerId, + const QString& upId, const QString& downId, const QString& leftId, const QString& rightId, + const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId); + virtual ~AnimBlendDirectional() override; + + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; + + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + + bool lookupChildIds(); + +protected: + // for AnimDebugDraw rendering + virtual const AnimPoseVec& getPosesInternal() const override; + + AnimPoseVec _poses; + + glm::vec3 _alpha; + QString _centerId; + QString _upId; + QString _downId; + QString _leftId; + QString _rightId; + QString _upLeftId; + QString _upRightId; + QString _downLeftId; + QString _downRightId; + + QString _alphaVar; + + int _childIndices[3][3]; + + // no copies + AnimBlendDirectional(const AnimBlendDirectional&) = delete; + AnimBlendDirectional& operator=(const AnimBlendDirectional&) = delete; +}; + +#endif // hifi_AnimBlendDirectional_h diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index c066dc92eb..d861f07847 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -110,11 +110,9 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c // copy translation and scale from nextPoses AnimPose pose = nextPoses[i]; - int parentIndex = _skeleton->getParentIndex((int)i); - if (parentIndex >= 0) { - // but transform nextPoses rot into absPrev parent frame. - pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot(); - } + // convert from a rotation that happens in the absolute space of the joint + // into a rotation that happens in the relative space of the joint. + pose.rot() = glm::inverse(absPrev[i].rot()) * pose.rot() * absPrev[i].rot(); relOffsetPoses.push_back(pose); } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 96bc87d738..3476adc80c 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -56,7 +56,7 @@ static void bakeRelativeDeltaAnim(std::vector& anim, const AnimPose // for each joint in animPoses for (size_t i = 0; i < animPoses.size(); ++i) { // convert this relative AnimPose into a delta animation. - animPoses[i] = animPoses[i] * invBasePoses[i]; + animPoses[i] = invBasePoses[i] * animPoses[i]; } } } @@ -80,13 +80,11 @@ void bakeAbsoluteDeltaAnim(std::vector& anim, const AnimPoseVec& ba for (size_t i = 0; i < animPoses.size(); ++i) { // scale and translation are relative frame - animPoses[i] = animPoses[i] * invBasePoses[i]; + animPoses[i] = invBasePoses[i] * animPoses[i]; - // but transform the rotation delta into the absolute frame. - int parentIndex = skeleton->getParentIndex((int)i); - if (parentIndex >= 0) { - animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[parentIndex].rot()); - } + // convert from a rotation that happens in the relative space of the joint + // into a rotation that happens in the absolute space of the joint. + animPoses[i].rot() = absBasePoses[i].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[i].rot()); } } } diff --git a/libraries/animation/src/AnimContext.h b/libraries/animation/src/AnimContext.h index 2ee8a30980..68b44c8f56 100644 --- a/libraries/animation/src/AnimContext.h +++ b/libraries/animation/src/AnimContext.h @@ -31,6 +31,7 @@ enum class AnimNodeType { TwoBoneIK, SplineIK, PoleVectorConstraint, + BlendDirectional, NumTypes }; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index a0fb51e891..3ec671fcd7 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -99,6 +99,15 @@ public: return result; } + int findChildIndexByName(const QString& id) { + for (size_t i = 0; i < _children.size(); ++i) { + if (_children[i]->getID() == id) { + return (int)i; + } + } + return -1; + } + const AnimPoseVec& getPoses() const { return getPosesInternal(); } protected: diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 100269c55c..9474c0309f 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -29,6 +29,7 @@ #include "AnimTwoBoneIK.h" #include "AnimSplineIK.h" #include "AnimPoleVectorConstraint.h" +#include "AnimBlendDirectional.h" #include "AnimUtil.h" using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); @@ -47,6 +48,7 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadSplineIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f; @@ -55,6 +57,7 @@ static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f; 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); bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const char* animNodeTypeToString(AnimNode::Type type) { switch (type) { @@ -70,6 +73,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) { case AnimNode::Type::TwoBoneIK: return "twoBoneIK"; case AnimNode::Type::SplineIK: return "splineIK"; case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint"; + case AnimNode::Type::BlendDirectional: return "blendDirectional"; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -211,6 +215,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode; case AnimNode::Type::SplineIK: return loadSplineIKNode; case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode; + case AnimNode::Type::BlendDirectional: return loadBlendDirectionalNode; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -230,6 +235,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { case AnimNode::Type::TwoBoneIK: return processDoNothing; case AnimNode::Type::SplineIK: return processDoNothing; case AnimNode::Type::PoleVectorConstraint: return processDoNothing; + case AnimNode::Type::BlendDirectional: return processBlendDirectionalNode; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -402,7 +408,6 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& auto tempUrl = QUrl(url); tempUrl = jsonUrl.resolved(tempUrl); - // AJT: AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value if (!blendType.isEmpty()) { blendTypeEnum = stringToAnimBlendType(blendType); @@ -772,6 +777,32 @@ static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj return node; } +static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { + + READ_VEC3(alpha, jsonObj, id, jsonUrl, nullptr); + READ_OPTIONAL_STRING(alphaVar, jsonObj); + + READ_OPTIONAL_STRING(centerId, jsonObj); + READ_OPTIONAL_STRING(upId, jsonObj); + READ_OPTIONAL_STRING(downId, jsonObj); + READ_OPTIONAL_STRING(leftId, jsonObj); + READ_OPTIONAL_STRING(rightId, jsonObj); + READ_OPTIONAL_STRING(upLeftId, jsonObj); + READ_OPTIONAL_STRING(upRightId, jsonObj); + READ_OPTIONAL_STRING(downLeftId, jsonObj); + READ_OPTIONAL_STRING(downRightId, jsonObj); + + auto node = std::make_shared(id, alpha, centerId, + upId, downId, leftId, rightId, + upLeftId, upRightId, downLeftId, downRightId); + + if (!alphaVar.isEmpty()) { + node->setAlphaVar(alphaVar); + } + + return node; +} + void buildChildMap(std::map& map, AnimNode::Pointer node) { for (int i = 0; i < (int)node->getChildCount(); ++i) { map.insert(std::pair(node->getChild(i)->getID(), i)); @@ -1042,7 +1073,12 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje return true; } +bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { + auto blendNode = std::static_pointer_cast(node); + assert(blendNode); + return blendNode->lookupChildIds(); +} AnimNodeLoader::AnimNodeLoader(const QUrl& url) : _url(url) diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index e5f7cf4182..8830cb78b1 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -26,6 +26,31 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A } } +void blend3(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, float* alphas, AnimPose* result) { + for (size_t i = 0; i < numPoses; i++) { + const AnimPose& aPose = a[i]; + const AnimPose& bPose = b[i]; + const AnimPose& cPose = c[i]; + + result[i].scale() = alphas[0] * aPose.scale() + alphas[1] * bPose.scale() + alphas[2] * cPose.scale(); + result[i].rot() = safeLinearCombine3(aPose.rot(), bPose.rot(), cPose.rot(), alphas); + result[i].trans() = alphas[0] * aPose.trans() + alphas[1] * bPose.trans() + alphas[2] * cPose.trans(); + } +} + +void blend4(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, const AnimPose* d, float* alphas, AnimPose* result) { + for (size_t i = 0; i < numPoses; i++) { + const AnimPose& aPose = a[i]; + const AnimPose& bPose = b[i]; + const AnimPose& cPose = c[i]; + const AnimPose& dPose = d[i]; + + result[i].scale() = alphas[0] * aPose.scale() + alphas[1] * bPose.scale() + alphas[2] * cPose.scale() + alphas[3] * dPose.scale(); + result[i].rot() = safeLinearCombine4(aPose.rot(), bPose.rot(), cPose.rot(), dPose.rot(), alphas); + result[i].trans() = alphas[0] * aPose.trans() + alphas[1] * bPose.trans() + alphas[2] * cPose.trans() + alphas[3] * dPose.trans(); + } +} + // additive blend void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { @@ -43,8 +68,7 @@ void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha delta = -delta; } delta = glm::lerp(identity, delta, alpha); - result[i].rot() = glm::normalize(delta * aPose.rot()); - + result[i].rot() = glm::normalize(aPose.rot() * delta); result[i].trans() = aPose.trans() + (alpha * bPose.trans()); } } diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 5d67b27abb..00bddb8b7e 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -16,6 +16,12 @@ // this is where the magic happens void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); +// blend between three sets of poses +void blend3(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, float* alphas, AnimPose* result); + +// blend between four sets of poses +void blend4(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, const AnimPose* d, float* alphas, AnimPose* result); + // additive blending void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); @@ -34,6 +40,41 @@ inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) { return glm::normalize(glm::lerp(a, bTemp, alpha)); } +inline glm::quat safeLinearCombine3(const glm::quat& a, const glm::quat& b, const glm::quat& c, float* alphas) { + // adjust signs for b & c if necessary + glm::quat bTemp = b; + float dot = glm::dot(a, bTemp); + if (dot < 0.0f) { + bTemp = -bTemp; + } + glm::quat cTemp = c; + dot = glm::dot(a, cTemp); + if (dot < 0.0f) { + cTemp = -cTemp; + } + return glm::normalize(alphas[0] * a + alphas[1] * bTemp + alphas[2] * cTemp); +} + +inline glm::quat safeLinearCombine4(const glm::quat& a, const glm::quat& b, const glm::quat& c, const glm::quat& d, float* alphas) { + // adjust signs for b, c & d if necessary + glm::quat bTemp = b; + float dot = glm::dot(a, bTemp); + if (dot < 0.0f) { + bTemp = -bTemp; + } + glm::quat cTemp = c; + dot = glm::dot(a, cTemp); + if (dot < 0.0f) { + cTemp = -cTemp; + } + glm::quat dTemp = d; + dot = glm::dot(a, dTemp); + if (dot < 0.0f) { + dTemp = -dTemp; + } + return glm::normalize(alphas[0] * a + alphas[1] * bTemp + alphas[2] * cTemp + alphas[3] * dTemp); +} + AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone); // This will attempt to determine the proper body facing of a characters body diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 930dbed2d9..eb9e595c88 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -141,32 +141,26 @@ std::map AnimVariantMap::toDebugMap() const { break; case AnimVariant::Type::Vec3: { // To prevent filling up debug stats, don't show vec3 values - /* glm::vec3 value = pair.second.getVec3(); result[pair.first] = QString("(%1, %2, %3)"). arg(QString::number(value.x, 'f', 3)). arg(QString::number(value.y, 'f', 3)). arg(QString::number(value.z, 'f', 3)); - */ break; } case AnimVariant::Type::Quat: { // To prevent filling up the anim stats, don't show quat values - /* glm::quat value = pair.second.getQuat(); result[pair.first] = QString("(%1, %2, %3, %4)"). arg(QString::number(value.x, 'f', 3)). arg(QString::number(value.y, 'f', 3)). arg(QString::number(value.z, 'f', 3)). arg(QString::number(value.w, 'f', 3)); - */ break; } case AnimVariant::Type::String: // To prevent filling up anim stats, don't show string values - /* result[pair.first] = pair.second.getString(); - */ break; default: // invalid AnimVariant::Type diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index e133f178df..51f843b83c 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -171,6 +171,8 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m return; } + // AJT: DON'T CHECK THIS IN, disable model URL overrides. + /* // only accept an override if this is for a trait type we override // and the version matches what we last sent for skeleton if (traitType == AvatarTraits::SkeletonModelURL @@ -192,6 +194,7 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m } else { message->seek(message->getPosition() + traitBinarySize); } + */ } } }