mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge pull request #14126 from amantley/squattyPottyFix
Sit State for the seated user in HMD: Squatty Potty Fix
This commit is contained in:
commit
e21e7d30d7
8 changed files with 297 additions and 28 deletions
|
@ -31,7 +31,7 @@ Rectangle {
|
||||||
|
|
||||||
scaleSlider.notify = false;
|
scaleSlider.notify = false;
|
||||||
scaleSlider.value = Math.round(avatarScale * 10);
|
scaleSlider.value = Math.round(avatarScale * 10);
|
||||||
scaleSlider.notify = true;;
|
scaleSlider.notify = true;
|
||||||
|
|
||||||
if (settings.dominantHand === 'left') {
|
if (settings.dominantHand === 'left') {
|
||||||
leftHandRadioButton.checked = true;
|
leftHandRadioButton.checked = true;
|
||||||
|
|
|
@ -464,10 +464,74 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::updateSitStandState(float newHeightReading, float dt) {
|
||||||
|
const float STANDING_HEIGHT_MULTIPLE = 1.2f;
|
||||||
|
const float SITTING_HEIGHT_MULTIPLE = 0.833f;
|
||||||
|
const float SITTING_TIMEOUT = 4.0f; // 4 seconds
|
||||||
|
const float STANDING_TIMEOUT = 0.3333f; // 1/3 second
|
||||||
|
const float SITTING_UPPER_BOUND = 1.52f;
|
||||||
|
if (!getIsSitStandStateLocked()) {
|
||||||
|
if (!getIsAway() && qApp->isHMDMode()) {
|
||||||
|
if (getIsInSittingState()) {
|
||||||
|
if (newHeightReading > (STANDING_HEIGHT_MULTIPLE * _tippingPoint)) {
|
||||||
|
// if we recenter upwards then no longer in sitting state
|
||||||
|
_sitStandStateTimer += dt;
|
||||||
|
if (_sitStandStateTimer > STANDING_TIMEOUT) {
|
||||||
|
_averageUserHeightSensorSpace = newHeightReading;
|
||||||
|
_tippingPoint = newHeightReading;
|
||||||
|
setIsInSittingState(false);
|
||||||
|
}
|
||||||
|
} else if (newHeightReading < (SITTING_HEIGHT_MULTIPLE * _tippingPoint)) {
|
||||||
|
// if we are mis labelled as sitting but we are standing in the real world this will
|
||||||
|
// make sure that a real sit is still recognized so we won't be stuck in sitting unable to change state
|
||||||
|
_sitStandStateTimer += dt;
|
||||||
|
if (_sitStandStateTimer > SITTING_TIMEOUT) {
|
||||||
|
_averageUserHeightSensorSpace = newHeightReading;
|
||||||
|
_tippingPoint = newHeightReading;
|
||||||
|
// here we stay in sit state but reset the average height
|
||||||
|
setIsInSittingState(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// sanity check if average height greater than 5ft they are not sitting(or get off your dangerous barstool please)
|
||||||
|
if (_averageUserHeightSensorSpace > SITTING_UPPER_BOUND) {
|
||||||
|
setIsInSittingState(false);
|
||||||
|
} else {
|
||||||
|
// tipping point is average height when sitting.
|
||||||
|
_tippingPoint = _averageUserHeightSensorSpace;
|
||||||
|
_sitStandStateTimer = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// in the standing state
|
||||||
|
if (newHeightReading < (SITTING_HEIGHT_MULTIPLE * _tippingPoint)) {
|
||||||
|
_sitStandStateTimer += dt;
|
||||||
|
if (_sitStandStateTimer > SITTING_TIMEOUT) {
|
||||||
|
_averageUserHeightSensorSpace = newHeightReading;
|
||||||
|
_tippingPoint = newHeightReading;
|
||||||
|
setIsInSittingState(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// use the mode height for the tipping point when we are standing.
|
||||||
|
_tippingPoint = getCurrentStandingHeight();
|
||||||
|
_sitStandStateTimer = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//if you are away then reset the average and set state to standing.
|
||||||
|
_averageUserHeightSensorSpace = _userHeight.get();
|
||||||
|
_tippingPoint = _userHeight.get();
|
||||||
|
setIsInSittingState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::update(float deltaTime) {
|
void MyAvatar::update(float deltaTime) {
|
||||||
// update moving average of HMD facing in xz plane.
|
// update moving average of HMD facing in xz plane.
|
||||||
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
|
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
|
||||||
const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders
|
const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders
|
||||||
|
const float COSINE_THIRTY_DEGREES = 0.866f;
|
||||||
|
const float SQUATTY_TIMEOUT = 30.0f; // 30 seconds
|
||||||
|
const float HEIGHT_FILTER_COEFFICIENT = 0.01f;
|
||||||
|
|
||||||
float tau = deltaTime / HMD_FACING_TIMESCALE;
|
float tau = deltaTime / HMD_FACING_TIMESCALE;
|
||||||
setHipToHandController(computeHandAzimuth());
|
setHipToHandController(computeHandAzimuth());
|
||||||
|
@ -494,11 +558,36 @@ void MyAvatar::update(float deltaTime) {
|
||||||
_smoothOrientationTimer += deltaTime;
|
_smoothOrientationTimer += deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
float newHeightReading = getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y;
|
controller::Pose newHeightReading = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||||
int newHeightReadingInCentimeters = glm::floor(newHeightReading * CENTIMETERS_PER_METER);
|
if (newHeightReading.isValid()) {
|
||||||
_recentModeReadings.insert(newHeightReadingInCentimeters);
|
int newHeightReadingInCentimeters = glm::floor(newHeightReading.getTranslation().y * CENTIMETERS_PER_METER);
|
||||||
setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
|
_averageUserHeightSensorSpace = lerp(_averageUserHeightSensorSpace, newHeightReading.getTranslation().y, HEIGHT_FILTER_COEFFICIENT);
|
||||||
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
|
_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_THIRTY_DEGREES)) {
|
||||||
|
_squatTimer += deltaTime;
|
||||||
|
if (_squatTimer > SQUATTY_TIMEOUT) {
|
||||||
|
_squatTimer = 0.0f;
|
||||||
|
_follow._squatDetected = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_squatTimer = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put update sit stand state counts here
|
||||||
|
updateSitStandState(newHeightReading.getTranslation().y, deltaTime);
|
||||||
|
|
||||||
if (_drawAverageFacingEnabled) {
|
if (_drawAverageFacingEnabled) {
|
||||||
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||||
|
@ -3558,12 +3647,9 @@ glm::vec3 MyAvatar::computeCounterBalance() {
|
||||||
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
|
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
|
||||||
|
|
||||||
// find the height of the hips
|
// find the height of the hips
|
||||||
const float UPPER_LEG_FRACTION = 0.3333f;
|
|
||||||
glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z));
|
glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z));
|
||||||
float headMinusHipXz = glm::length(xzDiff);
|
float headMinusHipXz = glm::length(xzDiff);
|
||||||
float headHipDefault = glm::length(tposeHead - tposeHips);
|
float headHipDefault = glm::length(tposeHead - tposeHips);
|
||||||
float hipFootDefault = tposeHips.y - tposeRightFoot.y;
|
|
||||||
float sitSquatThreshold = tposeHips.y - (UPPER_LEG_FRACTION * hipFootDefault);
|
|
||||||
float hipHeight = 0.0f;
|
float hipHeight = 0.0f;
|
||||||
if (headHipDefault > headMinusHipXz) {
|
if (headHipDefault > headMinusHipXz) {
|
||||||
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
|
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
|
||||||
|
@ -3575,10 +3661,6 @@ glm::vec3 MyAvatar::computeCounterBalance() {
|
||||||
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
|
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
|
||||||
// if the height is higher than default hips, clamp to default hips
|
// if the height is higher than default hips, clamp to default hips
|
||||||
counterBalancedCg.y = tposeHips.y + 0.05f;
|
counterBalancedCg.y = tposeHips.y + 0.05f;
|
||||||
} else if (counterBalancedCg.y < sitSquatThreshold) {
|
|
||||||
//do a height reset
|
|
||||||
setResetMode(true);
|
|
||||||
_follow.activate(FollowHelper::Vertical);
|
|
||||||
}
|
}
|
||||||
return counterBalancedCg;
|
return counterBalancedCg;
|
||||||
}
|
}
|
||||||
|
@ -3819,6 +3901,18 @@ bool MyAvatar::getIsInWalkingState() const {
|
||||||
return _isInWalkingState;
|
return _isInWalkingState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::getIsInSittingState() const {
|
||||||
|
return _isInSittingState.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
MyAvatar::SitStandModelType MyAvatar::getUserRecenterModel() const {
|
||||||
|
return _userRecenterModel.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::getIsSitStandStateLocked() const {
|
||||||
|
return _lockSitStandState.get();
|
||||||
|
}
|
||||||
|
|
||||||
float MyAvatar::getWalkSpeed() const {
|
float MyAvatar::getWalkSpeed() const {
|
||||||
return _walkSpeed.get() * _walkSpeedScalar;
|
return _walkSpeed.get() * _walkSpeedScalar;
|
||||||
}
|
}
|
||||||
|
@ -3839,6 +3933,61 @@ void MyAvatar::setIsInWalkingState(bool isWalking) {
|
||||||
_isInWalkingState = isWalking;
|
_isInWalkingState = isWalking;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setIsInSittingState(bool isSitting) {
|
||||||
|
_sitStandStateTimer = 0.0f;
|
||||||
|
_squatTimer = 0.0f;
|
||||||
|
// on reset height we need the count to be more than one in case the user sits and stands up quickly.
|
||||||
|
_isInSittingState.set(isSitting);
|
||||||
|
setResetMode(true);
|
||||||
|
if (isSitting) {
|
||||||
|
setCenterOfGravityModelEnabled(false);
|
||||||
|
} else {
|
||||||
|
setCenterOfGravityModelEnabled(true);
|
||||||
|
}
|
||||||
|
setSitStandStateChange(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setUserRecenterModel(MyAvatar::SitStandModelType modelName) {
|
||||||
|
|
||||||
|
_userRecenterModel.set(modelName);
|
||||||
|
|
||||||
|
switch (modelName) {
|
||||||
|
case MyAvatar::SitStandModelType::ForceSit:
|
||||||
|
setHMDLeanRecenterEnabled(true);
|
||||||
|
setIsInSittingState(true);
|
||||||
|
setIsSitStandStateLocked(true);
|
||||||
|
break;
|
||||||
|
case MyAvatar::SitStandModelType::ForceStand:
|
||||||
|
setHMDLeanRecenterEnabled(true);
|
||||||
|
setIsInSittingState(false);
|
||||||
|
setIsSitStandStateLocked(true);
|
||||||
|
break;
|
||||||
|
case MyAvatar::SitStandModelType::Auto:
|
||||||
|
default:
|
||||||
|
setHMDLeanRecenterEnabled(true);
|
||||||
|
setIsInSittingState(false);
|
||||||
|
setIsSitStandStateLocked(false);
|
||||||
|
break;
|
||||||
|
case MyAvatar::SitStandModelType::DisableHMDLean:
|
||||||
|
setHMDLeanRecenterEnabled(false);
|
||||||
|
setIsInSittingState(false);
|
||||||
|
setIsSitStandStateLocked(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setIsSitStandStateLocked(bool isLocked) {
|
||||||
|
_lockSitStandState.set(isLocked);
|
||||||
|
_sitStandStateTimer = 0.0f;
|
||||||
|
_squatTimer = 0.0f;
|
||||||
|
_averageUserHeightSensorSpace = _userHeight.get();
|
||||||
|
_tippingPoint = _userHeight.get();
|
||||||
|
if (!isLocked) {
|
||||||
|
// always start the auto transition mode in standing state.
|
||||||
|
setIsInSittingState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::setWalkSpeed(float value) {
|
void MyAvatar::setWalkSpeed(float value) {
|
||||||
_walkSpeed.set(value);
|
_walkSpeed.set(value);
|
||||||
}
|
}
|
||||||
|
@ -3855,6 +4004,14 @@ float MyAvatar::getSprintSpeed() const {
|
||||||
return _sprintSpeed.get();
|
return _sprintSpeed.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setSitStandStateChange(bool stateChanged) {
|
||||||
|
_sitStandStateChange = stateChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MyAvatar::getSitStandStateChange() const {
|
||||||
|
return _sitStandStateChange;
|
||||||
|
}
|
||||||
|
|
||||||
QVector<QString> MyAvatar::getScriptUrls() {
|
QVector<QString> MyAvatar::getScriptUrls() {
|
||||||
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
|
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
|
||||||
return scripts;
|
return scripts;
|
||||||
|
@ -3998,6 +4155,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
||||||
// x axis of currentBodyMatrix in world space.
|
// 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 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0]));
|
||||||
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||||
|
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||||
|
|
||||||
float forwardLeanAmount = glm::dot(forward, offset);
|
float forwardLeanAmount = glm::dot(forward, offset);
|
||||||
float lateralLeanAmount = glm::dot(right, offset);
|
float lateralLeanAmount = glm::dot(right, offset);
|
||||||
|
@ -4006,14 +4164,19 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
||||||
const float MAX_FORWARD_LEAN = 0.15f;
|
const float MAX_FORWARD_LEAN = 0.15f;
|
||||||
const float MAX_BACKWARD_LEAN = 0.1f;
|
const float MAX_BACKWARD_LEAN = 0.1f;
|
||||||
|
|
||||||
|
bool stepDetected = false;
|
||||||
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
if (myAvatar.getIsInSittingState()) {
|
||||||
return true;
|
if (!withinBaseOfSupport(currentHeadPose)) {
|
||||||
|
stepDetected = true;
|
||||||
|
}
|
||||||
|
} else if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||||
|
stepDetected = true;
|
||||||
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
||||||
return true;
|
stepDetected = true;
|
||||||
|
} else {
|
||||||
|
stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
||||||
}
|
}
|
||||||
|
return stepDetected;
|
||||||
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const {
|
bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const {
|
||||||
|
@ -4022,6 +4185,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
||||||
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||||
controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
||||||
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
||||||
|
controller::Pose currentHeadSensorPose = myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||||
|
|
||||||
bool stepDetected = false;
|
bool stepDetected = false;
|
||||||
float myScale = myAvatar.getAvatarScale();
|
float myScale = myAvatar.getAvatarScale();
|
||||||
|
@ -4031,7 +4195,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
||||||
} else {
|
} else {
|
||||||
if (!withinBaseOfSupport(currentHeadPose) &&
|
if (!withinBaseOfSupport(currentHeadPose) &&
|
||||||
headAngularVelocityBelowThreshold(currentHeadPose) &&
|
headAngularVelocityBelowThreshold(currentHeadPose) &&
|
||||||
isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight(), myScale) &&
|
isWithinThresholdHeightMode(currentHeadSensorPose, myAvatar.getCurrentStandingHeight(), myScale) &&
|
||||||
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
|
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
|
||||||
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
|
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
|
||||||
headVelocityGreaterThanThreshold(currentHeadPose) &&
|
headVelocityGreaterThanThreshold(currentHeadPose) &&
|
||||||
|
@ -4047,6 +4211,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
||||||
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
|
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
|
||||||
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
|
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
|
||||||
if (!isActive(Horizontal) &&
|
if (!isActive(Horizontal) &&
|
||||||
|
(!isActive(Vertical)) &&
|
||||||
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
|
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
|
||||||
myAvatar.setResetMode(true);
|
myAvatar.setResetMode(true);
|
||||||
stepDetected = true;
|
stepDetected = true;
|
||||||
|
@ -4062,10 +4227,32 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
||||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||||
const float CYLINDER_TOP = 0.1f;
|
const float CYLINDER_TOP = 0.1f;
|
||||||
const float CYLINDER_BOTTOM = -1.5f;
|
const float CYLINDER_BOTTOM = -1.5f;
|
||||||
|
const float SITTING_BOTTOM = -0.02f;
|
||||||
|
|
||||||
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||||
|
bool returnValue = false;
|
||||||
|
|
||||||
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
|
if (myAvatar.getSitStandStateChange()) {
|
||||||
|
returnValue = true;
|
||||||
|
} else {
|
||||||
|
if (myAvatar.getIsInSittingState()) {
|
||||||
|
if (myAvatar.getIsSitStandStateLocked()) {
|
||||||
|
returnValue = (offset.y > CYLINDER_TOP);
|
||||||
|
}
|
||||||
|
if (offset.y < SITTING_BOTTOM) {
|
||||||
|
// we recenter more easily when in sitting state.
|
||||||
|
returnValue = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// in the standing state
|
||||||
|
returnValue = (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
|
||||||
|
// finally check for squats in standing
|
||||||
|
if (_squatDetected) {
|
||||||
|
returnValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
|
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
|
||||||
|
@ -4086,9 +4273,10 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// center of gravity model is not enabled
|
||||||
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||||
activate(Horizontal);
|
activate(Horizontal);
|
||||||
if (myAvatar.getEnableStepResetRotation()) {
|
if (myAvatar.getEnableStepResetRotation() && !myAvatar.getIsInSittingState()) {
|
||||||
activate(Rotation);
|
activate(Rotation);
|
||||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||||
}
|
}
|
||||||
|
@ -4096,6 +4284,9 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
}
|
}
|
||||||
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||||
activate(Vertical);
|
activate(Vertical);
|
||||||
|
if (_squatDetected) {
|
||||||
|
_squatDetected = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isActive(Rotation) && getForceActivateRotation()) {
|
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||||
|
@ -4145,7 +4336,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining());
|
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()) {
|
if (isActive()) {
|
||||||
float dt = myAvatar.getCharacterController()->getFollowTime();
|
float dt = myAvatar.getCharacterController()->getFollowTime();
|
||||||
decrementTimeRemaining(dt);
|
decrementTimeRemaining(dt);
|
||||||
|
@ -4162,6 +4353,11 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
|
||||||
|
|
||||||
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
|
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
|
||||||
sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
|
sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
|
||||||
|
if (myAvatar.getSitStandStateChange()) {
|
||||||
|
myAvatar.setSitStandStateChange(false);
|
||||||
|
deactivate(Vertical);
|
||||||
|
setTranslation(newBodyMat, extractTranslation(myAvatar.deriveBodyFromHMDSensor()));
|
||||||
|
}
|
||||||
return newBodyMat;
|
return newBodyMat;
|
||||||
} else {
|
} else {
|
||||||
return currentBodyMatrix;
|
return currentBodyMatrix;
|
||||||
|
|
|
@ -141,6 +141,8 @@ class MyAvatar : public Avatar {
|
||||||
* @property {number} walkSpeed
|
* @property {number} walkSpeed
|
||||||
* @property {number} walkBackwardSpeed
|
* @property {number} walkBackwardSpeed
|
||||||
* @property {number} sprintSpeed
|
* @property {number} sprintSpeed
|
||||||
|
* @property {number} isInSittingState
|
||||||
|
* @property {number} userRecenterModel
|
||||||
*
|
*
|
||||||
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
|
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
|
||||||
* registration point of the 3D model.
|
* registration point of the 3D model.
|
||||||
|
@ -241,6 +243,9 @@ class MyAvatar : public Avatar {
|
||||||
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
|
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
|
||||||
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
|
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
|
||||||
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
|
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
|
||||||
|
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);
|
||||||
|
Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel);
|
||||||
|
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked);
|
||||||
|
|
||||||
const QString DOMINANT_LEFT_HAND = "left";
|
const QString DOMINANT_LEFT_HAND = "left";
|
||||||
const QString DOMINANT_RIGHT_HAND = "right";
|
const QString DOMINANT_RIGHT_HAND = "right";
|
||||||
|
@ -261,6 +266,15 @@ public:
|
||||||
};
|
};
|
||||||
Q_ENUM(DriveKeys)
|
Q_ENUM(DriveKeys)
|
||||||
|
|
||||||
|
enum SitStandModelType {
|
||||||
|
ForceSit = 0,
|
||||||
|
ForceStand,
|
||||||
|
Auto,
|
||||||
|
DisableHMDLean,
|
||||||
|
NumSitStandTypes
|
||||||
|
};
|
||||||
|
Q_ENUM(SitStandModelType)
|
||||||
|
|
||||||
explicit MyAvatar(QThread* thread);
|
explicit MyAvatar(QThread* thread);
|
||||||
virtual ~MyAvatar();
|
virtual ~MyAvatar();
|
||||||
|
|
||||||
|
@ -1120,12 +1134,21 @@ public:
|
||||||
|
|
||||||
void setIsInWalkingState(bool isWalking);
|
void setIsInWalkingState(bool isWalking);
|
||||||
bool getIsInWalkingState() const;
|
bool getIsInWalkingState() const;
|
||||||
|
void setIsInSittingState(bool isSitting);
|
||||||
|
bool getIsInSittingState() const;
|
||||||
|
void setUserRecenterModel(MyAvatar::SitStandModelType modelName);
|
||||||
|
MyAvatar::SitStandModelType getUserRecenterModel() const;
|
||||||
|
void setIsSitStandStateLocked(bool isLocked);
|
||||||
|
bool getIsSitStandStateLocked() const;
|
||||||
void setWalkSpeed(float value);
|
void setWalkSpeed(float value);
|
||||||
float getWalkSpeed() const;
|
float getWalkSpeed() const;
|
||||||
void setWalkBackwardSpeed(float value);
|
void setWalkBackwardSpeed(float value);
|
||||||
float getWalkBackwardSpeed() const;
|
float getWalkBackwardSpeed() const;
|
||||||
void setSprintSpeed(float value);
|
void setSprintSpeed(float value);
|
||||||
float getSprintSpeed() const;
|
float getSprintSpeed() const;
|
||||||
|
void setSitStandStateChange(bool stateChanged);
|
||||||
|
float getSitStandStateChange() const;
|
||||||
|
void updateSitStandState(float newHeightReading, float dt);
|
||||||
|
|
||||||
QVector<QString> getScriptUrls();
|
QVector<QString> getScriptUrls();
|
||||||
|
|
||||||
|
@ -1531,6 +1554,7 @@ signals:
|
||||||
*/
|
*/
|
||||||
void disableHandTouchForIDChanged(const QUuid& entityID, bool disable);
|
void disableHandTouchForIDChanged(const QUuid& entityID, bool disable);
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void leaveDomain();
|
void leaveDomain();
|
||||||
void updateCollisionCapsuleCache();
|
void updateCollisionCapsuleCache();
|
||||||
|
@ -1741,7 +1765,7 @@ private:
|
||||||
bool shouldActivateHorizontal(const 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;
|
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
|
||||||
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
|
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;
|
bool getForceActivateRotation() const;
|
||||||
void setForceActivateRotation(bool val);
|
void setForceActivateRotation(bool val);
|
||||||
bool getForceActivateVertical() const;
|
bool getForceActivateVertical() const;
|
||||||
|
@ -1750,6 +1774,7 @@ private:
|
||||||
void setForceActivateHorizontal(bool val);
|
void setForceActivateHorizontal(bool val);
|
||||||
bool getToggleHipsFollowing() const;
|
bool getToggleHipsFollowing() const;
|
||||||
void setToggleHipsFollowing(bool followHead);
|
void setToggleHipsFollowing(bool followHead);
|
||||||
|
bool _squatDetected { false };
|
||||||
std::atomic<bool> _forceActivateRotation { false };
|
std::atomic<bool> _forceActivateRotation { false };
|
||||||
std::atomic<bool> _forceActivateVertical { false };
|
std::atomic<bool> _forceActivateVertical { false };
|
||||||
std::atomic<bool> _forceActivateHorizontal { false };
|
std::atomic<bool> _forceActivateHorizontal { false };
|
||||||
|
@ -1819,10 +1844,13 @@ private:
|
||||||
std::mutex _pinnedJointsMutex;
|
std::mutex _pinnedJointsMutex;
|
||||||
std::vector<int> _pinnedJoints;
|
std::vector<int> _pinnedJoints;
|
||||||
|
|
||||||
|
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
|
||||||
|
|
||||||
// height of user in sensor space, when standing erect.
|
// height of user in sensor space, when standing erect.
|
||||||
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
|
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
|
||||||
|
float _averageUserHeightSensorSpace { _userHeight.get() };
|
||||||
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
|
bool _sitStandStateChange { false };
|
||||||
|
ThreadSafeValueCache<bool> _lockSitStandState { false };
|
||||||
|
|
||||||
// max unscaled forward movement speed
|
// max unscaled forward movement speed
|
||||||
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||||
|
@ -1830,6 +1858,11 @@ private:
|
||||||
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
|
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
|
||||||
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
||||||
bool _isInWalkingState { false };
|
bool _isInWalkingState { false };
|
||||||
|
ThreadSafeValueCache<bool> _isInSittingState { false };
|
||||||
|
ThreadSafeValueCache<MyAvatar::SitStandModelType> _userRecenterModel { MyAvatar::SitStandModelType::Auto };
|
||||||
|
float _sitStandStateTimer { 0.0f };
|
||||||
|
float _squatTimer { 0.0f };
|
||||||
|
float _tippingPoint { _userHeight.get() };
|
||||||
|
|
||||||
// load avatar scripts once when rig is ready
|
// load avatar scripts once when rig is ready
|
||||||
bool _shouldLoadScripts { false };
|
bool _shouldLoadScripts { false };
|
||||||
|
|
|
@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 hipsMat;
|
glm::mat4 hipsMat;
|
||||||
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && myAvatar->getHMDLeanRecenterEnabled()) {
|
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState()) && myAvatar->getHMDLeanRecenterEnabled()) {
|
||||||
// then we use center of gravity model
|
// then we use center of gravity model
|
||||||
hipsMat = myAvatar->deriveBodyUsingCgModel();
|
hipsMat = myAvatar->deriveBodyUsingCgModel();
|
||||||
} else {
|
} else {
|
||||||
|
@ -250,6 +250,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose);
|
bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose);
|
||||||
bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose);
|
bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose);
|
||||||
if (spine2Exists && headExists && hipsExists) {
|
if (spine2Exists && headExists && hipsExists) {
|
||||||
|
|
||||||
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
|
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
|
||||||
glm::vec3 u, v, w;
|
glm::vec3 u, v, w;
|
||||||
glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f);
|
glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||||
|
|
|
@ -259,6 +259,39 @@ void setupPreferences() {
|
||||||
auto preference = new CheckPreference(VR_MOVEMENT, "Show room boundaries while teleporting", getter, setter);
|
auto preference = new CheckPreference(VR_MOVEMENT, "Show room boundaries while teleporting", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto getter = [myAvatar]()->int {
|
||||||
|
switch (myAvatar->getUserRecenterModel()) {
|
||||||
|
case MyAvatar::SitStandModelType::Auto:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
case MyAvatar::SitStandModelType::ForceSit:
|
||||||
|
return 1;
|
||||||
|
case MyAvatar::SitStandModelType::DisableHMDLean:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto setter = [myAvatar](int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::Auto);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceSit);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::DisableHMDLean);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Disable Recenter", getter, setter);
|
||||||
|
QStringList items;
|
||||||
|
items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]";
|
||||||
|
preference->setHeading("Avatar leaning behavior");
|
||||||
|
preference->setItems(items);
|
||||||
|
preferences->addPreference(preference);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
|
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
|
||||||
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
|
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
|
||||||
|
|
|
@ -1731,6 +1731,7 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
||||||
|
|
||||||
glm::vec3 Avatar::getWorldFeetPosition() {
|
glm::vec3 Avatar::getWorldFeetPosition() {
|
||||||
ShapeInfo shapeInfo;
|
ShapeInfo shapeInfo;
|
||||||
|
|
||||||
computeShapeInfo(shapeInfo);
|
computeShapeInfo(shapeInfo);
|
||||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
|
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
|
||||||
glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y - halfExtents.x, 0.0f);
|
glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y - halfExtents.x, 0.0f);
|
||||||
|
|
|
@ -85,6 +85,11 @@ private:
|
||||||
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
|
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
|
||||||
qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError();
|
qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError();
|
||||||
return;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -463,7 +463,7 @@ bool OpenVrDisplayPlugin::internalActivate() {
|
||||||
auto chaperone = vr::VRChaperone();
|
auto chaperone = vr::VRChaperone();
|
||||||
if (chaperone) {
|
if (chaperone) {
|
||||||
float const UI_RADIUS = 1.0f;
|
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 const UI_Z_OFFSET = 0.5;
|
||||||
|
|
||||||
float xSize, zSize;
|
float xSize, zSize;
|
||||||
|
|
Loading…
Reference in a new issue