// // Created by Bradley Austin Davis on 2017/04/27 // Copyright 2013-2017 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 "MySkeletonModel.h" #include #include #include #include "Application.h" #include "InterfaceLogging.h" #include "AnimUtil.h" MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) { } Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { switch (state) { default: case CharacterController::State::Ground: return Rig::CharacterControllerState::Ground; case CharacterController::State::Takeoff: return Rig::CharacterControllerState::Takeoff; case CharacterController::State::InAir: return Rig::CharacterControllerState::InAir; case CharacterController::State::Hover: return Rig::CharacterControllerState::Hover; case CharacterController::State::Seated: return Rig::CharacterControllerState::Seated; }; } #if defined(Q_OS_ANDROID) || defined(HIFI_USE_OPTIMIZED_IK) static glm::vec3 computeSpine2WithHeadHipsSpline(MyAvatar* myAvatar, AnimPose hipsIKTargetPose, AnimPose headIKTargetPose) { // the the ik targets to compute the spline with CubicHermiteSplineFunctorWithArcLength splineFinal(headIKTargetPose.rot(), headIKTargetPose.trans(), hipsIKTargetPose.rot(), hipsIKTargetPose.trans()); // measure the total arc length along the spline float totalArcLength = splineFinal.arcLength(1.0f); float tFinal = splineFinal.arcLengthInverse(myAvatar->getSpine2SplineRatio() * totalArcLength); glm::vec3 spine2Translation = splineFinal(tFinal); return spine2Translation + myAvatar->getSpine2SplineOffset(); } #endif static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); // check for pinned hips. auto hipsIndex = myAvatar->getJointIndex("Hips"); if (myAvatar->isJointPinned(hipsIndex)) { Transform avatarTransform = myAvatar->getTransform(); AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180); result.scale() = glm::vec3(1.0f, 1.0f, 1.0f); return result; } glm::mat4 hipsMat; if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState()) && myAvatar->getHMDLeanRecenterEnabled()) { // then we use center of gravity model hipsMat = myAvatar->deriveBodyUsingCgModel(); } else { // otherwise use the default of putting the hips under the head hipsMat = myAvatar->deriveBodyFromHMDSensor(); } glm::vec3 hipsPos = extractTranslation(hipsMat); glm::quat hipsRot = glmExtractRotation(hipsMat); glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix(); glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat; // dampen hips rotation, by mixing it with the avatar orientation in sensor space // turning this off for center of gravity model because it is already mixed in there if (!(myAvatar->getCenterOfGravityModelEnabled())) { const float MIX_RATIO = 0.5f; hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO); } if (isFlying) { // rotate the hips back to match the flying animation. const float TILT_ANGLE = 0.523f; const glm::quat tiltRot = glm::angleAxis(TILT_ANGLE, glm::normalize(transformVectorFast(avatarToSensorMat, -Vectors::UNIT_X))); glm::vec3 headPos; int headIndex = myAvatar->getJointIndex("Head"); if (headIndex != -1) { headPos = transformPoint(avatarToSensorMat, myAvatar->getAbsoluteJointTranslationInObjectFrame(headIndex)); } else { headPos = transformPoint(myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition()); } hipsRot = tiltRot * hipsRot; hipsPos = headPos + tiltRot * (hipsPos - headPos); } // AJT: TODO can we remove this? return AnimPose(hipsRot * Quaternions::Y_180, hipsPos); } // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const HFMModel& hfmModel = getHFMModel(); MyAvatar* myAvatar = static_cast(_owningAvatar); assert(myAvatar); Head* head = _owningAvatar->getHead(); bool eyePosesValid = (myAvatar->getControllerPoseInSensorFrame(controller::Action::LEFT_EYE).isValid() || myAvatar->getControllerPoseInSensorFrame(controller::Action::RIGHT_EYE).isValid()); glm::vec3 lookAt; if (eyePosesValid) { lookAt = head->getLookAtPosition(); // don't apply no-crosseyes code when eyes are being tracked } else { lookAt = avoidCrossedEyes(head->getLookAtPosition()); } Rig::ControllerParameters params; AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f)); glm::mat4 rigToAvatarMatrix = Matrices::Y_180; glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()); glm::mat4 sensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); params.rigToSensorMatrix = glm::inverse(sensorToWorldMatrix) * avatarToWorldMatrix * rigToAvatarMatrix; // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and // down in desktop mode. // preMult 180 is necessary to convert from avatar to rig coordinates. // postMult 180 is necessary to convert head from -z forward to z forward. glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0; } // // primary controller poses, control IK targets directly. // static const std::vector> primaryControllers = { { controller::Action::LEFT_HAND, Rig::PrimaryControllerType_LeftHand }, { controller::Action::RIGHT_HAND, Rig::PrimaryControllerType_RightHand }, { controller::Action::HIPS, Rig::PrimaryControllerType_Hips }, { controller::Action::LEFT_FOOT, Rig::PrimaryControllerType_LeftFoot }, { controller::Action::RIGHT_FOOT, Rig::PrimaryControllerType_RightFoot }, { controller::Action::SPINE2, Rig::PrimaryControllerType_Spine2 } }; for (auto pair : primaryControllers) { auto controllerPose = myAvatar->getControllerPoseInAvatarFrame(pair.first); if (controllerPose.isValid()) { AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.primaryControllerPoses[pair.second] = avatarToRigPose * pose; params.primaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled; } else { params.primaryControllerPoses[pair.second] = AnimPose::identity; params.primaryControllerFlags[pair.second] = 0; } } // // secondary controller poses, influence the pose of the skeleton indirectly. // static const std::vector> secondaryControllers = { { controller::Action::LEFT_SHOULDER, Rig::SecondaryControllerType_LeftShoulder }, { controller::Action::RIGHT_SHOULDER, Rig::SecondaryControllerType_RightShoulder }, { controller::Action::LEFT_ARM, Rig::SecondaryControllerType_LeftArm }, { controller::Action::RIGHT_ARM, Rig::SecondaryControllerType_RightArm }, { controller::Action::LEFT_FORE_ARM, Rig::SecondaryControllerType_LeftForeArm }, { controller::Action::RIGHT_FORE_ARM, Rig::SecondaryControllerType_RightForeArm }, { controller::Action::LEFT_UP_LEG, Rig::SecondaryControllerType_LeftUpLeg }, { controller::Action::RIGHT_UP_LEG, Rig::SecondaryControllerType_RightUpLeg }, { controller::Action::LEFT_LEG, Rig::SecondaryControllerType_LeftLeg }, { controller::Action::RIGHT_LEG, Rig::SecondaryControllerType_RightLeg }, { controller::Action::LEFT_TOE_BASE, Rig::SecondaryControllerType_LeftToeBase }, { controller::Action::RIGHT_TOE_BASE, Rig::SecondaryControllerType_RightToeBase } }; for (auto pair : secondaryControllers) { auto controllerPose = myAvatar->getControllerPoseInAvatarFrame(pair.first); if (controllerPose.isValid()) { AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose; params.secondaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled; } else { params.secondaryControllerPoses[pair.second] = AnimPose::identity; params.secondaryControllerFlags[pair.second] = 0; } } bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS); if (isFlying != _prevIsFlying) { const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f; _flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME; } else { _flyIdleTimer -= deltaTime; } _prevIsFlying = isFlying; // if hips are not under direct control, estimate the hips position. if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) { bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS); // timescale in seconds const float TRANS_HORIZ_TIMESCALE = 0.15f; const float TRANS_VERT_TIMESCALE = 0.01f; // We want the vertical component of the hips to follow quickly to prevent spine squash/stretch. const float ROT_TIMESCALE = 0.15f; const float FLY_IDLE_TRANSITION_TIMESCALE = 0.25f; if (_flyIdleTimer < 0.0f) { _smoothHipsHelper.setHorizontalTranslationTimescale(TRANS_HORIZ_TIMESCALE); _smoothHipsHelper.setVerticalTranslationTimescale(TRANS_VERT_TIMESCALE); _smoothHipsHelper.setRotationTimescale(ROT_TIMESCALE); } else { _smoothHipsHelper.setHorizontalTranslationTimescale(FLY_IDLE_TRANSITION_TIMESCALE); _smoothHipsHelper.setVerticalTranslationTimescale(FLY_IDLE_TRANSITION_TIMESCALE); _smoothHipsHelper.setRotationTimescale(FLY_IDLE_TRANSITION_TIMESCALE); } AnimPose sensorHips = computeHipsInSensorFrame(myAvatar, isFlying); if (!_prevIsEstimatingHips) { _smoothHipsHelper.teleport(sensorHips); } sensorHips = _smoothHipsHelper.update(sensorHips, deltaTime); glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180); AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix()); params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * sensorHips; params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { #if defined(Q_OS_ANDROID) || defined(HIFI_USE_OPTIMIZED_IK) AnimPose headAvatarSpace(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); AnimPose headRigSpace = avatarToRigPose * headAvatarSpace; AnimPose hipsRigSpace = sensorToRigPose * sensorHips; glm::vec3 spine2TargetTranslation = computeSpine2WithHeadHipsSpline(myAvatar, hipsRigSpace, headRigSpace); #endif const float SPINE2_ROTATION_FILTER = 0.5f; AnimPose currentSpine2Pose; AnimPose currentHeadPose; AnimPose currentHipsPose; bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); if (spine2Exists && headExists && hipsExists) { AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); #if defined(Q_OS_ANDROID) || defined(HIFI_USE_OPTIMIZED_IK) rigSpaceYaw.rot() = safeLerp(Quaternions::IDENTITY, rigSpaceYaw.rot(), 0.5f); #endif glm::vec3 u, v, w; glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); if (glm::length(up) > 0.0f) { up = glm::normalize(up); } else { up = glm::vec3(0.0f, 1.0f, 0.0f); } generateBasisVectors(up, fwd, u, v, w); AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); #if defined(Q_OS_ANDROID) || defined(HIFI_USE_OPTIMIZED_IK) currentSpine2Pose.trans() = spine2TargetTranslation; #endif currentSpine2Pose.rot() = safeLerp(currentSpine2Pose.rot(), newSpinePose.rot(), SPINE2_ROTATION_FILTER); params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } } _prevIsEstimatingHips = true; } else { _prevIsEstimatingHips = false; } // pass detailed torso k-dops to rig. int hipsJoint = _rig.indexOfJoint("Hips"); if (hipsJoint >= 0) { params.hipsShapeInfo = hfmModel.joints[hipsJoint].shapeInfo; } int spineJoint = _rig.indexOfJoint("Spine"); if (spineJoint >= 0) { params.spineShapeInfo = hfmModel.joints[spineJoint].shapeInfo; } int spine1Joint = _rig.indexOfJoint("Spine1"); if (spine1Joint >= 0) { params.spine1ShapeInfo = hfmModel.joints[spine1Joint].shapeInfo; } int spine2Joint = _rig.indexOfJoint("Spine2"); if (spine2Joint >= 0) { params.spine2ShapeInfo = hfmModel.joints[spine2Joint].shapeInfo; } const float TALKING_TIME_THRESHOLD = 0.75f; params.isTalking = head->getTimeWithoutTalking() <= TALKING_TIME_THRESHOLD; //pass X and Z input key floats (-1 to 1) to rig params.inputX = myAvatar->getDriveKey(MyAvatar::TRANSLATE_X); params.inputZ = myAvatar->getDriveKey(MyAvatar::TRANSLATE_Z); myAvatar->updateRigControllerParameters(params); _rig.updateFromControllerParameters(params, deltaTime); Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); auto velocity = myAvatar->getLocalVelocity() / myAvatar->getSensorToWorldScale(); auto position = myAvatar->getLocalPosition(); auto orientation = myAvatar->getLocalOrientation(); _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState, myAvatar->getSensorToWorldScale()); // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; eyeParams.eyeLookAt = lookAt; eyeParams.eyeSaccade = head->getSaccade(); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye"); eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye"); _rig.updateFromEyeParameters(eyeParams); updateFingers(); } void MySkeletonModel::updateFingers() { MyAvatar* myAvatar = static_cast(_owningAvatar); static std::vector>> fingerChains = { { { controller::Action::LEFT_HAND, "LeftHand" }, { controller::Action::LEFT_HAND_THUMB1, "LeftHandThumb1" }, { controller::Action::LEFT_HAND_THUMB2, "LeftHandThumb2" }, { controller::Action::LEFT_HAND_THUMB3, "LeftHandThumb3" }, { controller::Action::LEFT_HAND_THUMB4, "LeftHandThumb4" } }, { { controller::Action::LEFT_HAND, "LeftHand" }, { controller::Action::LEFT_HAND_INDEX1, "LeftHandIndex1" }, { controller::Action::LEFT_HAND_INDEX2, "LeftHandIndex2" }, { controller::Action::LEFT_HAND_INDEX3, "LeftHandIndex3" }, { controller::Action::LEFT_HAND_INDEX4, "LeftHandIndex4" } }, { { controller::Action::LEFT_HAND, "LeftHand" }, { controller::Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1" }, { controller::Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2" }, { controller::Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3" }, { controller::Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4" } }, { { controller::Action::LEFT_HAND, "LeftHand" }, { controller::Action::LEFT_HAND_RING1, "LeftHandRing1" }, { controller::Action::LEFT_HAND_RING2, "LeftHandRing2" }, { controller::Action::LEFT_HAND_RING3, "LeftHandRing3" }, { controller::Action::LEFT_HAND_RING4, "LeftHandRing4" } }, { { controller::Action::LEFT_HAND, "LeftHand" }, { controller::Action::LEFT_HAND_PINKY1, "LeftHandPinky1" }, { controller::Action::LEFT_HAND_PINKY2, "LeftHandPinky2" }, { controller::Action::LEFT_HAND_PINKY3, "LeftHandPinky3" }, { controller::Action::LEFT_HAND_PINKY4, "LeftHandPinky4" } }, { { controller::Action::RIGHT_HAND, "RightHand" }, { controller::Action::RIGHT_HAND_THUMB1, "RightHandThumb1" }, { controller::Action::RIGHT_HAND_THUMB2, "RightHandThumb2" }, { controller::Action::RIGHT_HAND_THUMB3, "RightHandThumb3" }, { controller::Action::RIGHT_HAND_THUMB4, "RightHandThumb4" } }, { { controller::Action::RIGHT_HAND, "RightHand" }, { controller::Action::RIGHT_HAND_INDEX1, "RightHandIndex1" }, { controller::Action::RIGHT_HAND_INDEX2, "RightHandIndex2" }, { controller::Action::RIGHT_HAND_INDEX3, "RightHandIndex3" }, { controller::Action::RIGHT_HAND_INDEX4, "RightHandIndex4" } }, { { controller::Action::RIGHT_HAND, "RightHand" }, { controller::Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1" }, { controller::Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2" }, { controller::Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3" }, { controller::Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4" } }, { { controller::Action::RIGHT_HAND, "RightHand" }, { controller::Action::RIGHT_HAND_RING1, "RightHandRing1" }, { controller::Action::RIGHT_HAND_RING2, "RightHandRing2" }, { controller::Action::RIGHT_HAND_RING3, "RightHandRing3" }, { controller::Action::RIGHT_HAND_RING4, "RightHandRing4" } }, { { controller::Action::RIGHT_HAND, "RightHand" }, { controller::Action::RIGHT_HAND_PINKY1, "RightHandPinky1" }, { controller::Action::RIGHT_HAND_PINKY2, "RightHandPinky2" }, { controller::Action::RIGHT_HAND_PINKY3, "RightHandPinky3" }, { controller::Action::RIGHT_HAND_PINKY4, "RightHandPinky4" } } }; const float CONTROLLER_PRIORITY = 2.0f; for (auto& chain : fingerChains) { glm::quat prevAbsRot = Quaternions::IDENTITY; for (auto& link : chain) { int index = _rig.indexOfJoint(link.second); if (index >= 0) { auto rotationFrameOffset = _jointRotationFrameOffsetMap.find(index); if (rotationFrameOffset == _jointRotationFrameOffsetMap.end()) { _jointRotationFrameOffsetMap.insert(std::pair(index, 0)); rotationFrameOffset = _jointRotationFrameOffsetMap.find(index); } auto pose = myAvatar->getControllerPoseInSensorFrame(link.first); if (pose.valid) { glm::quat relRot = glm::inverse(prevAbsRot) * pose.getRotation(); // only set the rotation for the finger joints, not the hands. if (link.first != controller::Action::LEFT_HAND && link.first != controller::Action::RIGHT_HAND) { _rig.setJointRotation(index, true, relRot, CONTROLLER_PRIORITY); rotationFrameOffset->second = 0; } prevAbsRot = pose.getRotation(); } else if (rotationFrameOffset->second == 1) { // if the pose is invalid and was set on previous frame we do clear ( current frame offset = 1 ) _rig.clearJointAnimationPriority(index); } rotationFrameOffset->second++; } } } }