diff --git a/examples/sit.js b/examples/sit.js index 34a589bf99..70cb086e36 100644 --- a/examples/sit.js +++ b/examples/sit.js @@ -75,7 +75,7 @@ function updateJoints(factor){ for (var i = 0; i < startPoseAndTransition.length; i++){ var scaledTransition = Vec3.multiply(startPoseAndTransition[i].transition, factor); var rotation = Vec3.sum(startPoseAndTransition[i].start, scaledTransition); - MyAvatar.setJointData(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); + MyAvatar.setJointRotation(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); } } @@ -282,7 +282,8 @@ function update(deltaTime){ MyAvatar.position.z != avatarOldPosition.z || locationChanged) { avatarOldPosition = MyAvatar.position; - + + /* var SEARCH_RADIUS = 50; var foundModels = Entities.findEntities(MyAvatar.position, SEARCH_RADIUS); // Let's remove indicator that got out of radius @@ -306,6 +307,7 @@ function update(deltaTime){ if (hiddingSeats && passedTime >= animationLenght) { showIndicators(true); } + */ } } var oldHost = location.hostname; diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 8e5953fa69..ae470509be 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -104,7 +104,7 @@ "data": { "alpha": 0.0, "joints": [ - { "var": "lean", "jointName": "Spine" } + { "type": "absoluteRotation", "jointName": "Spine", "var": "lean" } ] }, "children": [] diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b07d15b414..dcdb6d4dcb 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -605,9 +605,9 @@ void SkeletonModel::computeBoundingShape() { */ // AJT: REMOVE HARDCODED BOUNDING VOLUME - _boundingCapsuleRadius = 0.5f; - _boundingCapsuleHeight = 2.0f; - _boundingCapsuleLocalOffset = glm::vec3(0.0f, 1.0f, 0.0f); + _boundingCapsuleRadius = 0.3f; + _boundingCapsuleHeight = 1.3f; + _boundingCapsuleLocalOffset = glm::vec3(0.0f, -0.25f, 0.0f); } void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 8b9089f450..e8f796f4dd 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -46,39 +46,15 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, floa if (jointVar.jointIndex >= 0) { - AnimPose defaultAbsPose; + // use the underPose as our default value if we can. AnimPose defaultRelPose; - AnimPose parentAbsPose = AnimPose::identity; if (jointVar.jointIndex <= (int)underPoses.size()) { - - // jointVar is an absolute rotation, if it is not set we will use the underPose as our default value defaultRelPose = underPoses[jointVar.jointIndex]; - defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); - defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); - - // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. - int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); - if (parentIndex >= 0) { - parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses); - } - } else { - - // jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value defaultRelPose = AnimPose::identity; - defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); - defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); - - // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose - // here we use the bind pose - int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); - if (parentIndex >= 0) { - parentAbsPose = _skeleton->getAbsoluteBindPose(parentIndex); - } } - // convert from absolute to relative - AnimPose relPose = parentAbsPose.inverse() * defaultAbsPose; + AnimPose relPose = computeRelativePoseFromJointVar(animVars, jointVar, defaultRelPose, underPoses); // blend with underPose ::blend(1, &defaultRelPose, &relPose, _alpha, &_poses[jointVar.jointIndex]); @@ -114,3 +90,44 @@ const AnimPoseVec& AnimManipulator::getPosesInternal() const { void AnimManipulator::addJointVar(const JointVar& jointVar) { _jointVars.push_back(jointVar); } + +void AnimManipulator::removeAllJointVars() { + _jointVars.clear(); +} + +AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap& animVars, const JointVar& jointVar, + const AnimPose& defaultRelPose, const AnimPoseVec& underPoses) { + + AnimPose defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); + + if (jointVar.type == JointVar::Type::AbsoluteRotation || jointVar.type == JointVar::Type::AbsolutePosition) { + + if (jointVar.type == JointVar::Type::AbsoluteRotation) { + defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); + } else if (jointVar.type == JointVar::Type::AbsolutePosition) { + defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); + } + + // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. + AnimPose parentAbsPose = AnimPose::identity; + int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); + if (parentIndex >= 0) { + parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses); + } + + // convert from absolute to relative + return parentAbsPose.inverse() * defaultAbsPose; + + } else { + + // override the default rel pose + AnimPose relPose = defaultRelPose; + if (jointVar.type == JointVar::Type::RelativeRotation) { + relPose.rot = animVars.lookup(jointVar.var, defaultRelPose.rot); + } else if (jointVar.type == JointVar::Type::RelativePosition) { + relPose.trans = animVars.lookup(jointVar.var, defaultRelPose.trans); + } + + return relPose; + } +} diff --git a/libraries/animation/src/AnimManipulator.h b/libraries/animation/src/AnimManipulator.h index eca1a4aa71..8534b9c269 100644 --- a/libraries/animation/src/AnimManipulator.h +++ b/libraries/animation/src/AnimManipulator.h @@ -30,19 +30,33 @@ public: virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; struct JointVar { - JointVar(const QString& varIn, const QString& jointNameIn) : var(varIn), jointName(jointNameIn), jointIndex(-1), hasPerformedJointLookup(false) {} + enum class Type { + AbsoluteRotation = 0, + AbsolutePosition, + RelativeRotation, + RelativePosition, + NumTypes + }; + + JointVar(const QString& varIn, const QString& jointNameIn, Type typeIn) : var(varIn), jointName(jointNameIn), type(typeIn), jointIndex(-1), hasPerformedJointLookup(false) {} QString var = ""; QString jointName = ""; + Type type = Type::AbsoluteRotation; int jointIndex = -1; bool hasPerformedJointLookup = false; + bool isRelative = false; }; void addJointVar(const JointVar& jointVar); + void removeAllJointVars(); protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; + AnimPose computeRelativePoseFromJointVar(const AnimVariantMap& animVars, const JointVar& jointVar, + const AnimPose& defaultRelPose, const AnimPoseVec& underPoses); + AnimPoseVec _poses; float _alpha; QString _alphaVar; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 83f72b9b5a..568da8dd63 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -55,6 +55,41 @@ static const char* animNodeTypeToString(AnimNode::Type type) { return nullptr; } +static AnimNode::Type stringToAnimNodeType(const QString& str) { + // O(n), move to map when number of types becomes large. + const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); + for (int i = 0; i < NUM_TYPES; i++) { + AnimNode::Type type = static_cast(i); + if (str == animNodeTypeToString(type)) { + return type; + } + } + return AnimNode::Type::NumTypes; +} + +static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) { + switch (type) { + case AnimManipulator::JointVar::Type::AbsoluteRotation: return "absoluteRotation"; + case AnimManipulator::JointVar::Type::AbsolutePosition: return "absolutePosition"; + case AnimManipulator::JointVar::Type::RelativeRotation: return "relativeRotation"; + case AnimManipulator::JointVar::Type::RelativePosition: return "relativePosition"; + case AnimManipulator::JointVar::Type::NumTypes: return nullptr; + }; + return nullptr; +} + +static AnimManipulator::JointVar::Type stringToAnimManipulatorJointVarType(const QString& str) { + // O(n), move to map when number of types becomes large. + const int NUM_TYPES = static_cast(AnimManipulator::JointVar::Type::NumTypes); + for (int i = 0; i < NUM_TYPES; i++) { + AnimManipulator::JointVar::Type type = static_cast(i); + if (str == animManipulatorJointVarTypeToString(type)) { + return type; + } + } + return AnimManipulator::JointVar::Type::NumTypes; +} + static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { switch (type) { case AnimNode::Type::Clip: return loadClipNode; @@ -120,17 +155,6 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { } \ float NAME = (float)NAME##_VAL.toDouble() -static AnimNode::Type stringToEnum(const QString& str) { - // O(n), move to map when number of types becomes large. - const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); - for (int i = 0; i < NUM_TYPES; i++) { - AnimNode::Type type = static_cast(i); - if (str == animNodeTypeToString(type)) { - return type; - } - } - return AnimNode::Type::NumTypes; -} static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) { auto idVal = jsonObj.value("id"); @@ -146,7 +170,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr return nullptr; } QString typeStr = typeVal.toString(); - AnimNode::Type type = stringToEnum(typeStr); + AnimNode::Type type = stringToAnimNodeType(typeStr); if (type == AnimNode::Type::NumTypes) { qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; @@ -355,10 +379,17 @@ static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const Q } auto jointObj = jointValue.toObject(); - READ_STRING(var, jointObj, id, jsonUrl, nullptr); + READ_STRING(type, jointObj, id, jsonUrl, nullptr); READ_STRING(jointName, jointObj, id, jsonUrl, nullptr); + READ_STRING(var, jointObj, id, jsonUrl, nullptr); - AnimManipulator::JointVar jointVar(var, jointName); + AnimManipulator::JointVar::Type jointVarType = stringToAnimManipulatorJointVarType(type); + if (jointVarType == AnimManipulator::JointVar::Type::NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad type in \"joints\", id =" << id << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + AnimManipulator::JointVar jointVar(var, jointName, jointVarType); node->addJointVar(jointVar); }; diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 12b2c4624f..eb522735b7 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -78,25 +78,40 @@ void AvatarRig::setHandPosition(int jointIndex, void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setTranslation(translation, priority); - } else { - state.restoreTranslation(1.0f, priority); + + // AJT: LEGACY + { + JointState& state = _jointStates[index]; + if (valid) { + state.setTranslation(translation, priority); + } else { + state.restoreTranslation(1.0f, priority); + } } + + _overrideFlags[index] = true; + _overridePoses[index].trans = translation; } } void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - state.setTranslation(translation, priority); - } else { - state.restoreRotation(1.0f, priority); - state.restoreTranslation(1.0f, priority); + + // AJT: LEGACY + { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + state.setTranslation(translation, priority); + } else { + state.restoreRotation(1.0f, priority); + state.restoreTranslation(1.0f, priority); + } } + + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; + _overridePoses[index].trans = translation; } } diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index 035760883d..9667fc569f 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -13,13 +13,20 @@ void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - // state.setTranslation(translation, priority); - } else { - state.restoreRotation(1.0f, priority); - // state.restoreTranslation(1.0f, priority); + + // AJT: LEGACY + { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + // state.setTranslation(translation, priority); + } else { + state.restoreRotation(1.0f, priority); + // state.restoreTranslation(1.0f, priority); + } } + + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index fa5153473c..f937144491 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -23,16 +23,28 @@ #include "AnimClip.h" #include "IKTarget.h" +#ifdef NDEBUG +#define ASSERT() +#else +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + int* ptr = nullptr; \ + *ptr = 10; \ + } \ + } while (0) +#endif + void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { // find an unused AnimClip clipNode std::shared_ptr clip; if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) { _userAnimState = UserAnimState::A; - clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); + clip = std::dynamic_pointer_cast(_animNode->findByName("userAnimA")); } else if (_userAnimState == UserAnimState::A) { _userAnimState = UserAnimState::B; - clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); + clip = std::dynamic_pointer_cast(_animNode->findByName("userAnimB")); } // set parameters @@ -132,6 +144,10 @@ void Rig::destroyAnimGraph() { _animSkeleton = nullptr; _animLoader = nullptr; _animNode = nullptr; + _relativePoses.clear(); + _absolutePoses.clear(); + _overridePoses.clear(); + _overrideFlags.clear(); } void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, int rootJointIndex, @@ -139,7 +155,18 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { _animSkeleton = std::make_shared(geometry); - _relativePoses.resize(_animSkeleton->getNumJoints()); + + _relativePoses.clear(); + _relativePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + + _absolutePoses.clear(); + _absolutePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + + _overridePoses.clear(); + _overridePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + + _overrideFlags.clear(); + _overrideFlags.resize(_animSkeleton->getNumJoints(), true); // AJT: LEGACY { @@ -264,14 +291,25 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { void Rig::clearJointState(int index) { if (index != -1 && index < _jointStates.size()) { + // AJT: REMOVE + /* JointState& state = _jointStates[index]; state.setRotationInConstrainedFrame(glm::quat(), 0.0f); state.setTranslation(state.getDefaultTranslationInConstrainedFrame(), 0.0f); + */ + _overrideFlags[index] = false; } } void Rig::clearJointStates() { - _jointStates.clear(); + // AJT: LEGACY + /* + { + _jointStates.clear(); + } + */ + _overrideFlags.clear(); + _overrideFlags.resize(_animSkeleton->getNumJoints()); } void Rig::clearJointAnimationPriority(int index) { @@ -375,12 +413,33 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return glm::mat4(); } + + /* + // AJT: test if _absolutePoses are the same as jointTransforms + glm::mat4 newMat = _absolutePoses[jointIndex]; + glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); + + const float EPSILON = 0.0001f; + if (glm::length(newMat[0] - oldMat[0]) > EPSILON || + glm::length(newMat[1] - oldMat[1]) > EPSILON || + glm::length(newMat[2] - oldMat[2]) > EPSILON || + glm::length(newMat[3] - oldMat[3]) > EPSILON) { + + // error? + qCDebug(animation) << "AJT: mismatch for joint[" << jointIndex; + qCDebug(animation) << "AJT: oldMat = " << AnimPose(oldMat); + qCDebug(animation) << "AJT: newMat = " << AnimPose(newMat); + + } + */ + + // AJT: LEGACY return _jointStates[jointIndex].getTransform(); } void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { - assert(referenceSpeeds.size() > 0); + ASSERT(referenceSpeeds.size() > 0); // calculate alpha from linear combination of referenceSpeeds. float alpha = 0.0f; @@ -664,33 +723,38 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { // evaluate the animation AnimNode::Triggers triggersOut; - AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); + _relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); + if (_relativePoses.size() != _animSkeleton->getNumJoints()) { + // animations haven't been loaded yet. + _relativePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + } _animVars.clearTriggers(); for (auto& trigger : triggersOut) { _animVars.setTrigger(trigger); } - clearJointStatePriorities(); + // AJT: LEGACY + { + clearJointStatePriorities(); - // copy poses into jointStates - const float PRIORITY = 1.0f; - for (size_t i = 0; i < poses.size(); i++) { - setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, 1.0f); - setJointTranslation((int)i, true, poses[i].trans, PRIORITY); + // copy poses into jointStates + const float PRIORITY = 1.0f; + for (size_t i = 0; i < _relativePoses.size(); i++) { + setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * _relativePoses[i].rot, PRIORITY, 1.0f); + setJointTranslation((int)i, true, _relativePoses[i].trans, PRIORITY); + } } } - // AJT: REMOVE - /* - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, rootTransform); - } - */ setModelOffset(rootTransform); + //applyOverridePoses(); buildAbsolutePoses(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); + // AJT: LEGACY + { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); + } } } @@ -1154,12 +1218,29 @@ bool Rig::getModelOffset(glm::vec3& modelOffsetOut) const { } } +void Rig::applyOverridePoses() { + if (!_animSkeleton) { + return; + } + + ASSERT(_animSkeleton->getNumJoints() == _relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == _overrideFlags.size()); + ASSERT(_animSkeleton->getNumJoints() == _overridePoses.size()); + + for (size_t i = 0; i < _overrideFlags.size(); i++) { + if (_overrideFlags[i]) { + _relativePoses[i] = _overridePoses[i]; + } + } +} + void Rig::buildAbsolutePoses() { if (!_animSkeleton) { return; } - assert(_animSkeleton->getNumJoints() == _relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == _relativePoses.size()); + _absolutePoses.resize(_relativePoses.size()); for (int i = 0; i < (int)_relativePoses.size(); i++) { int parentIndex = _animSkeleton->getParentIndex(i); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index f43f6629e6..2eea59bd7f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS @@ -165,6 +166,7 @@ public: protected: void updateAnimationStateHandlers(); + void applyOverridePoses(); void buildAbsolutePoses(); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); @@ -178,6 +180,8 @@ public: AnimPose _modelOffset; AnimPoseVec _relativePoses; AnimPoseVec _absolutePoses; + AnimPoseVec _overridePoses; + std::vector _overrideFlags; int _rootJointIndex { -1 };