Keep positioning active even when cursor is not on panel.

This commit is contained in:
amwatson 2024-03-20 23:10:57 -05:00
parent cff2dc7ffc
commit e486b927b2
2 changed files with 55 additions and 36 deletions

View file

@ -31,7 +31,7 @@ License : Licensed under GPLv3 or any later version.
namespace { namespace {
constexpr float kSuperImmersiveRadius = 0.5f; constexpr float kSuperImmersiveRadius = 0.5f;
constexpr float kDistanceBetweenPanelsInMeters = 0.75f;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Local sysprops // Local sysprops
@ -124,7 +124,7 @@ Panel CreateTopPanel(const XrVector3f& position, const float surfaceWidth,
Panel CreateLowerPanelFromTopPanel(const Panel& topPanel, const float resolutionFactor) { Panel CreateLowerPanelFromTopPanel(const Panel& topPanel, const float resolutionFactor) {
// Note: the fact that two constants are 0.75 is purely coincidental. // Note: the fact that two constants are 0.75 is purely coincidental.
constexpr float kDefaultLowerPanelScaleFactor = 0.75f * 0.75f; constexpr float kDefaultLowerPanelScaleFactor = 0.75f * 0.75f;
constexpr float kLowerPanelYOffsetInMeters = -0.75f; constexpr float kLowerPanelYOffsetInMeters = -kDistanceBetweenPanelsInMeters;
constexpr float kLowerPanelZOffsetInMeters = -1.5f; constexpr float kLowerPanelZOffsetInMeters = -1.5f;
// Pitch the lower panel away from the viewer 45 degrees // Pitch the lower panel away from the viewer 45 degrees
constexpr float kLowerPanelPitchInRadians = -MATH_FLOAT_PI / 4.0f; constexpr float kLowerPanelPitchInRadians = -MATH_FLOAT_PI / 4.0f;
@ -430,17 +430,21 @@ void GameSurfaceLayer::SetTopPanelFromController(const XrVector3f& controllerPos
CalculatePanelPosition(viewerPosition, controllerPosition, sphereRadius); CalculatePanelPosition(viewerPosition, controllerPosition, sphereRadius);
const XrQuaternionf windowRotation = const XrQuaternionf windowRotation =
CalculatePanelRotation(windowPosition, viewerPosition, windowUpDirection); CalculatePanelRotation(windowPosition, viewerPosition, windowUpDirection);
if (windowPosition.y < 0) { return; } if (windowPosition.y <
(mLowerPanel.mPanelFromWorld.position.y + kDistanceBetweenPanelsInMeters)) {
return;
}
if (XrMath::Quatf::GetYawInRadians(windowRotation) > MATH_FLOAT_PI / 3.0f) { return; } if (XrMath::Quatf::GetYawInRadians(windowRotation) > MATH_FLOAT_PI / 3.0f) { return; }
mTopPanel.mPanelFromWorld = XrPosef{windowRotation, windowPosition}; mTopPanel.mPanelFromWorld = XrPosef{windowRotation, windowPosition};
} }
static constexpr float kThumbstickSpeed = 0.05f;
void GameSurfaceLayer::SetTopPanelFromThumbstick(const float thumbstickY) { void GameSurfaceLayer::SetTopPanelFromThumbstick(const float thumbstickY) {
static constexpr float kDepthSpeed = 0.05f;
static constexpr float kMaxDepth = -10.0f; static constexpr float kMaxDepth = -10.0f;
mTopPanel.mPanelFromWorld.position.z -= (thumbstickY * kDepthSpeed); mTopPanel.mPanelFromWorld.position.z -= (thumbstickY * kThumbstickSpeed);
mTopPanel.mPanelFromWorld.position.z = mTopPanel.mPanelFromWorld.position.z =
std::min(mTopPanel.mPanelFromWorld.position.z, mLowerPanel.mPanelFromWorld.position.z); std::min(mTopPanel.mPanelFromWorld.position.z, mLowerPanel.mPanelFromWorld.position.z);
mTopPanel.mPanelFromWorld.position.z = mTopPanel.mPanelFromWorld.position.z =
@ -448,14 +452,14 @@ void GameSurfaceLayer::SetTopPanelFromThumbstick(const float thumbstickY) {
} }
XrPosef GameSurfaceLayer::SetLowerPanelFromThumbstick(const float thumbstickY) { XrPosef GameSurfaceLayer::SetLowerPanelFromThumbstick(const float thumbstickY) {
static constexpr float kDepthSpeed = 0.05f; static constexpr float kMinY = -3.0f;
static constexpr float kMaxDepth = -10.0f;
mLowerPanel.mPanelFromWorld.position.y += (thumbstickY * kDepthSpeed); mLowerPanel.mPanelFromWorld.position.y += (thumbstickY * kThumbstickSpeed);
mLowerPanel.mPanelFromWorld.position.y = mLowerPanel.mPanelFromWorld.position.y =
std::min(mLowerPanel.mPanelFromWorld.position.y, mTopPanel.mPanelFromWorld.position.y); std::min(mLowerPanel.mPanelFromWorld.position.y,
mTopPanel.mPanelFromWorld.position.y - kDistanceBetweenPanelsInMeters);
mLowerPanel.mPanelFromWorld.position.y = mLowerPanel.mPanelFromWorld.position.y =
std::max(mLowerPanel.mPanelFromWorld.position.y, kMaxDepth); std::max(mLowerPanel.mPanelFromWorld.position.y, kMinY);
return mLowerPanel.mPanelFromWorld; return mLowerPanel.mPanelFromWorld;
} }

View file

@ -650,7 +650,10 @@ private:
XrPosef cursorPose3d = XrMath::Posef::Identity(); XrPosef cursorPose3d = XrMath::Posef::Identity();
XrVector2f cursorPos2d = {0, 0}; XrVector2f cursorPos2d = {0, 0};
float scaleFactor = 0.01f; float scaleFactor = 0.01f;
CursorLayer::CursorType cursorType = appState.mLowerMenuType == LowerMenuType::POSITIONAL_MENU ? CursorLayer::CursorType::CURSOR_TYPE_TOP_PANEL : CursorLayer::CursorType::CURSOR_TYPE_NORMAL; CursorLayer::CursorType cursorType =
appState.mLowerMenuType == LowerMenuType::POSITIONAL_MENU
? CursorLayer::CursorType::CURSOR_TYPE_TOP_PANEL
: CursorLayer::CursorType::CURSOR_TYPE_NORMAL;
[[maybe_unused]] const auto nonPreferredController = [[maybe_unused]] const auto nonPreferredController =
mInputStateFrame.mPreferredHand == InputStateFrame::LEFT_CONTROLLER mInputStateFrame.mPreferredHand == InputStateFrame::LEFT_CONTROLLER
@ -660,10 +663,15 @@ private:
// unless neither controller is available. // unless neither controller is available.
assert(mInputStateFrame.mIsHandActive[mInputStateFrame.mPreferredHand] || assert(mInputStateFrame.mIsHandActive[mInputStateFrame.mPreferredHand] ||
!mInputStateFrame.mIsHandActive[nonPreferredController]); !mInputStateFrame.mIsHandActive[nonPreferredController]);
{ {
const bool isPreferredControllerActive = const bool isPreferredControllerActive =
mInputStateFrame.mIsHandActive[mInputStateFrame.mPreferredHand]; mInputStateFrame.mIsHandActive[mInputStateFrame.mPreferredHand];
static bool sIsLowerPanelBeingPositioned = false;
sIsLowerPanelBeingPositioned &=
appState.mLowerMenuType == LowerMenuType::POSITIONAL_MENU &&
isPreferredControllerActive;
if (isPreferredControllerActive) { if (isPreferredControllerActive) {
const XrPosef pose = const XrPosef pose =
mInputStateFrame.mHandPositions[mInputStateFrame.mPreferredHand].pose; mInputStateFrame.mHandPositions[mInputStateFrame.mPreferredHand].pose;
@ -677,6 +685,8 @@ private:
mInputStateFrame.mHandPositions[mInputStateFrame.mPreferredHand].pose, mInputStateFrame.mHandPositions[mInputStateFrame.mPreferredHand].pose,
XrVector3f{0, 0, -3.5f}); XrVector3f{0, 0, -3.5f});
sIsLowerPanelBeingPositioned &= triggerState.currentState > 0;
// Hit-test panels in order of priority (and known depth) // Hit-test panels in order of priority (and known depth)
if (appState.mShouldShowErrorMessage) { if (appState.mShouldShowErrorMessage) {
@ -696,6 +706,7 @@ private:
} }
// No dialogs/popups that should impede normal cursor interaction with // No dialogs/popups that should impede normal cursor interaction with
// applicable panels // applicable panels
if (!shouldRenderCursor && appState.ShouldShowLowerPanel()) { if (!shouldRenderCursor && appState.ShouldShowLowerPanel()) {
shouldRenderCursor = mGameSurfaceLayer->GetRayIntersectionWithPanel( shouldRenderCursor = mGameSurfaceLayer->GetRayIntersectionWithPanel(
start, end, cursorPos2d, cursorPose3d); start, end, cursorPos2d, cursorPose3d);
@ -713,13 +724,14 @@ private:
cursorPos2d.x, cursorPos2d.y, 2); cursorPos2d.x, cursorPos2d.y, 2);
} }
} }
if (!shouldRenderCursor) { if (!shouldRenderCursor) {
shouldRenderCursor = mRibbonLayer->GetRayIntersectionWithPanel( shouldRenderCursor = mRibbonLayer->GetRayIntersectionWithPanel(
start, end, cursorPos2d, cursorPose3d); start, end, cursorPos2d, cursorPose3d);
if (shouldRenderCursor && triggerState.changedSinceLastSync) { if (shouldRenderCursor && triggerState.changedSinceLastSync) {
mRibbonLayer->SendClickToUI(cursorPos2d, triggerState.currentState); mRibbonLayer->SendClickToUI(cursorPos2d, triggerState.currentState);
} }
if (shouldRenderCursor && if ((shouldRenderCursor || sIsLowerPanelBeingPositioned) &&
appState.mLowerMenuType == LowerMenuType::POSITIONAL_MENU && appState.mLowerMenuType == LowerMenuType::POSITIONAL_MENU &&
triggerState.currentState) { triggerState.currentState) {
@ -730,28 +742,15 @@ private:
mInputStateFrame.mThumbStickState[mInputStateFrame.mPreferredHand]; mInputStateFrame.mThumbStickState[mInputStateFrame.mPreferredHand];
if (std::abs(thumbstickState.currentState.y) > if (std::abs(thumbstickState.currentState.y) >
kThumbStickDirectionThreshold) { kThumbStickDirectionThreshold) {
const XrPosef lowerPanelPose = mGameSurfaceLayer->SetLowerPanelFromThumbstick( const XrPosef lowerPanelPose =
mGameSurfaceLayer->SetLowerPanelFromThumbstick(
thumbstickState.currentState.y); thumbstickState.currentState.y);
mRibbonLayer->SetPanelWithPose(lowerPanelPose); mRibbonLayer->SetPanelWithPose(lowerPanelPose);
sIsLowerPanelBeingPositioned = true;
} }
} }
} }
if (sIsLowerPanelBeingPositioned) { shouldRenderCursor = true; }
if (!shouldRenderCursor) {
// Handling this here means L2/R2 are liable to
// be slightly out of sync with the other
// buttons (which are handled before
// WaitFrame()). We'll see if that ends up being
// a problem for any games.
ForwardButtonStateIfNeeded(
jni, mActivityObject, mForwardVRInputMethodID, 104 /* BUTTON_L2 */,
mInputStateFrame.mIndexTriggerState[InputStateFrame::LEFT_CONTROLLER],
"l2");
ForwardButtonStateIfNeeded(
jni, mActivityObject, mForwardVRInputMethodID, 105 /* BUTTON_R2 */,
mInputStateFrame.mIndexTriggerState[InputStateFrame::RIGHT_CONTROLLER],
"r2");
}
// Hit test the top panel if positional menu is active. // Hit test the top panel if positional menu is active.
if (!shouldRenderCursor && if (!shouldRenderCursor &&
@ -779,6 +778,22 @@ private:
} }
} }
if (!shouldRenderCursor) {
// Handling this here means L2/R2 are liable to
// be slightly out of sync with the other
// buttons (which are handled before
// WaitFrame()). We'll see if that ends up being
// a problem for any games.
ForwardButtonStateIfNeeded(
jni, mActivityObject, mForwardVRInputMethodID, 104 /* BUTTON_L2 */,
mInputStateFrame.mIndexTriggerState[InputStateFrame::LEFT_CONTROLLER],
"l2");
ForwardButtonStateIfNeeded(
jni, mActivityObject, mForwardVRInputMethodID, 105 /* BUTTON_R2 */,
mInputStateFrame.mIndexTriggerState[InputStateFrame::RIGHT_CONTROLLER],
"r2");
}
// Add a scale factor so the cursor doesn't scale as // Add a scale factor so the cursor doesn't scale as
// quickly as the panel(s) with distance. This may be // quickly as the panel(s) with distance. This may be
// mildly unsettling, but it helps to ensure the cursor // mildly unsettling, but it helps to ensure the cursor