mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 04:04:18 +02:00
Added "Lean back angle" option
This commit is contained in:
1 parent
6e7e0f075b
commit
913eda2b3a
4 files changed
+99
-5
No files matched your search
@@ -63,6 +63,7 @@ struct RuntimeUserConfig {
|
||||
std::optional<bool> vrFirstPersonHideDriver;
|
||||
std::optional<int32_t> vrFirstPersonHiddenModel;
|
||||
std::optional<std::string> vrRecenterKey;
|
||||
std::optional<float> vrLeanBackDegrees;
|
||||
std::optional<float> audioVolume;
|
||||
std::optional<float> audioMusicVolume;
|
||||
std::optional<float> audioSoundEffectsVolume;
|
||||
@@ -150,6 +151,10 @@ inline constexpr float kVrFirstPersonHeadOffsetLimit = 10.0f;
|
||||
// SDL scancode name, spelled the way SDL_GetScancodeName produces it. An
|
||||
// empty string leaves the recenter hotkey unbound, menu button only.
|
||||
inline constexpr std::string_view kVrRecenterKeyDefault = "F9";
|
||||
// Fixed pitch of the game camera for a player sitting reclined, in degrees.
|
||||
// Positive leans the camera back with you; 0 disables it entirely.
|
||||
inline constexpr float kVrLeanBackDegreesDefault = 0.0f;
|
||||
inline constexpr float kVrLeanBackDegreesLimit = 45.0f;
|
||||
|
||||
inline std::string Trim(std::string_view text) {
|
||||
size_t begin = 0;
|
||||
@@ -387,7 +392,12 @@ inline void EnsureConfigFile() {
|
||||
"# the game. Applies during an immersive race; the menu screen is\n"
|
||||
"# head-locked already. Rebindable from the F10 menu under VR. Leave\n"
|
||||
"# empty to unbind.\n"
|
||||
"recenter_key = \"F9\"\n\n"
|
||||
"recenter_key = \"F9\"\n"
|
||||
"# Fixed pitch of the game camera, in degrees, for a player sitting\n"
|
||||
"# reclined. Positive tilts the camera back with you, so 20 here cancels\n"
|
||||
"# reclining about 20 degrees and puts the track back in front of you.\n"
|
||||
"# 0 disables it. Changeable live from the F10 menu under VR.\n"
|
||||
"lean_back_degrees = 0.0\n\n"
|
||||
"[audio]\n"
|
||||
"volume = 1.0\n"
|
||||
"music_volume = 1.0\n"
|
||||
@@ -573,6 +583,10 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
if (auto value = FindConfigValue<std::string>(document, "vr", "recenter_key")) {
|
||||
config.vrRecenterKey = *value;
|
||||
}
|
||||
if (auto value = FindConfigFloat(document, "vr", "lean_back_degrees");
|
||||
value && *value >= -kVrLeanBackDegreesLimit && *value <= kVrLeanBackDegreesLimit) {
|
||||
config.vrLeanBackDegrees = static_cast<float>(*value);
|
||||
}
|
||||
if (auto value = FindConfigInt(document, "vr", "first_person_hidden_model");
|
||||
value && *value >= -1 && *value <= 31) {
|
||||
config.vrFirstPersonHiddenModel = static_cast<int32_t>(*value);
|
||||
@@ -853,6 +867,14 @@ inline bool SetVrRecenterKey(std::string value) {
|
||||
return WriteSetting("vr", "recenter_key", FormatString(value));
|
||||
}
|
||||
|
||||
inline bool SetVrLeanBackDegrees(float value) {
|
||||
value = std::clamp(value, -kVrLeanBackDegreesLimit, kVrLeanBackDegreesLimit);
|
||||
Mutable().vrLeanBackDegrees = value;
|
||||
std::ostringstream formatted;
|
||||
formatted << value;
|
||||
return WriteSetting("vr", "lean_back_degrees", formatted.str());
|
||||
}
|
||||
|
||||
inline bool SetVrFirstPersonHideDriver(bool value) {
|
||||
Mutable().vrFirstPersonHideDriver = value;
|
||||
return WriteSetting("vr", "first_person_hide_driver", value ? "true" : "false");
|
||||
@@ -1149,6 +1171,11 @@ inline std::string VrRecenterKey(std::string fallback = std::string(kVrRecenterK
|
||||
return Get().vrRecenterKey.value_or(std::move(fallback));
|
||||
}
|
||||
|
||||
inline float VrLeanBackDegrees(float fallback = kVrLeanBackDegreesDefault) {
|
||||
return std::clamp(Get().vrLeanBackDegrees.value_or(fallback),
|
||||
-kVrLeanBackDegreesLimit, kVrLeanBackDegreesLimit);
|
||||
}
|
||||
|
||||
inline bool VrFirstPersonHideDriver(bool fallback = kVrFirstPersonHideDriverDefault) {
|
||||
return Get().vrFirstPersonHideDriver.value_or(fallback);
|
||||
}
|
||||
|
||||
@@ -44,4 +44,12 @@ std::string OpenXRLastError();
|
||||
// immersive race, where the head-locked menu screen has no origin to recenter.
|
||||
void OpenXRRequestRecenter() noexcept;
|
||||
|
||||
// Sets a fixed pitch of the game camera for a player sitting reclined, in
|
||||
// degrees, clamped to RuntimeConfigFile::kVrLeanBackDegreesLimit. Positive
|
||||
// tilts the camera back with the player, so an angle matching how far they are
|
||||
// reclined puts the track back in front of them. 0 applies no pitch at all.
|
||||
// Applies to the immersive race view only; callable from any thread and read
|
||||
// once per published frame.
|
||||
void OpenXRSetLeanBackDegrees(float degrees) noexcept;
|
||||
|
||||
} // namespace mkw::vr
|
||||
@@ -121,6 +121,7 @@ SDL_Scancode g_vrRecenterScancode = [] {
|
||||
: SDL_GetScancodeFromName(name.c_str());
|
||||
}();
|
||||
bool g_vrRecenterRebinding = false;
|
||||
float g_vrLeanBackDegrees = RuntimeConfigFile::VrLeanBackDegrees();
|
||||
uint32_t g_disabledPostProcessingPaths = RuntimeConfigFile::DisabledPostProcessingPaths(0);
|
||||
std::array<int32_t, PAD_MAX_CONTROLLERS> g_configuredControllerIndices = [] {
|
||||
std::array<int32_t, PAD_MAX_CONTROLLERS> indices{};
|
||||
@@ -909,6 +910,20 @@ void DrawVrSettings() {
|
||||
ImGui::SetTooltip("Click to rebind. Esc cancels, Backspace unbinds.");
|
||||
}
|
||||
}
|
||||
if (ImGui::SliderFloat("Lean back angle (deg)", &g_vrLeanBackDegrees,
|
||||
-RuntimeConfigFile::kVrLeanBackDegreesLimit,
|
||||
RuntimeConfigFile::kVrLeanBackDegreesLimit, "%.1f")) {
|
||||
mkw::vr::OpenXRSetLeanBackDegrees(g_vrLeanBackDegrees);
|
||||
RuntimeConfigFile::SetVrLeanBackDegrees(g_vrLeanBackDegrees);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Tilts the game camera back with you when you play reclined, so set it to "
|
||||
"roughly how far back your seat is and the track comes back in front of you "
|
||||
"instead of above. 0 applies no tilt. Unlike recentering, this deliberately "
|
||||
"does pitch the view, and looking sideways while it is set will roll the "
|
||||
"horizon the way a real recline would.");
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VR camera");
|
||||
if (ImGui::Checkbox("First-person camera", &g_vrFirstPerson)) {
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
namespace mkw::vr {
|
||||
namespace {
|
||||
|
||||
inline constexpr float kDegreesToRadians = 0.01745329252f;
|
||||
|
||||
void ConfigurePolicy(bool enabled) noexcept {
|
||||
MkwVRPolicyReset();
|
||||
MkwVRPolicyConfig config{};
|
||||
@@ -78,6 +80,15 @@ Quaternion Conjugate(Quaternion value) noexcept {
|
||||
return {-value.x, -value.y, -value.z, value.w};
|
||||
}
|
||||
|
||||
Quaternion Multiply(const Quaternion& left, const Quaternion& right) noexcept {
|
||||
return Normalize({
|
||||
left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
|
||||
left.w * right.y - left.x * right.z + left.y * right.w + left.z * right.x,
|
||||
left.w * right.z + left.x * right.y - left.y * right.x + left.z * right.w,
|
||||
left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z,
|
||||
});
|
||||
}
|
||||
|
||||
std::array<float, 3> Rotate(const Quaternion& q, const std::array<float, 3>& value) noexcept {
|
||||
// Expanded q * [v,0] * conjugate(q), avoiding two temporary normalizations.
|
||||
const float tx = 2.0f * (q.y * value[2] - q.z * value[1]);
|
||||
@@ -155,13 +166,26 @@ void ProjectionFromFov(const XrFovf& fov, float output[16]) noexcept {
|
||||
// leaves the game's horizon level and its forward fixed to the reference space.
|
||||
// Composing a latched head orientation in here instead would bake that instant's
|
||||
// pitch and roll into the neutral and tilt the horizon for the rest of the session.
|
||||
//
|
||||
// lean_back_radians is the one deliberate exception: a fixed pitch of the game
|
||||
// camera about the reference space's right axis, for a player sitting reclined.
|
||||
// It multiplies in on the right, so it turns the world before the head rotation
|
||||
// rather than after it, which is what makes it cancel a reclined head exactly
|
||||
// and, when you then look sideways, roll the view the way a real recline would.
|
||||
void ViewFromBase(const XrPosef& eye_pose, const std::array<float, 3>& base_position,
|
||||
bool position_valid, float units_per_meter, float output[12]) noexcept {
|
||||
bool position_valid, float units_per_meter, float lean_back_radians,
|
||||
float output[12]) noexcept {
|
||||
const Quaternion eye = Normalize({eye_pose.orientation.x, eye_pose.orientation.y,
|
||||
eye_pose.orientation.z, eye_pose.orientation.w});
|
||||
const Quaternion inverse_eye = Conjugate(eye);
|
||||
float rotation[9];
|
||||
RotationMatrix(inverse_eye, rotation);
|
||||
if (lean_back_radians == 0.0f) {
|
||||
RotationMatrix(inverse_eye, rotation);
|
||||
} else {
|
||||
const float half_angle = 0.5f * lean_back_radians;
|
||||
const Quaternion lean{std::sin(half_angle), 0.0f, 0.0f, std::cos(half_angle)};
|
||||
RotationMatrix(Multiply(inverse_eye, lean), rotation);
|
||||
}
|
||||
|
||||
std::array<float, 3> translation{};
|
||||
if (position_valid) {
|
||||
@@ -326,6 +350,13 @@ public:
|
||||
recenter_requested_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void SetLeanBackDegrees(float degrees) noexcept {
|
||||
lean_back_degrees_.store(
|
||||
std::clamp(degrees, -RuntimeConfigFile::kVrLeanBackDegreesLimit,
|
||||
RuntimeConfigFile::kVrLeanBackDegreesLimit),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ServiceProducerFrameBoundary() noexcept {
|
||||
if (teardown_requested_.load(std::memory_order_acquire)) {
|
||||
Shutdown();
|
||||
@@ -596,12 +627,16 @@ private:
|
||||
base_position_valid_ = true;
|
||||
}
|
||||
last_immersive_ = true;
|
||||
// Read once so both eyes are built from the same angle even if the
|
||||
// settings slider moves between them.
|
||||
const float lean_back_radians =
|
||||
lean_back_degrees_.load(std::memory_order_relaxed) * kDegreesToRadians;
|
||||
for (uint32_t eye = 0; eye < kOpenXREyeCount; ++eye) {
|
||||
ProjectionFromFov(source.xr_frame.views[eye].fov,
|
||||
destination.eyes[eye].projection);
|
||||
ViewFromBase(source.xr_frame.views[eye].pose, base_position_,
|
||||
position_valid && base_position_valid_,
|
||||
units_per_meter, destination.eyes[eye].viewFromCenter);
|
||||
position_valid && base_position_valid_, units_per_meter,
|
||||
lean_back_radians, destination.eyes[eye].viewFromCenter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,6 +679,7 @@ private:
|
||||
std::atomic_bool running_{false};
|
||||
std::atomic_bool teardown_requested_{false};
|
||||
std::atomic_bool recenter_requested_{false};
|
||||
std::atomic<float> lean_back_degrees_{RuntimeConfigFile::VrLeanBackDegrees()};
|
||||
std::atomic<PublishedFrame*> published_{nullptr};
|
||||
PublishedFrame published_frame_{};
|
||||
std::mutex published_mutex_;
|
||||
@@ -722,6 +758,14 @@ void OpenXRRequestRecenter() noexcept {
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenXRSetLeanBackDegrees(float degrees) noexcept {
|
||||
#if defined(MKW_ENABLE_OPENXR) && defined(_WIN32)
|
||||
OpenXRIntegration::Get().SetLeanBackDegrees(degrees);
|
||||
#else
|
||||
(void)degrees;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string OpenXRLastError() {
|
||||
#if !defined(MKW_ENABLE_OPENXR)
|
||||
return "this build was compiled without OpenXR support";
|
||||
|
||||
Reference in new issue
Block a user