mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 16:30:10 +02:00
Merge pull request #1135 from ey6es/ick
Basic IK/pointing based on mouse cursor with new skeleton.
This commit is contained in:
commit
0840fc6544
10 changed files with 137 additions and 42 deletions
|
@ -1876,19 +1876,6 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::
|
|||
void Application::update(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
// Use Transmitter Hand to move hand if connected, else use mouse
|
||||
if (_myTransmitter.isConnected()) {
|
||||
const float HAND_FORCE_SCALING = 0.01f;
|
||||
glm::vec3 estimatedRotation = _myTransmitter.getEstimatedRotation();
|
||||
glm::vec3 handForce(-estimatedRotation.z, -estimatedRotation.x, estimatedRotation.y);
|
||||
_myAvatar.setMovedHandOffset(handForce * HAND_FORCE_SCALING);
|
||||
} else {
|
||||
// update behaviors for avatar hand movement: handControl takes mouse values as input,
|
||||
// and gives back 3D values modulated for smooth transitioning between interaction modes.
|
||||
_handControl.update(_mouseX, _mouseY);
|
||||
_myAvatar.setMovedHandOffset(_handControl.getValues());
|
||||
}
|
||||
|
||||
// tell my avatar if the mouse is being pressed...
|
||||
_myAvatar.setMousePressed(_mousePressed);
|
||||
|
@ -1898,6 +1885,15 @@ void Application::update(float deltaTime) {
|
|||
_viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(),
|
||||
_mouseY / (float)_glWidget->height(), mouseRayOrigin, mouseRayDirection);
|
||||
|
||||
// adjust for mirroring
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
glm::vec3 mouseRayOffset = mouseRayOrigin - _viewFrustum.getPosition();
|
||||
mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) +
|
||||
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset));
|
||||
mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayDirection) +
|
||||
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayDirection));
|
||||
}
|
||||
|
||||
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
|
||||
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ Avatar::Avatar(Node* owningNode) :
|
|||
_skeletonModel(this),
|
||||
_ballSpringsInitialized(false),
|
||||
_bodyYawDelta(0.0f),
|
||||
_movedHandOffset(0.0f, 0.0f, 0.0f),
|
||||
_mode(AVATAR_MODE_STANDING),
|
||||
_velocity(0.0f, 0.0f, 0.0f),
|
||||
_thrust(0.0f, 0.0f, 0.0f),
|
||||
|
@ -444,23 +443,7 @@ void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
|||
}
|
||||
|
||||
void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovement) {
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
|
||||
// reset hand and arm positions according to hand movement
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
if (enableHandMovement) {
|
||||
glm::vec3 transformedHandMovement =
|
||||
right * _movedHandOffset.x * 2.0f +
|
||||
up * -_movedHandOffset.y * 2.0f +
|
||||
front * -_movedHandOffset.y * 2.0f;
|
||||
|
||||
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position += transformedHandMovement;
|
||||
}
|
||||
|
||||
enableHandMovement |= updateLeapHandPositions();
|
||||
|
||||
//constrain right arm length and re-adjust elbow position as it bends
|
||||
|
|
|
@ -205,7 +205,6 @@ protected:
|
|||
SkeletonModel _skeletonModel;
|
||||
bool _ballSpringsInitialized;
|
||||
float _bodyYawDelta;
|
||||
glm::vec3 _movedHandOffset;
|
||||
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _velocity;
|
||||
|
|
|
@ -828,17 +828,19 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
|
|||
glm::quat orientation = getOrientation();
|
||||
|
||||
// reset hand and arm positions according to hand movement
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
if (enableHandMovement) {
|
||||
glm::vec3 transformedHandMovement =
|
||||
right * _movedHandOffset.x * 2.0f +
|
||||
up * -_movedHandOffset.y * 2.0f +
|
||||
front * -_movedHandOffset.y * 2.0f;
|
||||
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement;
|
||||
// confine to the approximate shoulder plane
|
||||
glm::vec3 pointDirection = _mouseRayDirection;
|
||||
if (glm::dot(_mouseRayDirection, up) > 0.0f) {
|
||||
glm::vec3 projectedVector = glm::cross(up, glm::cross(_mouseRayDirection, up));
|
||||
if (glm::length(projectedVector) > EPSILON) {
|
||||
pointDirection = glm::normalize(projectedVector);
|
||||
}
|
||||
}
|
||||
const float FAR_AWAY_POINT = TREE_SCALE;
|
||||
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = _mouseRayOrigin + pointDirection * FAR_AWAY_POINT;
|
||||
}
|
||||
|
||||
_avatarTouch.setMyBodyPosition(_position);
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setMovedHandOffset(glm::vec3 movedHandOffset) { _movedHandOffset = movedHandOffset; }
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
|
|
|
@ -26,6 +26,8 @@ void SkeletonModel::simulate(float deltaTime) {
|
|||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE);
|
||||
|
||||
Model::simulate(deltaTime);
|
||||
|
||||
setRightHandPosition(_owningAvatar->getHandPosition());
|
||||
}
|
||||
|
||||
bool SkeletonModel::render(float alpha) {
|
||||
|
|
|
@ -326,11 +326,11 @@ QVariantHash parseMapping(QIODevice* device) {
|
|||
}
|
||||
QByteArray name = sections.at(0).trimmed();
|
||||
if (sections.size() == 2) {
|
||||
properties.insert(name, sections.at(1).trimmed());
|
||||
properties.insertMulti(name, sections.at(1).trimmed());
|
||||
|
||||
} else if (sections.size() == 3) {
|
||||
QVariantHash heading = properties.value(name).toHash();
|
||||
heading.insert(sections.at(1).trimmed(), sections.at(2).trimmed());
|
||||
heading.insertMulti(sections.at(1).trimmed(), sections.at(2).trimmed());
|
||||
properties.insert(name, heading);
|
||||
|
||||
} else if (sections.size() >= 4) {
|
||||
|
@ -696,12 +696,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
QString jointRootName = processID(joints.value("jointRoot", "jointRoot").toString());
|
||||
QString jointLeanName = processID(joints.value("jointLean", "jointLean").toString());
|
||||
QString jointHeadName = processID(joints.value("jointHead", "jointHead").toString());
|
||||
QString jointLeftHandName = processID(joints.value("jointLeftHand", "jointLeftHand").toString());
|
||||
QString jointRightHandName = processID(joints.value("jointRightHand", "jointRightHand").toString());
|
||||
QString jointEyeLeftID;
|
||||
QString jointEyeRightID;
|
||||
QString jointNeckID;
|
||||
QString jointRootID;
|
||||
QString jointLeanID;
|
||||
QString jointHeadID;
|
||||
QString jointLeftHandID;
|
||||
QString jointRightHandID;
|
||||
|
||||
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
||||
QHash<QByteArray, QPair<int, float> > blendshapeIndices;
|
||||
|
@ -775,6 +779,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
|
||||
} else if (name == jointHeadName) {
|
||||
jointHeadID = getID(object.properties);
|
||||
|
||||
} else if (name == jointLeftHandName) {
|
||||
jointLeftHandID = getID(object.properties);
|
||||
|
||||
} else if (name == jointRightHandName) {
|
||||
jointRightHandID = getID(object.properties);
|
||||
}
|
||||
glm::vec3 translation;
|
||||
glm::vec3 rotationOffset;
|
||||
|
@ -978,10 +988,24 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
}
|
||||
|
||||
// convert the models to joints
|
||||
QVariantList freeJoints = mapping.values("freeJoint");
|
||||
foreach (const QString& modelID, modelIDs) {
|
||||
const FBXModel& model = models[modelID];
|
||||
FBXJoint joint;
|
||||
joint.isFree = freeJoints.contains(model.name);
|
||||
joint.parentIndex = model.parentIndex;
|
||||
|
||||
// get the indices of all ancestors starting with the first free one (if any)
|
||||
joint.freeLineage.append(geometry.joints.size());
|
||||
int lastFreeIndex = joint.isFree ? 0 : -1;
|
||||
for (int index = joint.parentIndex; index != -1; index = geometry.joints.at(index).parentIndex) {
|
||||
if (geometry.joints.at(index).isFree) {
|
||||
lastFreeIndex = joint.freeLineage.size();
|
||||
}
|
||||
joint.freeLineage.append(index);
|
||||
}
|
||||
joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1);
|
||||
|
||||
joint.preTransform = model.preTransform;
|
||||
joint.preRotation = model.preRotation;
|
||||
joint.rotation = model.rotation;
|
||||
|
@ -1009,6 +1033,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
geometry.rootJointIndex = modelIDs.indexOf(jointRootID);
|
||||
geometry.leanJointIndex = modelIDs.indexOf(jointLeanID);
|
||||
geometry.headJointIndex = modelIDs.indexOf(jointHeadID);
|
||||
geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID);
|
||||
geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID);
|
||||
|
||||
// extract the translation component of the neck transform
|
||||
if (geometry.neckJointIndex != -1) {
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
class FBXJoint {
|
||||
public:
|
||||
|
||||
bool isFree;
|
||||
QVector<int> freeLineage;
|
||||
int parentIndex;
|
||||
glm::mat4 preTransform;
|
||||
glm::quat preRotation;
|
||||
|
@ -128,6 +130,8 @@ public:
|
|||
int rootJointIndex;
|
||||
int leanJointIndex;
|
||||
int headJointIndex;
|
||||
int leftHandJointIndex;
|
||||
int rightHandJointIndex;
|
||||
|
||||
glm::vec3 neckPivot;
|
||||
|
||||
|
|
|
@ -413,6 +413,22 @@ bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePos
|
|||
getJointPosition(geometry.rightEyeJointIndex, secondEyePosition);
|
||||
}
|
||||
|
||||
bool Model::setLeftHandPosition(const glm::vec3& position) {
|
||||
return isActive() && setJointPosition(_geometry->getFBXGeometry().leftHandJointIndex, position);
|
||||
}
|
||||
|
||||
bool Model::setLeftHandRotation(const glm::quat& rotation) {
|
||||
return isActive() && setJointRotation(_geometry->getFBXGeometry().leftHandJointIndex, rotation);
|
||||
}
|
||||
|
||||
bool Model::setRightHandPosition(const glm::vec3& position) {
|
||||
return isActive() && setJointPosition(_geometry->getFBXGeometry().rightHandJointIndex, position);
|
||||
}
|
||||
|
||||
bool Model::setRightHandRotation(const glm::quat& rotation) {
|
||||
return isActive() && setJointRotation(_geometry->getFBXGeometry().rightHandJointIndex, rotation);
|
||||
}
|
||||
|
||||
void Model::setURL(const QUrl& url) {
|
||||
// don't recreate the geometry if it's the same URL
|
||||
if (_url == url) {
|
||||
|
@ -492,6 +508,55 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& position) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||
|
||||
// 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;
|
||||
for (int i = 0; i < ITERATION_COUNT; i++) {
|
||||
// first, we go from the joint upwards, rotating the end as close as possible to the target
|
||||
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].transform);
|
||||
for (int j = 1; j < freeLineage.size(); j++) {
|
||||
int index = freeLineage.at(j);
|
||||
if (glm::distance(endPosition, position) < EPSILON) {
|
||||
return true; // close enough to target position
|
||||
}
|
||||
const FBXJoint& joint = geometry.joints.at(index);
|
||||
if (!joint.isFree) {
|
||||
continue;
|
||||
}
|
||||
JointState& state = _jointStates[index];
|
||||
glm::vec3 jointPosition = extractTranslation(state.transform);
|
||||
glm::vec3 jointVector = endPosition - jointPosition;
|
||||
glm::quat deltaRotation = rotationBetween(jointVector, position - jointPosition);
|
||||
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * deltaRotation * state.combinedRotation;
|
||||
endPosition = deltaRotation * jointVector + jointPosition;
|
||||
}
|
||||
|
||||
// then, from the first free joint downwards, update the transforms again
|
||||
for (int j = freeLineage.size() - 1; j >= 0; j--) {
|
||||
updateJointState(freeLineage.at(j));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation *
|
||||
glm::inverse(_geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Model::deleteGeometry() {
|
||||
foreach (Model* attachment, _attachments) {
|
||||
delete attachment;
|
||||
|
|
|
@ -70,6 +70,22 @@ public:
|
|||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
/// Sets the position of the left hand using inverse kinematics.
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool setLeftHandPosition(const glm::vec3& position);
|
||||
|
||||
/// Sets the rotation of the left hand.
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool setLeftHandRotation(const glm::quat& rotation);
|
||||
|
||||
/// Sets the position of the right hand using inverse kinematics.
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool setRightHandPosition(const glm::vec3& position);
|
||||
|
||||
/// Sets the rotation of the right hand.
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool setRightHandRotation(const glm::quat& rotation);
|
||||
|
||||
/// Returns the average color of all meshes in the geometry.
|
||||
glm::vec4 computeAverageColor() const;
|
||||
|
||||
|
@ -101,6 +117,9 @@ protected:
|
|||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& position);
|
||||
bool setJointRotation(int jointIndex, const glm::quat& rotation);
|
||||
|
||||
private:
|
||||
|
||||
void deleteGeometry();
|
||||
|
|
Loading…
Reference in a new issue