Merge pull request #3828 from AndrewMeadows/bispinor

don't slide feet during idle animations
This commit is contained in:
Brad Hefta-Gaub 2014-11-19 16:38:34 -08:00
commit 0b7c3d3356
9 changed files with 109 additions and 6 deletions

View file

@ -297,6 +297,8 @@ Menu::Menu() :
avatar, SLOT(updateMotionBehavior())); avatar, SLOT(updateMotionBehavior()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true, addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true,
avatar, SLOT(updateMotionBehavior())); avatar, SLOT(updateMotionBehavior()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
avatar, SLOT(updateMotionBehavior()));
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With..."); QMenu* collisionsMenu = avatarMenu->addMenu("Collide With...");
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false, addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false,

View file

@ -484,6 +484,7 @@ namespace MenuOption {
const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString SixenseMouseInput = "Enable Sixense Mouse Input";
const QString SixenseLasers = "Enable Sixense UI Lasers"; const QString SixenseLasers = "Enable Sixense UI Lasers";
const QString StandOnNearbyFloors = "Stand on nearby floors"; const QString StandOnNearbyFloors = "Stand on nearby floors";
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
const QString Stars = "Stars"; const QString Stars = "Stars";
const QString Stats = "Stats"; const QString Stats = "Stats";
const QString StereoAudio = "Stereo Audio"; const QString StereoAudio = "Stereo Audio";

View file

@ -787,7 +787,10 @@ void Avatar::setSkeletonOffset(const glm::vec3& offset) {
} }
glm::vec3 Avatar::getSkeletonPosition() const { glm::vec3 Avatar::getSkeletonPosition() const {
return _position + _skeletonOffset; // The avatar is rotated PI about the yAxis, so we have to correct for it
// to get the skeleton offset contribution in the world-frame.
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
return _position + getOrientation() * FLIP * _skeletonOffset;
} }
QVector<glm::quat> Avatar::getJointRotations() const { QVector<glm::quat> Avatar::getJointRotations() const {

View file

@ -89,6 +89,7 @@ MyAvatar::MyAvatar() :
_billboardValid(false), _billboardValid(false),
_physicsSimulation(), _physicsSimulation(),
_voxelShapeManager(), _voxelShapeManager(),
_feetTouchFloor(true),
_isLookingAtLeftEye(true) _isLookingAtLeftEye(true)
{ {
ShapeCollider::initDispatchTable(); ShapeCollider::initDispatchTable();
@ -115,7 +116,7 @@ QByteArray MyAvatar::toByteArray() {
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
// fake the avatar position that is sent up to the AvatarMixer // fake the avatar position that is sent up to the AvatarMixer
glm::vec3 oldPosition = _position; glm::vec3 oldPosition = _position;
_position += _skeletonOffset; _position = getSkeletonPosition();
QByteArray array = AvatarData::toByteArray(); QByteArray array = AvatarData::toByteArray();
// copy the correct position back // copy the correct position back
_position = oldPosition; _position = oldPosition;
@ -155,6 +156,9 @@ void MyAvatar::update(float deltaTime) {
} }
simulate(deltaTime); simulate(deltaTime);
if (_feetTouchFloor) {
_skeletonModel.updateStandingFoot();
}
} }
void MyAvatar::simulate(float deltaTime) { void MyAvatar::simulate(float deltaTime) {
@ -1034,7 +1038,14 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
glm::vec3 MyAvatar::getSkeletonPosition() const { glm::vec3 MyAvatar::getSkeletonPosition() const {
CameraMode mode = Application::getInstance()->getCamera()->getMode(); CameraMode mode = Application::getInstance()->getCamera()->getMode();
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
return Avatar::getSkeletonPosition(); // The avatar is rotated PI about the yAxis, so we have to correct for it
// to get the skeleton offset contribution in the world-frame.
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::vec3 skeletonOffset = _skeletonOffset;
if (_feetTouchFloor) {
skeletonOffset += _skeletonModel.getStandingOffset();
}
return _position + getOrientation() * FLIP * skeletonOffset;
} }
return Avatar::getPosition(); return Avatar::getPosition();
} }
@ -1939,6 +1950,7 @@ void MyAvatar::updateMotionBehavior() {
} else { } else {
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
} }
_feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
} }
void MyAvatar::onToggleRagdoll() { void MyAvatar::onToggleRagdoll() {

View file

@ -235,6 +235,7 @@ private:
PhysicsSimulation _physicsSimulation; PhysicsSimulation _physicsSimulation;
VoxelShapeManager _voxelShapeManager; VoxelShapeManager _voxelShapeManager;
bool _feetTouchFloor;
bool _isLookingAtLeftEye; bool _isLookingAtLeftEye;
RecorderPointer _recorder; RecorderPointer _recorder;

View file

@ -22,13 +22,22 @@
#include "SkeletonModel.h" #include "SkeletonModel.h"
#include "SkeletonRagdoll.h" #include "SkeletonRagdoll.h"
enum StandingFootState {
LEFT_FOOT,
RIGHT_FOOT,
NO_FOOT
};
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
Model(parent), Model(parent),
_owningAvatar(owningAvatar), _owningAvatar(owningAvatar),
_boundingShape(), _boundingShape(),
_boundingShapeLocalOffset(0.0f), _boundingShapeLocalOffset(0.0f),
_ragdoll(NULL), _ragdoll(NULL),
_defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)) { _defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)),
_standingFoot(NO_FOOT),
_standingOffset(0.0f),
_clampedFootPosition(0.0f) {
} }
SkeletonModel::~SkeletonModel() { SkeletonModel::~SkeletonModel() {
@ -607,6 +616,62 @@ void SkeletonModel::updateVisibleJointStates() {
} }
} }
/// \return offset of hips after foot animation
void SkeletonModel::updateStandingFoot() {
glm::vec3 offset(0.0f);
int leftFootIndex = _geometry->getFBXGeometry().leftToeJointIndex;
int rightFootIndex = _geometry->getFBXGeometry().rightToeJointIndex;
if (leftFootIndex != -1 && rightFootIndex != -1) {
glm::vec3 leftPosition, rightPosition;
getJointPosition(leftFootIndex, leftPosition);
getJointPosition(rightFootIndex, rightPosition);
int lowestFoot = (leftPosition.y < rightPosition.y) ? LEFT_FOOT : RIGHT_FOOT;
const float MIN_STEP_HEIGHT_THRESHOLD = 0.05f;
bool oneFoot = fabsf(leftPosition.y - rightPosition.y) > MIN_STEP_HEIGHT_THRESHOLD;
int currentFoot = oneFoot ? lowestFoot : _standingFoot;
if (_standingFoot == NO_FOOT) {
currentFoot = lowestFoot;
}
if (currentFoot != _standingFoot) {
if (_standingFoot == NO_FOOT) {
// pick the lowest foot
glm::vec3 lowestPosition = (currentFoot == LEFT_FOOT) ? leftPosition : rightPosition;
// we ignore zero length positions which can happen for a few frames until skeleton is fully loaded
if (glm::length(lowestPosition) > 0.0f) {
_standingFoot = currentFoot;
_clampedFootPosition = lowestPosition;
}
} else {
// swap feet
_standingFoot = currentFoot;
glm::vec3 nextPosition = leftPosition;
glm::vec3 prevPosition = rightPosition;
if (_standingFoot == RIGHT_FOOT) {
nextPosition = rightPosition;
prevPosition = leftPosition;
}
glm::vec3 oldOffset = _clampedFootPosition - prevPosition;
_clampedFootPosition = oldOffset + nextPosition;
offset = _clampedFootPosition - nextPosition;
}
} else {
glm::vec3 nextPosition = (_standingFoot == LEFT_FOOT) ? leftPosition : rightPosition;
offset = _clampedFootPosition - nextPosition;
}
// clamp the offset to not exceed some max distance
const float MAX_STEP_OFFSET = 1.0f;
float stepDistance = glm::length(offset);
if (stepDistance > MAX_STEP_OFFSET) {
offset *= (MAX_STEP_OFFSET / stepDistance);
}
}
_standingOffset = offset;
}
SkeletonRagdoll* SkeletonModel::buildRagdoll() { SkeletonRagdoll* SkeletonModel::buildRagdoll() {
if (!_ragdoll) { if (!_ragdoll) {
_ragdoll = new SkeletonRagdoll(this); _ragdoll = new SkeletonRagdoll(this);

View file

@ -101,6 +101,10 @@ 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;
/// skeleton offset caused by moving feet
void updateStandingFoot();
const glm::vec3& getStandingOffset() const { return _standingOffset; }
virtual void updateVisibleJointStates(); virtual void updateVisibleJointStates();
SkeletonRagdoll* buildRagdoll(); SkeletonRagdoll* buildRagdoll();
@ -154,6 +158,9 @@ private:
SkeletonRagdoll* _ragdoll; SkeletonRagdoll* _ragdoll;
glm::vec3 _defaultEyeModelPosition; glm::vec3 _defaultEyeModelPosition;
int _standingFoot;
glm::vec3 _standingOffset;
glm::vec3 _clampedFootPosition;
}; };
#endif // hifi_SkeletonModel_h #endif // hifi_SkeletonModel_h

View file

@ -1067,6 +1067,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QString jointHeadID; QString jointHeadID;
QString jointLeftHandID; QString jointLeftHandID;
QString jointRightHandID; QString jointRightHandID;
QString jointLeftToeID;
QString jointRightToeID;
QVector<QString> humanIKJointNames; QVector<QString> humanIKJointNames;
for (int i = 0;; i++) { for (int i = 0;; i++) {
@ -1166,11 +1168,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (name == jointHeadName) { } else if (name == jointHeadName) {
jointHeadID = getID(object.properties); jointHeadID = getID(object.properties);
} else if (name == jointLeftHandName) { } else if (name == jointLeftHandName || name == "LeftHand" || name == "joint_L_hand") {
jointLeftHandID = getID(object.properties); jointLeftHandID = getID(object.properties);
} else if (name == jointRightHandName) { } else if (name == jointRightHandName || name == "RightHand" || name == "joint_R_hand") {
jointRightHandID = getID(object.properties); jointRightHandID = getID(object.properties);
} else if (name == "LeftToe" || name == "joint_L_toe" || name == "LeftToe_End") {
jointLeftToeID = getID(object.properties);
} else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End") {
jointRightToeID = getID(object.properties);
} }
int humanIKJointIndex = humanIKJointNames.indexOf(name); int humanIKJointIndex = humanIKJointNames.indexOf(name);
if (humanIKJointIndex != -1) { if (humanIKJointIndex != -1) {
@ -1595,6 +1603,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.headJointIndex = modelIDs.indexOf(jointHeadID); geometry.headJointIndex = modelIDs.indexOf(jointHeadID);
geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID);
geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID);
geometry.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID);
geometry.rightToeJointIndex = modelIDs.indexOf(jointRightToeID);
foreach (const QString& id, humanIKJointIDs) { foreach (const QString& id, humanIKJointIDs) {
geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); geometry.humanIKJointIndices.append(modelIDs.indexOf(id));

View file

@ -201,6 +201,8 @@ public:
int headJointIndex; int headJointIndex;
int leftHandJointIndex; int leftHandJointIndex;
int rightHandJointIndex; int rightHandJointIndex;
int leftToeJointIndex;
int rightToeJointIndex;
QVector<int> humanIKJointIndices; QVector<int> humanIKJointIndices;