mirror of
https://github.com/amwatson/CitraVR.git
synced 2024-09-19 19:01:38 +02:00
split ribbon into a specific ribbon layer and give it methods for setting position
This commit is contained in:
parent
d012252cb8
commit
84a2725906
8 changed files with 134 additions and 30 deletions
|
@ -38,6 +38,7 @@ add_library(citra-android SHARED
|
|||
vr/layers/CursorLayer.cpp
|
||||
vr/layers/GameSurfaceLayer.cpp
|
||||
vr/layers/PassthroughLayer.cpp
|
||||
vr/layers/RibbonLayer.cpp
|
||||
vr/layers/UILayer.cpp
|
||||
vr/utils/JniUtils.cpp
|
||||
vr/utils/JniClassNames.cpp
|
||||
|
|
|
@ -593,3 +593,7 @@ void GameSurfaceLayer::CreateSwapchain() {
|
|||
mSwapchain.mWidth = xsci.width;
|
||||
mSwapchain.mHeight = xsci.height;
|
||||
}
|
||||
|
||||
void GameSurfaceLayer::SetLowerPanelWithPose(const XrPosef& pose) {
|
||||
mLowerPanel.mPanelFromWorld = pose;
|
||||
}
|
||||
|
|
|
@ -172,8 +172,9 @@ public:
|
|||
void SetTopPanelFromThumbstick(const float thumbstickY);
|
||||
void SetLowerPanelFromController(const XrVector3f& controllerPosition);
|
||||
XrPosef GetTopPanelFromHeadPose(uint32_t eye, const XrPosef& headPose);
|
||||
const XrPosef& GetLowerPanelPose() const { return mLowerPanel.mPanelFromWorld; }
|
||||
void ResetPanelPositions();
|
||||
const XrPosef& GetLowerPanelPose() const { return mLowerPanel.mPanelFromWorld; }
|
||||
void SetLowerPanelWithPose(const XrPosef& pose);
|
||||
|
||||
private:
|
||||
int Init(const XrSession& session, const jobject activityObject);
|
||||
|
|
91
src/android/app/src/main/jni/vr/layers/RibbonLayer.cpp
Normal file
91
src/android/app/src/main/jni/vr/layers/RibbonLayer.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
#include "RibbonLayer.h"
|
||||
|
||||
#include "../utils/LogUtils.h"
|
||||
#include "../utils/XrMath.h"
|
||||
|
||||
namespace {
|
||||
constexpr float kInitialLowerPanelPitchInRadians = -MATH_FLOAT_PI / 4.0f; // -45 degrees in radians
|
||||
|
||||
XrVector3f CalculatePanelPosition(const XrVector3f& viewerPosition,
|
||||
const XrVector3f& controllerPosition,
|
||||
float sphereRadius) {
|
||||
// Vector from viewer to controller
|
||||
XrVector3f viewerToController = controllerPosition - viewerPosition;
|
||||
XrMath::Vector3f::Normalize(viewerToController);
|
||||
|
||||
// Calculate position on the sphere's surface
|
||||
return viewerPosition + sphereRadius * viewerToController;
|
||||
}
|
||||
|
||||
XrQuaternionf CalculatePanelRotation(const XrVector3f& windowPosition,
|
||||
const XrVector3f& viewerPosition,
|
||||
const XrVector3f& upDirection) {
|
||||
// Compute forward direction (normalized)
|
||||
XrVector3f forward = viewerPosition - windowPosition;
|
||||
XrMath::Vector3f::Normalize(forward);
|
||||
|
||||
// Compute right direction (normalized)
|
||||
XrVector3f right = XrMath::Vector3f::Cross(upDirection, forward);
|
||||
XrMath::Vector3f::Normalize(right);
|
||||
|
||||
// Recompute up direction (normalized) to ensure orthogonality
|
||||
const XrVector3f up = XrMath::Vector3f::Cross(forward, right);
|
||||
|
||||
return XrMath::Quatf::FromThreeVectors(forward, up, right);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
RibbonLayer::RibbonLayer(const XrVector3f&& position, const XrQuaternionf&& orientation,
|
||||
JNIEnv* jni, jobject activityObject, const XrSession& session)
|
||||
: UILayer("org/citra/citra_emu/vr/ui/VrRibbonLayer", std::move(position),
|
||||
std::move(orientation), jni, activityObject, session)
|
||||
, mInitialPose({orientation, position}) {
|
||||
mIsMenuBackgroundSelectedMethodId =
|
||||
jni->GetMethodID(GetVrUILayerClass(), "isMenuBackgroundSelected", "()Z");
|
||||
if (mIsMenuBackgroundSelectedMethodId == nullptr) {
|
||||
FAIL("Could not find isMenuBackgroundSelected()");
|
||||
}
|
||||
}
|
||||
|
||||
bool RibbonLayer::IsMenuBackgroundSelected() const {
|
||||
return GetEnv()->CallBooleanMethod(GetVrUILayerObject(), mIsMenuBackgroundSelectedMethodId);
|
||||
}
|
||||
|
||||
void RibbonLayer::SetPanelFromController(const XrVector3f& controllerPosition) {
|
||||
constexpr XrVector3f viewerPosition{0.0f, 0.0f, 0.0f}; // Viewer position at origin
|
||||
constexpr XrVector3f windowUpDirection{0.0f, 1.0f, 0.0f}; // Y is up
|
||||
constexpr float pitchAdjustmentFactor = 0.5f;
|
||||
|
||||
// Calculate sphere radius based on panel position to viewer
|
||||
const float sphereRadius = XrMath::Vector3f::Length(mPanelFromWorld.position - viewerPosition);
|
||||
|
||||
// Calculate new window position based on controller and sphere radius
|
||||
const XrVector3f windowPosition =
|
||||
CalculatePanelPosition(viewerPosition, controllerPosition, sphereRadius);
|
||||
|
||||
// Limit vertical range to prevent the window from being too close to the viewer or the top
|
||||
// panel.
|
||||
if (windowPosition.z >= -0.5f) { return; }
|
||||
|
||||
// Calculate the base rotation of the panel to face the user
|
||||
const XrQuaternionf baseRotation =
|
||||
CalculatePanelRotation(windowPosition, viewerPosition, windowUpDirection);
|
||||
|
||||
// Calculate pitch adjustment based on vertical displacement from initial position
|
||||
const float verticalDisplacement = windowPosition.y - mInitialPose.position.y;
|
||||
// Arbitrary factor, chosen based on what change-in-pitch felt best
|
||||
// A higher factor will make the window pitch more aggressively
|
||||
const float pitchAdjustment = verticalDisplacement * pitchAdjustmentFactor;
|
||||
// Clamp the new pitch to reasonable bounds (-45 to 90 degrees)
|
||||
const float newPitchRadians =
|
||||
std::clamp(-std::abs(kInitialLowerPanelPitchInRadians + pitchAdjustment),
|
||||
kInitialLowerPanelPitchInRadians, MATH_FLOAT_PI / 2.0f);
|
||||
|
||||
// Construct a quaternion for the pitch adjustment
|
||||
const XrQuaternionf pitchAdjustmentQuat =
|
||||
XrMath::Quatf::FromAxisAngle({1.0f, 0.0f, 0.0f}, newPitchRadians / 2.0f);
|
||||
|
||||
// Combine the base rotation with the pitch adjustment
|
||||
mPanelFromWorld = {baseRotation * pitchAdjustmentQuat, windowPosition};
|
||||
}
|
18
src/android/app/src/main/jni/vr/layers/RibbonLayer.h
Normal file
18
src/android/app/src/main/jni/vr/layers/RibbonLayer.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "UILayer.h"
|
||||
|
||||
class RibbonLayer : public UILayer {
|
||||
public:
|
||||
RibbonLayer(const XrVector3f&& position, const XrQuaternionf&& orientation, JNIEnv* jni,
|
||||
jobject activityObject, const XrSession& session);
|
||||
|
||||
bool IsMenuBackgroundSelected() const;
|
||||
void SetPanelFromController(const XrVector3f& controllerPosition);
|
||||
|
||||
const XrPosef& GetPose() const { return mPanelFromWorld; }
|
||||
|
||||
private:
|
||||
const XrPosef mInitialPose;
|
||||
jmethodID mIsMenuBackgroundSelectedMethodId = nullptr;
|
||||
};
|
|
@ -162,8 +162,8 @@ XrVector2f GetDensityScaleForSize(const int32_t texWidth, const int32_t texHeigh
|
|||
UILayer::UILayer(const std::string& className, const XrVector3f&& position,
|
||||
const XrQuaternionf&& orientation, JNIEnv* env, jobject activityObject,
|
||||
const XrSession& session)
|
||||
: mSession(session)
|
||||
, mPanelFromWorld(XrPosef{orientation, position})
|
||||
: mPanelFromWorld(XrPosef{orientation, position})
|
||||
, mSession(session)
|
||||
, mEnv(env) {
|
||||
const int32_t initializationStatus = Init(className, activityObject, position, session);
|
||||
if (initializationStatus < 0) {
|
||||
|
@ -214,7 +214,7 @@ bool UILayer::GetRayIntersectionWithPanel(const XrVector3f& start,
|
|||
scale, start, end, result2d, result3d);
|
||||
}
|
||||
|
||||
// Next error code: -9
|
||||
// Next error code: -8
|
||||
int32_t UILayer::Init(const std::string& className, const jobject activityObject,
|
||||
const XrVector3f& position, const XrSession& session) {
|
||||
mVrUILayerClass = JniUtils::GetGlobalClassReference(mEnv, activityObject, className.c_str());
|
||||
|
@ -238,16 +238,7 @@ int32_t UILayer::Init(const std::string& className, const jobject activityObject
|
|||
|
||||
BAIL_ON_COND(mSendClickToUIMethodID == nullptr, "could not find sendClickToUI()", -6);
|
||||
|
||||
if (className == "org/citra/citra_emu/vr/ui/VrRibbonLayer") {
|
||||
ALOGD("UILayer: using companion class");
|
||||
|
||||
mIsMenuBackgroundSelectedMethodId =
|
||||
mEnv->GetMethodID(mVrUILayerClass, "isMenuBackgroundSelected", "()Z");
|
||||
BAIL_ON_COND(mIsMenuBackgroundSelectedMethodId == nullptr,
|
||||
"could not find isMenuBackgroundSelected()", -7);
|
||||
}
|
||||
|
||||
BAIL_ON_ERR(CreateSwapchain(), -8);
|
||||
BAIL_ON_ERR(CreateSwapchain(), -7);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -336,7 +327,3 @@ void UILayer::SendClickToUI(const XrVector2f& pos2d, const int type) {
|
|||
}
|
||||
|
||||
void UILayer::SetPanelWithPose(const XrPosef& pose) { mPanelFromWorld = pose; }
|
||||
|
||||
bool UILayer::IsMenuBackgroundSelected() const {
|
||||
return mEnv->CallBooleanMethod(mVrUILayerObject, mIsMenuBackgroundSelectedMethodId);
|
||||
}
|
||||
|
|
|
@ -125,7 +125,11 @@ public:
|
|||
|
||||
void SetPanelWithPose(const XrPosef& pose);
|
||||
|
||||
bool IsMenuBackgroundSelected() const;
|
||||
protected:
|
||||
jobject GetVrUILayerObject() const { return mVrUILayerObject; }
|
||||
jclass GetVrUILayerClass() const { return mVrUILayerClass; }
|
||||
JNIEnv* GetEnv() const { return mEnv; }
|
||||
XrPosef mPanelFromWorld;
|
||||
|
||||
private:
|
||||
int Init(const std::string& className, const jobject activityObject, const XrVector3f& position,
|
||||
|
@ -140,8 +144,6 @@ private:
|
|||
const XrSession mSession;
|
||||
Swapchain mSwapchain;
|
||||
|
||||
XrPosef mPanelFromWorld;
|
||||
|
||||
//============================
|
||||
// JNI objects
|
||||
JNIEnv* mEnv = nullptr;
|
||||
|
@ -156,8 +158,7 @@ private:
|
|||
// the decorView representing an entire window, it's important to accont for
|
||||
// the x, y offset of the view within the window, in case there are things
|
||||
// like window decorations or status bars.
|
||||
jmethodID mGetBoundsMethodID = nullptr;
|
||||
jmethodID mSendClickToUIMethodID = nullptr;
|
||||
jmethodID mSetSurfaceMethodId = nullptr;
|
||||
jmethodID mIsMenuBackgroundSelectedMethodId = nullptr;
|
||||
jmethodID mGetBoundsMethodID = nullptr;
|
||||
jmethodID mSendClickToUIMethodID = nullptr;
|
||||
jmethodID mSetSurfaceMethodId = nullptr;
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ License : Licensed under GPLv3 or any later version.
|
|||
#include "layers/CursorLayer.h"
|
||||
#include "layers/GameSurfaceLayer.h"
|
||||
#include "layers/PassthroughLayer.h"
|
||||
#include "layers/RibbonLayer.h"
|
||||
#include "layers/UILayer.h"
|
||||
#include "vr_settings.h"
|
||||
|
||||
|
@ -294,8 +295,8 @@ private:
|
|||
XrVector3f{0, 0, -2.0f}, jni, mActivityObject, gOpenXr->mSession, resolutionFactor);
|
||||
}
|
||||
|
||||
mRibbonLayer = std::make_unique<UILayer>(
|
||||
"org/citra/citra_emu/vr/ui/VrRibbonLayer", XrVector3f{0, -0.75f, -1.51f},
|
||||
mRibbonLayer = std::make_unique<RibbonLayer>(
|
||||
XrVector3f{0, -0.75f, -1.51f},
|
||||
XrMath::Quatf::FromEuler(-MATH_FLOAT_PI / 4.0f, 0.0f, 0.0f), jni, mActivityObject,
|
||||
gOpenXr->mSession);
|
||||
|
||||
|
@ -753,10 +754,10 @@ private:
|
|||
(shouldRenderCursor && mRibbonLayer->IsMenuBackgroundSelected())) &&
|
||||
triggerState.currentState) {
|
||||
|
||||
mGameSurfaceLayer->SetLowerPanelFromController(
|
||||
mRibbonLayer->SetPanelFromController(
|
||||
{appState.mIsHorizontalAxisLocked ? 0.0f : cursorPose3d.position.x,
|
||||
cursorPose3d.position.y, cursorPose3d.position.z});
|
||||
mRibbonLayer->SetPanelWithPose(mGameSurfaceLayer->GetLowerPanelPose());
|
||||
mGameSurfaceLayer->SetLowerPanelWithPose(mRibbonLayer->GetPose());
|
||||
|
||||
sIsLowerPanelBeingPositioned = true;
|
||||
}
|
||||
|
@ -1155,7 +1156,7 @@ private:
|
|||
std::unique_ptr<GameSurfaceLayer> mGameSurfaceLayer;
|
||||
std::unique_ptr<PassthroughLayer> mPassthroughLayer;
|
||||
std::unique_ptr<UILayer> mKeyboardLayer;
|
||||
std::unique_ptr<UILayer> mRibbonLayer;
|
||||
std::unique_ptr<RibbonLayer> mRibbonLayer;
|
||||
|
||||
std::unique_ptr<InputStateStatic> mInputStateStatic;
|
||||
InputStateFrame mInputStateFrame;
|
||||
|
|
Loading…
Reference in a new issue