Skip to content

Commit

Permalink
OpenXR: Add toggle for curved UI, angle adjustment
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Oct 3, 2023
1 parent aa73baf commit 8ab7fa0
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/mods/VR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ std::optional<std::string> VR::initialize_openxr() {

const std::unordered_set<std::string> wanted_extensions {
XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME,
XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME
// To be seen if we need more!
};

Expand All @@ -294,7 +295,6 @@ std::optional<std::string> VR::initialize_openxr() {
spdlog::info("[VR] Enabling {} extension", extension_property.extensionName);
m_openxr->enabled_extensions.insert(extension_property.extensionName);
extensions.push_back(extension_property.extensionName);
break;
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/mods/vr/D3D11Component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,18 +443,18 @@ vr::EVRCompositorError D3D11Component::on_frame(VR* vr) {
}

LOG_VERBOSE("Ending frame");
std::vector<XrCompositionLayerQuad> quad_layers{};
std::vector<XrCompositionLayerBaseHeader*> quad_layers{};

auto& openxr_overlay = vr->get_overlay_component().get_openxr();
const auto slate_quad = openxr_overlay.generate_slate_quad();
if (slate_quad) {
quad_layers.push_back(*slate_quad);
const auto slate_layer = openxr_overlay.generate_slate_layer();
if (slate_layer) {
quad_layers.push_back(&slate_layer->get());
}

const auto framework_quad = openxr_overlay.generate_framework_ui_quad();

if (framework_quad) {
quad_layers.push_back(*framework_quad);
quad_layers.push_back((XrCompositionLayerBaseHeader*)&framework_quad->get());
}

auto result = vr->m_openxr->end_frame(quad_layers, scene_depth_tex != nullptr);
Expand Down Expand Up @@ -1524,7 +1524,7 @@ std::optional<std::string> D3D11Component::OpenXR::create_swapchains() {

}
// Depth textures
if (vr->get_openxr_runtime()->enabled_extensions.contains(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME)) {
if (vr->get_openxr_runtime()->is_depth_allowed()) {
// Even when using AFR, the depth tex is always the size of a double wide.
// That's kind of unfortunate in terms of how many copies we have to do but whatever.
auto depth_swapchain_create_info = standard_swapchain_create_info;
Expand Down
14 changes: 7 additions & 7 deletions src/mods/vr/D3D12Component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) {
vr->m_openxr->begin_frame();
}

std::vector<XrCompositionLayerQuad> quad_layers{};
std::vector<XrCompositionLayerBaseHeader*> quad_layers{};

auto& openxr_overlay = vr->get_overlay_component().get_openxr();

Expand All @@ -426,23 +426,23 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) {
const auto right_quad = openxr_overlay.generate_slate_quad(runtimes::OpenXR::SwapchainIndex::UI_RIGHT, XrEyeVisibility::XR_EYE_VISIBILITY_RIGHT);

if (left_quad) {
quad_layers.push_back(*left_quad);
quad_layers.push_back((XrCompositionLayerBaseHeader*)&left_quad->get());
}

if (right_quad) {
quad_layers.push_back(*right_quad);
quad_layers.push_back((XrCompositionLayerBaseHeader*)&right_quad->get());
}
} else {
const auto slate_quad = openxr_overlay.generate_slate_quad();
const auto slate_layer = openxr_overlay.generate_slate_layer();

if (slate_quad) {
quad_layers.push_back(*slate_quad);
if (slate_layer) {
quad_layers.push_back(&slate_layer->get());
}
}

const auto framework_quad = openxr_overlay.generate_framework_ui_quad();
if (framework_quad) {
quad_layers.push_back(*framework_quad);
quad_layers.push_back((XrCompositionLayerBaseHeader*)&framework_quad);
}

auto result = vr->m_openxr->end_frame(quad_layers, scene_depth_tex.Get() != nullptr);
Expand Down
111 changes: 110 additions & 1 deletion src/mods/vr/OverlayComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ void OverlayComponent::on_config_load(const utility::Config& cfg, bool set_defau
void OverlayComponent::on_draw_ui() {
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
if (ImGui::TreeNode("Overlay Options")) {
if (VR::get()->get_runtime()->is_cylinder_layer_allowed()) {
m_slate_overlay_type->draw("Overlay Type");

if ((OverlayType)m_slate_overlay_type->value() == OverlayType::CYLINDER) {
m_slate_cylinder_angle->draw("UI Cylinder Angle");
}
}

float ui_offset[] { m_slate_x_offset->value(), m_slate_y_offset->value(), m_slate_distance->value() };

if (ImGui::SliderFloat3("UI Offset", ui_offset, -10.0f, 10.0f)) {
Expand Down Expand Up @@ -670,7 +678,108 @@ std::optional<std::reference_wrapper<XrCompositionLayerQuad>> OverlayComponent::
return layer;
}

std::optional<std::reference_wrapper<XrCompositionLayerQuad>> OverlayComponent::OpenXR::generate_framework_ui_quad() {
std::optional<std::reference_wrapper<XrCompositionLayerCylinderKHR>> OverlayComponent::OpenXR::generate_slate_cylinder(
runtimes::OpenXR::SwapchainIndex swapchain,
XrEyeVisibility eye)
{
auto& vr = VR::get();

if (!vr->is_gui_enabled()) {
return std::nullopt;
}

if (!vr->m_openxr->swapchains.contains((uint32_t)swapchain)) {
return std::nullopt;
}

const auto is_left_eye = eye == XR_EYE_VISIBILITY_BOTH || eye == XR_EYE_VISIBILITY_LEFT;

auto& layer = is_left_eye ? this->m_slate_layer_cylinder : this->m_slate_layer_cylinder_right;

layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR;
const auto& ui_swapchain = vr->m_openxr->swapchains[(uint32_t)swapchain];
layer.subImage.swapchain = ui_swapchain.handle;
layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
layer.subImage.imageRect.offset.x = 0;
layer.subImage.imageRect.offset.y = 0;
layer.subImage.imageRect.extent.width = ui_swapchain.width;
layer.subImage.imageRect.extent.height = ui_swapchain.height;
layer.eyeVisibility = eye;

auto glm_matrix = glm::identity<glm::mat4>();

if (vr->m_overlay_component.m_ui_follows_view->value()) {
layer.space = vr->m_openxr->view_space;
} else {
auto rotation_offset = glm::inverse(vr->get_rotation_offset());

if (vr->is_decoupled_pitch_enabled() && vr->is_decoupled_pitch_ui_adjust_enabled()) {
const auto pre_flat_rotation = vr->get_pre_flattened_rotation();
const auto pre_flat_pitch = utility::math::pitch_only(pre_flat_rotation);

// Add the inverse of the pitch rotation to the rotation offset
rotation_offset = glm::normalize(glm::inverse(pre_flat_pitch * vr->get_rotation_offset()));
}

glm_matrix = Matrix4x4f{rotation_offset};
glm_matrix[3] += vr->get_standing_origin();
layer.space = vr->m_openxr->stage_space;
}

const auto size_meters = m_parent->m_slate_size->value();
const auto meters_w = (float)ui_swapchain.width / (float)ui_swapchain.height * size_meters;
const auto meters_h = size_meters;

// OpenXR Docs:
// radius is the non-negative radius of the cylinder. Values of zero or floating point positive infinity are treated as an infinite cylinder.
// centralAngle is the angle of the visible section of the cylinder, based at 0 radians, in the range of [0, 2π). It grows symmetrically around the 0 radian angle.
// aspectRatio is the ratio of the visible cylinder section width / height. The height of the cylinder is given by: (cylinder radius × cylinder angle) / aspectRatio.
layer.radius = meters_w / 2.0f;
layer.centralAngle = glm::radians(m_parent->m_slate_cylinder_angle->value());
layer.aspectRatio = meters_w / meters_h;

glm_matrix[3] -= glm_matrix[2] * m_parent->m_slate_distance->value();
glm_matrix[3] += m_parent->m_slate_x_offset->value() * glm_matrix[0];
glm_matrix[3] += m_parent->m_slate_y_offset->value() * glm_matrix[1];
glm_matrix[3].w = 1.0f;

layer.pose.orientation = runtimes::OpenXR::to_openxr(glm::quat_cast(glm_matrix));
layer.pose.position = runtimes::OpenXR::to_openxr(glm_matrix[3]);

return layer;
}

std::optional<std::reference_wrapper<XrCompositionLayerBaseHeader>> OverlayComponent::OpenXR::generate_slate_layer(
runtimes::OpenXR::SwapchainIndex swapchain,
XrEyeVisibility eye)
{
switch ((OverlayComponent::OverlayType)m_parent->m_slate_overlay_type->value()) {
default:
case OverlayComponent::OverlayType::QUAD:
if (auto result = generate_slate_quad(swapchain, eye); result.has_value()) {
return *(XrCompositionLayerBaseHeader*)&result.value().get();
}

return std::nullopt;
case OverlayComponent::OverlayType::CYLINDER:
if (!VR::get()->get_runtime()->is_cylinder_layer_allowed()) {
if (auto result = generate_slate_quad(swapchain, eye); result.has_value()) {
return *(XrCompositionLayerBaseHeader*)&result.value().get();
}

return std::nullopt;
}

if (auto result = generate_slate_cylinder(swapchain, eye); result.has_value()) {
return *(XrCompositionLayerBaseHeader*)&result.value().get();
}

return std::nullopt;
};
}


std::optional<std::reference_wrapper<XrCompositionLayerQuad>> OverlayComponent::OpenXR::generate_framework_ui_quad() {
if (!g_framework->is_drawing_anything()) {
return std::nullopt;
}
Expand Down
26 changes: 26 additions & 0 deletions src/mods/vr/OverlayComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,24 @@ class OverlayComponent : public ModComponent {
bool m_just_closed_ui{false};
bool m_just_opened_ui{false};

enum OverlayType {
DEFAULT = 0,
QUAD = 0,
CYLINDER = 1,
MAX
};

static const inline std::vector<std::string> s_overlay_type_names{
"Quad",
"Cylinder"
};

const ModCombo::Ptr m_slate_overlay_type{ ModCombo::create("UI_OverlayType", s_overlay_type_names) };
const ModSlider::Ptr m_slate_distance{ ModSlider::create("UI_Distance", 0.5f, 10.0f, 2.0f) };
const ModSlider::Ptr m_slate_x_offset{ ModSlider::create("UI_X_Offset", -10.0f, 10.0f, 0.0f) };
const ModSlider::Ptr m_slate_y_offset{ ModSlider::create("UI_Y_Offset", -10.0f, 10.0f, 0.0f) };
const ModSlider::Ptr m_slate_size{ ModSlider::create("UI_Size", 0.5f, 10.0f, 2.0f) };
const ModSlider::Ptr m_slate_cylinder_angle{ ModSlider::create("UI_Cylinder_Angle", 0.0f, 360.0f, 90.0f) };
const ModToggle::Ptr m_ui_follows_view{ ModToggle::create("UI_FollowView", false) };

const ModSlider::Ptr m_framework_distance{ ModSlider::create("UI_Framework_Distance", 0.5f, 10.0f, 1.75f) };
Expand All @@ -80,10 +94,12 @@ class OverlayComponent : public ModComponent {
const ModToggle::Ptr m_framework_wrist_ui{ ModToggle::create("UI_Framework_WristUI", false) };

Mod::ValueList m_options{
*m_slate_overlay_type,
*m_slate_x_offset,
*m_slate_y_offset,
*m_slate_distance,
*m_slate_size,
*m_slate_cylinder_angle,
*m_ui_follows_view,
*m_framework_distance,
*m_framework_size,
Expand All @@ -105,11 +121,21 @@ class OverlayComponent : public ModComponent {
runtimes::OpenXR::SwapchainIndex swapchain = runtimes::OpenXR::SwapchainIndex::UI,
XrEyeVisibility eye = XR_EYE_VISIBILITY_BOTH
);
std::optional<std::reference_wrapper<XrCompositionLayerCylinderKHR>> generate_slate_cylinder(
runtimes::OpenXR::SwapchainIndex swapchain = runtimes::OpenXR::SwapchainIndex::UI,
XrEyeVisibility eye = XR_EYE_VISIBILITY_BOTH
);
std::optional<std::reference_wrapper<XrCompositionLayerBaseHeader>> generate_slate_layer(
runtimes::OpenXR::SwapchainIndex swapchain = runtimes::OpenXR::SwapchainIndex::UI,
XrEyeVisibility eye = XR_EYE_VISIBILITY_BOTH
);
std::optional<std::reference_wrapper<XrCompositionLayerQuad>> generate_framework_ui_quad();

private:
XrCompositionLayerQuad m_slate_layer{};
XrCompositionLayerQuad m_slate_layer_right{};
XrCompositionLayerCylinderKHR m_slate_layer_cylinder{};
XrCompositionLayerCylinderKHR m_slate_layer_cylinder_right{};
XrCompositionLayerQuad m_framework_ui_layer{};
OverlayComponent* m_parent{ nullptr };

Expand Down
4 changes: 2 additions & 2 deletions src/mods/vr/runtimes/OpenXR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ XrResult OpenXR::begin_frame() {
return result;
}

XrResult OpenXR::end_frame(const std::vector<XrCompositionLayerQuad>& quad_layers, bool has_depth) {
XrResult OpenXR::end_frame(const std::vector<XrCompositionLayerBaseHeader*>& quad_layers, bool has_depth) {
std::scoped_lock _{sync_mtx};

if (!this->ready() || !this->got_first_poses || !this->frame_synced) {
Expand Down Expand Up @@ -1761,7 +1761,7 @@ XrResult OpenXR::end_frame(const std::vector<XrCompositionLayerQuad>& quad_layer
}

for (auto& l : quad_layers) {
layers.push_back((XrCompositionLayerBaseHeader*)&l);
layers.push_back(l);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/mods/vr/runtimes/OpenXR.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ struct OpenXR final : public VRRuntime {
return this->enabled_extensions.contains(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
}

bool is_cylinder_layer_allowed() const override {
return this->enabled_extensions.contains(XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME);
}

void on_system_properties_acquired(const XrSystemProperties& props);

void on_config_load(const utility::Config& cfg, bool set_defaults) override;
Expand Down Expand Up @@ -132,7 +136,7 @@ struct OpenXR final : public VRRuntime {
std::optional<std::string> initialize_actions(const std::string& json_string);

XrResult begin_frame();
XrResult end_frame(const std::vector<XrCompositionLayerQuad>& quad_layers, bool has_depth = false);
XrResult end_frame(const std::vector<XrCompositionLayerBaseHeader*>& quad_layers, bool has_depth = false);

void begin_profile() {
if (!this->profile_calls) {
Expand Down
4 changes: 4 additions & 0 deletions src/mods/vr/runtimes/VRRuntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ struct VRRuntime {
return false;
}

virtual bool is_cylinder_layer_allowed() const {
return false;
}

virtual void on_config_load(const utility::Config& cfg, bool set_defaults) {}
virtual void on_config_save(utility::Config& cfg) {}
virtual void on_draw_ui() {}
Expand Down

0 comments on commit 8ab7fa0

Please sign in to comment.