mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 04:26:18 +02:00
Added head target to AnimGraph IK node.
* In HMD mode head orientation and position is set. * When not in HMD only orientation is set, position should default to the underlying pose position.
This commit is contained in:
parent
9a9838fd0d
commit
7996a02bd8
9 changed files with 101 additions and 33 deletions
|
@ -1276,7 +1276,8 @@ void MyAvatar::initAnimGraph() {
|
|||
//
|
||||
// or run a local web-server
|
||||
// python -m SimpleHTTPServer&
|
||||
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/2a994bef7726ce8e9efcee7622b8b1a1b6b67490/ik-avatar.json");
|
||||
// auto graphUrl = QUrl("http://localhost:8000/avatar.json");
|
||||
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/8f824da2908fd89ad1befadd1d8f5d7b3b6efa66/ik-avatar.json");
|
||||
_rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
params.leanForward = head->getFinalLeanForward();
|
||||
params.torsoTwist = head->getTorsoTwist();
|
||||
params.localHeadOrientation = head->getFinalOrientationInLocalFrame();
|
||||
params.localHeadPitch = head->getFinalPitch();
|
||||
params.localHeadYaw = head->getFinalYaw();
|
||||
params.localHeadRoll = head->getFinalRoll();
|
||||
params.isInHMD = qApp->getAvatarUpdater()->isHMDMode();
|
||||
|
||||
// get HMD position from sensor space into world space, and back into model space
|
||||
glm::mat4 worldToModel = glm::inverse(createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()));
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 hmdPosition = glm::angleAxis((float)M_PI, yAxis) * transformPoint(worldToModel * myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition());
|
||||
params.localHeadPosition = hmdPosition;
|
||||
|
||||
params.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
|
||||
params.eyeLookAt = head->getLookAtPosition();
|
||||
params.eyeSaccade = head->getSaccade();
|
||||
|
|
|
@ -40,7 +40,7 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) {
|
|||
void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) const {
|
||||
int numJoints = (int)_relativePoses.size();
|
||||
absolutePoses.clear();
|
||||
absolutePoses.reserve(numJoints);
|
||||
absolutePoses.resize(numJoints);
|
||||
assert(numJoints <= _skeleton->getNumJoints());
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
int parentIndex = _skeleton->getParentIndex(i);
|
||||
|
|
|
@ -149,6 +149,7 @@ public:
|
|||
void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); }
|
||||
void unset(const std::string& key) { _map.erase(key); }
|
||||
|
||||
void setTrigger(const std::string& key) { _triggers.insert(key); }
|
||||
void clearTriggers() { _triggers.clear(); }
|
||||
|
|
|
@ -30,6 +30,9 @@ void Rig::HeadParameters::dump() const {
|
|||
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);
|
||||
theta = glm::angle(worldHeadOrientation);
|
||||
qCDebug(animation, " localHead pitch = %.5f, yaw = %.5f, roll = %.5f", (double)localHeadPitch, (double)localHeadYaw, (double)localHeadRoll);
|
||||
qCDebug(animation, " localHeadPosition = (%.5f, %.5f, %.5f)", (double)localHeadPosition.x, (double)localHeadPosition.y, (double)localHeadPosition.z);
|
||||
qCDebug(animation, " isInHMD = %s", isInHMD ? "true" : "false");
|
||||
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);
|
||||
theta = glm::angle(modelRotation);
|
||||
|
@ -953,56 +956,71 @@ void Rig::updateFromHeadParameters(const HeadParameters& params) {
|
|||
if (params.enableLean) {
|
||||
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
}
|
||||
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
updateNeckJoint(params.neckJointIndex, params);
|
||||
updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation,
|
||||
params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
||||
}
|
||||
|
||||
static const glm::vec3 X_AXIS(1.0f, 0.0f, 0.0f);
|
||||
static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f);
|
||||
static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f);
|
||||
|
||||
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
if (_enableAnimGraph && _animSkeleton) {
|
||||
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 absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, zAxis) *
|
||||
glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, xAxis) *
|
||||
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, yAxis));
|
||||
glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) *
|
||||
glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) *
|
||||
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS));
|
||||
_animVars.set("lean", absRot);
|
||||
} else if (!_enableAnimGraph) {
|
||||
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
|
||||
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
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() * getJointDefaultRotationInParentFrame(index));
|
||||
setJointRotationInConstrainedFrame(index,
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) *
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) *
|
||||
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) *
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * Z_AXIS) *
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * X_AXIS) *
|
||||
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * Y_AXIS) *
|
||||
getJointState(index).getDefaultRotation(), DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) {
|
||||
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
auto& state = _jointStates[index];
|
||||
auto& parentState = _jointStates[state.getParentIndex()];
|
||||
if (_enableAnimGraph && _animSkeleton) {
|
||||
// the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll.
|
||||
glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) *
|
||||
glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) *
|
||||
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
|
||||
_animVars.set("headRotation", realLocalHeadOrientation);
|
||||
|
||||
// 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(getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
|
||||
glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation);
|
||||
glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways));
|
||||
pitchYawRoll -= lean;
|
||||
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])) *
|
||||
state.getDefaultRotation(), DEFAULT_PRIORITY);
|
||||
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
|
||||
|
||||
if (params.isInHMD) {
|
||||
_animVars.set("headPosition", params.localHeadPosition + rootTrans);
|
||||
} else {
|
||||
_animVars.unset("headPosition");
|
||||
}
|
||||
} else if (!_enableAnimGraph) {
|
||||
|
||||
auto& state = _jointStates[index];
|
||||
auto& parentState = _jointStates[state.getParentIndex()];
|
||||
|
||||
// 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(getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
|
||||
glm::vec3 pitchYawRoll = safeEulerAngles(params.localHeadOrientation);
|
||||
glm::vec3 lean = glm::radians(glm::vec3(params.leanForward, params.torsoTwist, params.leanSideways));
|
||||
pitchYawRoll -= lean;
|
||||
setJointRotationInConstrainedFrame(index,
|
||||
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * Z_AXIS)) *
|
||||
glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * Y_AXIS)) *
|
||||
glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * X_AXIS)) *
|
||||
state.getDefaultRotation(), DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,10 +59,15 @@ public:
|
|||
bool enableLean = false;
|
||||
glm::quat modelRotation = glm::quat();
|
||||
glm::quat localHeadOrientation = glm::quat();
|
||||
float localHeadPitch = 0.0f; // degrees
|
||||
float localHeadYaw = 0.0f; // degrees
|
||||
float localHeadRoll = 0.0f; // degrees
|
||||
glm::vec3 localHeadPosition = glm::vec3(0);
|
||||
bool isInHMD = false;
|
||||
glm::quat worldHeadOrientation = glm::quat();
|
||||
glm::vec3 eyeLookAt = glm::vec3(); // world space
|
||||
glm::vec3 eyeSaccade = glm::vec3(); // world space
|
||||
glm::vec3 modelTranslation = glm::vec3();
|
||||
glm::vec3 modelTranslation = glm::vec3(0);
|
||||
int leanJointIndex = -1;
|
||||
int neckJointIndex = -1;
|
||||
int leftEyeJointIndex = -1;
|
||||
|
@ -177,7 +182,7 @@ public:
|
|||
protected:
|
||||
|
||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||
void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist);
|
||||
void updateNeckJoint(int index, const HeadParameters& params);
|
||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
|
||||
QVector<JointState> _jointStates;
|
||||
|
|
|
@ -176,6 +176,14 @@ void AnimDebugDraw::removePoses(const std::string& key) {
|
|||
_poses.erase(key);
|
||||
}
|
||||
|
||||
void AnimDebugDraw::addPose(const std::string& key, const AnimPose& pose, const AnimPose& rootPose) {
|
||||
_singlePoses[key] = SinglePoseInfo(pose, rootPose);
|
||||
}
|
||||
|
||||
void AnimDebugDraw::removePose(const std::string& key) {
|
||||
_singlePoses.erase(key);
|
||||
}
|
||||
|
||||
static const uint32_t red = toRGBA(255, 0, 0, 255);
|
||||
static const uint32_t green = toRGBA(0, 255, 0, 255);
|
||||
static const uint32_t blue = toRGBA(0, 0, 255, 255);
|
||||
|
@ -333,6 +341,7 @@ void AnimDebugDraw::update() {
|
|||
const size_t VERTICES_PER_LINK = 8 * 2;
|
||||
|
||||
const float BONE_RADIUS = 0.01f; // 1 cm
|
||||
const float POSE_RADIUS = 0.1f; // 10 cm
|
||||
|
||||
// figure out how many verts we will need.
|
||||
int numVerts = 0;
|
||||
|
@ -371,6 +380,8 @@ void AnimDebugDraw::update() {
|
|||
}
|
||||
}
|
||||
|
||||
numVerts += _singlePoses.size() * VERTICES_PER_BONE;
|
||||
|
||||
data._vertexBuffer->resize(sizeof(Vertex) * numVerts);
|
||||
Vertex* verts = (Vertex*)data._vertexBuffer->editData();
|
||||
Vertex* v = verts;
|
||||
|
@ -475,6 +486,13 @@ void AnimDebugDraw::update() {
|
|||
}
|
||||
}
|
||||
|
||||
for (auto& iter : _singlePoses) {
|
||||
AnimPose pose = std::get<0>(iter.second);
|
||||
AnimPose rootPose = std::get<1>(iter.second);
|
||||
const float radius = POSE_RADIUS / (pose.scale.x * rootPose.scale.x);
|
||||
addBone(rootPose, pose, radius, v);
|
||||
}
|
||||
|
||||
assert(numVerts == (v - verts));
|
||||
|
||||
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);
|
||||
|
|
|
@ -27,15 +27,22 @@ public:
|
|||
AnimDebugDraw();
|
||||
~AnimDebugDraw();
|
||||
|
||||
// draw a skeleton bind pose
|
||||
void addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color);
|
||||
void removeSkeleton(const std::string& key);
|
||||
|
||||
// draw the interal poses for a specific animNode
|
||||
void addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color);
|
||||
void removeAnimNode(const std::string& key);
|
||||
|
||||
// draw a set of poses with a skeleton
|
||||
void addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color);
|
||||
void removePoses(const std::string& key);
|
||||
|
||||
// draw a single pose
|
||||
void addPose(const std::string& key, const AnimPose& rootPose, const AnimPose& pose);
|
||||
void removePose(const std::string& key);
|
||||
|
||||
void update();
|
||||
|
||||
protected:
|
||||
|
@ -48,10 +55,12 @@ protected:
|
|||
typedef std::tuple<AnimSkeleton::ConstPointer, AnimPose, glm::vec4> SkeletonInfo;
|
||||
typedef std::tuple<AnimNode::ConstPointer, AnimPose, glm::vec4> AnimNodeInfo;
|
||||
typedef std::tuple<AnimSkeleton::ConstPointer, AnimPoseVec, AnimPose, glm::vec4> PosesInfo;
|
||||
typedef std::tuple<AnimPose, AnimPose> SinglePoseInfo;
|
||||
|
||||
std::unordered_map<std::string, SkeletonInfo> _skeletons;
|
||||
std::unordered_map<std::string, AnimNodeInfo> _animNodes;
|
||||
std::unordered_map<std::string, PosesInfo> _poses;
|
||||
std::unordered_map<std::string, SinglePoseInfo> _singlePoses;
|
||||
|
||||
// no copies
|
||||
AnimDebugDraw(const AnimDebugDraw&) = delete;
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
"jointName": "LeftHand",
|
||||
"positionVar": "leftHandPosition",
|
||||
"rotationVar": "leftHandRotation"
|
||||
},
|
||||
{
|
||||
"jointName": "Head",
|
||||
"positionVar": "headPosition",
|
||||
"rotationVar": "headRotation"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue