mirror of
https://github.com/overte-org/overte.git
synced 2025-06-19 16:20:16 +02:00
Merge branch 'master' into ajt/new-anim-system
This commit is contained in:
commit
de31b92fd5
11 changed files with 177 additions and 77 deletions
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "NodeConnectionData.h"
|
#include "NodeConnectionData.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDataStream>
|
||||||
|
|
||||||
NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr,
|
NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr,
|
||||||
bool isConnectRequest) {
|
bool isConnectRequest) {
|
||||||
NodeConnectionData newHeader;
|
NodeConnectionData newHeader;
|
||||||
|
|
|
@ -4924,7 +4924,7 @@ void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float
|
||||||
|
|
||||||
// Store the one fingertip in the palm structure so we can track velocity
|
// Store the one fingertip in the palm structure so we can track velocity
|
||||||
const float FINGER_LENGTH = 0.3f; // meters
|
const float FINGER_LENGTH = 0.3f; // meters
|
||||||
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
|
const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
|
||||||
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
|
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
|
||||||
glm::vec3 oldTipPosition = palm->getTipRawPosition();
|
glm::vec3 oldTipPosition = palm->getTipRawPosition();
|
||||||
if (deltaTime > 0.0f) {
|
if (deltaTime > 0.0f) {
|
||||||
|
|
|
@ -86,7 +86,7 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||||
_rig->updateJointState(i, rootTransform);
|
_rig->updateJointState(i, rootTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildShapes();
|
computeBoundingShape();
|
||||||
|
|
||||||
Extents meshExtents = getMeshExtents();
|
Extents meshExtents = getMeshExtents();
|
||||||
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
||||||
|
@ -248,6 +248,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
||||||
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
|
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
|
||||||
true, PALM_PRIORITY);
|
true, PALM_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||||
return;
|
return;
|
||||||
|
@ -261,9 +262,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||||
// the palm's position must be transformed into the model-frame
|
// the palm's position must be transformed into the model-frame
|
||||||
glm::quat inverseRotation = glm::inverse(_rotation);
|
glm::quat inverseRotation = glm::inverse(_rotation);
|
||||||
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
||||||
|
glm::quat palmRotation = inverseRotation * palm.getRotation();
|
||||||
// the palm's "raw" rotation is already in the model-frame
|
|
||||||
glm::quat palmRotation = palm.getRawRotation();
|
|
||||||
|
|
||||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||||
}
|
}
|
||||||
|
@ -346,9 +345,9 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde
|
||||||
}
|
}
|
||||||
OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex];
|
OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex];
|
||||||
|
|
||||||
glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size;
|
glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size;
|
||||||
glm::vec3 pUp = position + orientation * IDENTITY_UP * size;
|
glm::vec3 pUp = position + orientation * IDENTITY_UP * size;
|
||||||
glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size;
|
glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size;
|
||||||
|
|
||||||
glm::vec3 red(1.0f, 0.0f, 0.0f);
|
glm::vec3 red(1.0f, 0.0f, 0.0f);
|
||||||
geometryCache->renderLine(batch, position, pRight, red, jointLineIDs._right);
|
geometryCache->renderLine(batch, position, pRight, red, jointLineIDs._right);
|
||||||
|
@ -466,7 +465,7 @@ float MIN_JOINT_MASS = 1.0f;
|
||||||
float VERY_BIG_MASS = 1.0e6f;
|
float VERY_BIG_MASS = 1.0e6f;
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void SkeletonModel::buildShapes() {
|
void SkeletonModel::computeBoundingShape() {
|
||||||
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -476,36 +475,86 @@ void SkeletonModel::buildShapes() {
|
||||||
// rootJointIndex == -1 if the avatar model has no skeleton
|
// rootJointIndex == -1 if the avatar model has no skeleton
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
computeBoundingShape(geometry);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
// BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the
|
||||||
// compute default joint transforms
|
// hands and feet into positions that are more correct than the default pose.
|
||||||
int numStates = _rig->getJointStateCount();
|
|
||||||
QVector<glm::mat4> transforms;
|
// Measure limb lengths so we can specify IK targets that will pull hands and feet tight to body
|
||||||
transforms.fill(glm::mat4(), numStates);
|
QVector<QString> endEffectors;
|
||||||
|
endEffectors.push_back("RightHand");
|
||||||
|
endEffectors.push_back("LeftHand");
|
||||||
|
endEffectors.push_back("RightFoot");
|
||||||
|
endEffectors.push_back("LeftFoot");
|
||||||
|
|
||||||
|
QVector<QString> baseJoints;
|
||||||
|
baseJoints.push_back("RightArm");
|
||||||
|
baseJoints.push_back("LeftArm");
|
||||||
|
baseJoints.push_back("RightUpLeg");
|
||||||
|
baseJoints.push_back("LeftUpLeg");
|
||||||
|
|
||||||
|
for (int i = 0; i < endEffectors.size(); ++i) {
|
||||||
|
QString tipName = endEffectors[i];
|
||||||
|
QString baseName = baseJoints[i];
|
||||||
|
float limbLength = 0.0f;
|
||||||
|
int tipIndex = _rig->indexOfJoint(tipName);
|
||||||
|
if (tipIndex == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// save tip's relative rotation for later
|
||||||
|
glm::quat tipRotation = _rig->getJointState(tipIndex).getRotationInConstrainedFrame();
|
||||||
|
|
||||||
|
// IK on each endpoint
|
||||||
|
int jointIndex = tipIndex;
|
||||||
|
QVector<int> freeLineage;
|
||||||
|
float priority = 1.0f;
|
||||||
|
while (jointIndex > -1) {
|
||||||
|
JointState limbJoint = _rig->getJointState(jointIndex);
|
||||||
|
freeLineage.push_back(jointIndex);
|
||||||
|
if (limbJoint.getName() == baseName) {
|
||||||
|
glm::vec3 targetPosition = limbJoint.getPosition() - glm::vec3(0.0f, 1.5f * limbLength, 0.0f);
|
||||||
|
// do IK a few times to make sure the endpoint gets close to its target
|
||||||
|
for (int j = 0; j < 5; ++j) {
|
||||||
|
_rig->inverseKinematics(tipIndex,
|
||||||
|
targetPosition,
|
||||||
|
glm::quat(),
|
||||||
|
priority,
|
||||||
|
freeLineage,
|
||||||
|
glm::mat4());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
limbLength += limbJoint.getDistanceToParent();
|
||||||
|
jointIndex = limbJoint.getParentIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// since this IK target is totally bogus we restore the tip's relative rotation
|
||||||
|
_rig->setJointRotationInConstrainedFrame(tipIndex, tipRotation, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
// recompute all joint model-frame transforms
|
||||||
|
glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
|
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||||
|
_rig->updateJointState(i, rootTransform);
|
||||||
|
}
|
||||||
|
// END BOUNDING SHAPE HACK
|
||||||
|
|
||||||
// compute bounding box that encloses all shapes
|
// compute bounding box that encloses all shapes
|
||||||
Extents totalExtents;
|
Extents totalExtents;
|
||||||
totalExtents.reset();
|
totalExtents.reset();
|
||||||
totalExtents.addPoint(glm::vec3(0.0f));
|
totalExtents.addPoint(glm::vec3(0.0f));
|
||||||
|
int numStates = _rig->getJointStateCount();
|
||||||
for (int i = 0; i < numStates; i++) {
|
for (int i = 0; i < numStates; i++) {
|
||||||
// compute the default transform of this joint
|
// compute the default transform of this joint
|
||||||
const JointState& state = _rig->getJointState(i);
|
const JointState& state = _rig->getJointState(i);
|
||||||
int parentIndex = state.getParentIndex();
|
|
||||||
if (parentIndex == -1) {
|
|
||||||
transforms[i] = _rig->getJointTransform(i);
|
|
||||||
} else {
|
|
||||||
glm::quat modifiedRotation = state.getPreRotation() * state.getDefaultRotation() * state.getPostRotation();
|
|
||||||
transforms[i] = transforms[parentIndex] * glm::translate(state.getTranslation())
|
|
||||||
* state.getPreTransform() * glm::mat4_cast(modifiedRotation) * state.getPostTransform();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Each joint contributes a sphere at its position
|
// HACK WORKAROUND: ignore joints that may have bad translation (e.g. have been flagged as such with zero radius)
|
||||||
glm::vec3 axis(state.getBoneRadius());
|
if (state.getBoneRadius() > 0.0f) {
|
||||||
glm::vec3 jointPosition = extractTranslation(transforms[i]);
|
// Each joint contributes a sphere at its position
|
||||||
totalExtents.addPoint(jointPosition + axis);
|
glm::vec3 axis(state.getBoneRadius());
|
||||||
totalExtents.addPoint(jointPosition - axis);
|
glm::vec3 jointPosition = state.getPosition();
|
||||||
|
totalExtents.addPoint(jointPosition + axis);
|
||||||
|
totalExtents.addPoint(jointPosition - axis);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute bounding shape parameters
|
// compute bounding shape parameters
|
||||||
|
@ -517,6 +566,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||||
|
|
||||||
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
|
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
|
||||||
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||||
|
|
||||||
|
// RECOVER FROM BOUNINDG SHAPE HACK: now that we're all done, restore the default pose
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
_rig->restoreJointRotation(i, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
|
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
|
||||||
|
@ -535,7 +589,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
||||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||||
|
|
||||||
// draw a yellow sphere at the capsule bottom point
|
// draw a yellow sphere at the capsule bottom point
|
||||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f);
|
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f);
|
||||||
glm::vec3 axis = topPoint - bottomPoint;
|
glm::vec3 axis = topPoint - bottomPoint;
|
||||||
transform.setTranslation(bottomPoint);
|
transform.setTranslation(bottomPoint);
|
||||||
batch.setModelTransform(transform);
|
batch.setModelTransform(transform);
|
||||||
|
|
|
@ -94,7 +94,6 @@ public:
|
||||||
/// \return whether or not the head was found.
|
/// \return whether or not the head was found.
|
||||||
glm::vec3 getDefaultEyeModelPosition() const;
|
glm::vec3 getDefaultEyeModelPosition() const;
|
||||||
|
|
||||||
void computeBoundingShape(const FBXGeometry& geometry);
|
|
||||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
||||||
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
|
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
|
||||||
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
|
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
|
||||||
|
@ -114,7 +113,7 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void buildShapes();
|
void computeBoundingShape();
|
||||||
|
|
||||||
/// \param jointIndex index of joint in model
|
/// \param jointIndex index of joint in model
|
||||||
/// \param position position of joint in model-frame
|
/// \param position position of joint in model-frame
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "AnimationHandle.h"
|
#include "AnimationHandle.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
void AnimationHandle::setURL(const QUrl& url) {
|
void AnimationHandle::setURL(const QUrl& url) {
|
||||||
if (_url != url) {
|
if (_url != url) {
|
||||||
|
@ -51,8 +51,8 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationHandle::setRunning(bool running, bool doRestoreJoints) {
|
void AnimationHandle::setRunning(bool running, bool doRestoreJoints) {
|
||||||
if (running && isRunning()) {
|
if (running && isRunning() && (getFadePerSecond() >= 0.0f)) {
|
||||||
// if we're already running, this is the same as a restart
|
// if we're already running, this is the same as a restart -- unless we're fading out.
|
||||||
setFrameIndex(getFirstFrame());
|
setFrameIndex(getFirstFrame());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,33 +9,34 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "Rig.h"
|
||||||
|
|
||||||
#include <glm/gtx/vector_angle.hpp>
|
#include <glm/gtx/vector_angle.hpp>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "AnimationHandle.h"
|
#include "AnimationHandle.h"
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
#include "AnimSkeleton.h"
|
#include "AnimSkeleton.h"
|
||||||
|
|
||||||
#include "Rig.h"
|
#include "Rig.h"
|
||||||
|
|
||||||
void Rig::HeadParameters::dump() const {
|
void Rig::HeadParameters::dump() const {
|
||||||
qCDebug(animation, "HeadParameters =");
|
qCDebug(animation, "HeadParameters =");
|
||||||
qCDebug(animation, " leanSideways = %0.5f", leanSideways);
|
qCDebug(animation, " leanSideways = %0.5f", (double)leanSideways);
|
||||||
qCDebug(animation, " leanForward = %0.5f", leanForward);
|
qCDebug(animation, " leanForward = %0.5f", (double)leanForward);
|
||||||
qCDebug(animation, " torsoTwist = %0.5f", torsoTwist);
|
qCDebug(animation, " torsoTwist = %0.5f", (double)torsoTwist);
|
||||||
glm::vec3 axis = glm::axis(localHeadOrientation);
|
glm::vec3 axis = glm::axis(localHeadOrientation);
|
||||||
float theta = glm::angle(localHeadOrientation);
|
float theta = glm::angle(localHeadOrientation);
|
||||||
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
|
||||||
axis = glm::axis(worldHeadOrientation);
|
axis = glm::axis(worldHeadOrientation);
|
||||||
theta = glm::angle(worldHeadOrientation);
|
theta = glm::angle(worldHeadOrientation);
|
||||||
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
|
||||||
axis = glm::axis(modelRotation);
|
axis = glm::axis(modelRotation);
|
||||||
theta = glm::angle(modelRotation);
|
theta = glm::angle(modelRotation);
|
||||||
qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
|
||||||
qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", modelTranslation.x, modelTranslation.y, modelTranslation.z);
|
qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", (double)modelTranslation.x, (double)modelTranslation.y, (double)modelTranslation.z);
|
||||||
qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z);
|
qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", (double)eyeLookAt.x, (double)eyeLookAt.y, (double)eyeLookAt.z);
|
||||||
qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z);
|
qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", (double)eyeSaccade.x, (double)eyeSaccade.y, (double)eyeSaccade.z);
|
||||||
qCDebug(animation, " leanJointIndex = %.d", leanJointIndex);
|
qCDebug(animation, " leanJointIndex = %.d", leanJointIndex);
|
||||||
qCDebug(animation, " neckJointIndex = %.d", neckJointIndex);
|
qCDebug(animation, " neckJointIndex = %.d", neckJointIndex);
|
||||||
qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex);
|
qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex);
|
||||||
|
@ -106,7 +107,7 @@ AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QStrin
|
||||||
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/";
|
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/";
|
||||||
if (role == "walk") {
|
if (role == "walk") {
|
||||||
standard = base + "walk_fwd.fbx";
|
standard = base + "walk_fwd.fbx";
|
||||||
} else if (role == "backup") {
|
} else if (role == "backup") {
|
||||||
standard = base + "walk_bwd.fbx";
|
standard = base + "walk_bwd.fbx";
|
||||||
} else if (role == "leftTurn") {
|
} else if (role == "leftTurn") {
|
||||||
standard = base + "turn_left.fbx";
|
standard = base + "turn_left.fbx";
|
||||||
|
@ -418,16 +419,16 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
|
|
||||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||||
|
|
||||||
// at the moment worldVelocity comes from the Avatar physics body, which is not always correct when
|
|
||||||
// moving in the HMD, so let's compute our own veloicty.
|
|
||||||
glm::vec3 worldVel = (worldPosition - _lastPosition) / deltaTime;
|
|
||||||
glm::vec3 localVel = glm::inverse(worldRotation) * worldVel;
|
|
||||||
float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT);
|
|
||||||
float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT);
|
|
||||||
float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
|
|
||||||
|
|
||||||
if (_enableAnimGraph) {
|
if (_enableAnimGraph) {
|
||||||
|
|
||||||
|
// at the moment worldVelocity comes from the Avatar physics body, which is not always correct when
|
||||||
|
// moving in the HMD, so let's compute our own veloicty.
|
||||||
|
glm::vec3 worldVel = (worldPosition - _lastPosition) / deltaTime;
|
||||||
|
glm::vec3 localVel = glm::inverse(worldRotation) * worldVel;
|
||||||
|
float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT);
|
||||||
|
float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT);
|
||||||
|
float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
|
||||||
|
|
||||||
// sine wave LFO var for testing.
|
// sine wave LFO var for testing.
|
||||||
static float t = 0.0f;
|
static float t = 0.0f;
|
||||||
_animVars.set("sine", static_cast<float>(0.5 * sin(t) + 0.5));
|
_animVars.set("sine", static_cast<float>(0.5 * sin(t) + 0.5));
|
||||||
|
@ -491,12 +492,38 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
|
|
||||||
if (_enableRig) {
|
if (_enableRig) {
|
||||||
bool isMoving = false;
|
bool isMoving = false;
|
||||||
|
|
||||||
|
glm::vec3 right = worldRotation * IDENTITY_RIGHT;
|
||||||
|
const float PERCEPTIBLE_DELTA = 0.001f;
|
||||||
|
const float PERCEPTIBLE_SPEED = 0.1f;
|
||||||
|
// It can be more accurate/smooth to use velocity rather than position,
|
||||||
|
// but some modes (e.g., hmd standing) update position without updating velocity.
|
||||||
|
// It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...)
|
||||||
|
// So, let's create our own workingVelocity from the worldPosition...
|
||||||
|
glm::vec3 positionDelta = worldPosition - _lastPosition;
|
||||||
|
glm::vec3 workingVelocity = positionDelta / deltaTime;
|
||||||
|
// But for smoothest (non-hmd standing) results, go ahead and use velocity:
|
||||||
|
#if !WANT_DEBUG
|
||||||
|
// Note: Separately, we've arranged for starting/stopping animations by role (as we've done here) to pick up where they've left off when fading,
|
||||||
|
// so that you wouldn't notice the start/stop if it happens fast enough (e.g., one frame). But the print below would still be noisy.
|
||||||
|
if (!positionDelta.x && !positionDelta.y && !positionDelta.z) {
|
||||||
|
workingVelocity = worldVelocity;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float forwardSpeed = glm::dot(workingVelocity, front);
|
||||||
|
float rightLateralSpeed = glm::dot(workingVelocity, right);
|
||||||
|
float rightTurningDelta = glm::orientedAngle(front, _lastFront, IDENTITY_UP);
|
||||||
|
float rightTurningSpeed = rightTurningDelta / deltaTime;
|
||||||
|
bool isTurning = (std::abs(rightTurningDelta) > PERCEPTIBLE_DELTA) && (std::abs(rightTurningSpeed) > PERCEPTIBLE_SPEED);
|
||||||
|
bool isStrafing = std::abs(rightLateralSpeed) > PERCEPTIBLE_SPEED;
|
||||||
auto updateRole = [&](const QString& role, bool isOn) {
|
auto updateRole = [&](const QString& role, bool isOn) {
|
||||||
isMoving = isMoving || isOn;
|
isMoving = isMoving || isOn;
|
||||||
if (isOn) {
|
if (isOn) {
|
||||||
if (!isRunningRole(role)) {
|
if (!isRunningRole(role)) {
|
||||||
qCDebug(animation) << "Rig STARTING" << role;
|
qCDebug(animation) << "Rig STARTING" << role;
|
||||||
startAnimationByRole(role);
|
startAnimationByRole(role);
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isRunningRole(role)) {
|
if (isRunningRole(role)) {
|
||||||
|
@ -505,15 +532,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
updateRole("walk", forwardSpeed > PERCEPTIBLE_SPEED);
|
||||||
updateRole("walk", forwardSpeed > 0.01f);
|
updateRole("backup", forwardSpeed < -PERCEPTIBLE_SPEED);
|
||||||
updateRole("backup", forwardSpeed < -0.01f);
|
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0.0f));
|
||||||
bool isTurning = std::abs(turningSpeed) > 0.5f;
|
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0.0f));
|
||||||
updateRole("rightTurn", isTurning && (turningSpeed > 0));
|
isStrafing = isStrafing && !isMoving;
|
||||||
updateRole("leftTurn", isTurning && (turningSpeed < 0));
|
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
|
||||||
bool isStrafing = !isTurning && (std::abs(lateralSpeed) > 0.01f);
|
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
|
||||||
updateRole("rightStrafe", isStrafing && (lateralSpeed > 0.0f));
|
|
||||||
updateRole("leftStrafe", isStrafing && (lateralSpeed < 0.0f));
|
|
||||||
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
|
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,12 +114,14 @@ float HandData::getBaseScale() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 PalmData::getFingerDirection() const {
|
glm::vec3 PalmData::getFingerDirection() const {
|
||||||
const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 0.0f, 1.0f);
|
// finger points along yAxis in hand-frame
|
||||||
|
const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 1.0f, 0.0f);
|
||||||
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION));
|
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 PalmData::getNormal() const {
|
glm::vec3 PalmData::getNormal() const {
|
||||||
const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, -1.0f, 0.0f);
|
// palm normal points along zAxis in hand-frame
|
||||||
|
const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, 0.0f, 1.0f);
|
||||||
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION));
|
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,12 +64,13 @@ public:
|
||||||
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
||||||
const PalmData*& collidingPalm) const;
|
const PalmData*& collidingPalm) const;
|
||||||
|
|
||||||
|
glm::quat getBaseOrientation() const;
|
||||||
|
|
||||||
friend class AvatarData;
|
friend class AvatarData;
|
||||||
protected:
|
protected:
|
||||||
AvatarData* _owningAvatarData;
|
AvatarData* _owningAvatarData;
|
||||||
std::vector<PalmData> _palms;
|
std::vector<PalmData> _palms;
|
||||||
|
|
||||||
glm::quat getBaseOrientation() const;
|
|
||||||
glm::vec3 getBasePosition() const;
|
glm::vec3 getBasePosition() const;
|
||||||
float getBaseScale() const;
|
float getBaseScale() const;
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ public:
|
||||||
|
|
||||||
void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; };
|
void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; };
|
||||||
glm::quat getRawRotation() const { return _rawRotation; }
|
glm::quat getRawRotation() const { return _rawRotation; }
|
||||||
|
glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }
|
||||||
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
|
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
|
||||||
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
||||||
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
||||||
|
@ -147,6 +149,7 @@ public:
|
||||||
glm::vec3 getNormal() const;
|
glm::vec3 getNormal() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// unless marked otherwise, these are all in the model-frame
|
||||||
glm::quat _rawRotation;
|
glm::quat _rawRotation;
|
||||||
glm::vec3 _rawPosition;
|
glm::vec3 _rawPosition;
|
||||||
glm::vec3 _rawVelocity;
|
glm::vec3 _rawVelocity;
|
||||||
|
@ -156,6 +159,7 @@ private:
|
||||||
glm::vec3 _tipPosition;
|
glm::vec3 _tipPosition;
|
||||||
glm::vec3 _tipVelocity;
|
glm::vec3 _tipVelocity;
|
||||||
glm::vec3 _totalPenetration; // accumulator for per-frame penetrations
|
glm::vec3 _totalPenetration; // accumulator for per-frame penetrations
|
||||||
|
|
||||||
unsigned int _controllerButtons;
|
unsigned int _controllerButtons;
|
||||||
unsigned int _lastControllerButtons;
|
unsigned int _lastControllerButtons;
|
||||||
float _trigger;
|
float _trigger;
|
||||||
|
|
|
@ -2682,8 +2682,21 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||||
averageRadius += glm::distance(vertex, averageVertex);
|
averageRadius += glm::distance(vertex, averageVertex);
|
||||||
}
|
}
|
||||||
jointShapeInfo.averageRadius = averageRadius * radiusScale;
|
jointShapeInfo.averageRadius = averageRadius * radiusScale / (float)jointShapeInfo.numVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BUG: the boneBegin and/or boneEnd are incorrect for meshes that are "connected
|
||||||
|
// under the bone" without weights. Unfortunately we haven't been able to find it yet.
|
||||||
|
// Although the the mesh vertices are correct in the model-frame, the joint's transform
|
||||||
|
// in the same frame is just BAD.
|
||||||
|
//
|
||||||
|
// HACK WORKAROUND: prevent these shapes from contributing to the collision capsule by setting
|
||||||
|
// some key members of jointShapeInfo to zero:
|
||||||
|
jointShapeInfo.numVertices = 0;
|
||||||
|
jointShapeInfo.sumVertexWeights = 0.0f;
|
||||||
|
jointShapeInfo.numVertexWeights = 0;
|
||||||
|
jointShapeInfo.boneBegin = glm::vec3(0.0f);
|
||||||
|
jointShapeInfo.averageRadius = 0.0f;
|
||||||
}
|
}
|
||||||
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
||||||
|
|
||||||
|
@ -2733,7 +2746,6 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
// the average radius to the average point.
|
// the average radius to the average point.
|
||||||
if (jointShapeInfo.numVertexWeights == 0
|
if (jointShapeInfo.numVertexWeights == 0
|
||||||
&& jointShapeInfo.numVertices > 0) {
|
&& jointShapeInfo.numVertices > 0) {
|
||||||
jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices;
|
|
||||||
joint.boneRadius = jointShapeInfo.averageRadius;
|
joint.boneRadius = jointShapeInfo.averageRadius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -481,6 +481,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
|
||||||
// Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis)
|
// Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis)
|
||||||
//
|
//
|
||||||
const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||||
|
const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||||
const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
|
const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||||
const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis);
|
const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis);
|
||||||
|
|
||||||
|
@ -491,13 +492,15 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
|
||||||
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis);
|
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis);
|
||||||
|
|
||||||
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
|
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
|
||||||
// (fingers forward, palm down) aligned properly in the avatar's model-frame.
|
// (fingers forward, palm down) aligned properly in the avatar's model-frame,
|
||||||
const glm::quat postOffset = glm::angleAxis(PI / 2.0f, xAxis);
|
// and then a flip about the yAxis to get into model-frame.
|
||||||
|
const glm::quat postOffset = glm::angleAxis(PI, yAxis) * glm::angleAxis(PI / 2.0f, xAxis);
|
||||||
|
|
||||||
// The total rotation of the hand uses the formula:
|
// The total rotation of the hand uses the formula:
|
||||||
//
|
//
|
||||||
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
|
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
|
||||||
//
|
//
|
||||||
|
// TODO: find a shortcut with fewer rotations.
|
||||||
rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
||||||
|
|
||||||
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
|
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
|
||||||
|
|
|
@ -362,18 +362,17 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
|
||||||
//
|
//
|
||||||
// Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down)
|
// Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down)
|
||||||
//
|
//
|
||||||
// An approximate offset for the Vive can be obtained by inpection:
|
// An approximate offset for the Vive can be obtained by inspection:
|
||||||
//
|
//
|
||||||
// Qoffset = glm::inverse(glm::angleAxis(sign * PI/4.0f, zAxis) * glm::angleAxis(PI/2.0f, xAxis))
|
// Qoffset = glm::inverse(glm::angleAxis(sign * PI/4.0f, zAxis) * glm::angleAxis(PI/2.0f, xAxis))
|
||||||
//
|
//
|
||||||
|
// So the full equation is:
|
||||||
// Finally there is another flip around the yAxis to re-align from model to Vive space, so the full equation is:
|
|
||||||
//
|
//
|
||||||
// Q = yFlip * combinedMeasurement * viveToHand
|
// Q = combinedMeasurement * viveToHand
|
||||||
//
|
//
|
||||||
// Q = yFlip * (deltaQ * QOffset) * (yFlip * quarterTurnAboutX)
|
// Q = (deltaQ * QOffset) * (yFlip * quarterTurnAboutX)
|
||||||
//
|
//
|
||||||
// Q = yFlip * (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX)
|
// Q = (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX)
|
||||||
|
|
||||||
const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
@ -381,7 +380,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
|
||||||
const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
|
const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
|
||||||
rotation = yFlip * rotation * offset * yFlip * quarterX;
|
rotation = rotation * offset * yFlip * quarterX;
|
||||||
|
|
||||||
position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
|
position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue