fix for pushing avatar into floor when exiting away mode.

* Removed MyAvatar.reset() access from JavaScript
* Added HMD.centerUI() to JavaScript, which can be used to reset the 3D UI sphere around the current HMD orientation.
* Added MyAvatar.clearIKJOintLimitHistory() which can be used to reset any remembered IK joint limit history.
* Added MyAvatar.centerBody() which can be used to instantly re-orient the avatar's so that the hips and toes
  are facing the same direction as the current HMD orientation.

away.js now uses the above new API's instead of MyAvatar.reset()
This commit is contained in:
Anthony J. Thibault 2016-07-19 14:42:24 -07:00
parent 866ba2f185
commit 837b19ed1b
13 changed files with 90 additions and 7 deletions

View file

@ -3404,6 +3404,10 @@ void Application::setOverlaysVisible(bool visible) {
menu->setIsOptionChecked(MenuOption::Overlays, true);
}
void Application::centerUI() {
_overlayConductor.centerUI();
}
void Application::cycleCamera() {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {

View file

@ -298,6 +298,7 @@ public slots:
void cameraMenuChanged();
void toggleOverlays();
void setOverlaysVisible(bool visible);
Q_INVOKABLE void centerUI();
void resetPhysicsReadyInformation();

View file

@ -231,13 +231,46 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
return AvatarData::toByteArray(cullSmallChanges, sendAll);
}
void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
void MyAvatar::centerBody() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, andRecenter), Q_ARG(bool, andReload), Q_ARG(bool, andHead));
QMetaObject::invokeMethod(this, "centerBody");
return;
}
// derive the desired body orientation from the current hmd orientation, before the sensor reset.
auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
// transform this body into world space
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
auto worldBodyPos = extractTranslation(worldBodyMatrix);
auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix));
// this will become our new position.
setPosition(worldBodyPos);
setOrientation(worldBodyRot);
// reset the body in sensor space
_bodySensorMatrix = newBodySensorMatrix;
// rebuild the sensor to world matrix
updateSensorToWorldMatrix();
}
void MyAvatar::clearIKJointLimitHistory() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "clearIKJointLimitHistory");
return;
}
if (_rig) {
_rig->clearIKJointLimitHistory();
}
}
void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
assert(QThread::currentThread() == thread());
// Reset dynamic state.
_wasPushing = _isPushing = _isBraking = false;
_follow.deactivate();

View file

@ -96,7 +96,11 @@ public:
AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; }
AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; }
Q_INVOKABLE void reset(bool andRecenter = false, bool andReload = true, bool andHead = true);
void reset(bool andRecenter = false, bool andReload = true, bool andHead = true);
Q_INVOKABLE void centerBody(); // thread-safe
Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe
void update(float deltaTime);
virtual void postUpdate(float deltaTime) override;
void preDisplaySide(RenderArgs* renderArgs);

View file

@ -135,3 +135,7 @@ void HMDScriptingInterface::unsuppressKeyboard() {
bool HMDScriptingInterface::isKeyboardVisible() {
return qApp->getActiveDisplayPlugin()->isKeyboardVisible();
}
void HMDScriptingInterface::centerUI() {
QMetaObject::invokeMethod(qApp, "centerUI", Qt::QueuedConnection);
}

View file

@ -55,6 +55,9 @@ public:
/// Query the display plugin to determine the current VR keyboard visibility
Q_INVOKABLE bool isKeyboardVisible();
// rotate the overlay UI sphere so that it is centered about the the current HMD position and orientation
Q_INVOKABLE void centerUI();
public:
HMDScriptingInterface();
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);

View file

@ -488,6 +488,12 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
return _relativePoses;
}
void AnimInverseKinematics::clearIKJointLimitHistory() {
for (auto& pair : _constraints) {
pair.second->clearHistory();
}
}
RotationConstraint* AnimInverseKinematics::getConstraint(int index) {
RotationConstraint* constraint = nullptr;
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.find(index);

View file

@ -37,6 +37,8 @@ public:
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
void clearIKJointLimitHistory();
protected:
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets);

View file

@ -295,6 +295,19 @@ void Rig::clearJointAnimationPriority(int index) {
}
}
void Rig::clearIKJointLimitHistory() {
if (_animNode) {
_animNode->traverse([&](AnimNode::Pointer node) {
// only report clip nodes as valid roles.
auto ikNode = std::dynamic_pointer_cast<AnimInverseKinematics>(node);
if (ikNode) {
ikNode->clearIKJointLimitHistory();
}
return true;
});
}
}
void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
if (isIndexValid(index)) {
if (valid) {

View file

@ -103,6 +103,8 @@ public:
void clearJointStates();
void clearJointAnimationPriority(int index);
void clearIKJointLimitHistory();
// geometry space
void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority);

View file

@ -35,6 +35,9 @@ public:
/// \brief clear previous adjustment and adjust constraint limits to allow rotation
virtual void dynamicallyAdjustLimits(const glm::quat& rotation) {}
/// \brief reset any remembered joint limit history
virtual void clearHistory() {};
protected:
glm::quat _referenceRotation = glm::quat();
};

View file

@ -97,8 +97,7 @@ public:
/// \return reference to SwingLimitFunction instance for unit-testing
const SwingLimitFunction& getSwingLimitFunction() const { return _swingLimitFunction; }
/// \brief exposed for unit testing
void clearHistory();
void clearHistory() override;
private:
float handleTwistBoundaryConditions(float twistAngle) const;

View file

@ -204,7 +204,16 @@ function goActive() {
}
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
stopAwayAnimation();
MyAvatar.reset(true);
// update the UI sphere to be centered about the current HMD orientation.
HMD.centerUI();
// forget about any IK joint limits
MyAvatar.clearIKJointLimitHistory();
// update the avatar hips to point in the same direction as the HMD orientation.
MyAvatar.centerBody();
hideOverlay();
// restore overlays state to what it was when we went "away"