Merge remote-tracking branch 'origin/squattyPottyFix' into squattyPottyFix

This commit is contained in:
amantley 2018-10-11 22:55:55 -07:00
commit ab7a70acba
9 changed files with 338 additions and 31 deletions

View file

@ -252,6 +252,8 @@ Rectangle {
var avatarSettings = {
dominantHand : settings.dominantHandIsLeft ? 'left' : 'right',
collisionsEnabled : settings.avatarCollisionsOn,
sittingEnabled : settings.avatarSittingOn,
lockStateEnabled : settings.avatarLockSitStandStateOn,
animGraphOverrideUrl : settings.avatarAnimationOverrideJSON,
collisionSoundUrl : settings.avatarCollisionSoundUrl
};

View file

@ -20,6 +20,8 @@ Rectangle {
property real scaleValue: scaleSlider.value / 10
property alias dominantHandIsLeft: leftHandRadioButton.checked
property alias avatarCollisionsOn: collisionsEnabledRadiobutton.checked
property alias avatarSittingOn: sitRadiobutton.checked
property alias avatarLockSitStandStateOn: lockSitStandStateCheckbox.checked
property alias avatarAnimationOverrideJSON: avatarAnimationUrlInputText.text
property alias avatarAnimationJSON: avatarAnimationUrlInputText.placeholderText
property alias avatarCollisionSoundUrl: avatarCollisionSoundUrlInputText.text
@ -45,6 +47,18 @@ Rectangle {
collisionsDisabledRadioButton.checked = true;
}
if (settings.sittingEnabled) {
sitRadiobutton.checked = true;
} else {
standRadioButton.checked = true;
}
if (settings.lockStateEnabled) {
lockSitStandStateCheckbox.checked = true;
} else {
lockSitStandStateCheckbox.checked = false;
}
avatarAnimationJSON = settings.animGraphUrl;
avatarAnimationOverrideJSON = settings.animGraphOverrideUrl;
avatarCollisionSoundUrl = settings.collisionSoundUrl;
@ -289,6 +303,65 @@ Rectangle {
text: "OFF"
boxSize: 20
}
// TextStyle9
RalewaySemiBold {
size: 17;
Layout.row: 2
Layout.column: 0
text: "Sitting State"
}
ButtonGroup {
id: sitStand
}
HifiControlsUit.RadioButton {
id: sitRadiobutton
Layout.row: 2
Layout.column: 1
Layout.leftMargin: -40
ButtonGroup.group: sitStand
checked: true
colorScheme: hifi.colorSchemes.light
fontSize: 17
letterSpacing: 1.4
text: "Sit"
boxSize: 20
}
HifiControlsUit.RadioButton {
id: standRadioButton
Layout.row: 2
Layout.column: 2
Layout.rightMargin: 20
ButtonGroup.group: sitStand
colorScheme: hifi.colorSchemes.light
fontSize: 17
letterSpacing: 1.4
text: "Stand"
boxSize: 20
}
// "Lock State" Checkbox
HifiControlsUit.CheckBox {
id: lockSitStandStateCheckbox;
visible: activeTab == "nearbyTab";
anchors.right: reloadNearbyContainer.left;
anchors.rightMargin: 20;
checked: settings.lockStateEnabled;
text: "lock";
boxSize: 24;
}
}
ColumnLayout {

View file

@ -468,6 +468,11 @@ void MyAvatar::update(float deltaTime) {
// update moving average of HMD facing in xz plane.
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders
const float STANDING_HEIGHT_MULTIPLE = 1.2f;
const float SITTING_HEIGHT_MULTIPLE = 0.833f;
const float COSINE_TEN_DEGREES = 0.98f;
const int SITTING_COUNT_THRESHOLD = 300;
const int SQUATTY_COUNT_THRESHOLD = 600;
float tau = deltaTime / HMD_FACING_TIMESCALE;
setHipToHandController(computeHandAzimuth());
@ -494,11 +499,90 @@ void MyAvatar::update(float deltaTime) {
_smoothOrientationTimer += deltaTime;
}
float newHeightReading = getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y;
int newHeightReadingInCentimeters = glm::floor(newHeightReading * CENTIMETERS_PER_METER);
_recentModeReadings.insert(newHeightReadingInCentimeters);
setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
controller::Pose newHeightReading = getControllerPoseInSensorFrame(controller::Action::HEAD);
if (newHeightReading.isValid()) {
int newHeightReadingInCentimeters = glm::floor(newHeightReading.getTranslation().y * CENTIMETERS_PER_METER);
_sumUserHeightSensorSpace += newHeightReading.getTranslation().y;
_averageUserHeightCount++;
_recentModeReadings.insert(newHeightReadingInCentimeters);
setCurrentStandingHeight(computeStandingHeightMode(newHeightReading));
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
}
// if the spine is straight and the head is below the default position by 5 cm then increment squatty count.
const float SQUAT_THRESHOLD = 0.05f;
glm::vec3 headDefaultPositionAvatarSpace = getAbsoluteDefaultJointTranslationInObjectFrame(getJointIndex("Head"));
glm::quat spine2OrientationAvatarSpace = getAbsoluteJointRotationInObjectFrame(getJointIndex("Spine2"));
glm::vec3 upSpine2 = spine2OrientationAvatarSpace * glm::vec3(0.0f, 1.0f, 0.0f);
if (glm::length(upSpine2) > 0.0f) {
upSpine2 = glm::normalize(upSpine2);
}
float angleSpine2 = glm::dot(upSpine2, glm::vec3(0.0f, 1.0f, 0.0f));
if (getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) && (angleSpine2 > COSINE_TEN_DEGREES)) {
_squatCount++;
} else {
_squatCount = 0;
}
float averageSensorSpaceHeight = _sumUserHeightSensorSpace / _averageUserHeightCount;
glm::vec3 avatarHips = getAbsoluteJointTranslationInObjectFrame(getJointIndex("Hips"));
glm::vec3 worldHips = transformPoint(getTransform().getMatrix(), avatarHips);
glm::vec3 sensorHips = transformPoint(glm::inverse(getSensorToWorldMatrix()), worldHips);
// put update sit stand state counts here
if (getIsSitStandStateLocked()) {
if (getIsInSittingState()) {
if (newHeightReading.getTranslation().y > (STANDING_HEIGHT_MULTIPLE * _tippingPoint)) {
// if we recenter upwards then no longer in sitting state
_sitStandStateCount++;
if (_sitStandStateCount > SITTING_COUNT_THRESHOLD) {
_sitStandStateCount = 0;
_squatCount = 0;
if (newHeightReading.isValid()) {
_sumUserHeightSensorSpace = newHeightReading.getTranslation().y;
_tippingPoint = newHeightReading.getTranslation().y;
}
_averageUserHeightCount = 1;
setResetMode(true);
setIsInSittingState(false);
setCenterOfGravityModelEnabled(true);
setSitStandStateChange(true);
}
qCDebug(interfaceapp) << "going to stand state";
} else {
_sitStandStateCount = 0;
// tipping point is average height when sitting.
_tippingPoint = averageSensorSpaceHeight;
}
} else {
// in the standing state
if ((newHeightReading.getTranslation().y < (SITTING_HEIGHT_MULTIPLE * _tippingPoint))) {// && (angleSpine2 > COSINE_TEN_DEGREES) && !(sensorHips.y > (0.4f * averageSensorSpaceHeight))) {
_sitStandStateCount++;
if (_sitStandStateCount > SITTING_COUNT_THRESHOLD) {
_sitStandStateCount = 0;
_squatCount = 0;
if (newHeightReading.isValid()) {
_sumUserHeightSensorSpace = newHeightReading.getTranslation().y;
_tippingPoint = newHeightReading.getTranslation().y;
}
_averageUserHeightCount = 1;
setResetMode(true);
setIsInSittingState(true);
setCenterOfGravityModelEnabled(false);
setSitStandStateChange(true);
}
qCDebug(interfaceapp) << "going to sit state";
} else {
// returnValue = (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
// use the mode height for the tipping point when we are standing.
_tippingPoint = getCurrentStandingHeight();
_sitStandStateCount = 0;
}
}
}
if (_drawAverageFacingEnabled) {
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
@ -510,7 +594,7 @@ void MyAvatar::update(float deltaTime) {
// draw hand azimuth vector
glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y));
DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f));
DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f));
}
if (_goToPending) {
@ -852,6 +936,13 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
// Find the vector halfway between the hip to hand azimuth vectors
// This midpoint hand azimuth is in Avatar space
glm::vec2 MyAvatar::computeHandAzimuth() const {
int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2");
glm::vec3 azimuthOrigin(0.0f,0.0f,0.0f);
if (!(spine2Index < 0)) {
// use the spine for the azimuth origin.
azimuthOrigin = getAbsoluteJointTranslationInObjectFrame(spine2Index);
}
controller::Pose leftHandPoseAvatarSpace = getLeftHandPose();
controller::Pose rightHandPoseAvatarSpace = getRightHandPose();
controller::Pose headPoseAvatarSpace = getControllerPoseInAvatarFrame(controller::Action::HEAD);
@ -859,11 +950,13 @@ glm::vec2 MyAvatar::computeHandAzimuth() const {
glm::vec2 latestHipToHandController = _hipToHandController;
if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid() && headPoseAvatarSpace.isValid()) {
glm::vec3 rightHandOffset = rightHandPoseAvatarSpace.translation - azimuthOrigin;
glm::vec3 leftHandOffset = leftHandPoseAvatarSpace.translation - azimuthOrigin;
// we need the old azimuth reading to prevent flipping the facing direction 180
// in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart.
glm::vec2 oldAzimuthReading = _hipToHandController;
if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) {
latestHipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY);
if ((glm::length(glm::vec2(rightHandOffset.x, rightHandOffset.z)) > 0.0f) && (glm::length(glm::vec2(leftHandOffset.x, leftHandOffset.z)) > 0.0f)) {
latestHipToHandController = lerp(glm::normalize(glm::vec2(rightHandOffset.x, rightHandOffset.z)), glm::normalize(glm::vec2(leftHandOffset.x, leftHandOffset.z)), HALFWAY);
} else {
latestHipToHandController = glm::vec2(0.0f, -1.0f);
}
@ -3535,10 +3628,6 @@ glm::vec3 MyAvatar::computeCounterBalance() {
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
// if the height is higher than default hips, clamp to default hips
counterBalancedCg.y = tposeHips.y + 0.05f;
} else if (counterBalancedCg.y < sitSquatThreshold) {
//do a height reset
setResetMode(true);
_follow.activate(FollowHelper::Vertical);
}
return counterBalancedCg;
}
@ -3779,6 +3868,14 @@ bool MyAvatar::getIsInWalkingState() const {
return _isInWalkingState;
}
bool MyAvatar::getIsInSittingState() const {
return _isInSittingState.get();
}
bool MyAvatar::getIsSitStandStateLocked() const {
return _lockSitStandState.get();
}
float MyAvatar::getWalkSpeed() const {
return _walkSpeed.get() * _walkSpeedScalar;
}
@ -3799,6 +3896,16 @@ void MyAvatar::setIsInWalkingState(bool isWalking) {
_isInWalkingState = isWalking;
}
void MyAvatar::setIsInSittingState(bool isSitting) {
_isInSittingState.set(isSitting);
emit sittingEnabledChanged(isSitting);
}
void MyAvatar::setIsSitStandStateLocked(bool isLocked) {
_lockSitStandState.set(isLocked);
emit sitStandStateLockEnabledChanged(isLocked);
}
void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value);
}
@ -3815,6 +3922,14 @@ float MyAvatar::getSprintSpeed() const {
return _sprintSpeed.get();
}
void MyAvatar::setSitStandStateChange(bool stateChanged) {
_sitStandStateChange = stateChanged;
}
float MyAvatar::getSitStandStateChange() const {
return _sitStandStateChange;
}
QVector<QString> MyAvatar::getScriptUrls() {
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
return scripts;
@ -3958,6 +4073,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
// x axis of currentBodyMatrix in world space.
glm::vec3 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0]));
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
float forwardLeanAmount = glm::dot(forward, offset);
float lateralLeanAmount = glm::dot(right, offset);
@ -3966,14 +4082,19 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
const float MAX_FORWARD_LEAN = 0.15f;
const float MAX_BACKWARD_LEAN = 0.1f;
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
return true;
bool stepDetected = false;
if (myAvatar.getIsInSittingState()) {
if (!withinBaseOfSupport(currentHeadPose)) {
stepDetected = true;
}
} else if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
stepDetected = true;
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
return true;
stepDetected = true;
} else {
stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
}
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
return stepDetected;
}
bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const {
@ -3982,6 +4103,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
controller::Pose currentHeadSensorPose = myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD);
bool stepDetected = false;
float myScale = myAvatar.getAvatarScale();
@ -3991,7 +4113,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
} else {
if (!withinBaseOfSupport(currentHeadPose) &&
headAngularVelocityBelowThreshold(currentHeadPose) &&
isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight(), myScale) &&
isWithinThresholdHeightMode(currentHeadSensorPose, myAvatar.getCurrentStandingHeight(), myScale) &&
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
headVelocityGreaterThanThreshold(currentHeadPose) &&
@ -4007,6 +4129,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
if (!isActive(Horizontal) &&
(!isActive(Vertical)) &&
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
myAvatar.setResetMode(true);
stepDetected = true;
@ -4019,13 +4142,39 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
return stepDetected;
}
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
bool MyAvatar::FollowHelper::shouldActivateVertical(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float CYLINDER_TOP = 0.1f;
const float CYLINDER_BOTTOM = -1.5f;
const float SITTING_BOTTOM = -0.02f;
const int SQUATTY_COUNT_THRESHOLD = 600;
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
bool returnValue = false;
returnValue = (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
if (myAvatar.getSitStandStateChange()) {
qCDebug(interfaceapp) << "sit state change";
returnValue = true;
}
if (myAvatar.getIsInSittingState()) {
if (offset.y < SITTING_BOTTOM) {
// we recenter when sitting.
qCDebug(interfaceapp) << "lean back sitting ";
returnValue = true;
}
} else {
// in the standing state
if (myAvatar._squatCount > SQUATTY_COUNT_THRESHOLD) {
myAvatar._squatCount = 0;
qCDebug(interfaceapp) << "squatting ";
// returnValue = true;
}
if (returnValue == true) {
qCDebug(interfaceapp) << "above or below capsule in standing";
}
}
return returnValue;
}
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
@ -4033,6 +4182,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
if (myAvatar.getHMDLeanRecenterEnabled() &&
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Rotation);
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
@ -4046,6 +4196,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
}
}
} else {
// center of gravity model is not enabled
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Horizontal);
if (myAvatar.getEnableStepResetRotation()) {
@ -4054,9 +4205,18 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
}
}
}
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Vertical);
if (_velocityCount > 60) {
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Vertical);
}
} else {
if ((glm::length(myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD).getVelocity()) > 0.1f)) {
_velocityCount++;
}
}
} else {
if (!isActive(Rotation) && getForceActivateRotation()) {
activate(Rotation);
@ -4105,7 +4265,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining());
}
glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) {
glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) {
if (isActive()) {
float dt = myAvatar.getCharacterController()->getFollowTime();
decrementTimeRemaining(dt);
@ -4122,6 +4282,11 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
if (myAvatar.getSitStandStateChange()) {
myAvatar.setSitStandStateChange(false);
deactivate(Vertical);
setTranslation(newBodyMat, extractTranslation(myAvatar.deriveBodyFromHMDSensor()));
}
return newBodyMat;
} else {
return currentBodyMatrix;

View file

@ -142,6 +142,7 @@ class MyAvatar : public Avatar {
* @property {number} walkSpeed
* @property {number} walkBackwardSpeed
* @property {number} sprintSpeed
* @property {number} isInSittingState
*
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
* registration point of the 3D model.
@ -242,6 +243,8 @@ class MyAvatar : public Avatar {
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked);
const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right";
@ -1101,12 +1104,18 @@ public:
void setIsInWalkingState(bool isWalking);
bool getIsInWalkingState() const;
void setIsInSittingState(bool isSitting);
bool getIsInSittingState() const;
void setIsSitStandStateLocked(bool isLocked);
bool getIsSitStandStateLocked() const;
void setWalkSpeed(float value);
float getWalkSpeed() const;
void setWalkBackwardSpeed(float value);
float getWalkBackwardSpeed() const;
void setSprintSpeed(float value);
float getSprintSpeed() const;
void setSitStandStateChange(bool stateChanged);
float getSitStandStateChange() const;
QVector<QString> getScriptUrls();
@ -1512,6 +1521,22 @@ signals:
*/
void disableHandTouchForIDChanged(const QUuid& entityID, bool disable);
/**jsdoc
* Triggered when the sit state is enabled or disabled
* @function MyAvatar.sittingEnabledChanged
* @param {boolean} enabled
* @returns {Signal}
*/
void sittingEnabledChanged(bool enabled);
/**jsdoc
* Triggered when the sit state is enabled or disabled
* @function MyAvatar.sitStandStateLockEnabledChanged
* @param {boolean} enabled
* @returns {Signal}
*/
void sitStandStateLockEnabledChanged(bool enabled);
private slots:
void leaveDomain();
void updateCollisionCapsuleCache();
@ -1718,11 +1743,11 @@ private:
float getMaxTimeRemaining() const;
void decrementTimeRemaining(float dt);
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateVertical(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
glm::mat4 postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
bool getForceActivateRotation() const;
void setForceActivateRotation(bool val);
bool getForceActivateVertical() const;
@ -1735,6 +1760,7 @@ private:
std::atomic<bool> _forceActivateVertical { false };
std::atomic<bool> _forceActivateHorizontal { false };
std::atomic<bool> _toggleHipsFollowing { true };
int _velocityCount { 0 };
};
FollowHelper _follow;
@ -1800,17 +1826,27 @@ private:
std::mutex _pinnedJointsMutex;
std::vector<int> _pinnedJoints;
// height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
const float DEFAULT_FLOOR_HEIGHT = 0.0f;
// height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight{ DEFAULT_AVATAR_HEIGHT };
float _sumUserHeightSensorSpace{ DEFAULT_AVATAR_HEIGHT };
int _averageUserHeightCount{ 1 };
bool _sitStandStateChange{ false };
ThreadSafeValueCache<bool> _lockSitStandState { true };
// max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
ThreadSafeValueCache<float> _walkBackwardSpeed { DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED };
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
bool _isInWalkingState { false };
ThreadSafeValueCache<bool> _isInSittingState { false };
int _sitStandStateCount { 0 };
int _squatCount { 0 };
float _tippingPoint { DEFAULT_FLOOR_HEIGHT };
// load avatar scripts once when rig is ready
bool _shouldLoadScripts { false };

View file

@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
}
glm::mat4 hipsMat;
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState())) {
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState())) {
// then we use center of gravity model
hipsMat = myAvatar->deriveBodyUsingCgModel();
} else {
@ -250,6 +250,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose);
bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose);
if (spine2Exists && headExists && hipsExists) {
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
glm::vec3 u, v, w;
glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f);

