diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1d14a0f464..26e218d05b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -45,6 +45,7 @@ #include "Util.h" #include "world.h" #include "InterfaceLogging.h" +#include "EntityRig.h" using namespace std; @@ -965,7 +966,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { if (_unusedAttachments.size() > 0) { model = _unusedAttachments.takeFirst(); } else { - model = new Model(this); + model = new Model(std::make_shared(), this); } model->init(); _attachmentModels.append(model); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 730c77e9c3..ead330a41f 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -35,6 +35,7 @@ #include "Menu.h" #include "MyAvatar.h" #include "SceneScriptingInterface.h" +#include "AvatarRig.h" // 70 times per second - target is 60hz, but this helps account for any small deviations // in the update loop @@ -65,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) : { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); - _myAvatar = std::make_shared(std::make_shared()); + _myAvatar = std::make_shared(std::make_shared()); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(std::make_shared(std::make_shared())); + return AvatarSharedPointer(std::make_shared(std::make_shared())); } // virtual diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 16b370d459..4a5eadccd3 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -16,9 +16,11 @@ #include "Head.h" #include "Menu.h" -FaceModel::FaceModel(Head* owningHead) : +FaceModel::FaceModel(Head* owningHead, RigPointer rig) : + Model(rig, nullptr), _owningHead(owningHead) { + assert(_rig); } void FaceModel::simulate(float deltaTime, bool fullUpdate) { @@ -48,54 +50,58 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } } -void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { +void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * - glm::translate(state.getDefaultTranslationInConstrainedFrame()) * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * joint.preTransform * glm::mat4_cast(joint.preRotation))); glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), _owningHead->getTorsoTwist(), _owningHead->getFinalLeanSideways())); pitchYawRoll -= lean; - state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) - * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) - * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) - * joint.rotation, DEFAULT_PRIORITY); + _rig->setJointRotationInConstrainedFrame(index, + glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) + * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) + * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) + * joint.rotation, DEFAULT_PRIORITY); } -void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) { +void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) { // likewise with the eye joints // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. - glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * - glm::translate(state.getDefaultTranslationInConstrainedFrame()) * - joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * + joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f)); glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() + _owningHead->getSaccade() - model->getTranslation(), 1.0f)); glm::quat between = rotationBetween(front, lookAt); const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation, DEFAULT_PRIORITY); + _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between), + -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation, DEFAULT_PRIORITY); } void FaceModel::updateJointState(int index) { - JointState& state = _jointStates[index]; + const JointState& state = _rig->getJointState(index); const FBXJoint& joint = state.getFBXJoint(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // guard against out-of-bounds access to _jointStates - if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(joint.parentIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) { + const JointState& parentState = _rig->getJointState(joint.parentIndex); if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint, index); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(this, parentState, joint, state); + maybeUpdateEyeRotation(this, parentState, joint, index); } } - Model::updateJointState(index); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->updateFaceJointState(index, parentTransform); } bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index 6c14beb587..ce78c51e70 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -22,12 +22,12 @@ class FaceModel : public Model { public: - FaceModel(Head* owningHead); + FaceModel(Head* owningHead, RigPointer rig); virtual void simulate(float deltaTime, bool fullUpdate = true); - virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state); + virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); + virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); virtual void updateJointState(int index); /// Retrieve the positions of up to two eye meshes. diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 43e68557ce..0d07f171c0 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -23,6 +23,7 @@ #include "Util.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" +#include "AvatarRig.h" using namespace std; @@ -55,11 +56,10 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), - _faceModel(this), + _faceModel(this, std::make_shared()), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { - } void Head::init() { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c8e6f03d69..765dc9160e 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -30,7 +30,7 @@ enum StandingFootState { }; SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : - Model(parent, rig), + Model(rig, parent), _triangleFanID(DependencyManager::get()->allocateID()), _owningAvatar(owningAvatar), _boundingShape(), @@ -51,11 +51,13 @@ SkeletonModel::~SkeletonModel() { } void SkeletonModel::initJointStates(QVector states) { - Model::initJointStates(states); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _boundingRadius = _rig->initJointStates(states, parentTransform); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = _geometry->getFBXGeometry().headJointIndex; - if (0 <= headJointIndex && headJointIndex < _jointStates.size()) { + if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) { glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); @@ -75,8 +77,8 @@ void SkeletonModel::initJointStates(QVector states) { // the SkeletonModel override of updateJointState() will clear the translation part // of its root joint and we need that done before we try to build shapes hence we // recompute all joint transforms at this time. - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); + for (int i = 0; i < _rig->getJointStateCount(); i++) { + _rig->updateJointState(i, parentTransform); } clearShapes(); @@ -168,7 +170,7 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) || jointIndex == getRightHandJointIndex()) { // get all shapes that have this hand as an ancestor in the skeleton heirarchy const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { + for (int i = 0; i < _rig->getJointStateCount(); i++) { const FBXJoint& joint = geometry.joints[i]; int parentIndex = joint.parentIndex; Shape* shape = _shapes[i]; @@ -211,7 +213,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) { } void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } // NOTE: 'position' is in model-frame @@ -226,16 +228,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) if (forearmLength < EPSILON) { return; } - JointState& state = _jointStates[jointIndex]; - glm::quat handRotation = state.getRotation(); + glm::quat handRotation; + if (!_rig->getJointStateRotation(jointIndex, handRotation)) { + return; + } // align hand with forearm float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; - state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY); + _rig->applyJointRotationDelta(jointIndex, + rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), + true, PALM_PRIORITY); } void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -261,43 +267,40 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f); setJointPosition(parentJointIndex, palmPosition + forearm, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); - JointState& parentState = _jointStates[parentJointIndex]; - parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity - _jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY); + _rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY); } else { inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } } void SkeletonModel::updateJointState(int index) { - if (index < 0 && index >= _jointStates.size()) { - return; // bail - } - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(joint.parentIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + + const JointState joint = _rig->getJointState(index); + if (joint.getParentIndex() >= 0 && joint.getParentIndex() < _rig->getJointStateCount()) { + const JointState parentState = _rig->getJointState(joint.getParentIndex()); if (index == geometry.leanJointIndex) { - maybeUpdateLeanRotation(parentState, state); - + maybeUpdateLeanRotation(parentState, index); + } else if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint.getFBXJoint(), index); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(parentState, joint, state); + maybeUpdateEyeRotation(parentState, joint.getFBXJoint(), index); } } - Model::updateJointState(index); + _rig->updateJointState(index, parentTransform); if (index == _geometry->getFBXGeometry().rootJointIndex) { - state.clearTransformTranslation(); + _rig->clearJointTransformTranslation(index); } } -void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) { +void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, int index) { if (!_owningAvatar->isMyAvatar()) { return; } @@ -305,24 +308,24 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, Joint glm::vec3 xAxis(1.0f, 0.0f, 0.0f); glm::vec3 yAxis(0.0f, 1.0f, 0.0f); glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame()); - state.setRotationInConstrainedFrame( - glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis) - * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis) - * glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis) - * state.getFBXJoint().rotation, LEAN_PRIORITY); + glm::quat inverse = glm::inverse(parentState.getRotation() * _rig->getJointDefaultRotationInParentFrame(index)); + _rig->setJointRotationInConstrainedFrame(index, + glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis) + * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis) + * glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis) + * _rig->getJointState(index).getFBXJoint().rotation, LEAN_PRIORITY); } -void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state); +void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { + _owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, index); } -void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state); +void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index) { + _owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, index); } void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -331,9 +334,11 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { batch._glLineWidth(3.0f); do { const FBXJoint& joint = geometry.joints.at(jointIndex); - const JointState& jointState = _jointStates.at(jointIndex); + const JointState& jointState = _rig->getJointState(jointIndex); glm::vec3 position = _rotation * jointState.getPosition() + _translation; - glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _rotation * _jointStates.at(joint.parentIndex).getRotation(); + glm::quat parentRotation = (joint.parentIndex == -1) ? + _rotation : + _rotation * _rig->getJointState(joint.parentIndex).getRotation(); float fanScale = directionSize * 0.75f; Transform transform = Transform(); @@ -464,14 +469,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); - JointState& shoulderState = _jointStates[shoulderJointIndex]; - shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY); - - JointState& elbowState = _jointStates[elbowJointIndex]; - elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY); - - JointState& handState = _jointStates[jointIndex]; - handState.setRotationInBindFrame(rotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(elbowJointIndex, + rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * + shoulderRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY); } bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const { @@ -526,7 +528,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP glm::quat worldFrameRotation; bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation); if (success) { - neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation; + neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation; } return success; } @@ -636,7 +638,7 @@ float VERY_BIG_MASS = 1.0e6f; // virtual void SkeletonModel::buildShapes() { - if (_geometry == NULL || _jointStates.isEmpty()) { + if (_geometry == NULL || _rig->jointStatesEmpty()) { return; } @@ -647,9 +649,8 @@ void SkeletonModel::buildShapes() { } float uniformScale = extractUniformScale(_scale); - const int numStates = _jointStates.size(); - for (int i = 0; i < numStates; i++) { - JointState& state = _jointStates[i]; + for (int i = 0; i < _rig->getJointStateCount(); i++) { + const JointState& state = _rig->getJointState(i); const FBXJoint& joint = state.getFBXJoint(); float radius = uniformScale * joint.boneRadius; float halfHeight = 0.5f * uniformScale * joint.distanceToParent; @@ -683,7 +684,7 @@ void SkeletonModel::buildShapes() { void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms - int numStates = _jointStates.size(); + int numStates = _rig->getJointStateCount(); assert(numStates == _shapes.size()); QVector transforms; transforms.fill(glm::mat4(), numStates); @@ -694,11 +695,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { totalExtents.addPoint(glm::vec3(0.0f)); for (int i = 0; i < numStates; i++) { // compute the default transform of this joint - JointState& state = _jointStates[i]; + const JointState& state = _rig->getJointState(i); const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { - transforms[i] = _jointStates[i].getTransform(); + transforms[i] = _rig->getJointTransform(i); } else { glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) @@ -748,7 +749,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); - glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition(); + glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition(); _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); } @@ -853,7 +854,7 @@ void SkeletonModel::cauterizeHead() { if (isActive()) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const int neckJointIndex = geometry.neckJointIndex; - if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) { + if (neckJointIndex > 0 && neckJointIndex < _rig->getJointStateCount()) { // lazy init of headBones if (_headBones.size() == 0) { @@ -861,13 +862,13 @@ void SkeletonModel::cauterizeHead() { } // preserve the translation for the neck - glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; + // glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; + glm::vec4 trans = _rig->getJointTransform(neckJointIndex)[3]; glm::vec4 zero(0, 0, 0, 0); for (const int &i : _headBones) { - JointState& joint = _jointStates[i]; glm::mat4 newXform(zero, zero, zero, trans); - joint.setTransform(newXform); - joint.setVisibleTransform(newXform); + _rig->setJointTransform(i, newXform); + _rig->setJointVisibleTransform(i, newXform); } } } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 6d33b3da7b..c4cd43b4df 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -134,9 +134,9 @@ protected: /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); - void maybeUpdateLeanRotation(const JointState& parentState, JointState& state); - void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); + void maybeUpdateLeanRotation(const JointState& parentState, int index); + void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); + void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index); void cauterizeHead(); void initHeadBones(); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ed15e57d43..78091cd1a6 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -14,7 +14,7 @@ #include "Application.h" ModelOverlay::ModelOverlay() - : _model(), + : _model(nullptr), _modelTextures(QVariantMap()), _updateModel(false) { @@ -24,7 +24,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(), + _model(nullptr), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index 7f45fb600e..d11dcacfc6 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -159,15 +159,15 @@ void AnimationHandle::applyFrame(float frameIndex) { const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); float frameFraction = glm::fract(frameIndex); - QVector jointStates = _rig->getJointStates(); + assert(_rig->getJointStateCount() >= _jointMappings.size()); for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = jointStates[mapping]; - state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), - ceilFrame.rotations.at(i), - frameFraction), - _priority); + _rig->setJointRotationInConstrainedFrame(mapping, + safeMix(floorFrame.rotations.at(i), + ceilFrame.rotations.at(i), + frameFraction), + _priority); } } } @@ -176,10 +176,9 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - QVector jointStates = _rig->getJointStates(); - JointState& state = jointStates[mapping]; + JointState state = _rig->getJointState(mapping); if (_priority == state._animationPriority) { - state._animationPriority = newPriority; + _rig->setJointAnimatinoPriority(mapping, newPriority); } } } @@ -189,9 +188,8 @@ void AnimationHandle::restoreJoints() { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - QVector jointStates = _rig->getJointStates(); - JointState& state = jointStates[mapping]; - state.restoreRotation(1.0f, state._animationPriority); + JointState state = _rig->getJointState(mapping); + _rig->restoreJointRotation(mapping, 1.0f, state._animationPriority); } } } diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp new file mode 100644 index 0000000000..cf05e61cdb --- /dev/null +++ b/libraries/animation/src/AvatarRig.cpp @@ -0,0 +1,51 @@ +// +// AvatarRig.cpp +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AvatarRig.h" + +/// Updates the state of the joint at the specified index. +void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { + if (index < 0 && index >= _jointStates.size()) { + return; // bail + } + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} + + +void AvatarRig::updateFaceJointState(int index, glm::mat4 parentTransform) { + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h new file mode 100644 index 0000000000..dbffd8aa45 --- /dev/null +++ b/libraries/animation/src/AvatarRig.h @@ -0,0 +1,28 @@ +// +// AvatarRig.h +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// Copyright 2015 High Fidelity, Inc. +// +// 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_AvatarRig_h +#define hifi_AvatarRig_h + +#include + +#include "Rig.h" + +class AvatarRig : public Rig { + Q_OBJECT + + public: + ~AvatarRig() {} + virtual void updateJointState(int index, glm::mat4 parentTransform); + virtual void updateFaceJointState(int index, glm::mat4 parentTransform); +}; + +#endif // hifi_AvatarRig_h diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp new file mode 100644 index 0000000000..5ed1799671 --- /dev/null +++ b/libraries/animation/src/EntityRig.cpp @@ -0,0 +1,30 @@ +// +// EntityRig.cpp +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "EntityRig.h" + +/// Updates the state of the joint at the specified index. +void EntityRig::updateJointState(int index, glm::mat4 parentTransform) { + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h new file mode 100644 index 0000000000..aa6a5fbd2b --- /dev/null +++ b/libraries/animation/src/EntityRig.h @@ -0,0 +1,28 @@ +// +// EntityRig.h +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// Copyright 2015 High Fidelity, Inc. +// +// 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_EntityRig_h +#define hifi_EntityRig_h + +#include + +#include "Rig.h" + +class EntityRig : public Rig { + Q_OBJECT + + public: + ~EntityRig() {} + virtual void updateJointState(int index, glm::mat4 parentTransform); + virtual void updateFaceJointState(int index, glm::mat4 parentTransform) { } +}; + +#endif // hifi_EntityRig_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 5e322e420f..12bfd606da 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -13,11 +13,7 @@ #include "Rig.h" -bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { - return _runningAnimations.removeOne(animationHandle); -} - -static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { +void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { handles.insert(it, handle); @@ -27,17 +23,27 @@ static void insertSorted(QList& handles, const Animation handles.append(handle); } +AnimationHandlePointer Rig::createAnimationHandle() { + AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); + _animationHandles.insert(handle); + return handle; +} + +bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { + return _runningAnimations.removeOne(animationHandle); +} + void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { - insertSorted(_runningAnimations, animationHandle); + insertSorted(_runningAnimations, animationHandle); } bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.contains(animationHandle); } -float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states) { +float Rig::initJointStates(QVector states, glm::mat4 parentTransform) { _jointStates = states; - initJointTransforms(scale, offset); + initJointTransforms(parentTransform); int numStates = _jointStates.size(); float radius = 0.0f; @@ -52,11 +58,11 @@ float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVectorgetFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset); // * geometry.offset; XXX state.initTransform(parentTransform); } else { const JointState& parentState = _jointStates.at(parentIndex); @@ -75,19 +78,30 @@ void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) { } } -void Rig::resetJoints() { +void Rig::clearJointTransformTranslation(int jointIndex) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].clearTransformTranslation(); +} + +void Rig::reset(const QVector& fbxJoints) { if (_jointStates.isEmpty()) { return; } - - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint(); - _jointStates[i].setRotationInConstrainedFrame(fbxJoint.rotation, 0.0f); + _jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f); } } -bool Rig::getJointState(int index, glm::quat& rotation) const { +JointState Rig::getJointState(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return JointState(); + } + return _jointStates[jointIndex]; +} + +bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; } @@ -105,12 +119,6 @@ bool Rig::getVisibleJointState(int index, glm::quat& rotation) const { return !state.rotationIsDefault(rotation); } -void Rig::updateVisibleJointStates() { - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } -} - void Rig::clearJointState(int index) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; @@ -122,6 +130,18 @@ void Rig::clearJointStates() { _jointStates.clear(); } +void Rig::clearJointAnimationPriority(int index) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = 0.0f; + } +} + +void Rig::setJointAnimatinoPriority(int index, float newPriority) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = newPriority; + } +} + void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; @@ -133,99 +153,103 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float } } - -void Rig::clearJointAnimationPriority(int index) { +void Rig::restoreJointRotation(int index, float fraction, float priority) { if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; + _jointStates[index].restoreRotation(fraction, priority); } } -AnimationHandlePointer Rig::createAnimationHandle() { - AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); - _animationHandles.insert(handle); - return handle; -} - -bool Rig::getJointStateAtIndex(int jointIndex, JointState& jointState) const { +bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - jointState = _jointStates[jointIndex]; + // position is in world-frame + position = translation + rotation * _jointStates[jointIndex].getPosition(); return true; } -void Rig::updateJointStates(glm::mat4 parentTransform) { +bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in model-frame + position = extractTranslation(_jointStates[jointIndex].getTransform()); + return true; +} + +bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getRotation(); + return true; +} + +bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _jointStates[jointIndex].getRotation(); + return true; +} + +bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getRotation(); + return true; +} + + +bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in world-frame + position = translation + rotation * _jointStates[jointIndex].getVisiblePosition(); + return true; +} + +bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getVisibleRotation(); + return true; +} + +glm::mat4 Rig::getJointTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + return _jointStates[jointIndex].getTransform(); +} + +glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + return _jointStates[jointIndex].getVisibleTransform(); +} + +void Rig::simulateInternal(glm::mat4 parentTransform) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i, parentTransform); } -} - -void Rig::updateJointState(int index, glm::mat4 parentTransform) { - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - - // compute model transforms - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - // glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset) * geometryOffset; - state.computeTransform(parentTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - -void Rig::resetAllTransformsChanged() { for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].resetTransformChanged(); } } -glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInBindFrame(rotation, priority, constrain); - endRotation = state.getRotationInBindFrame(); - return endRotation; -} - -glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInConstrainedFrame(targetRotation, priority, constrain); - endRotation = state.getRotationInConstrainedFrame(); - return endRotation; -} - -void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return; - } - _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority); -} - -bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, - float priority, glm::mat4 parentTransform) { +bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, + const QVector& freeLineage, glm::mat4 parentTransform) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } - - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; - - if (freeLineage.isEmpty()) { return false; } @@ -304,19 +328,14 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm: return true; } -void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, - const glm::quat& targetRotation, float priority, glm::mat4 parentTransform) { +void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, + const QVector& freeLineage, glm::mat4 parentTransform) { // NOTE: targetRotation is from bind- to model-frame if (endIndex == -1 || _jointStates.isEmpty()) { return; } - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[endIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; - if (freeLineage.isEmpty()) { return; } @@ -404,11 +423,11 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, } // recompute transforms from the top down - glm::mat4 parentTransform = topParentTransform; + glm::mat4 currentParentTransform = topParentTransform; for (int j = numFree - 1; j >= 0; --j) { JointState& freeState = _jointStates[freeLineage.at(j)]; - freeState.computeTransform(parentTransform); - parentTransform = freeState.getTransform(); + freeState.computeTransform(currentParentTransform); + currentParentTransform = freeState.getTransform(); } // measure our success @@ -420,14 +439,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, endState.setRotationInBindFrame(targetRotation, priority, true); } -bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) { +bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; foreach (int index, freeLineage) { JointState& state = _jointStates[index]; @@ -436,22 +451,78 @@ bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) { return true; } -float Rig::getLimbLength(int jointIndex, glm::vec3 scale) const { +float Rig::getLimbLength(int jointIndex, const QVector& freeLineage, + const glm::vec3 scale, const QVector& fbxJoints) const { if (jointIndex == -1 || _jointStates.isEmpty()) { return 0.0f; } - - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; - float length = 0.0f; float lengthScale = (scale.x + scale.y + scale.z) / 3.0f; for (int i = freeLineage.size() - 2; i >= 0; i--) { - int something = freeLineage.at(i); - const FBXJoint& fbxJointI = _jointStates[something].getFBXJoint(); - length += fbxJointI.distanceToParent * lengthScale; + length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale; } return length; } + +glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInBindFrame(rotation, priority, constrain); + endRotation = state.getRotationInBindFrame(); + return endRotation; +} + +glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return glm::vec3(); + } + return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); +} + +glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInConstrainedFrame(targetRotation, priority, constrain); + endRotation = state.getRotationInConstrainedFrame(); + return endRotation; +} + +void Rig::updateVisibleJointStates() { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } +} + +void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].setTransform(newTransform); +} + +void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].setVisibleTransform(newTransform); +} + +void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return; + } + _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority); +} + +glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return glm::quat(); + } + return _jointStates[jointIndex].getDefaultRotationInParentFrame(); +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 89ea99f162..87b0c7504e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -11,10 +11,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* TBD: - - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)? + - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)? - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object physics? - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly? - + - Distribute some doc from here to the right files if it turns out to be correct: - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything equivalent to editEntity. */ @@ -37,49 +37,69 @@ typedef std::shared_ptr RigPointer; class Rig : public QObject, public std::enable_shared_from_this { public: + + virtual ~Rig() {} + RigPointer getRigPointer() { return shared_from_this(); } + AnimationHandlePointer createAnimationHandle(); bool removeRunningAnimation(AnimationHandlePointer animationHandle); void addRunningAnimation(AnimationHandlePointer animationHandle); bool isRunningAnimation(AnimationHandlePointer animationHandle); const QList& getRunningAnimations() const { return _runningAnimations; } - float initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states); - void initJointTransforms(glm::vec3 scale, glm::vec3 offset); - void resetJoints(); + float initJointStates(QVector states, glm::mat4 parentTransform); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; - int jointStateCount() const { return _jointStates.size(); } - bool getJointStateAtIndex(int jointIndex, JointState& jointState) const; + int getJointStateCount() const { return _jointStates.size(); } - void updateJointStates(glm::mat4 parentTransform); - void updateJointState(int index, glm::mat4 parentTransform); - void resetAllTransformsChanged(); - - bool getJointState(int index, glm::quat& rotation) const; + void initJointTransforms(glm::mat4 parentTransform); + void clearJointTransformTranslation(int jointIndex); + void reset(const QVector& fbxJoints); + bool getJointStateRotation(int index, glm::quat& rotation) const; + void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); + JointState getJointState(int jointIndex) const; bool getVisibleJointState(int index, glm::quat& rotation) const; - void updateVisibleJointStates(); void clearJointState(int index); void clearJointStates(); - void setJointState(int index, bool valid, const glm::quat& rotation, float priority); void clearJointAnimationPriority(int index); + void setJointAnimatinoPriority(int index, float newPriority); + void setJointState(int index, bool valid, const glm::quat& rotation, float priority); + void restoreJointRotation(int index, float fraction, float priority); + bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const; + + bool getJointPosition(int jointIndex, glm::vec3& position) const; + bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getJointRotation(int jointIndex, glm::quat& rotation) const; + bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const; + bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const; + glm::mat4 getJointTransform(int jointIndex) const; + void setJointTransform(int jointIndex, glm::mat4 newTransform); + glm::mat4 getJointVisibleTransform(int jointIndex) const; + void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); + void simulateInternal(glm::mat4 parentTransform); + bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, + const QVector& freeLineage, glm::mat4 parentTransform); + void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, + const QVector& freeLineage, glm::mat4 parentTransform); + bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); + float getLimbLength(int jointIndex, const QVector& freeLineage, + const glm::vec3 scale, const QVector& fbxJoints) const; + glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false); + glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex); glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain = false); - void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); + glm::quat getJointDefaultRotationInParentFrame(int jointIndex); + void updateVisibleJointStates(); - QVector getJointStates() { return _jointStates; } + virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; + virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0; - AnimationHandlePointer createAnimationHandle(); - - bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, - float priority, glm::mat4 parentTransform); - void inverseKinematics(int endIndex, glm::vec3 targetPosition, - const glm::quat& targetRotation, float priority, glm::mat4 parentTransform); - bool restoreJointPosition(int jointIndex, float fraction, float priority); - float getLimbLength(int jointIndex, glm::vec3 scale) const; - -protected: + protected: QVector _jointStates; QSet _animationHandles; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index adc6ae9de2..2fcdd7f664 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -40,6 +40,7 @@ #include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" #include "AddressManager.h" +#include "EntityRig.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -695,7 +696,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll return model; } - model = new Model(); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); @@ -728,7 +729,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c } // create the model and correctly initialize it with the new url - model = new Model(); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(newUrl)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 2e03266253..bb0a35f7b0 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -67,7 +67,7 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch } Model* RenderableZoneEntityItem::getModel() { - Model* model = new Model(); + Model* model = new Model(nullptr); model->setIsWireframe(true); model->init(); return model; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5a6908fc4a..af60c91664 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -60,7 +60,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; -Model::Model(QObject* parent, RigPointer rig) : +Model::Model(RigPointer rig, QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), @@ -87,7 +87,7 @@ Model::Model(QObject* parent, RigPointer rig) : if (_viewState) { moveToThread(_viewState->getMainThread()); } - + setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); } @@ -252,22 +252,9 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - // compute model transforms - int numStates = _jointStates.size(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.initTransform(parentTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); - } - } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->initJointTransforms(parentTransform); } void Model::init() { @@ -396,14 +383,8 @@ void Model::init() { } void Model::reset() { - if (_jointStates.isEmpty()) { - return; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); - } - + _rig->reset(geometry.joints); _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -436,28 +417,30 @@ bool Model::updateGeometry() { const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector newJointStates = createJointStates(newGeometry); - if (! _jointStates.isEmpty()) { + + if (! _rig->jointStatesEmpty()) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { + it != oldGeometry.jointIndices.constEnd(); it++) { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { - newJointStates[newIndex].copyState(_jointStates[oldIndex]); + newJointStates[newIndex].copyState(_rig->getJointState(oldIndex)); } } - } + } + deleteGeometry(); _dilatedTextures.clear(); setGeometry(geometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid initJointStates(newJointStates); needToRebuild = true; - } else if (_jointStates.isEmpty()) { + } else if (_rig->jointStatesEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); @@ -493,22 +476,9 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates(QVector states) { - _jointStates = states; - initJointTransforms(); - - int numStates = _jointStates.size(); - float radius = 0.0f; - for (int i = 0; i < numStates; ++i) { - float distance = glm::length(_jointStates[i].getPosition()); - if (distance > radius) { - radius = distance; - } - _jointStates[i].buildConstraint(); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } - _boundingRadius = radius; + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _boundingRadius = _rig->initJointStates(states, parentTransform); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -1070,51 +1040,24 @@ glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { return translatedPoint; } - bool Model::getJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + return _rig->getJointStateRotation(index, rotation); } bool Model::getVisibleJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getVisibleRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + return _rig->getVisibleJointState(index, rotation); } void Model::clearJointState(int index) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - state.setRotationInConstrainedFrame(glm::quat(), 0.0f); - } + _rig->clearJointState(index); } void Model::clearJointAnimationPriority(int index) { - if (_rig) { - _rig->clearJointAnimationPriority(index); - } else { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; - } - } + _rig->clearJointAnimationPriority(index); } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); - } - } + _rig->setJointState(index, valid, rotation, priority); } int Model::getParentJointIndex(int jointIndex) const { @@ -1189,62 +1132,31 @@ void Model::setCollisionModelURL(const QUrl& url) { } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getPosition(); - return true; + return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in model-frame - position = extractTranslation(_jointStates[jointIndex].getTransform()); - return true; + return _rig->getJointPosition(jointIndex, position); } bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation); } bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointRotation(jointIndex, rotation); } bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation); } bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); - return true; + return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); - return true; + return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation); } QStringList Model::getJointNames() const { @@ -1438,12 +1350,14 @@ void Model::updateClusterMatrices() { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } } @@ -1457,16 +1371,12 @@ void Model::simulateInternal(float deltaTime) { handle->simulate(deltaTime); } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->simulateInternal(parentTransform); _shapesAreDirty = !_shapes.isEmpty(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -1474,12 +1384,14 @@ void Model::simulateInternal(float deltaTime) { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } } @@ -1492,261 +1404,42 @@ void Model::simulateInternal(float deltaTime) { } void Model::updateJointState(int index) { - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - - // compute model transforms - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.computeTransform(parentTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - -void Model::updateVisibleJointStates() { - if (_showTrueJointTransforms) { - // no need to update visible transforms - return; - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->updateJointState(index, parentTransform); } bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - if (freeLineage.isEmpty()) { - return false; + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + if (_rig->setJointPosition(jointIndex, position, rotation, useRotation, + lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) { + _shapesAreDirty = !_shapes.isEmpty(); + return true; } - if (lastFreeIndex == -1) { - lastFreeIndex = freeLineage.last(); - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - const int ITERATION_COUNT = 1; - glm::vec3 worldAlignment = alignment; - for (int i = 0; i < ITERATION_COUNT; i++) { - // first, try to rotate the end effector as close as possible to the target rotation, if any - glm::quat endRotation; - if (useRotation) { - JointState& state = _jointStates[jointIndex]; - - state.setRotationInBindFrame(rotation, priority); - endRotation = state.getRotationInBindFrame(); - } - - // then, we go from the joint upwards, rotating the end as close as possible to the target - glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform()); - for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) { - int index = freeLineage.at(j); - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - if (!(joint.isFree || allIntermediatesFree)) { - continue; - } - glm::vec3 jointPosition = extractTranslation(state.getTransform()); - glm::vec3 jointVector = endPosition - jointPosition; - glm::quat oldCombinedRotation = state.getRotation(); - glm::quat combinedDelta; - float combinedWeight; - if (useRotation) { - combinedDelta = safeMix(rotation * glm::inverse(endRotation), - rotationBetween(jointVector, position - jointPosition), 0.5f); - combinedWeight = 2.0f; - - } else { - combinedDelta = rotationBetween(jointVector, position - jointPosition); - combinedWeight = 1.0f; - } - if (alignment != glm::vec3() && j > 1) { - jointVector = endPosition - jointPosition; - glm::vec3 positionSum; - for (int k = j - 1; k > 0; k--) { - int index = freeLineage.at(k); - updateJointState(index); - positionSum += extractTranslation(_jointStates.at(index).getTransform()); - } - glm::vec3 projectedCenterOfMass = glm::cross(jointVector, - glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector)); - glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector)); - const float LENGTH_EPSILON = 0.001f; - if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) { - combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment), - 1.0f / (combinedWeight + 1.0f)); - } - } - state.applyRotationDelta(combinedDelta, true, priority); - glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation); - endPosition = actualDelta * jointVector + jointPosition; - if (useRotation) { - endRotation = actualDelta * endRotation; - } - } - } - - // now update the joint states from the top - for (int j = freeLineage.size() - 1; j >= 0; j--) { - updateJointState(freeLineage.at(j)); - } - _shapesAreDirty = !_shapes.isEmpty(); - - return true; + return false; } void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { - // NOTE: targetRotation is from bind- to model-frame - - if (endIndex == -1 || _jointStates.isEmpty()) { - return; - } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - if (freeLineage.isEmpty()) { - return; - } - int numFree = freeLineage.size(); - - // store and remember topmost parent transform - glm::mat4 topParentTransform; - { - int index = freeLineage.last(); - const JointState& state = _jointStates.at(index); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - } else { - topParentTransform = _jointStates[parentIndex].getTransform(); - } - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - - // keep track of the position of the end-effector - JointState& endState = _jointStates[endIndex]; - glm::vec3 endPosition = endState.getPosition(); - float distanceToGo = glm::distance(targetPosition, endPosition); - - const int MAX_ITERATION_COUNT = 2; - const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm - int numIterations = 0; - do { - ++numIterations; - // moving up, rotate each free joint to get endPosition closer to target - for (int j = 1; j < numFree; j++) { - int nextIndex = freeLineage.at(j); - JointState& nextState = _jointStates[nextIndex]; - FBXJoint nextJoint = nextState.getFBXJoint(); - if (! nextJoint.isFree) { - continue; - } - - glm::vec3 pivot = nextState.getPosition(); - glm::vec3 leverArm = endPosition - pivot; - float leverLength = glm::length(leverArm); - if (leverLength < EPSILON) { - continue; - } - glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); - - // We want to mix the shortest rotation with one that will pull the system down with gravity - // so that limbs don't float unrealistically. To do this we compute a simplified center of mass - // where each joint has unit mass and we don't bother averaging it because we only need direction. - if (j > 1) { - - glm::vec3 centerOfMass(0.0f); - for (int k = 0; k < j; ++k) { - int massIndex = freeLineage.at(k); - centerOfMass += _jointStates[massIndex].getPosition() - pivot; - } - // the gravitational effect is a rotation that tends to align the two cross products - const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); - glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), - glm::cross(worldAlignment, leverArm)); - - float gravityAngle = glm::angle(gravityDelta); - const float MIN_GRAVITY_ANGLE = 0.1f; - float mixFactor = 0.5f; - if (gravityAngle < MIN_GRAVITY_ANGLE) { - // the final rotation is a mix of the two - mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; - } - deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); - } - - // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose - // in the process. This provides stability to the IK solution for most models. - glm::quat oldNextRotation = nextState.getRotation(); - float mixFactor = 0.03f; - nextState.mixRotationDelta(deltaRotation, mixFactor, priority); - - // measure the result of the rotation which may have been modified by - // blending and constraints - glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); - endPosition = pivot + actualDelta * leverArm; - } - - // recompute transforms from the top down - glm::mat4 parentTransform = topParentTransform; - for (int j = numFree - 1; j >= 0; --j) { - JointState& freeState = _jointStates[freeLineage.at(j)]; - freeState.computeTransform(parentTransform); - parentTransform = freeState.getTransform(); - } - - // measure our success - endPosition = endState.getPosition(); - distanceToGo = glm::distance(targetPosition, endPosition); - } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); - - // set final rotation of the end joint - endState.setRotationInBindFrame(targetRotation, priority, true); - + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); _shapesAreDirty = !_shapes.isEmpty(); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - - foreach (int index, freeLineage) { - JointState& state = _jointStates[index]; - state.restoreRotation(fraction, priority); - } - return true; + return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); } float Model::getLimbLength(int jointIndex) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return 0.0f; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - float length = 0.0f; - float lengthScale = (_scale.x + _scale.y + _scale.z) / 3.0f; - for (int i = freeLineage.size() - 2; i >= 0; i--) { - length += geometry.joints.at(freeLineage.at(i)).distanceToParent * lengthScale; - } - return length; + return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } void Model::renderJointCollisionShapes(float alpha) { @@ -1816,7 +1509,7 @@ void Model::applyNextGeometry() { void Model::deleteGeometry() { _blendedVertexBuffers.clear(); - _jointStates.clear(); + _rig->clearJointStates(); _meshStates.clear(); clearShapes(); @@ -1945,7 +1638,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran args, locations); { - updateVisibleJointStates(); + if (!_showTrueJointTransforms) { + _rig->updateVisibleJointStates(); + } // else no need to update visible transforms } // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1b4df6821c..f8d8cbbd74 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -64,7 +64,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(QObject* parent = nullptr, RigPointer rig = nullptr); + Model(RigPointer rig, QObject* parent = nullptr); virtual ~Model(); /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension @@ -162,7 +162,7 @@ public: const QSharedPointer getCollisionGeometry(bool delayLoad = true); /// Returns the number of joint states in the model. - int getJointStateCount() const { return _jointStates.size(); } + int getJointStateCount() const { return _rig->getJointStateCount(); } /// Fetches the joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) @@ -224,9 +224,9 @@ public: void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } - QVector& getJointStates() { return _jointStates; } - const QVector& getJointStates() const { return _jointStates; } - + // QVector& getJointStates() { return _rig->getJointStates(); } + // const QVector& getJointStates() const { return _jointStates; } + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) @@ -259,8 +259,6 @@ protected: glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to bool _showTrueJointTransforms; - - QVector _jointStates; class MeshState { public: @@ -283,8 +281,6 @@ protected: /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); - virtual void updateVisibleJointStates(); - /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame