mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge pull request #10 from hyperlogic/ajt/face-lean-eyes-rig
Fix avatar head, eye and torso twist.
This commit is contained in:
commit
1dddf18243
8 changed files with 108 additions and 123 deletions
|
@ -50,60 +50,6 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
}
|
||||
|
||||
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(_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;
|
||||
_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, 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(_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 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation();
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f));
|
||||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
_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) {
|
||||
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 < _rig->getJointStateCount()) {
|
||||
const JointState& parentState = _rig->getJointState(joint.parentIndex);
|
||||
if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint, index);
|
||||
|
||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
||||
maybeUpdateEyeRotation(this, parentState, joint, 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 {
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
|
|
|
@ -19,23 +19,19 @@ class Head;
|
|||
/// A face formed from a linear mix of blendshapes according to a set of coefficients.
|
||||
class FaceModel : public Model {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
|
||||
FaceModel(Head* owningHead, RigPointer rig);
|
||||
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
|
||||
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.
|
||||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
Head* _owningHead;
|
||||
};
|
||||
|
||||
|
|
|
@ -118,6 +118,24 @@ void SkeletonModel::updateClusterMatrices() {
|
|||
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
||||
Model::updateRig(deltaTime, parentTransform);
|
||||
if (_owningAvatar->isMyAvatar()) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
||||
Rig::HeadParameters params;
|
||||
params.leanSideways = _owningAvatar->getHead()->getFinalLeanSideways();
|
||||
params.leanForward = _owningAvatar->getHead()->getFinalLeanSideways();
|
||||
params.torsoTwist = _owningAvatar->getHead()->getTorsoTwist();
|
||||
params.localHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInLocalFrame();
|
||||
params.worldHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInWorldFrame();
|
||||
params.eyeLookAt = _owningAvatar->getHead()->getCorrectedLookAtPosition();
|
||||
params.eyeSaccade = _owningAvatar->getHead()->getSaccade();
|
||||
params.leanJointIndex = geometry.leanJointIndex;
|
||||
params.neckJointIndex = geometry.neckJointIndex;
|
||||
params.leftEyeJointIndex = geometry.leftEyeJointIndex;
|
||||
params.rightEyeJointIndex = geometry.rightEyeJointIndex;
|
||||
|
||||
_rig->updateFromHeadParameters(params);
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
|
@ -259,51 +277,12 @@ void SkeletonModel::updateJointState(int index) {
|
|||
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, index);
|
||||
|
||||
} else if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint.getFBXJoint(), index);
|
||||
|
||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
||||
maybeUpdateEyeRotation(parentState, joint.getFBXJoint(), index);
|
||||
}
|
||||
}
|
||||
|
||||
_rig->updateJointState(index, parentTransform);
|
||||
|
||||
if (index == _geometry->getFBXGeometry().rootJointIndex) {
|
||||
_rig->clearJointTransformTranslation(index);
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, int index) {
|
||||
if (!_owningAvatar->isMyAvatar()) {
|
||||
return;
|
||||
}
|
||||
// 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() * _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, int index) {
|
||||
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, index);
|
||||
}
|
||||
|
||||
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 >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
|
|
|
@ -31,21 +31,3 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ class AvatarRig : public Rig {
|
|||
public:
|
||||
~AvatarRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
virtual void updateFaceJointState(int index, glm::mat4 parentTransform);
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarRig_h
|
||||
|
|
|
@ -22,7 +22,6 @@ class EntityRig : public Rig {
|
|||
public:
|
||||
~EntityRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
virtual void updateFaceJointState(int index, glm::mat4 parentTransform) { }
|
||||
};
|
||||
|
||||
#endif // hifi_EntityRig_h
|
||||
|
|
|
@ -665,3 +665,68 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
|
|||
}
|
||||
return _jointStates[jointIndex].getDefaultRotationInParentFrame();
|
||||
}
|
||||
|
||||
void Rig::updateFromHeadParameters(const HeadParameters& params) {
|
||||
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
updateEyeJoint(params.leftEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
||||
updateEyeJoint(params.rightEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
||||
}
|
||||
|
||||
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
|
||||
if (index > 0 && _jointStates[index].getParentIndex() > 0) {
|
||||
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) *
|
||||
getJointState(index).getFBXJoint().rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) {
|
||||
if (index > 0 && _jointStates[index].getParentIndex() > 0) {
|
||||
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
|
||||
auto joint = _jointStates[index].getFBXJoint();
|
||||
|
||||
// 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)) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
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])) *
|
||||
joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade) {
|
||||
if ( index > 0 && _jointStates[index].getParentIndex() > 0) {
|
||||
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
|
||||
auto joint = _jointStates[index].getFBXJoint();
|
||||
|
||||
// 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(parentState.getTransform() *
|
||||
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
||||
glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f));
|
||||
glm::vec3 lookAtDelta = lookAt;
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f));
|
||||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
float angle = glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE);
|
||||
glm::quat rot = glm::angleAxis(angle, glm::axis(between));
|
||||
setJointRotationInConstrainedFrame(index, rot * joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,14 +47,27 @@ typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
|||
class Rig;
|
||||
typedef std::shared_ptr<Rig> RigPointer;
|
||||
|
||||
|
||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||
|
||||
public:
|
||||
|
||||
struct HeadParameters {
|
||||
float leanSideways = 0.0f; // degrees
|
||||
float leanForward = 0.0f; // degrees
|
||||
float torsoTwist = 0.0f; // degrees
|
||||
glm::quat localHeadOrientation = glm::quat();
|
||||
glm::quat worldHeadOrientation = glm::quat();
|
||||
glm::vec3 eyeLookAt = glm::vec3(); // world space
|
||||
glm::vec3 eyeSaccade = glm::vec3(); // world space
|
||||
int leanJointIndex = -1;
|
||||
int neckJointIndex = -1;
|
||||
int leftEyeJointIndex = -1;
|
||||
int rightEyeJointIndex = -1;
|
||||
};
|
||||
|
||||
virtual ~Rig() {}
|
||||
|
||||
RigPointer getRigPointer() { return shared_from_this(); }
|
||||
RigPointer getRigPointer() { return shared_from_this(); }
|
||||
|
||||
AnimationHandlePointer createAnimationHandle();
|
||||
void removeAnimationHandle(const AnimationHandlePointer& handle);
|
||||
|
@ -129,11 +142,17 @@ public:
|
|||
void updateVisibleJointStates();
|
||||
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
|
||||
virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0;
|
||||
|
||||
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
|
||||
|
||||
void updateFromHeadParameters(const HeadParameters& params);
|
||||
|
||||
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 updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
|
||||
QVector<JointState> _jointStates;
|
||||
|
||||
QList<AnimationHandlePointer> _animationHandles;
|
||||
|
|
Loading…
Reference in a new issue