View file

@ -1717,6 +1717,7 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
glm::vec3 Avatar::getWorldFeetPosition() {
ShapeInfo shapeInfo;
computeShapeInfo(shapeInfo);
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y - halfExtents.x, 0.0f);

View file

@ -85,6 +85,11 @@ private:
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError();
return;
} else {
ovrResult setFloorLevelOrigin = ovr_SetTrackingOriginType(session, ovrTrackingOrigin::ovrTrackingOrigin_FloorLevel);
if (!OVR_SUCCESS(setFloorLevelOrigin)) {
qCWarning(oculusLog) << "Failed to set the Oculus tracking origin to floor level" << ovr::getError();
}
}
}

View file

@ -463,7 +463,7 @@ bool OpenVrDisplayPlugin::internalActivate() {
auto chaperone = vr::VRChaperone();
if (chaperone) {
float const UI_RADIUS = 1.0f;
float const UI_HEIGHT = 1.6f;
float const UI_HEIGHT = 0.0f;
float const UI_Z_OFFSET = 0.5;
float xSize, zSize;

View file

@ -64,6 +64,8 @@ function getMyAvatarSettings() {
return {
dominantHand: MyAvatar.getDominantHand(),
collisionsEnabled : MyAvatar.getCollisionsEnabled(),
sittingEnabled: MyAvatar.isInSittingState,
lockStateEnabled: MyAvatar.isSitStandStateLocked,
collisionSoundUrl : MyAvatar.collisionSoundURL,
animGraphUrl: MyAvatar.getAnimGraphUrl(),
animGraphOverrideUrl : MyAvatar.getAnimGraphOverrideUrl(),
@ -136,6 +138,22 @@ function onCollisionsEnabledChanged(enabled) {
}
}
function onSittingEnabledChanged(isSitting) {
if (currentAvatarSettings.sittingEnabled !== isSitting) {
currentAvatarSettings.sittingEnabled = isSitting;
print("emit sitting changed");
sendToQml({ 'method': 'settingChanged', 'name': 'sittingEnabled', 'value': isSitting })
}
}
function onSitStandStateLockedEnabledChanged(isLocked) {
if (currentAvatarSettings.lockStateEnabled !== isLocked) {
currentAvatarSettings.lockStateEnabled = isLocked;
print("emit lock sit stand state changed");
sendToQml({ 'method': 'settingChanged', 'name': 'lockStateEnabled', 'value': isLocked })
}
}
function onNewCollisionSoundUrl(url) {
if(currentAvatarSettings.collisionSoundUrl !== url) {
currentAvatarSettings.collisionSoundUrl = url;
@ -314,6 +332,8 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
MyAvatar.setDominantHand(message.settings.dominantHand);
MyAvatar.setCollisionsEnabled(message.settings.collisionsEnabled);
MyAvatar.isInSittingState = message.settings.sittingEnabled;
MyAvatar.isSitStandStateLocked = message.settings.lockStateEnabled;
MyAvatar.collisionSoundURL = message.settings.collisionSoundUrl;
MyAvatar.setAnimGraphOverrideUrl(message.settings.animGraphOverrideUrl);
@ -507,6 +527,8 @@ function off() {
MyAvatar.skeletonModelURLChanged.disconnect(onSkeletonModelURLChanged);
MyAvatar.dominantHandChanged.disconnect(onDominantHandChanged);
MyAvatar.collisionsEnabledChanged.disconnect(onCollisionsEnabledChanged);
MyAvatar.sittingEnabledChanged.disconnect(onSittingEnabledChanged);
MyAvatar.sitStandStateLockEnabledChanged.disconnect(onSitStandStateLockedEnabledChanged);
MyAvatar.newCollisionSoundURL.disconnect(onNewCollisionSoundUrl);
MyAvatar.animGraphUrlChanged.disconnect(onAnimGraphUrlChanged);
MyAvatar.targetScaleChanged.disconnect(onTargetScaleChanged);
@ -521,6 +543,8 @@ function on() {
MyAvatar.skeletonModelURLChanged.connect(onSkeletonModelURLChanged);
MyAvatar.dominantHandChanged.connect(onDominantHandChanged);
MyAvatar.collisionsEnabledChanged.connect(onCollisionsEnabledChanged);
MyAvatar.sittingEnabledChanged.connect(onSittingEnabledChanged);
MyAvatar.sitStandStateLockEnabledChanged.connect(onSitStandStateLockedEnabledChanged);
MyAvatar.newCollisionSoundURL.connect(onNewCollisionSoundUrl);
MyAvatar.animGraphUrlChanged.connect(onAnimGraphUrlChanged);
MyAvatar.targetScaleChanged.connect(onTargetScaleChanged);