initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

117
modules/openxr/SCsub Normal file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import sys
Import("env")
Import("env_modules")
env_openxr = env_modules.Clone()
# Thirdparty source files
thirdparty_obj = []
# Khronos OpenXR loader
# Needs even for build against shared library, at least the defines used in public headers.
if env["platform"] == "android":
# may need to set OPENXR_ANDROID_VERSION_SUFFIX
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
env_openxr.AppendUnique(CPPDEFINES=["JSON_USE_EXCEPTION=0"])
# may need to include java parts of the openxr loader
elif env["platform"] == "linuxbsd":
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
if env["x11"]:
env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
if env["wayland"] and env["opengl3"]:
env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_EGL", "XRDEPENDENCIES_USE_GLAD"])
env_openxr.Prepend(CPPEXTPATH=["#thirdparty/glad"])
# FIXME: Review what needs to be set for Android and macOS.
# FreeBSD uses non-standard getenv functions.
if not sys.platform.startswith("freebsd"):
env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
elif env["platform"] == "macos":
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_APPLE"])
# There does not seem to be a XR_USE_PLATFORM_XYZ for Apple
# may need to check and set:
# - XR_USE_TIMESPEC
if env["builtin_openxr"]:
thirdparty_dir = "#thirdparty/openxr"
env_openxr.Prepend(
CPPEXTPATH=[
thirdparty_dir,
thirdparty_dir + "/include",
thirdparty_dir + "/src",
thirdparty_dir + "/src/common",
thirdparty_dir + "/src/external/jsoncpp/include",
]
)
env_thirdparty = env_openxr.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
if env["disable_exceptions"]:
env_thirdparty.AppendUnique(CPPDEFINES=["XRLOADER_DISABLE_EXCEPTION_HANDLING", ("JSON_USE_EXCEPTION", 0)])
env_thirdparty.Append(CPPEXTPATH=[thirdparty_dir + "/src/loader"])
# add in external jsoncpp dependency
thirdparty_jsoncpp_dir = thirdparty_dir + "/src/external/jsoncpp/src/lib_json/"
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_reader.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_value.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_writer.cpp")
# add in load
if env["platform"] != "android":
# On Android the openxr_loader is provided by separate plugins for each device
# Build the engine using object files
khrloader_obj = []
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table_core.c")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
env.modules_sources += khrloader_obj
env.modules_sources += thirdparty_obj
# Godot source files
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj
Export("env_openxr")
SConscript("action_map/SCsub")
SConscript("extensions/SCsub")
SConscript("scene/SCsub")
if env.editor_build:
SConscript("editor/SCsub")
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj

View File

@@ -0,0 +1,121 @@
/**************************************************************************/
/* openxr_action.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_action.h"
#include "openxr_action_set.h"
void OpenXRAction::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_localized_name", "localized_name"), &OpenXRAction::set_localized_name);
ClassDB::bind_method(D_METHOD("get_localized_name"), &OpenXRAction::get_localized_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "localized_name"), "set_localized_name", "get_localized_name");
ClassDB::bind_method(D_METHOD("set_action_type", "action_type"), &OpenXRAction::set_action_type);
ClassDB::bind_method(D_METHOD("get_action_type"), &OpenXRAction::get_action_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_type", PROPERTY_HINT_ENUM, "bool,float,vector2,pose"), "set_action_type", "get_action_type");
ClassDB::bind_method(D_METHOD("set_toplevel_paths", "toplevel_paths"), &OpenXRAction::set_toplevel_paths);
ClassDB::bind_method(D_METHOD("get_toplevel_paths"), &OpenXRAction::get_toplevel_paths);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "toplevel_paths"), "set_toplevel_paths", "get_toplevel_paths");
BIND_ENUM_CONSTANT(OPENXR_ACTION_BOOL);
BIND_ENUM_CONSTANT(OPENXR_ACTION_FLOAT);
BIND_ENUM_CONSTANT(OPENXR_ACTION_VECTOR2);
BIND_ENUM_CONSTANT(OPENXR_ACTION_POSE);
}
Ref<OpenXRAction> OpenXRAction::new_action(const char *p_name, const char *p_localized_name, const ActionType p_action_type, const char *p_toplevel_paths) {
// This is a helper function to help build our default action sets
Ref<OpenXRAction> action;
action.instantiate();
action->set_name(String(p_name));
action->set_localized_name(String(p_localized_name));
action->set_action_type(p_action_type);
action->parse_toplevel_paths(String(p_toplevel_paths));
return action;
}
String OpenXRAction::get_name_with_set() const {
String action_name = get_name();
if (action_set != nullptr) {
action_name = action_set->get_name() + "/" + action_name;
}
return action_name;
}
void OpenXRAction::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
emit_changed();
}
String OpenXRAction::get_localized_name() const {
return localized_name;
}
void OpenXRAction::set_action_type(const OpenXRAction::ActionType p_action_type) {
action_type = p_action_type;
emit_changed();
}
OpenXRAction::ActionType OpenXRAction::get_action_type() const {
return action_type;
}
void OpenXRAction::set_toplevel_paths(const PackedStringArray p_toplevel_paths) {
toplevel_paths = p_toplevel_paths;
emit_changed();
}
PackedStringArray OpenXRAction::get_toplevel_paths() const {
return toplevel_paths;
}
void OpenXRAction::add_toplevel_path(const String p_toplevel_path) {
if (!toplevel_paths.has(p_toplevel_path)) {
toplevel_paths.push_back(p_toplevel_path);
emit_changed();
}
}
void OpenXRAction::rem_toplevel_path(const String p_toplevel_path) {
if (toplevel_paths.has(p_toplevel_path)) {
toplevel_paths.erase(p_toplevel_path);
emit_changed();
}
}
void OpenXRAction::parse_toplevel_paths(const String p_toplevel_paths) {
toplevel_paths = p_toplevel_paths.split(",", false);
emit_changed();
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* openxr_action.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/resource.h"
class OpenXRActionSet;
class OpenXRAction : public Resource {
GDCLASS(OpenXRAction, Resource);
public:
enum ActionType {
OPENXR_ACTION_BOOL,
OPENXR_ACTION_FLOAT,
OPENXR_ACTION_VECTOR2,
OPENXR_ACTION_POSE,
OPENXR_ACTION_HAPTIC,
OPENXR_ACTION_MAX
};
private:
String localized_name;
ActionType action_type = OPENXR_ACTION_FLOAT;
PackedStringArray toplevel_paths;
protected:
friend class OpenXRActionSet;
OpenXRActionSet *action_set = nullptr; // action belongs to this action set.
static void _bind_methods();
public:
static Ref<OpenXRAction> new_action(const char *p_name, const char *p_localized_name, const ActionType p_action_type, const char *p_toplevel_paths); // Helper function to add and configure an action
OpenXRActionSet *get_action_set() const { return action_set; } // Get the action set this action belongs to
String get_name_with_set() const; // Retrieve the name of this action as <action_set>/<action>
void set_localized_name(const String p_localized_name); // Set the localized name of this action
String get_localized_name() const; // Get the localized name of this action
void set_action_type(const ActionType p_action_type); // Set the type of this action
ActionType get_action_type() const; // Get the type of this action
void set_toplevel_paths(const PackedStringArray p_toplevel_paths); // Set the toplevel paths of this action
PackedStringArray get_toplevel_paths() const; // Get the toplevel paths of this action
void add_toplevel_path(const String p_toplevel_path); // Add a top level path to this action
void rem_toplevel_path(const String p_toplevel_path); // Remove a toplevel path from this action
void parse_toplevel_paths(const String p_toplevel_paths); // Parse and set the top level paths from a comma separated string
};
VARIANT_ENUM_CAST(OpenXRAction::ActionType);

View File

@@ -0,0 +1,636 @@
/**************************************************************************/
/* openxr_action_map.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_action_map.h"
void OpenXRActionMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_action_sets", "action_sets"), &OpenXRActionMap::set_action_sets);
ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRActionMap::get_action_sets);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "action_sets", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRActionSet", PROPERTY_USAGE_NO_EDITOR), "set_action_sets", "get_action_sets");
ClassDB::bind_method(D_METHOD("get_action_set_count"), &OpenXRActionMap::get_action_set_count);
ClassDB::bind_method(D_METHOD("find_action_set", "name"), &OpenXRActionMap::find_action_set);
ClassDB::bind_method(D_METHOD("get_action_set", "idx"), &OpenXRActionMap::get_action_set);
ClassDB::bind_method(D_METHOD("add_action_set", "action_set"), &OpenXRActionMap::add_action_set);
ClassDB::bind_method(D_METHOD("remove_action_set", "action_set"), &OpenXRActionMap::remove_action_set);
ClassDB::bind_method(D_METHOD("set_interaction_profiles", "interaction_profiles"), &OpenXRActionMap::set_interaction_profiles);
ClassDB::bind_method(D_METHOD("get_interaction_profiles"), &OpenXRActionMap::get_interaction_profiles);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "interaction_profiles", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRInteractionProfile", PROPERTY_USAGE_NO_EDITOR), "set_interaction_profiles", "get_interaction_profiles");
ClassDB::bind_method(D_METHOD("get_interaction_profile_count"), &OpenXRActionMap::get_interaction_profile_count);
ClassDB::bind_method(D_METHOD("find_interaction_profile", "name"), &OpenXRActionMap::find_interaction_profile);
ClassDB::bind_method(D_METHOD("get_interaction_profile", "idx"), &OpenXRActionMap::get_interaction_profile);
ClassDB::bind_method(D_METHOD("add_interaction_profile", "interaction_profile"), &OpenXRActionMap::add_interaction_profile);
ClassDB::bind_method(D_METHOD("remove_interaction_profile", "interaction_profile"), &OpenXRActionMap::remove_interaction_profile);
ClassDB::bind_method(D_METHOD("create_default_action_sets"), &OpenXRActionMap::create_default_action_sets);
}
void OpenXRActionMap::set_action_sets(Array p_action_sets) {
action_sets.clear();
for (int i = 0; i < p_action_sets.size(); i++) {
Ref<OpenXRActionSet> action_set = p_action_sets[i];
if (action_set.is_valid() && !action_sets.has(action_set)) {
action_sets.push_back(action_set);
}
}
}
Array OpenXRActionMap::get_action_sets() const {
return action_sets;
}
int OpenXRActionMap::get_action_set_count() const {
return action_sets.size();
}
Ref<OpenXRActionSet> OpenXRActionMap::find_action_set(String p_name) const {
for (int i = 0; i < action_sets.size(); i++) {
Ref<OpenXRActionSet> action_set = action_sets[i];
if (action_set->get_name() == p_name) {
return action_set;
}
}
return Ref<OpenXRActionSet>();
}
Ref<OpenXRActionSet> OpenXRActionMap::get_action_set(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, action_sets.size(), Ref<OpenXRActionSet>());
return action_sets[p_idx];
}
void OpenXRActionMap::add_action_set(Ref<OpenXRActionSet> p_action_set) {
ERR_FAIL_COND(p_action_set.is_null());
if (!action_sets.has(p_action_set)) {
action_sets.push_back(p_action_set);
emit_changed();
}
}
void OpenXRActionMap::remove_action_set(Ref<OpenXRActionSet> p_action_set) {
int idx = action_sets.find(p_action_set);
if (idx != -1) {
action_sets.remove_at(idx);
emit_changed();
}
}
void OpenXRActionMap::clear_interaction_profiles() {
if (interaction_profiles.is_empty()) {
return;
}
// Interaction profiles held within our action map set should be released and destroyed but just in case they are still used some where else.
for (Ref<OpenXRInteractionProfile> interaction_profile : interaction_profiles) {
interaction_profile->action_map = nullptr;
}
interaction_profiles.clear();
emit_changed();
}
void OpenXRActionMap::set_interaction_profiles(Array p_interaction_profiles) {
clear_interaction_profiles();
for (const Variant &interaction_profile : p_interaction_profiles) {
// Add them anew so we verify our interaction profile pointer.
add_interaction_profile(interaction_profile);
}
}
Array OpenXRActionMap::get_interaction_profiles() const {
return interaction_profiles;
}
int OpenXRActionMap::get_interaction_profile_count() const {
return interaction_profiles.size();
}
Ref<OpenXRInteractionProfile> OpenXRActionMap::find_interaction_profile(String p_path) const {
for (Ref<OpenXRInteractionProfile> interaction_profile : interaction_profiles) {
if (interaction_profile->get_interaction_profile_path() == p_path) {
return interaction_profile;
}
}
return Ref<OpenXRInteractionProfile>();
}
Ref<OpenXRInteractionProfile> OpenXRActionMap::get_interaction_profile(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, interaction_profiles.size(), Ref<OpenXRInteractionProfile>());
return interaction_profiles[p_idx];
}
void OpenXRActionMap::add_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile) {
ERR_FAIL_COND(p_interaction_profile.is_null());
if (!interaction_profiles.has(p_interaction_profile)) {
if (p_interaction_profile->action_map && p_interaction_profile->action_map != this) {
// Interaction profiles should only relate to our action map.
p_interaction_profile->action_map->remove_interaction_profile(p_interaction_profile);
}
p_interaction_profile->action_map = this;
interaction_profiles.push_back(p_interaction_profile);
emit_changed();
}
}
void OpenXRActionMap::remove_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile) {
int idx = interaction_profiles.find(p_interaction_profile);
if (idx != -1) {
interaction_profiles.remove_at(idx);
ERR_FAIL_COND_MSG(p_interaction_profile->action_map != this, "Removing interaction profile that belongs to this action map but had incorrect action map pointer."); // This should never happen!
p_interaction_profile->action_map = nullptr;
emit_changed();
}
}
void OpenXRActionMap::create_default_action_sets() {
// Note:
// - if you make changes here make sure to delete your default_action_map.tres file of it will load an old version.
// - our palm pose is only available if the relevant extension is supported,
// we still want it to be part of our action map as we may deploy the same game to platforms that do and don't support it.
// - the same applies for interaction profiles that are only supported if the relevant extension is supported.
// Create our Godot action set.
Ref<OpenXRActionSet> action_set = OpenXRActionSet::new_action_set("godot", "Godot action set");
add_action_set(action_set);
// Create our actions.
Ref<OpenXRAction> trigger = action_set->add_new_action("trigger", "Trigger", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> trigger_click = action_set->add_new_action("trigger_click", "Trigger click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> trigger_touch = action_set->add_new_action("trigger_touch", "Trigger touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_force = action_set->add_new_action("grip_force", "Grip force", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary_touch = action_set->add_new_action("primary_touch", "Primary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> secondary = action_set->add_new_action("secondary", "Secondary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> secondary_click = action_set->add_new_action("secondary_click", "Secondary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> secondary_touch = action_set->add_new_action("secondary_touch", "Secondary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> menu_button = action_set->add_new_action("menu_button", "Menu button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> select_button = action_set->add_new_action("select_button", "Select button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> ax_button = action_set->add_new_action("ax_button", "A/X button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> ax_touch = action_set->add_new_action("ax_touch", "A/X touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_button = action_set->add_new_action("by_button", "B/Y button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_touch = action_set->add_new_action("by_touch", "B/Y touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE,
"/user/hand/left,"
"/user/hand/right,"
// "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot,"
"/user/vive_tracker_htcx/role/right_foot,"
"/user/vive_tracker_htcx/role/left_shoulder,"
"/user/vive_tracker_htcx/role/right_shoulder,"
"/user/vive_tracker_htcx/role/left_elbow,"
"/user/vive_tracker_htcx/role/right_elbow,"
"/user/vive_tracker_htcx/role/left_knee,"
"/user/vive_tracker_htcx/role/right_knee,"
"/user/vive_tracker_htcx/role/waist,"
"/user/vive_tracker_htcx/role/chest,"
"/user/vive_tracker_htcx/role/camera,"
"/user/vive_tracker_htcx/role/keyboard,"
"/user/vive_tracker_htcx/role/left_wrist,"
"/user/vive_tracker_htcx/role/right_wrist,"
"/user/vive_tracker_htcx/role/left_ankle,"
"/user/vive_tracker_htcx/role/right_ankle,"
"/user/eyes_ext");
Ref<OpenXRAction> aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> palm_pose = action_set->add_new_action("palm_pose", "Palm pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC,
"/user/hand/left,"
"/user/hand/right,"
// "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot,"
"/user/vive_tracker_htcx/role/right_foot,"
"/user/vive_tracker_htcx/role/left_shoulder,"
"/user/vive_tracker_htcx/role/right_shoulder,"
"/user/vive_tracker_htcx/role/left_elbow,"
"/user/vive_tracker_htcx/role/right_elbow,"
"/user/vive_tracker_htcx/role/left_knee,"
"/user/vive_tracker_htcx/role/right_knee,"
"/user/vive_tracker_htcx/role/waist,"
"/user/vive_tracker_htcx/role/chest,"
"/user/vive_tracker_htcx/role/camera,"
"/user/vive_tracker_htcx/role/keyboard,"
"/user/vive_tracker_htcx/role/left_wrist,"
"/user/vive_tracker_htcx/role/right_wrist,"
"/user/vive_tracker_htcx/role/left_ankle,"
"/user/vive_tracker_htcx/role/right_ankle");
// Create our interaction profiles.
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/select/click,/user/hand/right/input/select/click");
// generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Vive controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
// wmr controller has no a/b/x/y buttons.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our vive controller is our trackpad.
profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
// vive controllers have no secondary input.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our WMR controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// wmr controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// wmr controller has no a/b/x/y buttons.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // OpenXR will convert float to bool.
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our wmr controller is our thumbstick, no touch.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
// secondary on our wmr controller is our trackpad.
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Meta touch controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// touch controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available.
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean.
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
// primary on our touch controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// touch controller has no secondary input.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Pico 4 controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/bytedance/pico4_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean.
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
// primary on our pico controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// pico controller has no secondary input.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Valve index controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// index controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers.
profile->add_new_binding(ax_touch, "/user/hand/left/input/a/touch,/user/hand/right/input/a/touch");
profile->add_new_binding(by_button, "/user/hand/left/input/b/click,/user/hand/right/input/b/click"); // b on both controllers.
profile->add_new_binding(by_touch, "/user/hand/left/input/b/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion.
profile->add_new_binding(grip_force, "/user/hand/left/input/squeeze/force,/user/hand/right/input/squeeze/force"); // grip force seems to be unique to the Valve Index.
// primary on our index controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// secondary on our index controller is our trackpad.
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/force,/user/hand/right/input/trackpad/force"); // not sure if this will work but doesn't seem to support click...
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our HP MR controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// hpmr controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// hpmr controllers only register click, not touch, on our a/b/x/y buttons.
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
// primary on our hpmr controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
// No secondary on our hpmr controller.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Samsung Odyssey controller profile,
// Note that this controller is only identified specifically on WMR, on SteamVR this is identified as a normal WMR controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/samsung/odyssey_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// Odyssey controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// Odyssey controller has no a/b/x/y buttons.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our Odyssey controller is our thumbstick, no touch.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
// secondary on our Odyssey controller is our trackpad.
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Vive Cosmos controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select.
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our Cosmos controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// No secondary on our cosmos controller.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Vive Focus 3 controller.
// Note, Vive Focus 3 currently is not yet supported as a stand alone device
// however HTC currently has a beta OpenXR runtime in testing we may support in the near future.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select.
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our Focus 3 controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// We only have a thumb rest.
profile->add_new_binding(secondary_touch, "/user/hand/left/input/thumbrest/touch,/user/hand/right/input/thumbrest/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Huawei controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/home/click,/user/hand/right/input/home/click");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
// primary on our Huawei controller is our trackpad.
profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our HTC Vive tracker profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_tracker_htcx");
profile->add_new_binding(default_pose,
// "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_foot/input/grip/pose,"
"/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose,"
"/user/vive_tracker_htcx/role/left_elbow/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_elbow/input/grip/pose,"
"/user/vive_tracker_htcx/role/left_knee/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_knee/input/grip/pose,"
"/user/vive_tracker_htcx/role/waist/input/grip/pose,"
"/user/vive_tracker_htcx/role/chest/input/grip/pose,"
"/user/vive_tracker_htcx/role/camera/input/grip/pose,"
"/user/vive_tracker_htcx/role/keyboard/input/grip/pose,"
"/user/vive_tracker_htcx/role/left_wrist/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_wrist/input/grip/pose,"
"/user/vive_tracker_htcx/role/left_ankle/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_ankle/input/grip/pose");
profile->add_new_binding(haptic,
// "/user/vive_tracker_htcx/role/handheld_object/output/haptic," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot/output/haptic,"
"/user/vive_tracker_htcx/role/right_foot/output/haptic,"
"/user/vive_tracker_htcx/role/left_shoulder/output/haptic,"
"/user/vive_tracker_htcx/role/right_shoulder/output/haptic,"
"/user/vive_tracker_htcx/role/left_elbow/output/haptic,"
"/user/vive_tracker_htcx/role/right_elbow/output/haptic,"
"/user/vive_tracker_htcx/role/left_knee/output/haptic,"
"/user/vive_tracker_htcx/role/right_knee/output/haptic,"
"/user/vive_tracker_htcx/role/waist/output/haptic,"
"/user/vive_tracker_htcx/role/chest/output/haptic,"
"/user/vive_tracker_htcx/role/camera/output/haptic,"
"/user/vive_tracker_htcx/role/keyboard/output/haptic,"
"/user/vive_tracker_htcx/role/left_wrist/output/haptic,"
"/user/vive_tracker_htcx/role/right_wrist/output/haptic,"
"/user/vive_tracker_htcx/role/left_ankle/output/haptic,"
"/user/vive_tracker_htcx/role/right_ankle/output/haptic");
add_interaction_profile(profile);
// Create our eye gaze interaction profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/ext/eye_gaze_interaction");
profile->add_new_binding(default_pose, "/user/eyes_ext/input/gaze_ext/pose");
add_interaction_profile(profile);
// Create our hand interaction profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/ext/hand_interaction_ext");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// Use pinch as primary.
profile->add_new_binding(primary, "/user/hand/left/input/pinch_ext/value,/user/hand/right/input/pinch_ext/value");
profile->add_new_binding(primary_click, "/user/hand/left/input/pinch_ext/ready_ext,/user/hand/right/input/pinch_ext/ready_ext");
// Use activation as secondary.
profile->add_new_binding(secondary, "/user/hand/left/input/aim_activate_ext/value,/user/hand/right/input/aim_activate_ext/value");
profile->add_new_binding(secondary_click, "/user/hand/left/input/aim_activate_ext/ready_ext,/user/hand/right/input/aim_activate_ext/ready_ext");
// We link grasp to our grip.
profile->add_new_binding(grip, "/user/hand/left/input/grasp_ext/value,/user/hand/right/input/grasp_ext/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/grasp_ext/ready_ext,/user/hand/right/input/grasp_ext/ready_ext");
add_interaction_profile(profile);
}
void OpenXRActionMap::create_editor_action_sets() {
// TODO implement
}
Ref<OpenXRAction> OpenXRActionMap::get_action(const String p_path) const {
PackedStringArray paths = p_path.split("/", false);
ERR_FAIL_COND_V(paths.size() != 2, Ref<OpenXRAction>());
Ref<OpenXRActionSet> action_set = find_action_set(paths[0]);
if (action_set.is_valid()) {
return action_set->get_action(paths[1]);
}
return Ref<OpenXRAction>();
}
void OpenXRActionMap::remove_action(const String p_path, bool p_remove_interaction_profiles) {
Ref<OpenXRAction> action = get_action(p_path);
if (action.is_valid()) {
for (Ref<OpenXRInteractionProfile> interaction_profile : interaction_profiles) {
if (p_remove_interaction_profiles) {
// Remove any bindings for this action
interaction_profile->remove_binding_for_action(action);
} else {
ERR_FAIL_COND(interaction_profile->has_binding_for_action(action));
}
}
OpenXRActionSet *action_set = action->get_action_set();
if (action_set != nullptr) {
// Remove the action from this action set
action_set->remove_action(action);
}
}
}
PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p_action) {
PackedStringArray arr;
for (Ref<OpenXRInteractionProfile> ip : interaction_profiles) {
const OpenXRInteractionProfileMetadata::InteractionProfile *profile = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(ip->get_interaction_profile_path());
if (profile != nullptr) {
Vector<Ref<OpenXRIPBinding>> bindings = ip->get_bindings_for_action(p_action);
for (const Ref<OpenXRIPBinding> &binding : bindings) {
String binding_path = binding->get_binding_path();
const OpenXRInteractionProfileMetadata::IOPath *io_path = profile->get_io_path(binding_path);
if (io_path != nullptr) {
String top_path = io_path->top_level_path;
if (!arr.has(top_path)) {
arr.push_back(top_path);
}
}
}
}
}
// print_line("Toplevel paths for", p_action->get_name_with_set(), "are", arr);
return arr;
}
OpenXRActionMap::~OpenXRActionMap() {
action_sets.clear();
clear_interaction_profiles();
}

View File

@@ -0,0 +1,80 @@
/**************************************************************************/
/* openxr_action_map.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_action.h"
#include "openxr_action_set.h"
#include "openxr_interaction_profile.h"
#include "core/io/resource.h"
class OpenXRActionMap : public Resource {
GDCLASS(OpenXRActionMap, Resource);
private:
Array action_sets;
Array interaction_profiles;
protected:
static void _bind_methods();
public:
void set_action_sets(Array p_action_sets); // Set our actions sets by providing an array with action sets (for loading from resource)
Array get_action_sets() const; // Get our action sets as an array (for saving to resource)
int get_action_set_count() const; // Retrieve the number of action sets we have
Ref<OpenXRActionSet> find_action_set(String p_name) const; // Find an action set by name
Ref<OpenXRActionSet> get_action_set(int p_idx) const; // Retrieve an action set by index
void add_action_set(Ref<OpenXRActionSet> p_action_set); // Add an action set to our action map
void remove_action_set(Ref<OpenXRActionSet> p_action_set); // Remove an action set from our action map
void clear_interaction_profiles(); // Remove all our interaction profiles
void set_interaction_profiles(Array p_interaction_profiles); // Set our interaction profiles by providing an array (for loading from resource)
Array get_interaction_profiles() const; // Get our interaction profiles as an array (for saving to resource)
int get_interaction_profile_count() const; // Retrieve the number of interaction profiles we have
Ref<OpenXRInteractionProfile> find_interaction_profile(String p_path) const; // Find an interaction profile by path
Ref<OpenXRInteractionProfile> get_interaction_profile(int p_idx) const; // Retrieve an interaction profile by index
void add_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile); // Add an interaction profile to our action map
void remove_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile); // remove an interaction profile from our action map
void create_default_action_sets(); // Create our default action set for runtime
void create_editor_action_sets(); // Create our action set for the editor
// Helper functions for editor
Ref<OpenXRAction> get_action(const String p_path) const; // Retrieve an action using <action name>/<action> as our parameter
void remove_action(const String p_path, bool p_remove_interaction_profiles = false); // Remove action from action set, also removes it from interaction profiles
PackedStringArray get_top_level_paths(const Ref<OpenXRAction> p_action); // Determines the top level paths based on where an action is bound in interaction profiles
// TODO add validation to display in the interface that checks if we have action sets with the same name or if we have interaction profiles for the same path
~OpenXRActionMap();
};

View File

@@ -0,0 +1,161 @@
/**************************************************************************/
/* openxr_action_set.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_action_set.h"
void OpenXRActionSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_localized_name", "localized_name"), &OpenXRActionSet::set_localized_name);
ClassDB::bind_method(D_METHOD("get_localized_name"), &OpenXRActionSet::get_localized_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "localized_name"), "set_localized_name", "get_localized_name");
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &OpenXRActionSet::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &OpenXRActionSet::get_priority);
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority"), "set_priority", "get_priority");
ClassDB::bind_method(D_METHOD("get_action_count"), &OpenXRActionSet::get_action_count);
ClassDB::bind_method(D_METHOD("set_actions", "actions"), &OpenXRActionSet::set_actions);
ClassDB::bind_method(D_METHOD("get_actions"), &OpenXRActionSet::get_actions);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "actions", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRAction", PROPERTY_USAGE_NO_EDITOR), "set_actions", "get_actions");
ClassDB::bind_method(D_METHOD("add_action", "action"), &OpenXRActionSet::add_action);
ClassDB::bind_method(D_METHOD("remove_action", "action"), &OpenXRActionSet::remove_action);
}
Ref<OpenXRActionSet> OpenXRActionSet::new_action_set(const char *p_name, const char *p_localized_name, const int p_priority) {
// This is a helper function to help build our default action sets
Ref<OpenXRActionSet> action_set;
action_set.instantiate();
action_set->set_name(String(p_name));
action_set->set_localized_name(p_localized_name);
action_set->set_priority(p_priority);
return action_set;
}
void OpenXRActionSet::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
emit_changed();
}
String OpenXRActionSet::get_localized_name() const {
return localized_name;
}
void OpenXRActionSet::set_priority(const int p_priority) {
priority = p_priority;
emit_changed();
}
int OpenXRActionSet::get_priority() const {
return priority;
}
int OpenXRActionSet::get_action_count() const {
return actions.size();
}
void OpenXRActionSet::clear_actions() {
// Actions held within our action set should be released and destroyed but just in case they are still used some where else
if (actions.is_empty()) {
return;
}
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
action->action_set = nullptr;
}
actions.clear();
emit_changed();
}
void OpenXRActionSet::set_actions(Array p_actions) {
// Any actions not retained in p_actions should be freed automatically, those held within our Array will have be relinked to our action set.
clear_actions();
for (int i = 0; i < p_actions.size(); i++) {
// add them anew so we verify our action_set pointer
add_action(p_actions[i]);
}
}
Array OpenXRActionSet::get_actions() const {
return actions;
}
Ref<OpenXRAction> OpenXRActionSet::get_action(const String p_name) const {
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
if (action->get_name() == p_name) {
return action;
}
}
return Ref<OpenXRAction>();
}
void OpenXRActionSet::add_action(Ref<OpenXRAction> p_action) {
ERR_FAIL_COND(p_action.is_null());
if (!actions.has(p_action)) {
if (p_action->action_set && p_action->action_set != this) {
// action should only relate to our action set
p_action->action_set->remove_action(p_action);
}
p_action->action_set = this;
actions.push_back(p_action);
emit_changed();
}
}
void OpenXRActionSet::remove_action(Ref<OpenXRAction> p_action) {
int idx = actions.find(p_action);
if (idx != -1) {
actions.remove_at(idx);
ERR_FAIL_COND_MSG(p_action->action_set != this, "Removing action that belongs to this action set but had incorrect action set pointer."); // This should never happen!
p_action->action_set = nullptr;
emit_changed();
}
}
Ref<OpenXRAction> OpenXRActionSet::add_new_action(const char *p_name, const char *p_localized_name, const OpenXRAction::ActionType p_action_type, const char *p_toplevel_paths) {
// This is a helper function to help build our default action sets
Ref<OpenXRAction> new_action = OpenXRAction::new_action(p_name, p_localized_name, p_action_type, p_toplevel_paths);
add_action(new_action);
return new_action;
}
OpenXRActionSet::~OpenXRActionSet() {
clear_actions();
}

View File

@@ -0,0 +1,72 @@
/**************************************************************************/
/* openxr_action_set.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_action.h"
#include "core/io/resource.h"
class OpenXRActionSet : public Resource {
GDCLASS(OpenXRActionSet, Resource);
private:
String localized_name;
int priority = 0;
Array actions;
void clear_actions();
protected:
static void _bind_methods();
public:
static Ref<OpenXRActionSet> new_action_set(const char *p_name, const char *p_localized_name, const int p_priority = 0); // Helper function for adding and setting up an action set
void set_localized_name(const String p_localized_name); // Set the localized name of this action set
String get_localized_name() const; // Get the localized name of this action set
void set_priority(const int p_priority); // Set the priority of this action set
int get_priority() const; // Get the priority of this action set
int get_action_count() const; // Retrieve the number of actions in our action set
void set_actions(Array p_actions); // Set our actions using an array of actions (for loading a resource)
Array get_actions() const; // Get our actions as an array (for saving a resource)
Ref<OpenXRAction> get_action(const String p_name) const; // Retrieve an action by name
void add_action(Ref<OpenXRAction> p_action); // Add a new action to our action set
void remove_action(Ref<OpenXRAction> p_action); // remove a action from our action set
Ref<OpenXRAction> add_new_action(const char *p_name, const char *p_localized_name, const OpenXRAction::ActionType p_action_type, const char *p_toplevel_paths); // Helper function for adding and setting up an action
// TODO add validation to display in the interface that checks if we have duplicate action names within our action set
~OpenXRActionSet();
};

View File

@@ -0,0 +1,52 @@
/**************************************************************************/
/* openxr_binding_modifier.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_binding_modifier.h"
void OpenXRBindingModifier::_bind_methods() {
GDVIRTUAL_BIND(_get_description);
GDVIRTUAL_BIND(_get_ip_modification);
}
String OpenXRBindingModifier::get_description() const {
String desc;
if (GDVIRTUAL_CALL(_get_description, desc)) {
return desc;
}
return "";
}
PackedByteArray OpenXRBindingModifier::get_ip_modification() {
PackedByteArray data;
if (GDVIRTUAL_CALL(_get_ip_modification, data)) {
return data;
}
return PackedByteArray();
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************/
/* openxr_binding_modifier.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action.h"
#include "core/io/resource.h"
// Part of implementation for:
// https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_binding_modification
class OpenXRInteractionProfile;
class OpenXRIPBinding;
class OpenXRBindingModifier : public Resource {
GDCLASS(OpenXRBindingModifier, Resource);
protected:
static void _bind_methods();
GDVIRTUAL0RC_REQUIRED(String, _get_description)
GDVIRTUAL0R_REQUIRED(PackedByteArray, _get_ip_modification)
public:
virtual String get_description() const; // Returns the description shown in the editor
virtual PackedByteArray get_ip_modification(); // Return the XrBindingModificationsKHR binding modifier struct data used when calling xrSuggestInteractionProfileBindings
};
class OpenXRIPBindingModifier : public OpenXRBindingModifier {
GDCLASS(OpenXRIPBindingModifier, OpenXRBindingModifier);
protected:
friend class OpenXRInteractionProfile;
OpenXRInteractionProfile *interaction_profile = nullptr; // action belongs to this interaction profile
public:
OpenXRInteractionProfile *get_interaction_profile() const { return interaction_profile; }
};
class OpenXRActionBindingModifier : public OpenXRBindingModifier {
GDCLASS(OpenXRActionBindingModifier, OpenXRBindingModifier);
protected:
friend class OpenXRIPBinding;
OpenXRIPBinding *ip_binding = nullptr; // action belongs to this binding
public:
OpenXRIPBinding *get_ip_binding() const { return ip_binding; }
};

View File

@@ -0,0 +1,93 @@
/**************************************************************************/
/* openxr_haptic_feedback.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_haptic_feedback.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRHapticBase
void OpenXRHapticBase::_bind_methods() {
}
////////////////////////////////////////////////////////////////////////////
// OpenXRHapticVibration
void OpenXRHapticVibration::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_duration", "duration"), &OpenXRHapticVibration::set_duration);
ClassDB::bind_method(D_METHOD("get_duration"), &OpenXRHapticVibration::get_duration);
ADD_PROPERTY(PropertyInfo(Variant::INT, "duration"), "set_duration", "get_duration");
ClassDB::bind_method(D_METHOD("set_frequency", "frequency"), &OpenXRHapticVibration::set_frequency);
ClassDB::bind_method(D_METHOD("get_frequency"), &OpenXRHapticVibration::get_frequency);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency"), "set_frequency", "get_frequency");
ClassDB::bind_method(D_METHOD("set_amplitude", "amplitude"), &OpenXRHapticVibration::set_amplitude);
ClassDB::bind_method(D_METHOD("get_amplitude"), &OpenXRHapticVibration::get_amplitude);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amplitude", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_amplitude", "get_amplitude");
}
void OpenXRHapticVibration::set_duration(int64_t p_duration) {
haptic_vibration.duration = p_duration;
emit_changed();
}
int64_t OpenXRHapticVibration::get_duration() const {
return haptic_vibration.duration;
}
void OpenXRHapticVibration::set_frequency(float p_frequency) {
haptic_vibration.frequency = p_frequency;
emit_changed();
}
float OpenXRHapticVibration::get_frequency() const {
return haptic_vibration.frequency;
}
void OpenXRHapticVibration::set_amplitude(float p_amplitude) {
haptic_vibration.amplitude = p_amplitude;
emit_changed();
}
float OpenXRHapticVibration::get_amplitude() const {
return haptic_vibration.amplitude;
}
const XrHapticBaseHeader *OpenXRHapticVibration::get_xr_structure() {
return (XrHapticBaseHeader *)&haptic_vibration;
}
OpenXRHapticVibration::OpenXRHapticVibration() {
haptic_vibration.type = XR_TYPE_HAPTIC_VIBRATION;
haptic_vibration.next = nullptr;
haptic_vibration.duration = -1;
haptic_vibration.frequency = 0.0;
haptic_vibration.amplitude = 1.0;
}

View File

@@ -0,0 +1,69 @@
/**************************************************************************/
/* openxr_haptic_feedback.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/resource.h"
#include <openxr/openxr.h>
class OpenXRHapticBase : public Resource {
GDCLASS(OpenXRHapticBase, Resource);
private:
protected:
static void _bind_methods();
public:
virtual const XrHapticBaseHeader *get_xr_structure() = 0;
};
class OpenXRHapticVibration : public OpenXRHapticBase {
GDCLASS(OpenXRHapticVibration, OpenXRHapticBase);
private:
XrHapticVibration haptic_vibration;
protected:
static void _bind_methods();
public:
void set_duration(int64_t p_duration);
int64_t get_duration() const;
void set_frequency(float p_frequency);
float get_frequency() const;
void set_amplitude(float p_amplitude);
float get_amplitude() const;
virtual const XrHapticBaseHeader *get_xr_structure() override;
OpenXRHapticVibration();
};

View File

@@ -0,0 +1,431 @@
/**************************************************************************/
/* openxr_interaction_profile.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_interaction_profile.h"
void OpenXRIPBinding::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_action", "action"), &OpenXRIPBinding::set_action);
ClassDB::bind_method(D_METHOD("get_action"), &OpenXRIPBinding::get_action);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "action", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRAction"), "set_action", "get_action");
ClassDB::bind_method(D_METHOD("set_binding_path", "binding_path"), &OpenXRIPBinding::set_binding_path);
ClassDB::bind_method(D_METHOD("get_binding_path"), &OpenXRIPBinding::get_binding_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "binding_path"), "set_binding_path", "get_binding_path");
ClassDB::bind_method(D_METHOD("get_binding_modifier_count"), &OpenXRIPBinding::get_binding_modifier_count);
ClassDB::bind_method(D_METHOD("get_binding_modifier", "index"), &OpenXRIPBinding::get_binding_modifier);
ClassDB::bind_method(D_METHOD("set_binding_modifiers", "binding_modifiers"), &OpenXRIPBinding::set_binding_modifiers);
ClassDB::bind_method(D_METHOD("get_binding_modifiers"), &OpenXRIPBinding::get_binding_modifiers);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "binding_modifiers", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRActionBindingModifier", PROPERTY_USAGE_NO_EDITOR), "set_binding_modifiers", "get_binding_modifiers");
// Deprecated
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_paths", "paths"), &OpenXRIPBinding::set_paths);
ClassDB::bind_method(D_METHOD("get_paths"), &OpenXRIPBinding::get_paths);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_paths", "get_paths");
ClassDB::bind_method(D_METHOD("get_path_count"), &OpenXRIPBinding::get_path_count);
ClassDB::bind_method(D_METHOD("has_path", "path"), &OpenXRIPBinding::has_path);
ClassDB::bind_method(D_METHOD("add_path", "path"), &OpenXRIPBinding::add_path);
ClassDB::bind_method(D_METHOD("remove_path", "path"), &OpenXRIPBinding::remove_path);
#endif // DISABLE_DEPRECATED
}
Ref<OpenXRIPBinding> OpenXRIPBinding::new_binding(const Ref<OpenXRAction> p_action, const String &p_binding_path) {
// This is a helper function to help build our default action sets
Ref<OpenXRIPBinding> binding;
binding.instantiate();
binding->set_action(p_action);
binding->set_binding_path(p_binding_path);
return binding;
}
void OpenXRIPBinding::set_action(const Ref<OpenXRAction> &p_action) {
action = p_action;
emit_changed();
}
Ref<OpenXRAction> OpenXRIPBinding::get_action() const {
return action;
}
void OpenXRIPBinding::set_binding_path(const String &path) {
binding_path = path;
emit_changed();
}
String OpenXRIPBinding::get_binding_path() const {
return binding_path;
}
int OpenXRIPBinding::get_binding_modifier_count() const {
return binding_modifiers.size();
}
Ref<OpenXRActionBindingModifier> OpenXRIPBinding::get_binding_modifier(int p_index) const {
ERR_FAIL_INDEX_V(p_index, binding_modifiers.size(), nullptr);
return binding_modifiers[p_index];
}
void OpenXRIPBinding::clear_binding_modifiers() {
// Binding modifiers held within our interaction profile set should be released and destroyed but just in case they are still used some where else.
if (binding_modifiers.is_empty()) {
return;
}
for (int i = 0; i < binding_modifiers.size(); i++) {
Ref<OpenXRActionBindingModifier> binding_modifier = binding_modifiers[i];
binding_modifier->ip_binding = nullptr;
}
binding_modifiers.clear();
emit_changed();
}
void OpenXRIPBinding::set_binding_modifiers(const Array &p_binding_modifiers) {
clear_binding_modifiers();
// Any binding modifier not retained in p_binding_modifiers should be freed automatically, those held within our Array will have be relinked to our interaction profile.
for (int i = 0; i < p_binding_modifiers.size(); i++) {
// Add them anew so we verify our binding modifier pointer.
add_binding_modifier(p_binding_modifiers[i]);
}
}
Array OpenXRIPBinding::get_binding_modifiers() const {
Array ret;
for (const Ref<OpenXRActionBindingModifier> &binding_modifier : binding_modifiers) {
ret.push_back(binding_modifier);
}
return ret;
}
void OpenXRIPBinding::add_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier) {
ERR_FAIL_COND(p_binding_modifier.is_null());
if (!binding_modifiers.has(p_binding_modifier)) {
if (p_binding_modifier->ip_binding && p_binding_modifier->ip_binding != this) {
// Binding modifier should only relate to our binding.
p_binding_modifier->ip_binding->remove_binding_modifier(p_binding_modifier);
}
p_binding_modifier->ip_binding = this;
binding_modifiers.push_back(p_binding_modifier);
emit_changed();
}
}
void OpenXRIPBinding::remove_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier) {
int idx = binding_modifiers.find(p_binding_modifier);
if (idx != -1) {
binding_modifiers.remove_at(idx);
ERR_FAIL_COND_MSG(p_binding_modifier->ip_binding != this, "Removing binding modifier that belongs to this binding but had incorrect binding pointer."); // This should never happen!
p_binding_modifier->ip_binding = nullptr;
emit_changed();
}
}
#ifndef DISABLE_DEPRECATED
void OpenXRIPBinding::set_paths(const PackedStringArray p_paths) { // Deprecated, but needed for loading old action maps.
// Fallback logic, this should ONLY be called when loading older action maps.
// We'll parse this momentarily and extract individual bindings.
binding_path = "";
for (const String &path : p_paths) {
if (!binding_path.is_empty()) {
binding_path += ",";
}
binding_path += path;
}
}
PackedStringArray OpenXRIPBinding::get_paths() const { // Deprecated, but needed for converting old action maps.
// Fallback logic, return an array.
// If we just loaded an old action map from disc, this will be a comma separated list of actions.
// Once parsed there should be only one path in our array.
PackedStringArray paths = binding_path.split(",", false);
return paths;
}
int OpenXRIPBinding::get_path_count() const { // Deprecated.
// Fallback logic, we only have one entry.
return binding_path.is_empty() ? 0 : 1;
}
bool OpenXRIPBinding::has_path(const String p_path) const { // Deprecated.
// Fallback logic, return true if this is our path.
return binding_path == p_path;
}
void OpenXRIPBinding::add_path(const String p_path) { // Deprecated.
// Fallback logic, only assign first time this is called.
if (binding_path != p_path) {
ERR_FAIL_COND_MSG(!binding_path.is_empty(), "Method add_path has been deprecated. A binding path was already set, create separate binding resources for each path and use set_binding_path instead.");
binding_path = p_path;
emit_changed();
}
}
void OpenXRIPBinding::remove_path(const String p_path) { // Deprecated.
ERR_FAIL_COND_MSG(binding_path != p_path, "Method remove_path has been deprecated. Attempt at removing a different binding path, remove the correct binding record from the interaction profile instead.");
// Fallback logic, clear if this is our path.
binding_path = p_path;
emit_changed();
}
#endif // DISABLE_DEPRECATED
OpenXRIPBinding::~OpenXRIPBinding() {
action.unref();
}
void OpenXRInteractionProfile::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_interaction_profile_path", "interaction_profile_path"), &OpenXRInteractionProfile::set_interaction_profile_path);
ClassDB::bind_method(D_METHOD("get_interaction_profile_path"), &OpenXRInteractionProfile::get_interaction_profile_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "interaction_profile_path"), "set_interaction_profile_path", "get_interaction_profile_path");
ClassDB::bind_method(D_METHOD("get_binding_count"), &OpenXRInteractionProfile::get_binding_count);
ClassDB::bind_method(D_METHOD("get_binding", "index"), &OpenXRInteractionProfile::get_binding);
ClassDB::bind_method(D_METHOD("set_bindings", "bindings"), &OpenXRInteractionProfile::set_bindings);
ClassDB::bind_method(D_METHOD("get_bindings"), &OpenXRInteractionProfile::get_bindings);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bindings", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRIPBinding", PROPERTY_USAGE_NO_EDITOR), "set_bindings", "get_bindings");
ClassDB::bind_method(D_METHOD("get_binding_modifier_count"), &OpenXRInteractionProfile::get_binding_modifier_count);
ClassDB::bind_method(D_METHOD("get_binding_modifier", "index"), &OpenXRInteractionProfile::get_binding_modifier);
ClassDB::bind_method(D_METHOD("set_binding_modifiers", "binding_modifiers"), &OpenXRInteractionProfile::set_binding_modifiers);
ClassDB::bind_method(D_METHOD("get_binding_modifiers"), &OpenXRInteractionProfile::get_binding_modifiers);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "binding_modifiers", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRIPBindingModifier", PROPERTY_USAGE_NO_EDITOR), "set_binding_modifiers", "get_binding_modifiers");
}
Ref<OpenXRInteractionProfile> OpenXRInteractionProfile::new_profile(const char *p_input_profile_path) {
Ref<OpenXRInteractionProfile> profile;
profile.instantiate();
profile->set_interaction_profile_path(String(p_input_profile_path));
return profile;
}
void OpenXRInteractionProfile::set_interaction_profile_path(const String p_input_profile_path) {
OpenXRInteractionProfileMetadata *pmd = OpenXRInteractionProfileMetadata::get_singleton();
if (pmd) {
interaction_profile_path = pmd->check_profile_name(p_input_profile_path);
} else {
// OpenXR module not enabled, ignore checks.
interaction_profile_path = p_input_profile_path;
}
emit_changed();
}
String OpenXRInteractionProfile::get_interaction_profile_path() const {
return interaction_profile_path;
}
int OpenXRInteractionProfile::get_binding_count() const {
return bindings.size();
}
Ref<OpenXRIPBinding> OpenXRInteractionProfile::get_binding(int p_index) const {
ERR_FAIL_INDEX_V(p_index, bindings.size(), Ref<OpenXRIPBinding>());
return bindings[p_index];
}
void OpenXRInteractionProfile::set_bindings(const Array &p_bindings) {
bindings.clear();
for (Ref<OpenXRIPBinding> binding : p_bindings) {
String binding_path = binding->get_binding_path();
if (binding_path.find_char(',') >= 0) {
// Convert old binding approach to new...
add_new_binding(binding->get_action(), binding_path);
} else {
add_binding(binding);
}
}
emit_changed();
}
Array OpenXRInteractionProfile::get_bindings() const {
return bindings;
}
Ref<OpenXRIPBinding> OpenXRInteractionProfile::find_binding(const Ref<OpenXRAction> &p_action, const String &p_binding_path) const {
for (Ref<OpenXRIPBinding> binding : bindings) {
if (binding->get_action() == p_action && binding->get_binding_path() == p_binding_path) {
return binding;
}
}
return Ref<OpenXRIPBinding>();
}
Vector<Ref<OpenXRIPBinding>> OpenXRInteractionProfile::get_bindings_for_action(const Ref<OpenXRAction> &p_action) const {
Vector<Ref<OpenXRIPBinding>> ret_bindings;
for (Ref<OpenXRIPBinding> binding : bindings) {
if (binding->get_action() == p_action) {
ret_bindings.push_back(binding);
}
}
return ret_bindings;
}
void OpenXRInteractionProfile::add_binding(const Ref<OpenXRIPBinding> &p_binding) {
ERR_FAIL_COND(p_binding.is_null());
if (!bindings.has(p_binding)) {
ERR_FAIL_COND_MSG(find_binding(p_binding->get_action(), p_binding->get_binding_path()).is_valid(), "There is already a binding for this action and binding path in this interaction profile.");
bindings.push_back(p_binding);
emit_changed();
}
}
void OpenXRInteractionProfile::remove_binding(const Ref<OpenXRIPBinding> &p_binding) {
int idx = bindings.find(p_binding);
if (idx != -1) {
bindings.remove_at(idx);
emit_changed();
}
}
void OpenXRInteractionProfile::add_new_binding(const Ref<OpenXRAction> &p_action, const String &p_paths) {
// This is a helper function to help build our default action sets
PackedStringArray paths = p_paths.split(",", false);
for (const String &path : paths) {
Ref<OpenXRIPBinding> binding = OpenXRIPBinding::new_binding(p_action, path);
add_binding(binding);
}
}
void OpenXRInteractionProfile::remove_binding_for_action(const Ref<OpenXRAction> &p_action) {
for (int i = bindings.size() - 1; i >= 0; i--) {
Ref<OpenXRIPBinding> binding = bindings[i];
if (binding->get_action() == p_action) {
remove_binding(binding);
}
}
}
bool OpenXRInteractionProfile::has_binding_for_action(const Ref<OpenXRAction> &p_action) {
for (int i = bindings.size() - 1; i >= 0; i--) {
Ref<OpenXRIPBinding> binding = bindings[i];
if (binding->get_action() == p_action) {
return true;
}
}
return false;
}
int OpenXRInteractionProfile::get_binding_modifier_count() const {
return binding_modifiers.size();
}
Ref<OpenXRIPBindingModifier> OpenXRInteractionProfile::get_binding_modifier(int p_index) const {
ERR_FAIL_INDEX_V(p_index, binding_modifiers.size(), nullptr);
return binding_modifiers[p_index];
}
void OpenXRInteractionProfile::clear_binding_modifiers() {
// Binding modifiers held within our interaction profile set should be released and destroyed but just in case they are still used some where else.
if (binding_modifiers.is_empty()) {
return;
}
for (int i = 0; i < binding_modifiers.size(); i++) {
Ref<OpenXRIPBindingModifier> binding_modifier = binding_modifiers[i];
binding_modifier->interaction_profile = nullptr;
}
binding_modifiers.clear();
emit_changed();
}
void OpenXRInteractionProfile::set_binding_modifiers(const Array &p_binding_modifiers) {
clear_binding_modifiers();
// Any binding modifier not retained in p_binding_modifiers should be freed automatically, those held within our Array will have be relinked to our interaction profile.
for (int i = 0; i < p_binding_modifiers.size(); i++) {
// Add them anew so we verify our binding modifier pointer.
add_binding_modifier(p_binding_modifiers[i]);
}
}
Array OpenXRInteractionProfile::get_binding_modifiers() const {
Array ret;
for (const Ref<OpenXRIPBindingModifier> &binding_modifier : binding_modifiers) {
ret.push_back(binding_modifier);
}
return ret;
}
void OpenXRInteractionProfile::add_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier) {
ERR_FAIL_COND(p_binding_modifier.is_null());
if (!binding_modifiers.has(p_binding_modifier)) {
if (p_binding_modifier->interaction_profile && p_binding_modifier->interaction_profile != this) {
// Binding modifier should only relate to our interaction profile.
p_binding_modifier->interaction_profile->remove_binding_modifier(p_binding_modifier);
}
p_binding_modifier->interaction_profile = this;
binding_modifiers.push_back(p_binding_modifier);
emit_changed();
}
}
void OpenXRInteractionProfile::remove_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier) {
int idx = binding_modifiers.find(p_binding_modifier);
if (idx != -1) {
binding_modifiers.remove_at(idx);
ERR_FAIL_COND_MSG(p_binding_modifier->interaction_profile != this, "Removing binding modifier that belongs to this interaction profile but had incorrect interaction profile pointer."); // This should never happen!
p_binding_modifier->interaction_profile = nullptr;
emit_changed();
}
}
OpenXRInteractionProfile::~OpenXRInteractionProfile() {
bindings.clear();
clear_binding_modifiers();
}

View File

@@ -0,0 +1,138 @@
/**************************************************************************/
/* openxr_interaction_profile.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_action.h"
#include "openxr_binding_modifier.h"
#include "openxr_interaction_profile_metadata.h"
#include "core/io/resource.h"
class OpenXRActionMap;
class OpenXRIPBinding : public Resource {
GDCLASS(OpenXRIPBinding, Resource);
private:
Ref<OpenXRAction> action;
String binding_path;
Vector<Ref<OpenXRActionBindingModifier>> binding_modifiers;
protected:
friend class OpenXRActionMap;
OpenXRActionMap *action_map = nullptr;
static void _bind_methods();
public:
static Ref<OpenXRIPBinding> new_binding(const Ref<OpenXRAction> p_action, const String &p_binding_path); // Helper function for adding a new binding.
OpenXRActionMap *get_action_map() { return action_map; } // Return the action map we're a part of.
void set_action(const Ref<OpenXRAction> &p_action); // Set the action for this binding.
Ref<OpenXRAction> get_action() const; // Get the action for this binding.
void set_binding_path(const String &path);
String get_binding_path() const;
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path
Ref<OpenXRActionBindingModifier> get_binding_modifier(int p_index) const;
void clear_binding_modifiers(); // Remove all binding modifiers
void set_binding_modifiers(const Array &p_bindings); // Set the binding modifiers (for loading from a resource)
Array get_binding_modifiers() const; // Get the binding modifiers (for saving to a resource)
void add_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Add a binding modifier object
void remove_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Remove a binding modifier object
// Deprecated.
#ifndef DISABLE_DEPRECATED
void set_paths(const PackedStringArray p_paths); // Set our paths (for loading from resource), needed for loading old action maps.
PackedStringArray get_paths() const; // Get our paths (for saving to resource), needed for converted old action maps.
int get_path_count() const; // Get the number of io paths.
bool has_path(const String p_path) const; // Has this io path.
void add_path(const String p_path); // Add an io path.
void remove_path(const String p_path); // Remove an io path.
#endif // DISABLE_DEPRECATED
// TODO add validation that we can display in the interface that checks if no two paths belong to the same top level path
~OpenXRIPBinding();
};
class OpenXRInteractionProfile : public Resource {
GDCLASS(OpenXRInteractionProfile, Resource);
private:
String interaction_profile_path;
Array bindings;
Vector<Ref<OpenXRIPBindingModifier>> binding_modifiers;
protected:
friend class OpenXRActionMap;
OpenXRActionMap *action_map = nullptr;
static void _bind_methods();
public:
static Ref<OpenXRInteractionProfile> new_profile(const char *p_input_profile_path); // Helper function to create a new interaction profile
OpenXRActionMap *get_action_map() { return action_map; }
void set_interaction_profile_path(const String p_input_profile_path); // Set our input profile path
String get_interaction_profile_path() const; // get our input profile path
int get_binding_count() const; // Retrieve the number of bindings in this profile path
Ref<OpenXRIPBinding> get_binding(int p_index) const;
void set_bindings(const Array &p_bindings); // Set the bindings (for loading from a resource)
Array get_bindings() const; // Get the bindings (for saving to a resource)
Ref<OpenXRIPBinding> find_binding(const Ref<OpenXRAction> &p_action, const String &p_binding_path) const; // Get our binding record
Vector<Ref<OpenXRIPBinding>> get_bindings_for_action(const Ref<OpenXRAction> &p_action) const; // Get our binding record for a given action
void add_binding(const Ref<OpenXRIPBinding> &p_binding); // Add a binding object
void remove_binding(const Ref<OpenXRIPBinding> &p_binding); // Remove a binding object
void add_new_binding(const Ref<OpenXRAction> &p_action, const String &p_paths); // Create a new binding for this profile
void remove_binding_for_action(const Ref<OpenXRAction> &p_action); // Remove all bindings for this action
bool has_binding_for_action(const Ref<OpenXRAction> &p_action); // Returns true if we have a binding for this action
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path
Ref<OpenXRIPBindingModifier> get_binding_modifier(int p_index) const;
void clear_binding_modifiers(); // Remove all binding modifiers
void set_binding_modifiers(const Array &p_bindings); // Set the binding modifiers (for loading from a resource)
Array get_binding_modifiers() const; // Get the binding modifiers (for saving to a resource)
void add_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Add a binding modifier object
void remove_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Remove a binding modifier object
~OpenXRInteractionProfile();
};

View File

@@ -0,0 +1,388 @@
/**************************************************************************/
/* openxr_interaction_profile_metadata.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_interaction_profile_metadata.h"
#include "../openxr_api.h"
OpenXRInteractionProfileMetadata *OpenXRInteractionProfileMetadata::singleton = nullptr;
OpenXRInteractionProfileMetadata::OpenXRInteractionProfileMetadata() {
singleton = this;
_register_core_metadata();
OpenXRAPI::register_extension_metadata();
}
OpenXRInteractionProfileMetadata::~OpenXRInteractionProfileMetadata() {
singleton = nullptr;
}
void OpenXRInteractionProfileMetadata::_bind_methods() {
ClassDB::bind_method(D_METHOD("register_profile_rename", "old_name", "new_name"), &OpenXRInteractionProfileMetadata::register_profile_rename);
ClassDB::bind_method(D_METHOD("register_top_level_path", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetadata::register_top_level_path);
ClassDB::bind_method(D_METHOD("register_interaction_profile", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetadata::register_interaction_profile);
ClassDB::bind_method(D_METHOD("register_io_path", "interaction_profile", "display_name", "toplevel_path", "openxr_path", "openxr_extension_name", "action_type"), &OpenXRInteractionProfileMetadata::register_io_path);
}
void OpenXRInteractionProfileMetadata::register_profile_rename(const String &p_old_name, const String &p_new_name) {
ERR_FAIL_COND(profile_renames.has(p_old_name));
profile_renames[p_old_name] = p_new_name;
}
String OpenXRInteractionProfileMetadata::check_profile_name(const String &p_name) const {
if (profile_renames.has(p_name)) {
return profile_renames[p_name];
}
return p_name;
}
void OpenXRInteractionProfileMetadata::register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) {
ERR_FAIL_COND_MSG(has_top_level_path(p_openxr_path), p_openxr_path + " had already been registered");
TopLevelPath new_toplevel_path = {
p_display_name,
p_openxr_path,
p_openxr_extension_name
};
top_level_paths.push_back(new_toplevel_path);
}
void OpenXRInteractionProfileMetadata::register_interaction_profile(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) {
ERR_FAIL_COND_MSG(has_interaction_profile(p_openxr_path), p_openxr_path + " has already been registered");
InteractionProfile new_profile;
new_profile.display_name = p_display_name;
new_profile.openxr_path = p_openxr_path;
new_profile.openxr_extension_name = p_openxr_extension_name;
interaction_profiles.push_back(new_profile);
}
void OpenXRInteractionProfileMetadata::register_io_path(const String &p_interaction_profile, const String &p_display_name, const String &p_toplevel_path, const String &p_openxr_path, const String &p_openxr_extension_name, OpenXRAction::ActionType p_action_type) {
ERR_FAIL_COND_MSG(!has_interaction_profile(p_interaction_profile), "Unknown interaction profile " + p_interaction_profile);
ERR_FAIL_COND_MSG(!has_top_level_path(p_toplevel_path), "Unknown top level path " + p_toplevel_path);
for (InteractionProfile &interaction_profile : interaction_profiles) {
if (interaction_profile.openxr_path == p_interaction_profile) {
ERR_FAIL_COND_MSG(interaction_profile.has_io_path(p_openxr_path), p_interaction_profile + " already has io path " + p_openxr_path + " registered!");
IOPath new_io_path = {
p_display_name,
p_toplevel_path,
p_openxr_path,
p_openxr_extension_name,
p_action_type
};
interaction_profile.io_paths.push_back(new_io_path);
}
}
}
bool OpenXRInteractionProfileMetadata::has_top_level_path(const String p_openxr_path) const {
for (int i = 0; i < top_level_paths.size(); i++) {
if (top_level_paths[i].openxr_path == p_openxr_path) {
return true;
}
}
return false;
}
String OpenXRInteractionProfileMetadata::get_top_level_name(const String p_openxr_path) const {
for (int i = 0; i < top_level_paths.size(); i++) {
if (top_level_paths[i].openxr_path == p_openxr_path) {
return top_level_paths[i].display_name;
}
}
return String();
}
String OpenXRInteractionProfileMetadata::get_top_level_extension(const String p_openxr_path) const {
for (int i = 0; i < top_level_paths.size(); i++) {
if (top_level_paths[i].openxr_path == p_openxr_path) {
return top_level_paths[i].openxr_extension_name;
}
}
return XR_PATH_UNSUPPORTED_NAME;
}
bool OpenXRInteractionProfileMetadata::has_interaction_profile(const String p_openxr_path) const {
for (const InteractionProfile &interaction_profile : interaction_profiles) {
if (interaction_profile.openxr_path == p_openxr_path) {
return true;
}
}
return false;
}
String OpenXRInteractionProfileMetadata::get_interaction_profile_extension(const String p_openxr_path) const {
for (const InteractionProfile &interaction_profile : interaction_profiles) {
if (interaction_profile.openxr_path == p_openxr_path) {
return interaction_profile.openxr_extension_name;
}
}
return XR_PATH_UNSUPPORTED_NAME;
}
const OpenXRInteractionProfileMetadata::InteractionProfile *OpenXRInteractionProfileMetadata::get_profile(const String p_openxr_path) const {
for (const InteractionProfile &interaction_profile : interaction_profiles) {
if (interaction_profile.openxr_path == p_openxr_path) {
return &interaction_profile;
}
}
return nullptr;
}
bool OpenXRInteractionProfileMetadata::InteractionProfile::has_io_path(const String p_io_path) const {
for (int i = 0; i < io_paths.size(); i++) {
if (io_paths[i].openxr_path == p_io_path) {
return true;
}
}
return false;
}
const OpenXRInteractionProfileMetadata::IOPath *OpenXRInteractionProfileMetadata::InteractionProfile::get_io_path(const String p_io_path) const {
for (int i = 0; i < io_paths.size(); i++) {
if (io_paths[i].openxr_path == p_io_path) {
return &io_paths[i];
}
}
return nullptr;
}
const OpenXRInteractionProfileMetadata::IOPath *OpenXRInteractionProfileMetadata::get_io_path(const String p_interaction_profile, const String p_io_path) const {
const OpenXRInteractionProfileMetadata::InteractionProfile *profile = get_profile(p_interaction_profile);
if (profile != nullptr) {
return profile->get_io_path(p_io_path);
}
return nullptr;
}
PackedStringArray OpenXRInteractionProfileMetadata::get_interaction_profile_paths() const {
PackedStringArray arr;
for (const InteractionProfile &interaction_profile : interaction_profiles) {
arr.push_back(interaction_profile.openxr_path);
}
return arr;
}
void OpenXRInteractionProfileMetadata::_register_core_metadata() {
// Note, currently we add definitions that belong in extensions.
// Extensions are registered when our OpenXRAPI is instantiated
// however this does not happen in the editor.
// We are changing this in another PR, once that is accepted we
// can make the changes to move code into extensions where needed.
// Note that we'll make an exception for XR_EXT_palm_pose, which is used everywhere
// Our core toplevel paths
register_top_level_path("Left hand controller", "/user/hand/left", "");
register_top_level_path("Right hand controller", "/user/hand/right", "");
register_top_level_path("Head", "/user/head", "");
register_top_level_path("Gamepad", "/user/gamepad", "");
register_top_level_path("Treadmill", "/user/treadmill", "");
{ // Fallback Khronos simple controller
const String profile_path = "/interaction_profiles/khr/simple_controller";
register_interaction_profile("Simple controller", profile_path, "");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Select click", user_path, user_path + "/input/select/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
{ // Original HTC Vive wands
const String profile_path = "/interaction_profiles/htc/vive_controller";
register_interaction_profile("HTC Vive wand", profile_path, "");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "System click", user_path, user_path + "/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
{ // Microsoft motion controller (original WMR controllers)
const String profile_path = "/interaction_profiles/microsoft/motion_controller";
register_interaction_profile("MS Motion controller", profile_path, "");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
{ // Meta touch controller (original touch controllers, Quest 1 and Quest 2 controllers)
const String profile_path = "/interaction_profiles/oculus/touch_controller";
register_interaction_profile("Touch controller", profile_path, "");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbrest touch", user_path, user_path + "/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // Valve Index controller
const String profile_path = "/interaction_profiles/valve/index_controller";
register_interaction_profile("Index controller", profile_path, "");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
register_io_path(profile_path, "System click", user_path, user_path + "/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "A click", user_path, user_path + "/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "A touch", user_path, user_path + "/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "B click", user_path, user_path + "/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "B touch", user_path, user_path + "/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Squeeze force", user_path, user_path + "/input/squeeze/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path(profile_path, "Trackpad force", user_path, user_path + "/input/trackpad/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
}

View File

@@ -0,0 +1,122 @@
/**************************************************************************/
/* openxr_interaction_profile_metadata.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
///////////////////////////////////////////////////////////////////////////
// Stores available interaction profile metadata
//
// OpenXR defines and hardcodes all the supported input devices and their
// paths as part of the OpenXR spec. When support for new devices is
// introduced this often starts life as an extension that needs to be enabled
// until it's adopted into the core. As there is no interface to
// enumerate the possibly paths, and that any OpenXR runtime would likely
// limit such enumeration to those input devices supported by that runtime
// there is no other option than to hardcode this.
//
// Note that we need to include paths of our extensions in our action map
// regardless of whether the developers machine supports the extension or
// not. Unsupported paths are filtered out when the action map is submitted
// to the OpenXR runtime.
//
// Note on action type that automatic conversions between boolean and float
// are supported but otherwise action types should match between action and
// input/output paths.
#include "openxr_action.h"
#include "core/object/object.h"
#include "core/templates/hash_map.h"
#define XR_PATH_UNSUPPORTED_NAME "unsupported"
class OpenXRInteractionProfileMetadata : public Object {
GDCLASS(OpenXRInteractionProfileMetadata, Object);
public:
struct TopLevelPath {
String display_name; // User friendly display name (i.e. Left controller)
String openxr_path; // Path in OpenXR (i.e. /user/hand/left)
String openxr_extension_name; // If set, only available if extension is enabled (i.e. XR_HTCX_vive_tracker_interaction)
};
struct IOPath {
String display_name; // User friendly display name (i.e. Grip pose (left controller))
String top_level_path; // Top level path identifying the usage of the device in relation to this input/output
String openxr_path; // Path in OpenXR (i.e. /user/hand/left/input/grip/pose)
String openxr_extension_name; // If set, only available if extension is enabled (i.e. XR_EXT_palm_pose)
OpenXRAction::ActionType action_type; // Type of input/output
};
struct InteractionProfile {
String display_name; // User friendly display name (i.e. Simple controller)
String openxr_path; // Path in OpenXR (i.e. /interaction_profiles/khr/simple_controller)
String openxr_extension_name; // If set, only available if extension is enabled (i.e. XR_HTCX_vive_tracker_interaction)
Vector<IOPath> io_paths; // Inputs and outputs for this device
bool has_io_path(const String p_io_path) const;
const IOPath *get_io_path(const String p_io_path) const;
};
private:
static OpenXRInteractionProfileMetadata *singleton;
HashMap<String, String> profile_renames;
Vector<TopLevelPath> top_level_paths;
Vector<InteractionProfile> interaction_profiles;
void _register_core_metadata();
protected:
static void _bind_methods();
public:
static OpenXRInteractionProfileMetadata *get_singleton() { return singleton; }
OpenXRInteractionProfileMetadata();
~OpenXRInteractionProfileMetadata();
void register_profile_rename(const String &p_old_name, const String &p_new_name);
String check_profile_name(const String &p_name) const;
void register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name);
bool has_top_level_path(const String p_openxr_path) const;
String get_top_level_name(const String p_openxr_path) const;
String get_top_level_extension(const String p_openxr_path) const;
void register_interaction_profile(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name);
bool has_interaction_profile(const String p_openxr_path) const;
String get_interaction_profile_extension(const String p_openxr_path) const;
const InteractionProfile *get_profile(const String p_openxr_path) const;
PackedStringArray get_interaction_profile_paths() const;
void register_io_path(const String &p_interaction_profile, const String &p_display_name, const String &p_toplevel_path, const String &p_openxr_path, const String &p_openxr_extension_name, OpenXRAction::ActionType p_action_type);
const IOPath *get_io_path(const String p_interaction_profile, const String p_io_path) const;
};

50
modules/openxr/config.py Normal file
View File

@@ -0,0 +1,50 @@
def can_build(env, platform):
if platform in ("linuxbsd", "windows", "android", "macos"):
return not env["disable_xr"]
else:
# Not supported on these platforms.
return False
def configure(env):
pass
def get_doc_classes():
return [
"OpenXRInterface",
"OpenXRAction",
"OpenXRActionSet",
"OpenXRActionMap",
"OpenXRAPIExtension",
"OpenXRExtensionWrapper",
"OpenXRExtensionWrapperExtension",
"OpenXRFutureResult",
"OpenXRFutureExtension",
"OpenXRInteractionProfile",
"OpenXRInteractionProfileMetadata",
"OpenXRIPBinding",
"OpenXRHand",
"OpenXRVisibilityMask",
"OpenXRCompositionLayer",
"OpenXRCompositionLayerQuad",
"OpenXRCompositionLayerCylinder",
"OpenXRCompositionLayerEquirect",
"OpenXRBindingModifier",
"OpenXRIPBindingModifier",
"OpenXRActionBindingModifier",
"OpenXRAnalogThresholdModifier",
"OpenXRDpadBindingModifier",
"OpenXRInteractionProfileEditorBase",
"OpenXRInteractionProfileEditor",
"OpenXRBindingModifierEditor",
"OpenXRHapticBase",
"OpenXRHapticVibration",
"OpenXRRenderModelExtension",
"OpenXRRenderModel",
"OpenXRRenderModelManager",
]
def get_doc_path():
return "doc_classes"

View File

@@ -0,0 +1,347 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRAPIExtension" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Makes the OpenXR API available for GDExtension.
</brief_description>
<description>
[OpenXRAPIExtension] makes OpenXR available for GDExtension. It provides the OpenXR API to GDExtension through the [method get_instance_proc_addr] method, and the OpenXR instance through [method get_instance].
It also provides methods for querying the status of OpenXR initialization, and helper methods for ease of use of the API with GDExtension.
</description>
<tutorials>
<link title="XrResult documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html</link>
<link title="XrInstance documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrInstance.html</link>
<link title="XrSpace documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html</link>
<link title="XrSession documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSession.html</link>
<link title="XrSystemId documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html</link>
<link title="xrBeginSession documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrBeginSession.html</link>
<link title="XrPosef documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html</link>
</tutorials>
<methods>
<method name="action_get_handle">
<return type="int" />
<param index="0" name="action" type="RID" />
<description>
Returns the corresponding [code]XrAction[/code] OpenXR handle for the given action RID.
</description>
</method>
<method name="begin_debug_label_region">
<return type="void" />
<param index="0" name="label_name" type="String" />
<description>
Begins a new debug label region, this label will be reported in debug messages for any calls following this until [method end_debug_label_region] is called. Debug labels can be stacked.
</description>
</method>
<method name="can_render">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR is initialized for rendering with an XR viewport.
</description>
</method>
<method name="end_debug_label_region">
<return type="void" />
<description>
Marks the end of a debug label region. Removes the latest debug label region added by calling [method begin_debug_label_region].
</description>
</method>
<method name="find_action">
<return type="RID" />
<param index="0" name="name" type="String" />
<param index="1" name="action_set" type="RID" />
<description>
Returns the [RID] corresponding to an [code]Action[/code] of a matching name, optionally limited to a specified action set.
</description>
</method>
<method name="get_error_string">
<return type="String" />
<param index="0" name="result" type="int" />
<description>
Returns an error string for the given [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url].
</description>
</method>
<method name="get_hand_tracker">
<return type="int" />
<param index="0" name="hand_index" type="int" />
<description>
Returns the corresponding [code]XRHandTrackerEXT[/code] handle for the given hand index value.
</description>
</method>
<method name="get_instance">
<return type="int" />
<description>
Returns the [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrInstance.html]XrInstance[/url] created during the initialization of the OpenXR API.
</description>
</method>
<method name="get_instance_proc_addr">
<return type="int" />
<param index="0" name="name" type="String" />
<description>
Returns the function pointer of the OpenXR function with the specified name, cast to an integer. If the function with the given name does not exist, the method returns [code]0[/code].
[b]Note:[/b] [code]openxr/util.h[/code] contains utility macros for acquiring OpenXR functions, e.g. [code]GDEXTENSION_INIT_XR_FUNC_V(xrCreateAction)[/code].
</description>
</method>
<method name="get_next_frame_time">
<return type="int" />
<description>
Returns the predicted display timing for the next frame.
</description>
</method>
<method name="get_play_space">
<return type="int" />
<description>
Returns the play space, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html]XrSpace[/url] cast to an integer.
</description>
</method>
<method name="get_predicted_display_time">
<return type="int" />
<description>
Returns the predicted display timing for the current frame.
</description>
</method>
<method name="get_projection_layer">
<return type="int" />
<description>
Returns a pointer to the render state's [code]XrCompositionLayerProjection[/code] struct.
[b]Note:[/b] This method should only be called from the rendering thread.
</description>
</method>
<method name="get_render_state_z_far">
<return type="float" />
<description>
Returns the far boundary value of the camera frustum.
[b]Note:[/b] This is only accessible in the render thread.
</description>
</method>
<method name="get_render_state_z_near">
<return type="float" />
<description>
Returns the near boundary value of the camera frustum.
[b]Note:[/b] This is only accessible in the render thread.
</description>
</method>
<method name="get_session">
<return type="int" />
<description>
Returns the OpenXR session, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSession.html]XrSession[/url] cast to an integer.
</description>
</method>
<method name="get_supported_swapchain_formats">
<return type="PackedInt64Array" />
<description>
Returns an array of supported swapchain formats.
</description>
</method>
<method name="get_swapchain_format_name">
<return type="String" />
<param index="0" name="swapchain_format" type="int" />
<description>
Returns the name of the specified swapchain format.
</description>
</method>
<method name="get_system_id">
<return type="int" />
<description>
Returns the id of the system, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer.
</description>
</method>
<method name="insert_debug_label">
<return type="void" />
<param index="0" name="label_name" type="String" />
<description>
Inserts a debug label, this label is reported in any debug message resulting from the OpenXR calls that follows, until any of [method begin_debug_label_region], [method end_debug_label_region], or [method insert_debug_label] is called.
</description>
</method>
<method name="is_environment_blend_mode_alpha_supported">
<return type="int" enum="OpenXRAPIExtension.OpenXRAlphaBlendModeSupport" />
<description>
Returns [enum OpenXRAPIExtension.OpenXRAlphaBlendModeSupport] denoting if [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is really supported, emulated or not supported at all.
</description>
</method>
<method name="is_initialized">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR is initialized.
</description>
</method>
<method name="is_running">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR is running ([url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrBeginSession.html]xrBeginSession[/url] was successfully called and the swapchains were created).
</description>
</method>
<method name="openxr_is_enabled" qualifiers="static">
<return type="bool" />
<param index="0" name="check_run_in_editor" type="bool" />
<description>
Returns [code]true[/code] if OpenXR is enabled.
</description>
</method>
<method name="openxr_swapchain_acquire">
<return type="void" />
<param index="0" name="swapchain" type="int" />
<description>
Acquires the image of the provided swapchain.
</description>
</method>
<method name="openxr_swapchain_create">
<return type="int" />
<param index="0" name="create_flags" type="int" />
<param index="1" name="usage_flags" type="int" />
<param index="2" name="swapchain_format" type="int" />
<param index="3" name="width" type="int" />
<param index="4" name="height" type="int" />
<param index="5" name="sample_count" type="int" />
<param index="6" name="array_size" type="int" />
<description>
Returns a pointer to a new swapchain created using the provided parameters.
</description>
</method>
<method name="openxr_swapchain_free">
<return type="void" />
<param index="0" name="swapchain" type="int" />
<description>
Destroys the provided swapchain and frees it from memory.
</description>
</method>
<method name="openxr_swapchain_get_image">
<return type="RID" />
<param index="0" name="swapchain" type="int" />
<description>
Returns the RID of the provided swapchain's image.
</description>
</method>
<method name="openxr_swapchain_get_swapchain">
<return type="int" />
<param index="0" name="swapchain" type="int" />
<description>
Returns the [code]XrSwapchain[/code] handle of the provided swapchain.
</description>
</method>
<method name="openxr_swapchain_release">
<return type="void" />
<param index="0" name="swapchain" type="int" />
<description>
Releases the image of the provided swapchain.
</description>
</method>
<method name="register_composition_layer_provider">
<return type="void" />
<param index="0" name="extension" type="OpenXRExtensionWrapper" />
<description>
Registers the given extension as a composition layer provider.
</description>
</method>
<method name="register_frame_info_extension">
<return type="void" />
<param index="0" name="extension" type="OpenXRExtensionWrapper" />
<description>
Registers the given extension as modifying frame info via the [method OpenXRExtensionWrapper._set_frame_wait_info_and_get_next_pointer], [method OpenXRExtensionWrapper._set_view_locate_info_and_get_next_pointer], or [method OpenXRExtensionWrapper._set_frame_end_info_and_get_next_pointer] virtual methods.
</description>
</method>
<method name="register_projection_views_extension">
<return type="void" />
<param index="0" name="extension" type="OpenXRExtensionWrapper" />
<description>
Registers the given extension as a provider of additional data structures to projections views.
</description>
</method>
<method name="set_custom_play_space">
<return type="void" />
<param index="0" name="space" type="const void*" />
<description>
Sets the reference space used by OpenXR to the given [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html]XrSpace[/url] (cast to a [code]void *[/code]).
</description>
</method>
<method name="set_emulate_environment_blend_mode_alpha_blend">
<return type="void" />
<param index="0" name="enabled" type="bool" />
<description>
If set to [code]true[/code], an OpenXR extension is loaded which is capable of emulating the [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] blend mode.
</description>
</method>
<method name="set_object_name">
<return type="void" />
<param index="0" name="object_type" type="int" />
<param index="1" name="object_handle" type="int" />
<param index="2" name="object_name" type="String" />
<description>
Set the object name of an OpenXR object, used for debug output. [param object_type] must be a valid OpenXR [code]XrObjectType[/code] enum and [param object_handle] must be a valid OpenXR object handle.
</description>
</method>
<method name="set_render_region">
<return type="void" />
<param index="0" name="render_region" type="Rect2i" />
<description>
Sets the render region to [param render_region], overriding the normal render target's rect.
</description>
</method>
<method name="set_velocity_depth_texture">
<return type="void" />
<param index="0" name="render_target" type="RID" />
<description>
Sets the render target of the velocity depth texture.
</description>
</method>
<method name="set_velocity_target_size">
<return type="void" />
<param index="0" name="target_size" type="Vector2i" />
<description>
Sets the target size of the velocity and velocity depth textures.
</description>
</method>
<method name="set_velocity_texture">
<return type="void" />
<param index="0" name="render_target" type="RID" />
<description>
Sets the render target of the velocity texture.
</description>
</method>
<method name="transform_from_pose">
<return type="Transform3D" />
<param index="0" name="pose" type="const void*" />
<description>
Creates a [Transform3D] from an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html]XrPosef[/url].
</description>
</method>
<method name="unregister_composition_layer_provider">
<return type="void" />
<param index="0" name="extension" type="OpenXRExtensionWrapper" />
<description>
Unregisters the given extension as a composition layer provider.
</description>
</method>
<method name="unregister_frame_info_extension">
<return type="void" />
<param index="0" name="extension" type="OpenXRExtensionWrapper" />
<description>
Unregisters the given extension as modifying frame info.
</description>
</method>
<method name="unregister_projection_views_extension">
<return type="void" />
<param index="0" name="extension" type="OpenXRExtensionWrapper" />
<description>
Unregisters the given extension as a provider of additional data structures to projections views.
</description>
</method>
<method name="xr_result">
<return type="bool" />
<param index="0" name="result" type="int" />
<param index="1" name="format" type="String" />
<param index="2" name="args" type="Array" />
<description>
Returns [code]true[/code] if the provided [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url] (cast to an integer) is successful. Otherwise returns [code]false[/code] and prints the [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url] converted to a string, with the specified additional information.
</description>
</method>
</methods>
<constants>
<constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE" value="0" enum="OpenXRAlphaBlendModeSupport">
Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] isn't supported at all.
</constant>
<constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL" value="1" enum="OpenXRAlphaBlendModeSupport">
Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is really supported.
</constant>
<constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING" value="2" enum="OpenXRAlphaBlendModeSupport">
Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is emulated.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRAction" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR action.
</brief_description>
<description>
This resource defines an OpenXR action. Actions can be used both for inputs (buttons, joysticks, triggers, etc.) and outputs (haptics).
OpenXR performs automatic conversion between action type and input type whenever possible. An analog trigger bound to a boolean action will thus return [code]false[/code] if the trigger is depressed and [code]true[/code] if pressed fully.
Actions are not directly bound to specific devices, instead OpenXR recognizes a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths.
Note that the name of the resource is used to register the action with.
</description>
<tutorials>
</tutorials>
<members>
<member name="action_type" type="int" setter="set_action_type" getter="get_action_type" enum="OpenXRAction.ActionType" default="1">
The type of action.
</member>
<member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default="&quot;&quot;">
The localized description of this action.
</member>
<member name="toplevel_paths" type="PackedStringArray" setter="set_toplevel_paths" getter="get_toplevel_paths" default="PackedStringArray()">
A collections of toplevel paths to which this action can be bound.
</member>
</members>
<constants>
<constant name="OPENXR_ACTION_BOOL" value="0" enum="ActionType">
This action provides a boolean value.
</constant>
<constant name="OPENXR_ACTION_FLOAT" value="1" enum="ActionType">
This action provides a float value between [code]0.0[/code] and [code]1.0[/code] for any analog input such as triggers.
</constant>
<constant name="OPENXR_ACTION_VECTOR2" value="2" enum="ActionType">
This action provides a [Vector2] value and can be bound to embedded trackpads and joysticks.
</constant>
<constant name="OPENXR_ACTION_POSE" value="3" enum="ActionType">
</constant>
</constants>
</class>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRActionBindingModifier" inherits="OpenXRBindingModifier" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Binding modifier that applies on individual actions related to an interaction profile.
</brief_description>
<description>
Binding modifier that applies on individual actions related to an interaction profile.
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRActionMap" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Collection of [OpenXRActionSet] and [OpenXRInteractionProfile] resources for the OpenXR module.
</brief_description>
<description>
OpenXR uses an action system similar to Godots Input map system to bind inputs and outputs on various types of XR controllers to named actions. OpenXR specifies more detail on these inputs and outputs than Godot supports.
Another important distinction is that OpenXR offers no control over these bindings. The bindings we register are suggestions, it is up to the XR runtime to offer users the ability to change these bindings. This allows the XR runtime to fill in the gaps if new hardware becomes available.
The action map therefore needs to be loaded at startup and can't be changed afterwards. This resource is a container for the entire action map.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_action_set">
<return type="void" />
<param index="0" name="action_set" type="OpenXRActionSet" />
<description>
Add an action set.
</description>
</method>
<method name="add_interaction_profile">
<return type="void" />
<param index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Add an interaction profile.
</description>
</method>
<method name="create_default_action_sets">
<return type="void" />
<description>
Setup this action set with our default actions.
</description>
</method>
<method name="find_action_set" qualifiers="const">
<return type="OpenXRActionSet" />
<param index="0" name="name" type="String" />
<description>
Retrieve an action set by name.
</description>
</method>
<method name="find_interaction_profile" qualifiers="const">
<return type="OpenXRInteractionProfile" />
<param index="0" name="name" type="String" />
<description>
Find an interaction profile by its name (path).
</description>
</method>
<method name="get_action_set" qualifiers="const">
<return type="OpenXRActionSet" />
<param index="0" name="idx" type="int" />
<description>
Retrieve the action set at this index.
</description>
</method>
<method name="get_action_set_count" qualifiers="const">
<return type="int" />
<description>
Retrieve the number of actions sets in our action map.
</description>
</method>
<method name="get_interaction_profile" qualifiers="const">
<return type="OpenXRInteractionProfile" />
<param index="0" name="idx" type="int" />
<description>
Get the interaction profile at this index.
</description>
</method>
<method name="get_interaction_profile_count" qualifiers="const">
<return type="int" />
<description>
Retrieve the number of interaction profiles in our action map.
</description>
</method>
<method name="remove_action_set">
<return type="void" />
<param index="0" name="action_set" type="OpenXRActionSet" />
<description>
Remove an action set.
</description>
</method>
<method name="remove_interaction_profile">
<return type="void" />
<param index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Remove an interaction profile.
</description>
</method>
</methods>
<members>
<member name="action_sets" type="Array" setter="set_action_sets" getter="get_action_sets" default="[]">
Collection of [OpenXRActionSet]s that are part of this action map.
</member>
<member name="interaction_profiles" type="Array" setter="set_interaction_profiles" getter="get_interaction_profiles" default="[]">
Collection of [OpenXRInteractionProfile]s that are part of this action map.
</member>
</members>
</class>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRActionSet" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Collection of [OpenXRAction] resources that make up an action set.
</brief_description>
<description>
Action sets in OpenXR define a collection of actions that can be activated in unison. This allows games to easily change between different states that require different inputs or need to reinterpret inputs. For instance we could have an action set that is active when a menu is open, an action set that is active when the player is freely walking around and an action set that is active when the player is controlling a vehicle.
Action sets can contain the same action with the same name, if such action sets are active at the same time the action set with the highest priority defines which binding is active.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_action">
<return type="void" />
<param index="0" name="action" type="OpenXRAction" />
<description>
Add an action to this action set.
</description>
</method>
<method name="get_action_count" qualifiers="const">
<return type="int" />
<description>
Retrieve the number of actions in our action set.
</description>
</method>
<method name="remove_action">
<return type="void" />
<param index="0" name="action" type="OpenXRAction" />
<description>
Remove an action from this action set.
</description>
</method>
</methods>
<members>
<member name="actions" type="Array" setter="set_actions" getter="get_actions" default="[]">
Collection of actions for this action set.
</member>
<member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default="&quot;&quot;">
The localized name of this action set.
</member>
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="0">
The priority for this action set.
</member>
</members>
</class>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRAnalogThresholdModifier" inherits="OpenXRActionBindingModifier" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The analog threshold binding modifier can modify a float input to a boolean input with specified thresholds.
</brief_description>
<description>
The analog threshold binding modifier can modify a float input to a boolean input with specified thresholds.
See [url=https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_VALVE_analog_threshold]XR_VALVE_analog_threshold[/url] for in-depth details.
</description>
<tutorials>
</tutorials>
<members>
<member name="off_haptic" type="OpenXRHapticBase" setter="set_off_haptic" getter="get_off_haptic">
Haptic pulse to emit when the user releases the input.
</member>
<member name="off_threshold" type="float" setter="set_off_threshold" getter="get_off_threshold" default="0.4">
When our input value falls below this, our output becomes [code]false[/code].
</member>
<member name="on_haptic" type="OpenXRHapticBase" setter="set_on_haptic" getter="get_on_haptic">
Haptic pulse to emit when the user presses the input.
</member>
<member name="on_threshold" type="float" setter="set_on_threshold" getter="get_on_threshold" default="0.6">
When our input value is equal or larger than this value, our output becomes [code]true[/code]. It stays [code]true[/code] until it falls under the [member off_threshold] value.
</member>
</members>
</class>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRBindingModifier" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Binding modifier base class.
</brief_description>
<description>
Binding modifier base class. Subclasses implement various modifiers that alter how an OpenXR runtime processes inputs.
</description>
<tutorials>
</tutorials>
<methods>
<method name="_get_description" qualifiers="virtual required const">
<return type="String" />
<description>
Return the description of this class that is used for the title bar of the binding modifier editor.
</description>
</method>
<method name="_get_ip_modification" qualifiers="virtual required">
<return type="PackedByteArray" />
<description>
Returns the data that is sent to OpenXR when submitting the suggested interacting bindings this modifier is a part of.
[b]Note:[/b] This must be data compatible with an [code]XrBindingModificationBaseHeaderKHR[/code] structure.
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRBindingModifierEditor" inherits="PanelContainer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Binding modifier editor.
</brief_description>
<description>
This is the default binding modifier editor used in the OpenXR action map.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_binding_modifier" qualifiers="const">
<return type="OpenXRBindingModifier" />
<description>
Returns the [OpenXRBindingModifier] currently being edited.
</description>
</method>
<method name="setup">
<return type="void" />
<param index="0" name="action_map" type="OpenXRActionMap" />
<param index="1" name="binding_modifier" type="OpenXRBindingModifier" />
<description>
Setup this editor for the provided [param action_map] and [param binding_modifier].
</description>
</method>
</methods>
<members>
<member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="3" />
</members>
<signals>
<signal name="binding_modifier_removed">
<param index="0" name="binding_modifier_editor" type="Object" />
<description>
Signal emitted when the user presses the delete binding modifier button for this modifier.
</description>
</signal>
</signals>
</class>

View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayer" inherits="Node3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The parent class of all OpenXR composition layer nodes.
</brief_description>
<description>
Composition layers allow 2D viewports to be displayed inside of the headset by the XR compositor through special projections that retain their quality. This allows for rendering clear text while keeping the layer at a native resolution.
[b]Note:[/b] If the OpenXR runtime doesn't support the given composition layer type, a fallback mesh can be generated with a [ViewportTexture], in order to emulate the composition layer.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_android_surface">
<return type="JavaObject" />
<description>
Returns a [JavaObject] representing an [code]android.view.Surface[/code] if [member use_android_surface] is enabled and OpenXR has created the surface. Otherwise, this will return [code]null[/code].
[b]Note:[/b] The surface can only be created during an active OpenXR session. So, if [member use_android_surface] is enabled outside of an OpenXR session, it won't be created until a new session fully starts.
</description>
</method>
<method name="intersects_ray" qualifiers="const">
<return type="Vector2" />
<param index="0" name="origin" type="Vector3" />
<param index="1" name="direction" type="Vector3" />
<description>
Returns UV coordinates where the given ray intersects with the composition layer. [param origin] and [param direction] must be in global space.
Returns [code]Vector2(-1.0, -1.0)[/code] if the ray doesn't intersect.
</description>
</method>
<method name="is_natively_supported" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the OpenXR runtime natively supports this composition layer type.
[b]Note:[/b] This will only return an accurate result after the OpenXR session has started.
</description>
</method>
</methods>
<members>
<member name="alpha_blend" type="bool" setter="set_alpha_blend" getter="get_alpha_blend" default="false">
Enables the blending the layer using its alpha channel.
Can be combined with [member Viewport.transparent_bg] to give the layer a transparent background.
</member>
<member name="android_surface_size" type="Vector2i" setter="set_android_surface_size" getter="get_android_surface_size" default="Vector2i(1024, 1024)">
The size of the Android surface to create if [member use_android_surface] is enabled.
</member>
<member name="enable_hole_punch" type="bool" setter="set_enable_hole_punch" getter="get_enable_hole_punch" default="false">
Enables a technique called "hole punching", which allows putting the composition layer behind the main projection layer (i.e. setting [member sort_order] to a negative value) while "punching a hole" through everything rendered by Godot so that the layer is still visible.
This can be used to create the illusion that the composition layer exists in the same 3D space as everything rendered by Godot, allowing objects to appear to pass both behind or in front of the composition layer.
</member>
<member name="layer_viewport" type="SubViewport" setter="set_layer_viewport" getter="get_layer_viewport">
The [SubViewport] to render on the composition layer.
</member>
<member name="sort_order" type="int" setter="set_sort_order" getter="get_sort_order" default="1">
The sort order for this composition layer. Higher numbers will be shown in front of lower numbers.
[b]Note:[/b] This will have no effect if a fallback mesh is being used.
</member>
<member name="swapchain_state_alpha_swizzle" type="int" setter="set_alpha_swizzle" getter="get_alpha_swizzle" enum="OpenXRCompositionLayer.Swizzle" default="3">
The swizzle value for the alpha channel of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_blue_swizzle" type="int" setter="set_blue_swizzle" getter="get_blue_swizzle" enum="OpenXRCompositionLayer.Swizzle" default="2">
The swizzle value for the blue channel of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_border_color" type="Color" setter="set_border_color" getter="get_border_color" default="Color(0, 0, 0, 0)">
The border color of the swapchain state that is used when the wrap mode clamps to the border.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_green_swizzle" type="int" setter="set_green_swizzle" getter="get_green_swizzle" enum="OpenXRCompositionLayer.Swizzle" default="1">
The swizzle value for the green channel of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_horizontal_wrap" type="int" setter="set_horizontal_wrap" getter="get_horizontal_wrap" enum="OpenXRCompositionLayer.Wrap" default="0">
The horizontal wrap mode of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_mag_filter" type="int" setter="set_mag_filter" getter="get_mag_filter" enum="OpenXRCompositionLayer.Filter" default="1">
The magnification filter of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_max_anisotropy" type="float" setter="set_max_anisotropy" getter="get_max_anisotropy" default="1.0">
The max anisotropy of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_min_filter" type="int" setter="set_min_filter" getter="get_min_filter" enum="OpenXRCompositionLayer.Filter" default="1">
The minification filter of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_mipmap_mode" type="int" setter="set_mipmap_mode" getter="get_mipmap_mode" enum="OpenXRCompositionLayer.MipmapMode" default="2">
The mipmap mode of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_red_swizzle" type="int" setter="set_red_swizzle" getter="get_red_swizzle" enum="OpenXRCompositionLayer.Swizzle" default="0">
The swizzle value for the red channel of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="swapchain_state_vertical_wrap" type="int" setter="set_vertical_wrap" getter="get_vertical_wrap" enum="OpenXRCompositionLayer.Wrap" default="0">
The vertical wrap mode of the swapchain state.
[b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions.
</member>
<member name="use_android_surface" type="bool" setter="set_use_android_surface" getter="get_use_android_surface" default="false">
If enabled, an Android surface will be created (with the dimensions from [member android_surface_size]) which will provide the 2D content for the composition layer, rather than using [member layer_viewport].
See [method get_android_surface] for information about how to get the surface so that your application can draw to it.
[b]Note:[/b] This will only work in Android builds.
</member>
</members>
<constants>
<constant name="FILTER_NEAREST" value="0" enum="Filter">
Perform nearest-neighbor filtering when sampling the texture.
</constant>
<constant name="FILTER_LINEAR" value="1" enum="Filter">
Perform linear filtering when sampling the texture.
</constant>
<constant name="FILTER_CUBIC" value="2" enum="Filter">
Perform cubic filtering when sampling the texture.
</constant>
<constant name="MIPMAP_MODE_DISABLED" value="0" enum="MipmapMode">
Disable mipmapping.
[b]Note:[/b] Mipmapping can only be disabled in the Compatibility renderer.
</constant>
<constant name="MIPMAP_MODE_NEAREST" value="1" enum="MipmapMode">
Use the mipmap of the nearest resolution.
</constant>
<constant name="MIPMAP_MODE_LINEAR" value="2" enum="MipmapMode">
Use linear interpolation of the two mipmaps of the nearest resolution.
</constant>
<constant name="WRAP_CLAMP_TO_BORDER" value="0" enum="Wrap">
Clamp the texture to its specified border color.
</constant>
<constant name="WRAP_CLAMP_TO_EDGE" value="1" enum="Wrap">
Clamp the texture to its edge color.
</constant>
<constant name="WRAP_REPEAT" value="2" enum="Wrap">
Repeat the texture infinitely.
</constant>
<constant name="WRAP_MIRRORED_REPEAT" value="3" enum="Wrap">
Repeat the texture infinitely, mirroring it on each repeat.
</constant>
<constant name="WRAP_MIRROR_CLAMP_TO_EDGE" value="4" enum="Wrap">
Mirror the texture once and then clamp the texture to its edge color.
[b]Note:[/b] This wrap mode is not available in the Compatibility renderer.
</constant>
<constant name="SWIZZLE_RED" value="0" enum="Swizzle">
Maps a color channel to the value of the red channel.
</constant>
<constant name="SWIZZLE_GREEN" value="1" enum="Swizzle">
Maps a color channel to the value of the green channel.
</constant>
<constant name="SWIZZLE_BLUE" value="2" enum="Swizzle">
Maps a color channel to the value of the blue channel.
</constant>
<constant name="SWIZZLE_ALPHA" value="3" enum="Swizzle">
Maps a color channel to the value of the alpha channel.
</constant>
<constant name="SWIZZLE_ZERO" value="4" enum="Swizzle">
Maps a color channel to the value of zero.
</constant>
<constant name="SWIZZLE_ONE" value="5" enum="Swizzle">
Maps a color channel to the value of one.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayerCylinder" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as an internal slice of a cylinder.
</brief_description>
<description>
An OpenXR composition layer that allows rendering a [SubViewport] on an internal slice of a cylinder.
</description>
<tutorials>
</tutorials>
<members>
<member name="aspect_ratio" type="float" setter="set_aspect_ratio" getter="get_aspect_ratio" default="1.0">
The aspect ratio of the slice. Used to set the height relative to the width.
</member>
<member name="central_angle" type="float" setter="set_central_angle" getter="get_central_angle" default="1.5707964">
The central angle of the cylinder. Used to set the width.
</member>
<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
The number of segments to use in the fallback mesh.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the cylinder.
</member>
</members>
</class>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayerEquirect" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as an internal slice of a sphere.
</brief_description>
<description>
An OpenXR composition layer that allows rendering a [SubViewport] on an internal slice of a sphere.
</description>
<tutorials>
</tutorials>
<members>
<member name="central_horizontal_angle" type="float" setter="set_central_horizontal_angle" getter="get_central_horizontal_angle" default="1.5707964">
The central horizontal angle of the sphere. Used to set the width.
</member>
<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
The number of segments to use in the fallback mesh.
</member>
<member name="lower_vertical_angle" type="float" setter="set_lower_vertical_angle" getter="get_lower_vertical_angle" default="0.7853982">
The lower vertical angle of the sphere. Used (together with [member upper_vertical_angle]) to set the height.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the sphere.
</member>
<member name="upper_vertical_angle" type="float" setter="set_upper_vertical_angle" getter="get_upper_vertical_angle" default="0.7853982">
The upper vertical angle of the sphere. Used (together with [member lower_vertical_angle]) to set the height.
</member>
</members>
</class>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayerQuad" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as a quad.
</brief_description>
<description>
An OpenXR composition layer that allows rendering a [SubViewport] on a quad.
</description>
<tutorials>
</tutorials>
<members>
<member name="quad_size" type="Vector2" setter="set_quad_size" getter="get_quad_size" default="Vector2(1, 1)">
The dimensions of the quad.
</member>
</members>
</class>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRDpadBindingModifier" inherits="OpenXRIPBindingModifier" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The DPad binding modifier converts an axis input to a dpad output.
</brief_description>
<description>
The DPad binding modifier converts an axis input to a dpad output, emulating a DPad. New input paths for each dpad direction will be added to the interaction profile. When bound to actions the DPad emulation will be activated. You should [b]not[/b] combine dpad inputs with normal inputs in the same action set for the same control, this will result in an error being returned when suggested bindings are submitted to OpenXR.
See [url=https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_dpad_binding]XR_EXT_dpad_binding[/url] for in-depth details.
[b]Note:[/b] If the DPad binding modifier extension is enabled, all dpad binding paths will be available in the action map. Adding the modifier to an interaction profile allows you to further customize the behavior.
</description>
<tutorials>
</tutorials>
<members>
<member name="action_set" type="OpenXRActionSet" setter="set_action_set" getter="get_action_set">
Action set for which this dpad binding modifier is active.
</member>
<member name="center_region" type="float" setter="set_center_region" getter="get_center_region" default="0.1">
Center region in which our center position of our dpad return [code]true[/code].
</member>
<member name="input_path" type="String" setter="set_input_path" getter="get_input_path" default="&quot;&quot;">
Input path for this dpad binding modifier.
</member>
<member name="is_sticky" type="bool" setter="set_is_sticky" getter="get_is_sticky" default="false">
If [code]false[/code], when the joystick enters a new dpad zone this becomes [code]true[/code].
If [code]true[/code], when the joystick remains in active dpad zone, this remains [code]true[/code] even if we overlap with another zone.
</member>
<member name="off_haptic" type="OpenXRHapticBase" setter="set_off_haptic" getter="get_off_haptic">
Haptic pulse to emit when the user releases the input.
</member>
<member name="on_haptic" type="OpenXRHapticBase" setter="set_on_haptic" getter="get_on_haptic">
Haptic pulse to emit when the user presses the input.
</member>
<member name="threshold" type="float" setter="set_threshold" getter="get_threshold" default="0.6">
When our input value is equal or larger than this value, our dpad in that direction becomes [code]true[/code]. It stays [code]true[/code] until it falls under the [member threshold_released] value.
</member>
<member name="threshold_released" type="float" setter="set_threshold_released" getter="get_threshold_released" default="0.4">
When our input value falls below this, our output becomes [code]false[/code].
</member>
<member name="wedge_angle" type="float" setter="set_wedge_angle" getter="get_wedge_angle" default="1.5707964">
The angle of each wedge that identifies the 4 directions of the emulated dpad.
</member>
</members>
</class>

View File

@@ -0,0 +1,311 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRExtensionWrapper" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Allows implementing OpenXR extensions with GDExtension.
</brief_description>
<description>
[OpenXRExtensionWrapper] allows implementing OpenXR extensions with GDExtension. The extension should be registered with [method register_extension_wrapper].
</description>
<tutorials>
</tutorials>
<methods>
<method name="_get_composition_layer" qualifiers="virtual">
<return type="int" />
<param index="0" name="index" type="int" />
<description>
Returns a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct to provide the given composition layer.
This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
</description>
</method>
<method name="_get_composition_layer_count" qualifiers="virtual">
<return type="int" />
<description>
Returns the number of composition layers this extension wrapper provides via [method _get_composition_layer].
This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
</description>
</method>
<method name="_get_composition_layer_order" qualifiers="virtual">
<return type="int" />
<param index="0" name="index" type="int" />
<description>
Returns an integer that will be used to sort the given composition layer provided via [method _get_composition_layer]. Lower numbers will move the layer to the front of the list, and higher numbers to the end. The default projection layer has an order of [code]0[/code], so layers provided by this method should probably be above or below (but not exactly) [code]0[/code].
This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
</description>
</method>
<method name="_get_requested_extensions" qualifiers="virtual">
<return type="Dictionary" />
<description>
Returns a [Dictionary] of OpenXR extensions related to this extension. The [Dictionary] should contain the name of the extension, mapped to a [code]bool *[/code] cast to an integer:
- If the [code]bool *[/code] is a [code]nullptr[/code] this extension is mandatory.
- If the [code]bool *[/code] points to a boolean, the boolean will be updated to [code]true[/code] if the extension is enabled.
</description>
</method>
<method name="_get_suggested_tracker_names" qualifiers="virtual">
<return type="PackedStringArray" />
<description>
Returns a [PackedStringArray] of positional tracker names that are used within the extension wrapper.
</description>
</method>
<method name="_get_viewport_composition_layer_extension_properties" qualifiers="virtual">
<return type="Dictionary[]" />
<description>
Gets an array of [Dictionary]s that represent properties, just like [method Object._get_property_list], that will be added to [OpenXRCompositionLayer] nodes.
</description>
</method>
<method name="_get_viewport_composition_layer_extension_property_defaults" qualifiers="virtual">
<return type="Dictionary" />
<description>
Gets a [Dictionary] containing the default values for the properties returned by [method _get_viewport_composition_layer_extension_properties].
</description>
</method>
<method name="_on_before_instance_created" qualifiers="virtual">
<return type="void" />
<description>
Called before the OpenXR instance is created.
</description>
</method>
<method name="_on_event_polled" qualifiers="virtual">
<return type="bool" />
<param index="0" name="event" type="const void*" />
<description>
Called when there is an OpenXR event to process. When implementing, return [code]true[/code] if the event was handled, return [code]false[/code] otherwise.
</description>
</method>
<method name="_on_instance_created" qualifiers="virtual">
<return type="void" />
<param index="0" name="instance" type="int" />
<description>
Called right after the OpenXR instance is created.
</description>
</method>
<method name="_on_instance_destroyed" qualifiers="virtual">
<return type="void" />
<description>
Called right before the OpenXR instance is destroyed.
</description>
</method>
<method name="_on_main_swapchains_created" qualifiers="virtual">
<return type="void" />
<description>
Called right after the main swapchains are (re)created.
</description>
</method>
<method name="_on_post_draw_viewport" qualifiers="virtual">
<return type="void" />
<param index="0" name="viewport" type="RID" />
<description>
Called right after the given viewport is rendered.
[b]Note:[/b] The draw commands might only be queued at this point, not executed.
</description>
</method>
<method name="_on_pre_draw_viewport" qualifiers="virtual">
<return type="void" />
<param index="0" name="viewport" type="RID" />
<description>
Called right before the given viewport is rendered.
</description>
</method>
<method name="_on_pre_render" qualifiers="virtual">
<return type="void" />
<description>
Called right before the XR viewports begin their rendering step.
</description>
</method>
<method name="_on_process" qualifiers="virtual">
<return type="void" />
<description>
Called as part of the OpenXR process handling. This happens right before general and physics processing steps of the main loop. During this step controller data is queried and made available to game logic.
</description>
</method>
<method name="_on_register_metadata" qualifiers="virtual">
<return type="void" />
<description>
Allows extensions to register additional controller metadata. This function is called even when the OpenXR API is not constructed as the metadata needs to be available to the editor.
Extensions should also provide metadata regardless of whether they are supported on the host system. The controller data is used to setup action maps for users who may have access to the relevant hardware.
</description>
</method>
<method name="_on_session_created" qualifiers="virtual">
<return type="void" />
<param index="0" name="session" type="int" />
<description>
Called right after the OpenXR session is created.
</description>
</method>
<method name="_on_session_destroyed" qualifiers="virtual">
<return type="void" />
<description>
Called right before the OpenXR session is destroyed.
</description>
</method>
<method name="_on_state_exiting" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to exiting.
</description>
</method>
<method name="_on_state_focused" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to focused. This state is the active state when the game runs.
</description>
</method>
<method name="_on_state_idle" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to idle.
</description>
</method>
<method name="_on_state_loss_pending" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to loss pending.
</description>
</method>
<method name="_on_state_ready" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to ready. This means OpenXR is ready to set up the session.
</description>
</method>
<method name="_on_state_stopping" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to stopping.
</description>
</method>
<method name="_on_state_synchronized" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to synchronized. OpenXR also returns to this state when the application loses focus.
</description>
</method>
<method name="_on_state_visible" qualifiers="virtual">
<return type="void" />
<description>
Called when the OpenXR session state is changed to visible. This means OpenXR is now ready to receive frames.
</description>
</method>
<method name="_on_sync_actions" qualifiers="virtual">
<return type="void" />
<description>
Called when OpenXR has performed its action sync.
</description>
</method>
<method name="_on_viewport_composition_layer_destroyed" qualifiers="virtual">
<return type="void" />
<param index="0" name="layer" type="const void*" />
<description>
Called when a composition layer created via [OpenXRCompositionLayer] is destroyed.
[param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct.
</description>
</method>
<method name="_set_android_surface_swapchain_create_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="property_values" type="Dictionary" />
<param index="1" name="next_pointer" type="void*" />
<description>
Adds additional data structures to Android surface swapchains created by [OpenXRCompositionLayer].
[param property_values] contains the values of the properties returned by [method _get_viewport_composition_layer_extension_properties].
</description>
</method>
<method name="_set_frame_end_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures to [code]XrFrameEndInfo[/code].
This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_frame_info_extension].
</description>
</method>
<method name="_set_frame_wait_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures to [code]XrFrameWaitInfo[/code].
This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_frame_info_extension].
</description>
</method>
<method name="_set_hand_joint_locations_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="hand_index" type="int" />
<param index="1" name="next_pointer" type="void*" />
<description>
Adds additional data structures when each hand tracker is created.
</description>
</method>
<method name="_set_instance_create_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures when the OpenXR instance is created.
</description>
</method>
<method name="_set_projection_views_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="view_index" type="int" />
<param index="1" name="next_pointer" type="void*" />
<description>
Adds additional data structures to the projection view of the given [param view_index].
</description>
</method>
<method name="_set_reference_space_create_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="reference_space_type" type="int" />
<param index="1" name="next_pointer" type="void*" />
<description>
Adds additional data structures to [code]XrReferenceSpaceCreateInfo[/code].
</description>
</method>
<method name="_set_session_create_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures when the OpenXR session is created.
</description>
</method>
<method name="_set_swapchain_create_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures when creating OpenXR swapchains.
</description>
</method>
<method name="_set_system_properties_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures when querying OpenXR system abilities.
</description>
</method>
<method name="_set_view_locate_info_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="next_pointer" type="void*" />
<description>
Adds additional data structures to [code]XrViewLocateInfo[/code].
This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_frame_info_extension].
</description>
</method>
<method name="_set_viewport_composition_layer_and_get_next_pointer" qualifiers="virtual">
<return type="int" />
<param index="0" name="layer" type="const void*" />
<param index="1" name="property_values" type="Dictionary" />
<param index="2" name="next_pointer" type="void*" />
<description>
Adds additional data structures to composition layers created by [OpenXRCompositionLayer].
[param property_values] contains the values of the properties returned by [method _get_viewport_composition_layer_extension_properties].
[param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct.
</description>
</method>
<method name="get_openxr_api">
<return type="OpenXRAPIExtension" />
<description>
Returns the created [OpenXRAPIExtension], which can be used to access the OpenXR API.
</description>
</method>
<method name="register_extension_wrapper">
<return type="void" />
<description>
Registers the extension. This should happen at core module initialization level.
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRExtensionWrapperExtension" inherits="OpenXRExtensionWrapper" deprecated="Use [OpenXRExtensionWrapper] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Allows implementing OpenXR extensions with GDExtension.
</brief_description>
<description>
[OpenXRExtensionWrapperExtension] allows implementing OpenXR extensions with GDExtension. The extension should be registered with [method OpenXRExtensionWrapper.register_extension_wrapper].
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRFutureExtension" inherits="OpenXRExtensionWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The OpenXR Future extension allows for asynchronous APIs to be used.
</brief_description>
<description>
This is a support extension in OpenXR that allows other OpenXR extensions to start asynchronous functions and get a callback after this function finishes. It is not intended for consumption within GDScript but can be accessed from GDExtension.
</description>
<tutorials>
</tutorials>
<methods>
<method name="cancel_future">
<return type="void" />
<param index="0" name="future" type="int" />
<description>
Cancels an in-progress future. [param future] must be an [code]XrFutureEXT[/code] value previously returned by an API that started an asynchronous function.
</description>
</method>
<method name="is_active" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if futures are available in the OpenXR runtime used. This function will only return a usable result after OpenXR has been initialized.
</description>
</method>
<method name="register_future">
<return type="OpenXRFutureResult" />
<param index="0" name="future" type="int" />
<param index="1" name="on_success" type="Callable" default="Callable()" />
<description>
Register an OpenXR Future object so we monitor for completion. [param future] must be an [code]XrFutureEXT[/code] value previously returned by an API that started an asynchronous function.
You can optionally specify [param on_success], it will be invoked on successful completion of the future.
Or you can use the returned [OpenXRFutureResult] object to [code]await[/code] its [signal OpenXRFutureResult.completed] signal.
[codeblock]
var future_result = OpenXRFutureExtension.register_future(future)
await future_result.completed
if future_result.get_status() == OpenXRFutureResult.RESULT_FINISHED:
# Handle your success
pass
[/codeblock]
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRFutureResult" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Result object tracking the asynchronous result of an OpenXR Future object.
</brief_description>
<description>
Result object tracking the asynchronous result of an OpenXR Future object, you can use this object to track the result status.
</description>
<tutorials>
</tutorials>
<methods>
<method name="cancel_future">
<return type="void" />
<description>
Cancel this future, this will interrupt and stop the asynchronous function.
</description>
</method>
<method name="get_future" qualifiers="const">
<return type="int" />
<description>
Return the [code]XrFutureEXT[/code] value this result relates to.
</description>
</method>
<method name="get_result_value" qualifiers="const">
<return type="Variant" />
<description>
Returns the result value of our asynchronous function (if set by the extension). The type of this result value depends on the function being called. Consult the documentation of the relevant function.
</description>
</method>
<method name="get_status" qualifiers="const">
<return type="int" enum="OpenXRFutureResult.ResultStatus" />
<description>
Returns the status of this result.
</description>
</method>
<method name="set_result_value">
<return type="void" />
<param index="0" name="result_value" type="Variant" />
<description>
Stores the result value we expose to the user.
[b]Note:[/b] This method should only be called by an OpenXR extension that implements an asynchronous function.
</description>
</method>
</methods>
<signals>
<signal name="completed">
<param index="0" name="result" type="OpenXRFutureResult" />
<description>
Emitted when the asynchronous function is finished or has been cancelled.
</description>
</signal>
</signals>
<constants>
<constant name="RESULT_RUNNING" value="0" enum="ResultStatus">
The asynchronous function is running.
</constant>
<constant name="RESULT_FINISHED" value="1" enum="ResultStatus">
The asynchronous function has finished.
</constant>
<constant name="RESULT_CANCELLED" value="2" enum="ResultStatus">
The asynchronous function has been cancelled.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Node supporting hand and finger tracking in OpenXR.
</brief_description>
<description>
This node enables OpenXR's hand tracking functionality. The node should be a child node of an [XROrigin3D] node, tracking will update its position to the player's tracked hand Palm joint location (the center of the middle finger's metacarpal bone). This node also updates the skeleton of a properly skinned hand or avatar model.
If the skeleton is a hand (one of the hand bones is the root node of the skeleton), then the skeleton will be placed relative to the hand palm location and the hand mesh and skeleton should be children of the OpenXRHand node.
If the hand bones are part of a full skeleton, then the root of the hand will keep its location with the assumption that IK is used to position the hand and arm.
By default the skeleton hand bones are repositioned to match the size of the tracked hand. To preserve the modeled bone sizes change [member bone_update] to apply rotation only.
</description>
<tutorials>
</tutorials>
<members>
<member name="bone_update" type="int" setter="set_bone_update" getter="get_bone_update" enum="OpenXRHand.BoneUpdate" default="0">
Specify the type of updates to perform on the bone.
</member>
<member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
Specifies whether this node tracks the left or right hand of the player.
</member>
<member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath(&quot;&quot;)">
Set a [Skeleton3D] node for which the pose positions will be updated.
</member>
<member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0">
Set the motion range (if supported) limiting the hand motion.
</member>
<member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0">
Set the type of skeleton rig the [member hand_skeleton] is compliant with.
</member>
</members>
<constants>
<constant name="HAND_LEFT" value="0" enum="Hands">
Tracking the player's left hand.
</constant>
<constant name="HAND_RIGHT" value="1" enum="Hands">
Tracking the player's right hand.
</constant>
<constant name="HAND_MAX" value="2" enum="Hands">
Maximum supported hands.
</constant>
<constant name="MOTION_RANGE_UNOBSTRUCTED" value="0" enum="MotionRange">
When player grips, hand skeleton will form a full fist.
</constant>
<constant name="MOTION_RANGE_CONFORM_TO_CONTROLLER" value="1" enum="MotionRange">
When player grips, hand skeleton conforms to the controller the player is holding.
</constant>
<constant name="MOTION_RANGE_MAX" value="2" enum="MotionRange">
Maximum supported motion ranges.
</constant>
<constant name="SKELETON_RIG_OPENXR" value="0" enum="SkeletonRig">
An OpenXR compliant skeleton.
</constant>
<constant name="SKELETON_RIG_HUMANOID" value="1" enum="SkeletonRig">
A [SkeletonProfileHumanoid] compliant skeleton.
</constant>
<constant name="SKELETON_RIG_MAX" value="2" enum="SkeletonRig">
Maximum supported hands.
</constant>
<constant name="BONE_UPDATE_FULL" value="0" enum="BoneUpdate">
The skeletons bones are fully updated (both position and rotation) to match the tracked bones.
</constant>
<constant name="BONE_UPDATE_ROTATION_ONLY" value="1" enum="BoneUpdate">
The skeletons bones are only rotated to align with the tracked bones, preserving bone length.
</constant>
<constant name="BONE_UPDATE_MAX" value="2" enum="BoneUpdate">
Maximum supported bone update mode.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRHapticBase" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
OpenXR Haptic feedback base class.
</brief_description>
<description>
This is a base class for haptic feedback resources.
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRHapticVibration" inherits="OpenXRHapticBase" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Vibration haptic feedback.
</brief_description>
<description>
This haptic feedback resource makes it possible to define a vibration based haptic feedback pulse that can be triggered through actions in the OpenXR action map.
</description>
<tutorials>
</tutorials>
<members>
<member name="amplitude" type="float" setter="set_amplitude" getter="get_amplitude" default="1.0">
The amplitude of the pulse between [code]0.0[/code] and [code]1.0[/code].
</member>
<member name="duration" type="int" setter="set_duration" getter="get_duration" default="-1">
The duration of the pulse in nanoseconds. Use [code]-1[/code] for a minimum duration pulse for the current XR runtime.
</member>
<member name="frequency" type="float" setter="set_frequency" getter="get_frequency" default="0.0">
The frequency of the pulse in Hz. [code]0.0[/code] will let the XR runtime chose an optimal frequency for the device used.
</member>
</members>
</class>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRIPBinding" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Defines a binding between an [OpenXRAction] and an XR input or output.
</brief_description>
<description>
This binding resource binds an [OpenXRAction] to an input or output. As most controllers have left hand and right versions that are handled by the same interaction profile we can specify multiple bindings. For instance an action "Fire" could be bound to both "/user/hand/left/input/trigger" and "/user/hand/right/input/trigger". This would require two binding entries.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_path" deprecated="Binding is for a single path.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Add an input/output path to this binding.
</description>
</method>
<method name="get_binding_modifier" qualifiers="const">
<return type="OpenXRActionBindingModifier" />
<param index="0" name="index" type="int" />
<description>
Get the [OpenXRBindingModifier] at this index.
</description>
</method>
<method name="get_binding_modifier_count" qualifiers="const">
<return type="int" />
<description>
Get the number of binding modifiers for this binding.
</description>
</method>
<method name="get_path_count" qualifiers="const" deprecated="Binding is for a single path.">
<return type="int" />
<description>
Get the number of input/output paths in this binding.
</description>
</method>
<method name="has_path" qualifiers="const" deprecated="Binding is for a single path.">
<return type="bool" />
<param index="0" name="path" type="String" />
<description>
Returns [code]true[/code] if this input/output path is part of this binding.
</description>
</method>
<method name="remove_path" deprecated="Binding is for a single path.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Removes this input/output path from this binding.
</description>
</method>
</methods>
<members>
<member name="action" type="OpenXRAction" setter="set_action" getter="get_action">
[OpenXRAction] that is bound to [member binding_path].
</member>
<member name="binding_modifiers" type="Array" setter="set_binding_modifiers" getter="get_binding_modifiers" default="[]">
Binding modifiers for this binding.
</member>
<member name="binding_path" type="String" setter="set_binding_path" getter="get_binding_path" default="&quot;&quot;">
Binding path that defines the input or output bound to [member action].
[b]Note:[/b] Binding paths are suggestions, an XR runtime may choose to bind the action to a different input or output emulating this input or output.
</member>
<member name="paths" type="PackedStringArray" setter="set_paths" getter="get_paths" deprecated="Use [member binding_path] instead.">
Paths that define the inputs or outputs bound on the device.
</member>
</members>
</class>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRIPBindingModifier" inherits="OpenXRBindingModifier" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Binding modifier that applies directly on an interaction profile.
</brief_description>
<description>
Binding modifier that applies directly on an interaction profile.
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInteractionProfile" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Suggested bindings object for OpenXR.
</brief_description>
<description>
This object stores suggested bindings for an interaction profile. Interaction profiles define the metadata for a tracked XR device such as an XR controller.
For more information see the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles]interaction profiles info in the OpenXR specification[/url].
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_binding" qualifiers="const">
<return type="OpenXRIPBinding" />
<param index="0" name="index" type="int" />
<description>
Retrieve the binding at this index.
</description>
</method>
<method name="get_binding_count" qualifiers="const">
<return type="int" />
<description>
Get the number of bindings in this interaction profile.
</description>
</method>
<method name="get_binding_modifier" qualifiers="const">
<return type="OpenXRIPBindingModifier" />
<param index="0" name="index" type="int" />
<description>
Get the [OpenXRBindingModifier] at this index.
</description>
</method>
<method name="get_binding_modifier_count" qualifiers="const">
<return type="int" />
<description>
Get the number of binding modifiers in this interaction profile.
</description>
</method>
</methods>
<members>
<member name="binding_modifiers" type="Array" setter="set_binding_modifiers" getter="get_binding_modifiers" default="[]">
Binding modifiers for this interaction profile.
</member>
<member name="bindings" type="Array" setter="set_bindings" getter="get_bindings" default="[]">
Action bindings for this interaction profile.
</member>
<member name="interaction_profile_path" type="String" setter="set_interaction_profile_path" getter="get_interaction_profile_path" default="&quot;&quot;">
The interaction profile path identifying the XR device.
</member>
</members>
</class>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInteractionProfileEditor" inherits="OpenXRInteractionProfileEditorBase" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Default OpenXR interaction profile editor.
</brief_description>
<description>
This is the default OpenXR interaction profile editor that provides a generic interface for editing any interaction profile for which no custom editor has been defined.
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInteractionProfileEditorBase" inherits="HBoxContainer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Base class for editing interaction profiles.
</brief_description>
<description>
This is a base class for interaction profile editors used by the OpenXR action map editor. It can be used to create bespoke editors for specific interaction profiles.
</description>
<tutorials>
</tutorials>
<methods>
<method name="setup">
<return type="void" />
<param index="0" name="action_map" type="OpenXRActionMap" />
<param index="1" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Setup this editor for the provided [param action_map] and [param interaction_profile].
</description>
</method>
</methods>
<members>
<member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="3" />
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="3" />
</members>
</class>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInteractionProfileMetadata" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Meta class registering supported devices in OpenXR.
</brief_description>
<description>
This class allows OpenXR core and extensions to register metadata relating to supported interaction devices such as controllers, trackers, haptic devices, etc. It is primarily used by the action map editor and to sanitize any action map by removing extension-dependent entries when applicable.
</description>
<tutorials>
</tutorials>
<methods>
<method name="register_interaction_profile">
<return type="void" />
<param index="0" name="display_name" type="String" />
<param index="1" name="openxr_path" type="String" />
<param index="2" name="openxr_extension_name" type="String" />
<description>
Registers an interaction profile using its OpenXR designation (e.g. [code]/interaction_profiles/khr/simple_controller[/code] is the profile for OpenXR's simple controller profile).
[param display_name] is the description shown to the user. [param openxr_path] is the interaction profile path being registered. [param openxr_extension_name] optionally restricts this profile to the given extension being enabled/available. If the extension is not available, the profile and all related entries used in an action map are filtered out.
</description>
</method>
<method name="register_io_path">
<return type="void" />
<param index="0" name="interaction_profile" type="String" />
<param index="1" name="display_name" type="String" />
<param index="2" name="toplevel_path" type="String" />
<param index="3" name="openxr_path" type="String" />
<param index="4" name="openxr_extension_name" type="String" />
<param index="5" name="action_type" type="int" enum="OpenXRAction.ActionType" />
<description>
Registers an input/output path for the given [param interaction_profile]. The profile should previously have been registered using [method register_interaction_profile]. [param display_name] is the description shown to the user. [param toplevel_path] specifies the bind path this input/output can be bound to (e.g. [code]/user/hand/left[/code] or [code]/user/hand/right[/code]). [param openxr_path] is the action input/output being registered (e.g. [code]/user/hand/left/input/aim/pose[/code]). [param openxr_extension_name] restricts this input/output to an enabled/available extension, this doesn't need to repeat the extension on the profile but relates to overlapping extension (e.g. [code]XR_EXT_palm_pose[/code] that introduces [code]…/input/palm_ext/pose[/code] input paths). [param action_type] defines the type of input or output provided by OpenXR.
</description>
</method>
<method name="register_profile_rename">
<return type="void" />
<param index="0" name="old_name" type="String" />
<param index="1" name="new_name" type="String" />
<description>
Allows for renaming old interaction profile paths to new paths to maintain backwards compatibility with older action maps.
</description>
</method>
<method name="register_top_level_path">
<return type="void" />
<param index="0" name="display_name" type="String" />
<param index="1" name="openxr_path" type="String" />
<param index="2" name="openxr_extension_name" type="String" />
<description>
Registers a top level path to which profiles can be bound. For instance [code]/user/hand/left[/code] refers to the bind point for the player's left hand. Extensions can register additional top level paths, for instance a haptic vest extension might register [code]/user/body/vest[/code].
[param display_name] is the name shown to the user. [param openxr_path] is the top level path being registered. [param openxr_extension_name] is optional and ensures the top level path is only used if the specified extension is available/enabled.
When a top level path ends up being bound by OpenXR, an [XRPositionalTracker] is instantiated to manage the state of the device.
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,442 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInterface" inherits="XRInterface" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Our OpenXR interface.
</brief_description>
<description>
The OpenXR interface allows Godot to interact with OpenXR runtimes and make it possible to create XR experiences and games.
Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialized when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset.
</description>
<tutorials>
<link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link>
</tutorials>
<methods>
<method name="get_action_sets" qualifiers="const">
<return type="Array" />
<description>
Returns a list of action sets registered with Godot (loaded from the action map at runtime).
</description>
</method>
<method name="get_available_display_refresh_rates" qualifiers="const">
<return type="Array" />
<description>
Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized.
</description>
</method>
<method name="get_hand_joint_angular_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_angular_velocity] obtained from [method XRServer.get_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
<description>
If handtracking is enabled, returns the angular velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D]!
</description>
</method>
<method name="get_hand_joint_flags" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_flags] obtained from [method XRServer.get_tracker] instead.">
<return type="int" enum="OpenXRInterface.HandJointFlags" is_bitfield="true" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
<description>
If handtracking is enabled, returns flags that inform us of the validity of the tracking data.
</description>
</method>
<method name="get_hand_joint_linear_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_linear_velocity] obtained from [method XRServer.get_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
<description>
If handtracking is enabled, returns the linear velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
</description>
</method>
<method name="get_hand_joint_position" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
<description>
If handtracking is enabled, returns the position of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
</description>
</method>
<method name="get_hand_joint_radius" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_radius] obtained from [method XRServer.get_tracker] instead.">
<return type="float" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
<description>
If handtracking is enabled, returns the radius of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is without worldscale applied!
</description>
</method>
<method name="get_hand_joint_rotation" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_tracker] instead.">
<return type="Quaternion" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
<description>
If handtracking is enabled, returns the rotation of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR.
</description>
</method>
<method name="get_hand_tracking_source" qualifiers="const" deprecated="Use [member XRHandTracker.hand_tracking_source] obtained from [method XRServer.get_tracker] instead.">
<return type="int" enum="OpenXRInterface.HandTrackedSource" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<description>
If handtracking is enabled and hand tracking source is supported, gets the source of the hand tracking data for [param hand].
</description>
</method>
<method name="get_motion_range" qualifiers="const">
<return type="int" enum="OpenXRInterface.HandMotionRange" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<description>
If handtracking is enabled and motion range is supported, gets the currently configured motion range for [param hand].
</description>
</method>
<method name="get_session_state">
<return type="int" enum="OpenXRInterface.SessionState" />
<description>
Returns the current state of our OpenXR session.
</description>
</method>
<method name="is_action_set_active" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="String" />
<description>
Returns [code]true[/code] if the given action set is active.
</description>
</method>
<method name="is_eye_gaze_interaction_supported">
<return type="bool" />
<description>
Returns the capabilities of the eye gaze interaction extension.
[b]Note:[/b] This only returns a valid value after OpenXR has been initialized.
</description>
</method>
<method name="is_foveation_supported" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR's foveation extension is supported, the interface must be initialized before this returns a valid value.
[b]Note:[/b] This feature is only available on the Compatibility renderer and currently only available on some stand alone headsets. For Vulkan set [member Viewport.vrs_mode] to [code]VRS_XR[/code] on desktop.
</description>
</method>
<method name="is_hand_interaction_supported" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR's hand interaction profile is supported and enabled.
[b]Note:[/b] This only returns a valid value after OpenXR has been initialized.
</description>
</method>
<method name="is_hand_tracking_supported">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR's hand tracking is supported and enabled.
[b]Note:[/b] This only returns a valid value after OpenXR has been initialized.
</description>
</method>
<method name="set_action_set_active">
<return type="void" />
<param index="0" name="name" type="String" />
<param index="1" name="active" type="bool" />
<description>
Sets the given action set as active or inactive.
</description>
</method>
<method name="set_cpu_level">
<return type="void" />
<param index="0" name="level" type="int" enum="OpenXRInterface.PerfSettingsLevel" />
<description>
Sets the CPU performance level of the OpenXR device.
</description>
</method>
<method name="set_gpu_level">
<return type="void" />
<param index="0" name="level" type="int" enum="OpenXRInterface.PerfSettingsLevel" />
<description>
Sets the GPU performance level of the OpenXR device.
</description>
</method>
<method name="set_motion_range">
<return type="void" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="motion_range" type="int" enum="OpenXRInterface.HandMotionRange" />
<description>
If handtracking is enabled and motion range is supported, sets the currently configured motion range for [param hand] to [param motion_range].
</description>
</method>
</methods>
<members>
<member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0">
The display refresh rate for the current HMD. Only functional if this feature is supported by the OpenXR runtime and after the interface has been initialized.
</member>
<member name="foveation_dynamic" type="bool" setter="set_foveation_dynamic" getter="get_foveation_dynamic" default="false">
Enable dynamic foveation adjustment, the interface must be initialized before this is accessible. If enabled foveation will automatically adjusted between low and [member foveation_level].
[b]Note:[/b] Only works on the Compatibility renderer.
</member>
<member name="foveation_level" type="int" setter="set_foveation_level" getter="get_foveation_level" default="0">
Set foveation level from 0 (off) to 3 (high), the interface must be initialized before this is accessible.
[b]Note:[/b] Only works on the Compatibility renderer.
</member>
<member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0">
The render size multiplier for the current HMD. Must be set before the interface has been initialized.
</member>
<member name="vrs_min_radius" type="float" setter="set_vrs_min_radius" getter="get_vrs_min_radius" default="20.0">
The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size.
[b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
</member>
<member name="vrs_strength" type="float" setter="set_vrs_strength" getter="get_vrs_strength" default="1.0">
The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality.
[b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
</member>
</members>
<signals>
<signal name="cpu_level_changed">
<param index="0" name="sub_domain" type="int" />
<param index="1" name="from_level" type="int" />
<param index="2" name="to_level" type="int" />
<description>
Informs the device CPU performance level has changed in the specified subdomain.
</description>
</signal>
<signal name="gpu_level_changed">
<param index="0" name="sub_domain" type="int" />
<param index="1" name="from_level" type="int" />
<param index="2" name="to_level" type="int" />
<description>
Informs the device GPU performance level has changed in the specified subdomain.
</description>
</signal>
<signal name="instance_exiting">
<description>
Informs our OpenXR instance is exiting.
</description>
</signal>
<signal name="pose_recentered">
<description>
Informs the user queued a recenter of the player position.
</description>
</signal>
<signal name="refresh_rate_changed">
<param index="0" name="refresh_rate" type="float" />
<description>
Informs the user the HMD refresh rate has changed.
[b]Note:[/b] Only emitted if XR runtime supports the refresh rate extension.
</description>
</signal>
<signal name="session_begun">
<description>
Informs our OpenXR session has been started.
</description>
</signal>
<signal name="session_focussed">
<description>
Informs our OpenXR session now has focus, for example output is sent to the HMD and we're receiving XR input.
</description>
</signal>
<signal name="session_loss_pending">
<description>
Informs our OpenXR session is in the process of being lost.
</description>
</signal>
<signal name="session_stopping">
<description>
Informs our OpenXR session is stopping.
</description>
</signal>
<signal name="session_synchronized">
<description>
Informs our OpenXR session has been synchronized.
</description>
</signal>
<signal name="session_visible">
<description>
Informs our OpenXR session is now visible, for example output is sent to the HMD but we don't receive XR input.
</description>
</signal>
</signals>
<constants>
<constant name="SESSION_STATE_UNKNOWN" value="0" enum="SessionState">
The state of the session is unknown, we haven't tried setting up OpenXR yet.
</constant>
<constant name="SESSION_STATE_IDLE" value="1" enum="SessionState">
The initial state after the OpenXR session is created or after the session is destroyed.
</constant>
<constant name="SESSION_STATE_READY" value="2" enum="SessionState">
OpenXR is ready to begin our session. [signal session_begun] is emitted when we change to this state.
</constant>
<constant name="SESSION_STATE_SYNCHRONIZED" value="3" enum="SessionState">
The application has synched its frame loop with the runtime but we're not rendering anything. [signal session_synchronized] is emitted when we change to this state.
</constant>
<constant name="SESSION_STATE_VISIBLE" value="4" enum="SessionState">
The application has synched its frame loop with the runtime and we're rendering output to the user, however we receive no user input. [signal session_visible] is emitted when we change to this state.
[b]Note:[/b] This is the current state just before we get the focused state, whenever the user opens a system menu, switches to another application, or takes off their headset.
</constant>
<constant name="SESSION_STATE_FOCUSED" value="5" enum="SessionState">
The application has synched its frame loop with the runtime, we're rendering output to the user and we're receiving XR input. [signal session_focussed] is emitted when we change to this state.
[b]Note:[/b] This is the state OpenXR will be in when the user can fully interact with your game.
</constant>
<constant name="SESSION_STATE_STOPPING" value="6" enum="SessionState">
Our session is being stopped. [signal session_stopping] is emitted when we change to this state.
</constant>
<constant name="SESSION_STATE_LOSS_PENDING" value="7" enum="SessionState">
The session is about to be lost. [signal session_loss_pending] is emitted when we change to this state.
</constant>
<constant name="SESSION_STATE_EXITING" value="8" enum="SessionState">
The OpenXR instance is about to be destroyed and we're existing. [signal instance_exiting] is emitted when we change to this state.
</constant>
<constant name="HAND_LEFT" value="0" enum="Hand">
Left hand.
</constant>
<constant name="HAND_RIGHT" value="1" enum="Hand">
Right hand.
</constant>
<constant name="HAND_MAX" value="2" enum="Hand">
Maximum value for the hand enum.
</constant>
<constant name="HAND_MOTION_RANGE_UNOBSTRUCTED" value="0" enum="HandMotionRange">
Full hand range, if user closes their hands, we make a full fist.
</constant>
<constant name="HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER" value="1" enum="HandMotionRange">
Conform to controller, if user closes their hands, the tracked data conforms to the shape of the controller.
</constant>
<constant name="HAND_MOTION_RANGE_MAX" value="2" enum="HandMotionRange">
Maximum value for the motion range enum.
</constant>
<constant name="HAND_TRACKED_SOURCE_UNKNOWN" value="0" enum="HandTrackedSource">
The source of hand tracking data is unknown (the extension is likely unsupported).
</constant>
<constant name="HAND_TRACKED_SOURCE_UNOBSTRUCTED" value="1" enum="HandTrackedSource">
The source of hand tracking is unobstructed, this means that an accurate method of hand tracking is used, e.g. optical hand tracking, data gloves, etc.
</constant>
<constant name="HAND_TRACKED_SOURCE_CONTROLLER" value="2" enum="HandTrackedSource">
The source of hand tracking is a controller, bone positions are inferred from controller inputs.
</constant>
<constant name="HAND_TRACKED_SOURCE_MAX" value="3" enum="HandTrackedSource">
Represents the size of the [enum HandTrackedSource] enum.
</constant>
<constant name="HAND_JOINT_PALM" value="0" enum="HandJoints">
Palm joint.
</constant>
<constant name="HAND_JOINT_WRIST" value="1" enum="HandJoints">
Wrist joint.
</constant>
<constant name="HAND_JOINT_THUMB_METACARPAL" value="2" enum="HandJoints">
Thumb metacarpal joint.
</constant>
<constant name="HAND_JOINT_THUMB_PROXIMAL" value="3" enum="HandJoints">
Thumb proximal joint.
</constant>
<constant name="HAND_JOINT_THUMB_DISTAL" value="4" enum="HandJoints">
Thumb distal joint.
</constant>
<constant name="HAND_JOINT_THUMB_TIP" value="5" enum="HandJoints">
Thumb tip joint.
</constant>
<constant name="HAND_JOINT_INDEX_METACARPAL" value="6" enum="HandJoints">
Index finger metacarpal joint.
</constant>
<constant name="HAND_JOINT_INDEX_PROXIMAL" value="7" enum="HandJoints">
Index finger phalanx proximal joint.
</constant>
<constant name="HAND_JOINT_INDEX_INTERMEDIATE" value="8" enum="HandJoints">
Index finger phalanx intermediate joint.
</constant>
<constant name="HAND_JOINT_INDEX_DISTAL" value="9" enum="HandJoints">
Index finger phalanx distal joint.
</constant>
<constant name="HAND_JOINT_INDEX_TIP" value="10" enum="HandJoints">
Index finger tip joint.
</constant>
<constant name="HAND_JOINT_MIDDLE_METACARPAL" value="11" enum="HandJoints">
Middle finger metacarpal joint.
</constant>
<constant name="HAND_JOINT_MIDDLE_PROXIMAL" value="12" enum="HandJoints">
Middle finger phalanx proximal joint.
</constant>
<constant name="HAND_JOINT_MIDDLE_INTERMEDIATE" value="13" enum="HandJoints">
Middle finger phalanx intermediate joint.
</constant>
<constant name="HAND_JOINT_MIDDLE_DISTAL" value="14" enum="HandJoints">
Middle finger phalanx distal joint.
</constant>
<constant name="HAND_JOINT_MIDDLE_TIP" value="15" enum="HandJoints">
Middle finger tip joint.
</constant>
<constant name="HAND_JOINT_RING_METACARPAL" value="16" enum="HandJoints">
Ring finger metacarpal joint.
</constant>
<constant name="HAND_JOINT_RING_PROXIMAL" value="17" enum="HandJoints">
Ring finger phalanx proximal joint.
</constant>
<constant name="HAND_JOINT_RING_INTERMEDIATE" value="18" enum="HandJoints">
Ring finger phalanx intermediate joint.
</constant>
<constant name="HAND_JOINT_RING_DISTAL" value="19" enum="HandJoints">
Ring finger phalanx distal joint.
</constant>
<constant name="HAND_JOINT_RING_TIP" value="20" enum="HandJoints">
Ring finger tip joint.
</constant>
<constant name="HAND_JOINT_LITTLE_METACARPAL" value="21" enum="HandJoints">
Pinky finger metacarpal joint.
</constant>
<constant name="HAND_JOINT_LITTLE_PROXIMAL" value="22" enum="HandJoints">
Pinky finger phalanx proximal joint.
</constant>
<constant name="HAND_JOINT_LITTLE_INTERMEDIATE" value="23" enum="HandJoints">
Pinky finger phalanx intermediate joint.
</constant>
<constant name="HAND_JOINT_LITTLE_DISTAL" value="24" enum="HandJoints">
Pinky finger phalanx distal joint.
</constant>
<constant name="HAND_JOINT_LITTLE_TIP" value="25" enum="HandJoints">
Pinky finger tip joint.
</constant>
<constant name="HAND_JOINT_MAX" value="26" enum="HandJoints">
Represents the size of the [enum HandJoints] enum.
</constant>
<constant name="PERF_SETTINGS_LEVEL_POWER_SAVINGS" value="0" enum="PerfSettingsLevel">
The application has entered a non-XR section (head-locked / static screen), during which power savings are to be prioritized.
</constant>
<constant name="PERF_SETTINGS_LEVEL_SUSTAINED_LOW" value="1" enum="PerfSettingsLevel">
The application has entered a low and stable complexity section, during which reducing power is more important than occasional late rendering frames.
</constant>
<constant name="PERF_SETTINGS_LEVEL_SUSTAINED_HIGH" value="2" enum="PerfSettingsLevel">
The application has entered a high or dynamic complexity section, during which the XR Runtime strives for consistent XR compositing and frame rendering within a thermally sustainable range.
</constant>
<constant name="PERF_SETTINGS_LEVEL_BOOST" value="3" enum="PerfSettingsLevel">
The application has entered a section with very high complexity, during which the XR Runtime is allowed to step up beyond the thermally sustainable range.
</constant>
<constant name="PERF_SETTINGS_SUB_DOMAIN_COMPOSITING" value="0" enum="PerfSettingsSubDomain">
The compositing performance within the runtime has reached a new level.
</constant>
<constant name="PERF_SETTINGS_SUB_DOMAIN_RENDERING" value="1" enum="PerfSettingsSubDomain">
The application rendering performance has reached a new level.
</constant>
<constant name="PERF_SETTINGS_SUB_DOMAIN_THERMAL" value="2" enum="PerfSettingsSubDomain">
The temperature of the device has reached a new level.
</constant>
<constant name="PERF_SETTINGS_NOTIF_LEVEL_NORMAL" value="0" enum="PerfSettingsNotificationLevel">
The sub-domain has reached a level where no further actions other than currently applied are necessary.
</constant>
<constant name="PERF_SETTINGS_NOTIF_LEVEL_WARNING" value="1" enum="PerfSettingsNotificationLevel">
The sub-domain has reached an early warning level where the application should start proactive mitigation actions.
</constant>
<constant name="PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED" value="2" enum="PerfSettingsNotificationLevel">
The sub-domain has reached a critical level where the application should start drastic mitigation actions.
</constant>
<constant name="HAND_JOINT_NONE" value="0" enum="HandJointFlags" is_bitfield="true">
No flags are set.
</constant>
<constant name="HAND_JOINT_ORIENTATION_VALID" value="1" enum="HandJointFlags" is_bitfield="true">
If set, the orientation data is valid, otherwise, the orientation data is unreliable and should not be used.
</constant>
<constant name="HAND_JOINT_ORIENTATION_TRACKED" value="2" enum="HandJointFlags" is_bitfield="true">
If set, the orientation data comes from tracking data, otherwise, the orientation data contains predicted data.
</constant>
<constant name="HAND_JOINT_POSITION_VALID" value="4" enum="HandJointFlags" is_bitfield="true">
If set, the positional data is valid, otherwise, the positional data is unreliable and should not be used.
</constant>
<constant name="HAND_JOINT_POSITION_TRACKED" value="8" enum="HandJointFlags" is_bitfield="true">
If set, the positional data comes from tracking data, otherwise, the positional data contains predicted data.
</constant>
<constant name="HAND_JOINT_LINEAR_VELOCITY_VALID" value="16" enum="HandJointFlags" is_bitfield="true">
If set, our linear velocity data is valid, otherwise, the linear velocity data is unreliable and should not be used.
</constant>
<constant name="HAND_JOINT_ANGULAR_VELOCITY_VALID" value="32" enum="HandJointFlags" is_bitfield="true">
If set, our angular velocity data is valid, otherwise, the angular velocity data is unreliable and should not be used.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRRenderModel" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
This node will display an OpenXR render model.
</brief_description>
<description>
This node will display an OpenXR render model by accessing the associated GLTF and processes all animation data (if supported by the XR runtime).
Render models were introduced to allow showing the correct model for the controller (or other device) the user has in hand, since the OpenXR action map does not provide information about the hardware used by the user. Note that while the controller (or device) can be somewhat inferred by the bound action map profile, this is a dangerous approach as the user may be using hardware not known at time of development and OpenXR will simply simulate an available interaction profile.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_top_level_path" qualifiers="const">
<return type="String" />
<description>
Returns the top level path related to this render model.
</description>
</method>
</methods>
<members>
<member name="render_model" type="RID" setter="set_render_model" getter="get_render_model" default="RID()">
The render model RID for the render model to load, as returned by [method OpenXRRenderModelExtension.render_model_create] or [method OpenXRRenderModelExtension.render_model_get_all].
</member>
</members>
<signals>
<signal name="render_model_top_level_path_changed">
<description>
Emitted when the top level path of this render model has changed.
</description>
</signal>
</signals>
</class>

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRRenderModelExtension" inherits="OpenXRExtensionWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
This class implements the OpenXR Render Model Extension.
</brief_description>
<description>
This class implements the OpenXR Render Model Extension, if enabled it will maintain a list of active render models and provides an interface to the render model data.
</description>
<tutorials>
</tutorials>
<methods>
<method name="is_active" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR's render model extension is supported and enabled.
[b]Note:[/b] This only returns a valid value after OpenXR has been initialized.
</description>
</method>
<method name="render_model_create">
<return type="RID" />
<param index="0" name="render_model_id" type="int" />
<description>
Creates a render model object within OpenXR using a render model id.
[b]Note:[/b] This function is exposed for dependent OpenXR extensions that provide render model ids to be used with the render model extension.
</description>
</method>
<method name="render_model_destroy">
<return type="void" />
<param index="0" name="render_model" type="RID" />
<description>
Destroys a render model object within OpenXR that was previously created with [method render_model_create].
[b]Note:[/b] This function is exposed for dependent OpenXR extensions that provide render model ids to be used with the render model extension.
</description>
</method>
<method name="render_model_get_all">
<return type="RID[]" />
<description>
Returns an array of all currently active render models registered with this extension.
</description>
</method>
<method name="render_model_get_animatable_node_count" qualifiers="const">
<return type="int" />
<param index="0" name="render_model" type="RID" />
<description>
Returns the number of animatable nodes this render model has.
</description>
</method>
<method name="render_model_get_animatable_node_name" qualifiers="const">
<return type="String" />
<param index="0" name="render_model" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns the name of the given animatable node.
</description>
</method>
<method name="render_model_get_animatable_node_transform" qualifiers="const">
<return type="Transform3D" />
<param index="0" name="render_model" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns the current local transform for an animatable node. This is updated every frame.
</description>
</method>
<method name="render_model_get_confidence" qualifiers="const">
<return type="int" enum="XRPose.TrackingConfidence" />
<param index="0" name="render_model" type="RID" />
<description>
Returns the tracking confidence of the tracking data for the render model.
</description>
</method>
<method name="render_model_get_root_transform" qualifiers="const">
<return type="Transform3D" />
<param index="0" name="render_model" type="RID" />
<description>
Returns the root transform of a render model. This is the tracked position relative to our [XROrigin3D] node.
</description>
</method>
<method name="render_model_get_subaction_paths">
<return type="PackedStringArray" />
<param index="0" name="render_model" type="RID" />
<description>
Returns a list of active subaction paths for this [param render_model].
[b]Note:[/b] If different devices are bound to your actions than available in suggested interaction bindings, this information shows paths related to the interaction bindings being mimicked by that device.
</description>
</method>
<method name="render_model_get_top_level_path" qualifiers="const">
<return type="String" />
<param index="0" name="render_model" type="RID" />
<description>
Returns the top level path associated with this [param render_model]. If provided this identifies whether the render model is associated with the player's hands or other body part.
</description>
</method>
<method name="render_model_is_animatable_node_visible" qualifiers="const">
<return type="bool" />
<param index="0" name="render_model" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns [code]true[/code] if this animatable node should be visible.
</description>
</method>
<method name="render_model_new_scene_instance" qualifiers="const">
<return type="Node3D" />
<param index="0" name="render_model" type="RID" />
<description>
Returns an instance of a subscene that contains all [MeshInstance3D] nodes that allow you to visualize the render model.
</description>
</method>
</methods>
<signals>
<signal name="render_model_added">
<param index="0" name="render_model" type="RID" />
<description>
Emitted when a new render model is added.
</description>
</signal>
<signal name="render_model_removed">
<param index="0" name="render_model" type="RID" />
<description>
Emitted when a render model is removed.
</description>
</signal>
<signal name="render_model_top_level_path_changed">
<param index="0" name="render_model" type="RID" />
<description>
Emitted when the top level path associated with a render model changed.
</description>
</signal>
</signals>
</class>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRRenderModelManager" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Helper node that will automatically manage displaying render models.
</brief_description>
<description>
This helper node will automatically manage displaying render models. It will create new [OpenXRRenderModel] nodes as controllers and other hand held devices are detected, and remove those nodes when they are deactivated.
[b]Note:[/b] If you want more control over this logic you can alternatively call [method OpenXRRenderModelExtension.render_model_get_all] to obtain a list of active render model ids and create [OpenXRRenderModel] instances for each render model id provided.
</description>
<tutorials>
</tutorials>
<members>
<member name="make_local_to_pose" type="String" setter="set_make_local_to_pose" getter="get_make_local_to_pose" default="&quot;&quot;">
Position render models local to this pose (this will adjust the position of the render models container node).
</member>
<member name="tracker" type="int" setter="set_tracker" getter="get_tracker" enum="OpenXRRenderModelManager.RenderModelTracker" default="0">
Limits render models to the specified tracker. Include: 0 = All render models, 1 = Render models not related to a tracker, 2 = Render models related to the left hand tracker, 3 = Render models related to the right hand tracker.
</member>
</members>
<signals>
<signal name="render_model_added">
<param index="0" name="render_model" type="OpenXRRenderModel" />
<description>
Emitted when a render model node is added as a child to this node.
</description>
</signal>
<signal name="render_model_removed">
<param index="0" name="render_model" type="OpenXRRenderModel" />
<description>
Emitted when a render model child node is about to be removed from this node.
</description>
</signal>
</signals>
<constants>
<constant name="RENDER_MODEL_TRACKER_ANY" value="0" enum="RenderModelTracker">
All active render models are shown regardless of what tracker they relate to.
</constant>
<constant name="RENDER_MODEL_TRACKER_NONE_SET" value="1" enum="RenderModelTracker">
Only active render models are shown that are not related to any tracker we manage.
</constant>
<constant name="RENDER_MODEL_TRACKER_LEFT_HAND" value="2" enum="RenderModelTracker">
Only active render models are shown that are related to the left hand tracker.
</constant>
<constant name="RENDER_MODEL_TRACKER_RIGHT_HAND" value="3" enum="RenderModelTracker">
Only active render models are shown that are related to the right hand tracker.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRVisibilityMask" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Draws a stereo correct visibility mask.
</brief_description>
<description>
The visibility mask allows us to black out the part of the render result that is invisible due to lens distortion.
As this is rendered first, it prevents fragments with expensive lighting calculations to be processed as they are discarded through z-checking.
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj

View File

@@ -0,0 +1,171 @@
/**************************************************************************/
/* openxr_action_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_action_editor.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
void OpenXRActionEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionEditor::_do_set_name);
ClassDB::bind_method(D_METHOD("_do_set_localized_name", "name"), &OpenXRActionEditor::_do_set_localized_name);
ClassDB::bind_method(D_METHOD("_do_set_action_type", "type"), &OpenXRActionEditor::_do_set_action_type);
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_editor")));
}
void OpenXRActionEditor::_theme_changed() {
rem_action->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
}
void OpenXRActionEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
} break;
}
}
void OpenXRActionEditor::_on_action_name_changed(const String p_new_text) {
if (action->get_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Action"));
undo_redo->add_do_method(this, "_do_set_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_name", action->get_name());
undo_redo->commit_action(false);
// If our localized name matches our action name, set this too
if (action->get_name() == action->get_localized_name()) {
undo_redo->create_action(TTR("Rename Actions Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action->get_localized_name());
undo_redo->commit_action(false);
action->set_localized_name(p_new_text);
action_localized_name->set_text(p_new_text);
}
action->set_name(p_new_text);
action->set_edited(true);
}
}
void OpenXRActionEditor::_do_set_name(const String p_new_text) {
action->set_name(p_new_text);
action->set_edited(true);
action_name->set_text(p_new_text);
}
void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_text) {
if (action->get_localized_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Actions Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action->get_localized_name());
undo_redo->commit_action(false);
action->set_localized_name(p_new_text);
action->set_edited(true);
}
}
void OpenXRActionEditor::_do_set_localized_name(const String p_new_text) {
action->set_localized_name(p_new_text);
action->set_edited(true);
action_localized_name->set_text(p_new_text);
}
void OpenXRActionEditor::_on_item_selected(int p_idx) {
ERR_FAIL_INDEX(p_idx, OpenXRAction::OPENXR_ACTION_MAX);
OpenXRAction::ActionType action_type = OpenXRAction::ActionType(p_idx);
if (action->get_action_type() != action_type) {
undo_redo->create_action(TTR("Change Action Type"));
undo_redo->add_do_method(this, "_do_set_action_type", action_type);
undo_redo->add_undo_method(this, "_do_set_action_type", action->get_action_type());
undo_redo->commit_action(false);
action->set_action_type(action_type);
action->set_edited(true);
}
}
void OpenXRActionEditor::_do_set_action_type(OpenXRAction::ActionType p_action_type) {
action->set_action_type(p_action_type);
action->set_edited(true);
action_type_button->select(int(action->get_action_type()));
}
void OpenXRActionEditor::_on_remove_action() {
emit_signal("remove", this);
}
OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
undo_redo = EditorUndoRedoManager::get_singleton();
action = p_action;
set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_name = memnew(LineEdit);
action_name->set_text(action->get_name());
action_name->set_tooltip_text(TTR("Internal name of the action. Some XR runtimes don't allow spaces or special characters."));
action_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionEditor::_on_action_name_changed));
action_name->set_accessibility_name(TTRC("Action Name"));
add_child(action_name);
action_localized_name = memnew(LineEdit);
action_localized_name->set_text(action->get_localized_name());
action_localized_name->set_tooltip_text(TTR("Human-readable name of the action. This can be displayed to end users."));
action_localized_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_localized_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionEditor::_on_action_localized_name_changed));
action_localized_name->set_accessibility_name(TTRC("Action Localized Name"));
add_child(action_localized_name);
action_type_button = memnew(OptionButton);
action_type_button->set_tooltip_text(TTR("Type of the action"));
action_type_button->add_item("Bool", OpenXRAction::OPENXR_ACTION_BOOL);
action_type_button->add_item("Float", OpenXRAction::OPENXR_ACTION_FLOAT);
action_type_button->add_item("Vector2", OpenXRAction::OPENXR_ACTION_VECTOR2);
action_type_button->add_item("Pose", OpenXRAction::OPENXR_ACTION_POSE);
action_type_button->add_item("Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC);
action_type_button->set_accessibility_name(TTRC("Action Type"));
action_type_button->select(int(action->get_action_type()));
action_type_button->set_custom_minimum_size(Size2(100.0 * EDSCALE, 0.0));
action_type_button->connect(SceneStringName(item_selected), callable_mp(this, &OpenXRActionEditor::_on_item_selected));
add_child(action_type_button);
// maybe add dropdown to edit our toplevel paths, or do we deduce them from our suggested bindings?
rem_action = memnew(Button);
rem_action->set_tooltip_text(TTR("Remove action"));
rem_action->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionEditor::_on_remove_action));
rem_action->set_flat(true);
add_child(rem_action);
}

View File

@@ -0,0 +1,72 @@
/**************************************************************************/
/* openxr_action_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action.h"
#include "editor/editor_undo_redo_manager.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
#include "scene/gui/text_edit.h"
class OpenXRActionEditor : public HBoxContainer {
GDCLASS(OpenXRActionEditor, HBoxContainer);
private:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRAction> action;
LineEdit *action_name = nullptr;
LineEdit *action_localized_name = nullptr;
OptionButton *action_type_button = nullptr;
Button *rem_action = nullptr;
void _theme_changed();
void _on_action_name_changed(const String p_new_text);
void _on_action_localized_name_changed(const String p_new_text);
void _on_item_selected(int p_idx);
void _on_remove_action();
protected:
static void _bind_methods();
void _notification(int p_what);
// used for undo/redo
void _do_set_name(const String p_new_text);
void _do_set_localized_name(const String p_new_text);
void _do_set_action_type(OpenXRAction::ActionType p_action_type);
public:
Ref<OpenXRAction> get_action() { return action; }
OpenXRActionEditor(Ref<OpenXRAction> p_action);
};

View File

@@ -0,0 +1,497 @@
/**************************************************************************/
/* openxr_action_map_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_action_map_editor.h"
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
HashMap<String, String> OpenXRActionMapEditor::interaction_profile_editors;
HashMap<String, String> OpenXRActionMapEditor::binding_modifier_editors;
void OpenXRActionMapEditor::_bind_methods() {
ClassDB::bind_method("_add_action_set_editor", &OpenXRActionMapEditor::_add_action_set_editor);
ClassDB::bind_method("_add_interaction_profile_editor", &OpenXRActionMapEditor::_add_interaction_profile_editor);
ClassDB::bind_method(D_METHOD("_add_action_set", "name"), &OpenXRActionMapEditor::_add_action_set);
ClassDB::bind_method(D_METHOD("_remove_action_set", "name"), &OpenXRActionMapEditor::_remove_action_set);
ClassDB::bind_method(D_METHOD("_do_add_action_set_editor", "action_set_editor"), &OpenXRActionMapEditor::_do_add_action_set_editor);
ClassDB::bind_method(D_METHOD("_do_remove_action_set_editor", "action_set_editor"), &OpenXRActionMapEditor::_do_remove_action_set_editor);
ClassDB::bind_method(D_METHOD("_do_add_interaction_profile_editor", "interaction_profile_editor"), &OpenXRActionMapEditor::_do_add_interaction_profile_editor);
ClassDB::bind_method(D_METHOD("_do_remove_interaction_profile_editor", "interaction_profile_editor"), &OpenXRActionMapEditor::_do_remove_interaction_profile_editor);
ClassDB::bind_static_method("OpenXRActionMapEditor", D_METHOD("register_interaction_profile_editor", "interaction_profile_path", "editor_class"), &OpenXRActionMapEditor::register_interaction_profile_editor);
ClassDB::bind_static_method("OpenXRActionMapEditor", D_METHOD("register_binding_modifier_editor", "binding_modifier_class", "editor_class"), &OpenXRActionMapEditor::register_binding_modifier_editor);
}
void OpenXRActionMapEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
for (int i = 0; i < tabs->get_child_count(); i++) {
Control *tab = Object::cast_to<Control>(tabs->get_child(i));
if (tab) {
tab->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
}
}
} break;
case NOTIFICATION_READY: {
_create_action_sets();
_create_interaction_profiles();
} break;
}
}
OpenXRActionSetEditor *OpenXRActionMapEditor::_add_action_set_editor(Ref<OpenXRActionSet> p_action_set) {
ERR_FAIL_COND_V(p_action_set.is_null(), nullptr);
OpenXRActionSetEditor *action_set_editor = memnew(OpenXRActionSetEditor(action_map, p_action_set));
action_set_editor->connect("remove", callable_mp(this, &OpenXRActionMapEditor::_on_remove_action_set));
action_set_editor->connect("action_removed", callable_mp(this, &OpenXRActionMapEditor::_on_action_removed));
actionsets_vb->add_child(action_set_editor);
return action_set_editor;
}
void OpenXRActionMapEditor::_create_action_sets() {
if (action_map.is_valid()) {
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
Ref<OpenXRActionSet> action_set = action_sets[i];
_add_action_set_editor(action_set);
}
}
}
OpenXRInteractionProfileEditorBase *OpenXRActionMapEditor::_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile) {
ERR_FAIL_COND_V(p_interaction_profile.is_null(), nullptr);
String profile_path = p_interaction_profile->get_interaction_profile_path();
// need to instance the correct editor for our profile
OpenXRInteractionProfileEditorBase *new_profile_editor = nullptr;
if (interaction_profile_editors.has(profile_path)) {
Object *new_editor = ClassDB::instantiate(interaction_profile_editors[profile_path]);
if (new_editor) {
new_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(new_editor);
if (!new_profile_editor) {
WARN_PRINT("Interaction profile editor type mismatch for " + profile_path);
memfree(new_editor);
}
}
}
if (!new_profile_editor) {
// instance generic editor
new_profile_editor = memnew(OpenXRInteractionProfileEditor);
}
// now add it in..
ERR_FAIL_NULL_V(new_profile_editor, nullptr);
new_profile_editor->setup(action_map, p_interaction_profile);
tabs->add_child(new_profile_editor);
new_profile_editor->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
if (!new_profile_editor->tooltip.is_empty()) {
tabs->set_tab_tooltip(tabs->get_tab_count() - 1, new_profile_editor->tooltip);
}
return new_profile_editor;
}
void OpenXRActionMapEditor::_create_interaction_profiles() {
if (action_map.is_valid()) {
Array new_interaction_profiles = action_map->get_interaction_profiles();
for (int i = 0; i < new_interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> interaction_profile = new_interaction_profiles[i];
_add_interaction_profile_editor(interaction_profile);
}
}
}
OpenXRActionSetEditor *OpenXRActionMapEditor::_add_action_set(String p_name) {
ERR_FAIL_COND_V(action_map.is_null(), nullptr);
Ref<OpenXRActionSet> new_action_set;
// add our new action set
new_action_set.instantiate();
new_action_set->set_name(p_name);
new_action_set->set_localized_name(p_name);
action_map->add_action_set(new_action_set);
action_map->set_edited(true);
// update our editor right away
OpenXRActionSetEditor *action_set_editor = _add_action_set_editor(new_action_set);
undo_redo->create_action(TTR("Add action set"));
undo_redo->add_do_method(this, "_do_add_action_set_editor", action_set_editor);
undo_redo->add_undo_method(this, "_do_remove_action_set_editor", action_set_editor);
undo_redo->commit_action(false);
return action_set_editor;
}
void OpenXRActionMapEditor::_remove_action_set(String p_name) {
ERR_FAIL_COND(action_map.is_null());
Ref<OpenXRActionSet> action_set = action_map->find_action_set(p_name);
ERR_FAIL_COND(action_set.is_null());
for (int i = 0; i < actionsets_vb->get_child_count(); i++) {
OpenXRActionSetEditor *action_set_editor = Object::cast_to<OpenXRActionSetEditor>(actionsets_vb->get_child(i));
if (action_set_editor && action_set_editor->get_action_set() == action_set) {
_on_remove_action_set(action_set_editor);
}
}
}
void OpenXRActionMapEditor::_on_add_action_set() {
ERR_FAIL_COND(action_map.is_null());
String new_name = "New";
int count = 0;
while (action_map->find_action_set(new_name).is_valid()) {
new_name = "New_" + itos(count++);
}
OpenXRActionSetEditor *new_action_set_editor = _add_action_set(new_name);
// Make sure our action set is the current tab
tabs->set_current_tab(0);
callable_mp(this, &OpenXRActionMapEditor::_set_focus_on_action_set).call_deferred(new_action_set_editor);
}
void OpenXRActionMapEditor::_set_focus_on_action_set(OpenXRActionSetEditor *p_action_set_editor) {
// Scroll down to our new entry
actionsets_scroll->ensure_control_visible(p_action_set_editor);
// Set focus on this entry
p_action_set_editor->set_focus_on_entry();
}
void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) {
ERR_FAIL_COND(action_map.is_null());
OpenXRActionSetEditor *action_set_editor = Object::cast_to<OpenXRActionSetEditor>(p_action_set_editor);
ERR_FAIL_NULL(action_set_editor);
ERR_FAIL_COND(action_set_editor->get_parent() != actionsets_vb);
Ref<OpenXRActionSet> action_set = action_set_editor->get_action_set();
ERR_FAIL_COND(action_set.is_null());
// Remove all actions first.
action_set_editor->remove_all_actions();
// Make sure we update our interaction profiles.
for (int i = 0; i < tabs->get_tab_count(); i++) {
// First tab won't be an interaction profile editor, but being thorough..
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
if (interaction_profile_editor) {
interaction_profile_editor->remove_all_for_action_set(action_set);
}
}
// And now we can remove our action set.
undo_redo->create_action(TTR("Remove action set"));
undo_redo->add_do_method(this, "_do_remove_action_set_editor", action_set_editor);
undo_redo->add_undo_method(this, "_do_add_action_set_editor", action_set_editor);
undo_redo->commit_action(true);
action_map->set_edited(true);
}
void OpenXRActionMapEditor::_on_action_removed(Ref<OpenXRAction> p_action) {
for (int i = 0; i < tabs->get_tab_count(); i++) {
// First tab won't be an interaction profile editor, but being thorough..
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
if (interaction_profile_editor) {
interaction_profile_editor->remove_all_for_action(p_action);
}
}
}
void OpenXRActionMapEditor::_on_add_interaction_profile() {
ERR_FAIL_COND(action_map.is_null());
PackedStringArray already_selected;
for (int i = 0; i < action_map->get_interaction_profile_count(); i++) {
already_selected.push_back(action_map->get_interaction_profile(i)->get_interaction_profile_path());
}
select_interaction_profile_dialog->open(already_selected);
}
void OpenXRActionMapEditor::_on_interaction_profile_selected(const String p_path) {
ERR_FAIL_COND(action_map.is_null());
Ref<OpenXRInteractionProfile> new_profile;
new_profile.instantiate();
new_profile->set_interaction_profile_path(p_path);
action_map->add_interaction_profile(new_profile);
action_map->set_edited(true);
OpenXRInteractionProfileEditorBase *interaction_profile_editor = _add_interaction_profile_editor(new_profile);
undo_redo->create_action(TTR("Add interaction profile"));
undo_redo->add_do_method(this, "_do_add_interaction_profile_editor", interaction_profile_editor);
undo_redo->add_undo_method(this, "_do_remove_interaction_profile_editor", interaction_profile_editor);
undo_redo->commit_action(false);
tabs->set_current_tab(tabs->get_tab_count() - 1);
}
void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) {
Error err = OK;
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists(p_path)) {
action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error loading %s: %s."), edited_path, error_names[err]));
edited_path = "";
header_label->set_text("");
return;
}
} else if (p_create_new_if_missing) {
action_map.instantiate();
action_map->create_default_action_sets();
// Save it immediately
err = ResourceSaver::save(action_map, p_path);
if (err != OK) {
// Show warning but continue.
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), p_path, error_names[err]));
}
}
edited_path = p_path;
header_label->set_text(TTR("OpenXR Action map:") + " " + edited_path.get_file());
}
void OpenXRActionMapEditor::_on_save_action_map() {
Error err = ResourceSaver::save(action_map, edited_path);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err]));
return;
}
// TODO should clear undo/redo history
// out with the old
_clear_action_map();
_create_action_sets();
_create_interaction_profiles();
}
void OpenXRActionMapEditor::_on_reset_to_default_layout() {
// TODO should clear undo/redo history
// out with the old
_clear_action_map();
// create a new one
action_map.unref();
action_map.instantiate();
action_map->create_default_action_sets();
action_map->set_edited(true);
_create_action_sets();
_create_interaction_profiles();
}
void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) {
}
void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) {
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(p_tab));
ERR_FAIL_NULL(interaction_profile_editor);
undo_redo->create_action(TTR("Remove interaction profile"));
undo_redo->add_do_method(this, "_do_remove_interaction_profile_editor", interaction_profile_editor);
undo_redo->add_undo_method(this, "_do_add_interaction_profile_editor", interaction_profile_editor);
undo_redo->commit_action(true);
action_map->set_edited(true);
}
void OpenXRActionMapEditor::_do_add_action_set_editor(OpenXRActionSetEditor *p_action_set_editor) {
Ref<OpenXRActionSet> action_set = p_action_set_editor->get_action_set();
ERR_FAIL_COND(action_set.is_null());
action_map->add_action_set(action_set);
actionsets_vb->add_child(p_action_set_editor);
}
void OpenXRActionMapEditor::_do_remove_action_set_editor(OpenXRActionSetEditor *p_action_set_editor) {
Ref<OpenXRActionSet> action_set = p_action_set_editor->get_action_set();
ERR_FAIL_COND(action_set.is_null());
actionsets_vb->remove_child(p_action_set_editor);
action_map->remove_action_set(action_set);
}
void OpenXRActionMapEditor::_do_add_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor) {
Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profile_editor->get_interaction_profile();
ERR_FAIL_COND(interaction_profile.is_null());
action_map->add_interaction_profile(interaction_profile);
tabs->add_child(p_interaction_profile_editor);
tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
tabs->set_current_tab(tabs->get_tab_count() - 1);
}
void OpenXRActionMapEditor::_do_remove_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor) {
Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profile_editor->get_interaction_profile();
ERR_FAIL_COND(interaction_profile.is_null());
tabs->remove_child(p_interaction_profile_editor);
action_map->remove_interaction_profile(interaction_profile);
}
void OpenXRActionMapEditor::open_action_map(String p_path) {
EditorNode::get_bottom_panel()->make_item_visible(this);
// out with the old...
_clear_action_map();
// now load in our new action map
_load_action_map(p_path);
_create_action_sets();
_create_interaction_profiles();
}
void OpenXRActionMapEditor::_clear_action_map() {
while (actionsets_vb->get_child_count() > 0) {
Node *child = actionsets_vb->get_child(0);
actionsets_vb->remove_child(child);
child->queue_free();
}
for (int i = tabs->get_tab_count() - 1; i >= 0; --i) {
// First tab won't be an interaction profile editor, but being thorough..
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
if (interaction_profile_editor) {
tabs->remove_child(interaction_profile_editor);
interaction_profile_editor->queue_free();
}
}
}
void OpenXRActionMapEditor::register_interaction_profile_editor(const String &p_for_path, const String &p_editor_class) {
interaction_profile_editors[p_for_path] = p_editor_class;
}
void OpenXRActionMapEditor::register_binding_modifier_editor(const String &p_binding_modifier_class, const String &p_editor_class) {
binding_modifier_editors[p_binding_modifier_class] = p_editor_class;
}
String OpenXRActionMapEditor::get_binding_modifier_editor_class(const String &p_binding_modifier_class) {
if (binding_modifier_editors.has(p_binding_modifier_class)) {
return binding_modifier_editors[p_binding_modifier_class];
}
return OpenXRBindingModifierEditor::get_class_static();
}
OpenXRActionMapEditor::OpenXRActionMapEditor() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_custom_minimum_size(Size2(0.0, 300.0 * EDSCALE));
top_hb = memnew(HBoxContainer);
add_child(top_hb);
header_label = memnew(Label);
header_label->set_text(String(TTR("Action Map")));
header_label->set_clip_text(true);
header_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
top_hb->add_child(header_label);
add_action_set = memnew(Button);
add_action_set->set_text(TTR("Add Action Set"));
add_action_set->set_tooltip_text(TTR("Add an action set."));
add_action_set->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionMapEditor::_on_add_action_set));
top_hb->add_child(add_action_set);
add_interaction_profile = memnew(Button);
add_interaction_profile->set_text(TTR("Add profile"));
add_interaction_profile->set_tooltip_text(TTR("Add an interaction profile."));
add_interaction_profile->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionMapEditor::_on_add_interaction_profile));
top_hb->add_child(add_interaction_profile);
VSeparator *vseparator = memnew(VSeparator);
top_hb->add_child(vseparator);
save_as = memnew(Button);
save_as->set_text(TTR("Save"));
save_as->set_tooltip_text(TTR("Save this OpenXR action map."));
save_as->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionMapEditor::_on_save_action_map));
top_hb->add_child(save_as);
_default = memnew(Button);
_default->set_text(TTR("Reset to Default"));
_default->set_tooltip_text(TTR("Reset to default OpenXR action map."));
_default->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionMapEditor::_on_reset_to_default_layout));
top_hb->add_child(_default);
tabs = memnew(TabContainer);
tabs->set_h_size_flags(SIZE_EXPAND_FILL);
tabs->set_v_size_flags(SIZE_EXPAND_FILL);
tabs->set_theme_type_variation("TabContainerOdd");
tabs->connect("tab_changed", callable_mp(this, &OpenXRActionMapEditor::_on_tabs_tab_changed));
tabs->connect("tab_button_pressed", callable_mp(this, &OpenXRActionMapEditor::_on_tab_button_pressed));
add_child(tabs);
actionsets_scroll = memnew(ScrollContainer);
actionsets_scroll->set_h_size_flags(SIZE_EXPAND_FILL);
actionsets_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
actionsets_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
tabs->add_child(actionsets_scroll);
actionsets_scroll->set_name(TTR("Action Sets"));
actionsets_vb = memnew(VBoxContainer);
actionsets_vb->set_h_size_flags(SIZE_EXPAND_FILL);
actionsets_scroll->add_child(actionsets_vb);
select_interaction_profile_dialog = memnew(OpenXRSelectInteractionProfileDialog);
select_interaction_profile_dialog->connect("interaction_profile_selected", callable_mp(this, &OpenXRActionMapEditor::_on_interaction_profile_selected));
add_child(select_interaction_profile_dialog);
// Our Action map editor is only shown if openxr is enabled in project settings
// So load our action map and if it doesn't exist, create it right away.
_load_action_map(ResourceUID::ensure_path(GLOBAL_GET("xr/openxr/default_action_map")), true);
}

View File

@@ -0,0 +1,113 @@
/**************************************************************************/
/* openxr_action_map_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "openxr_action_set_editor.h"
#include "openxr_interaction_profile_editor.h"
#include "openxr_select_interaction_profile_dialog.h"
#include "core/templates/hash_map.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/tab_container.h"
class OpenXRActionMapEditor : public VBoxContainer {
GDCLASS(OpenXRActionMapEditor, VBoxContainer);
private:
static HashMap<String, String> interaction_profile_editors; // interaction profile path, interaction profile editor
static HashMap<String, String> binding_modifier_editors; // binding modifier class, binding modifiers editor
EditorUndoRedoManager *undo_redo;
String edited_path;
Ref<OpenXRActionMap> action_map;
HBoxContainer *top_hb = nullptr;
Label *header_label = nullptr;
Button *add_action_set = nullptr;
Button *add_interaction_profile = nullptr;
Button *load = nullptr;
Button *save_as = nullptr;
Button *_default = nullptr;
TabContainer *tabs = nullptr;
ScrollContainer *actionsets_scroll = nullptr;
VBoxContainer *actionsets_vb = nullptr;
OpenXRSelectInteractionProfileDialog *select_interaction_profile_dialog = nullptr;
OpenXRActionSetEditor *_add_action_set_editor(Ref<OpenXRActionSet> p_action_set);
void _create_action_sets();
OpenXRInteractionProfileEditorBase *_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile);
void _create_interaction_profiles();
OpenXRActionSetEditor *_add_action_set(String p_name);
void _remove_action_set(String p_name);
void _on_add_action_set();
void _set_focus_on_action_set(OpenXRActionSetEditor *p_action_set_editor);
void _on_remove_action_set(Object *p_action_set_editor);
void _on_action_removed(Ref<OpenXRAction> p_action);
void _on_add_interaction_profile();
void _on_interaction_profile_selected(const String p_path);
void _load_action_map(const String p_path, bool p_create_new_if_missing = false);
void _on_save_action_map();
void _on_reset_to_default_layout();
void _on_tabs_tab_changed(int p_tab);
void _on_tab_button_pressed(int p_tab);
protected:
static void _bind_methods();
void _notification(int p_what);
void _clear_action_map();
// used for undo/redo
void _do_add_action_set_editor(OpenXRActionSetEditor *p_action_set_editor);
void _do_remove_action_set_editor(OpenXRActionSetEditor *p_action_set_editor);
void _do_add_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor);
void _do_remove_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor);
public:
static void register_interaction_profile_editor(const String &p_for_path, const String &p_editor_class);
static void register_binding_modifier_editor(const String &p_binding_modifier_class, const String &p_editor_class);
static String get_binding_modifier_editor_class(const String &p_binding_modifier_class);
void open_action_map(String p_path);
OpenXRActionMapEditor();
};

View File

@@ -0,0 +1,301 @@
/**************************************************************************/
/* openxr_action_set_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_action_set_editor.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_spin_slider.h"
#include "editor/themes/editor_scale.h"
#include "openxr_action_editor.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/text_edit.h"
void OpenXRActionSetEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionSetEditor::_do_set_name);
ClassDB::bind_method(D_METHOD("_do_set_localized_name", "name"), &OpenXRActionSetEditor::_do_set_localized_name);
ClassDB::bind_method(D_METHOD("_do_set_priority", "value"), &OpenXRActionSetEditor::_do_set_priority);
ClassDB::bind_method(D_METHOD("_do_add_action_editor", "action_editor"), &OpenXRActionSetEditor::_do_add_action_editor);
ClassDB::bind_method(D_METHOD("_do_remove_action_editor", "action_editor"), &OpenXRActionSetEditor::_do_remove_action_editor);
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_set_editor")));
ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::OBJECT, "action")));
}
void OpenXRActionSetEditor::_set_fold_icon() {
if (is_expanded) {
fold_btn->set_button_icon(get_theme_icon(SNAME("GuiTreeArrowDown"), EditorStringName(EditorIcons)));
} else {
fold_btn->set_button_icon(get_theme_icon(SNAME("GuiTreeArrowRight"), EditorStringName(EditorIcons)));
}
}
void OpenXRActionSetEditor::_theme_changed() {
_set_fold_icon();
add_action->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
rem_action_set->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
}
void OpenXRActionSetEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
} break;
}
}
OpenXRActionEditor *OpenXRActionSetEditor::_add_action_editor(Ref<OpenXRAction> p_action) {
OpenXRActionEditor *action_editor = memnew(OpenXRActionEditor(p_action));
action_editor->connect("remove", callable_mp(this, &OpenXRActionSetEditor::_on_remove_action));
actions_vb->add_child(action_editor);
return action_editor;
}
void OpenXRActionSetEditor::_on_toggle_expand() {
is_expanded = !is_expanded;
actions_vb->set_visible(is_expanded);
_set_fold_icon();
}
void OpenXRActionSetEditor::_on_action_set_name_changed(const String p_new_text) {
if (action_set->get_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Action Set"));
undo_redo->add_do_method(this, "_do_set_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_name", action_set->get_name());
undo_redo->commit_action(false);
// If our localized name matches our action set name, set this too
if (action_set->get_name() == action_set->get_localized_name()) {
undo_redo->create_action(TTR("Rename Action Sets Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action_set->get_localized_name());
undo_redo->commit_action(false);
action_set->set_localized_name(p_new_text);
action_set_localized_name->set_text(p_new_text);
}
action_set->set_name(p_new_text);
action_set->set_edited(true);
}
}
void OpenXRActionSetEditor::_do_set_name(const String p_new_text) {
action_set->set_name(p_new_text);
action_set_name->set_text(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_localized_name_changed(const String p_new_text) {
if (action_set->get_localized_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Action Sets Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action_set->get_localized_name());
undo_redo->commit_action(false);
action_set->set_localized_name(p_new_text);
action_set->set_edited(true);
}
}
void OpenXRActionSetEditor::_do_set_localized_name(const String p_new_text) {
action_set->set_localized_name(p_new_text);
action_set_localized_name->set_text(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_priority_changed(const double p_new_value) {
int64_t value = (int64_t)p_new_value;
if (action_set->get_priority() != value) {
undo_redo->create_action(TTR("Change Action Sets priority"));
undo_redo->add_do_method(this, "_do_set_priority", value);
undo_redo->add_undo_method(this, "_do_set_priority", action_set->get_priority());
undo_redo->commit_action(false);
action_set->set_priority(value);
action_set->set_edited(true);
}
}
void OpenXRActionSetEditor::_do_set_priority(int64_t p_value) {
action_set->set_priority(p_value);
action_set_priority->set_value_no_signal(p_value);
}
void OpenXRActionSetEditor::_on_add_action() {
Ref<OpenXRAction> new_action;
new_action.instantiate();
new_action->set_name("New");
new_action->set_localized_name("New");
action_set->add_action(new_action);
action_set->set_edited(true);
OpenXRActionEditor *action_editor = _add_action_editor(new_action);
undo_redo->create_action(TTR("Add action"));
undo_redo->add_do_method(this, "_do_add_action_editor", action_editor);
undo_redo->add_undo_method(this, "_do_remove_action_editor", action_editor);
undo_redo->commit_action(false);
// TODO handle focus
}
void OpenXRActionSetEditor::_on_remove_action_set() {
emit_signal("remove", this);
}
void OpenXRActionSetEditor::_on_remove_action(Object *p_action_editor) {
OpenXRActionEditor *action_editor = Object::cast_to<OpenXRActionEditor>(p_action_editor);
ERR_FAIL_NULL(action_editor);
ERR_FAIL_COND(action_editor->get_parent() != actions_vb);
Ref<OpenXRAction> action = action_editor->get_action();
ERR_FAIL_COND(action.is_null());
emit_signal("action_removed", action);
undo_redo->create_action(TTR("Delete action"));
undo_redo->add_do_method(this, "_do_remove_action_editor", action_editor);
undo_redo->add_undo_method(this, "_do_add_action_editor", action_editor);
undo_redo->commit_action(true);
action_set->set_edited(true);
}
void OpenXRActionSetEditor::_do_add_action_editor(OpenXRActionEditor *p_action_editor) {
Ref<OpenXRAction> action = p_action_editor->get_action();
ERR_FAIL_COND(action.is_null());
action_set->add_action(action);
actions_vb->add_child(p_action_editor);
}
void OpenXRActionSetEditor::_do_remove_action_editor(OpenXRActionEditor *p_action_editor) {
Ref<OpenXRAction> action = p_action_editor->get_action();
ERR_FAIL_COND(action.is_null());
actions_vb->remove_child(p_action_editor);
action_set->remove_action(action);
}
void OpenXRActionSetEditor::remove_all_actions() {
for (int i = actions_vb->get_child_count(); i > 0; --i) {
_on_remove_action(actions_vb->get_child(i));
}
}
void OpenXRActionSetEditor::set_focus_on_entry() {
ERR_FAIL_NULL(action_set_name);
action_set_name->grab_focus();
}
OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) {
undo_redo = EditorUndoRedoManager::get_singleton();
action_map = p_action_map;
action_set = p_action_set;
set_h_size_flags(Control::SIZE_EXPAND_FILL);
panel = memnew(PanelContainer);
panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
add_child(panel);
HBoxContainer *panel_hb = memnew(HBoxContainer);
panel_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
panel->add_child(panel_hb);
fold_btn = memnew(Button);
fold_btn->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
fold_btn->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionSetEditor::_on_toggle_expand));
fold_btn->set_accessibility_name(TTRC("Fold"));
fold_btn->set_flat(true);
panel_hb->add_child(fold_btn);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
panel_hb->add_child(main_vb);
action_set_hb = memnew(HBoxContainer);
action_set_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(action_set_hb);
action_set_name = memnew(LineEdit);
action_set_name->set_text(action_set->get_name());
action_set_name->set_tooltip_text(TTR("Internal name of the action. Some XR runtimes don't allow spaces or special characters."));
action_set_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_set_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionSetEditor::_on_action_set_name_changed));
action_set_name->set_accessibility_name(TTRC("Action Set Name"));
action_set_hb->add_child(action_set_name);
action_set_localized_name = memnew(LineEdit);
action_set_localized_name->set_text(action_set->get_localized_name());
action_set_localized_name->set_tooltip_text(TTR("Human-readable name of the action set. This can be displayed to end users."));
action_set_localized_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_set_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_set_localized_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionSetEditor::_on_action_set_localized_name_changed));
action_set_localized_name->set_accessibility_name(TTRC("Action Set Localized Name"));
action_set_hb->add_child(action_set_localized_name);
action_set_priority = memnew(EditorSpinSlider);
action_set_priority->set_tooltip_text(TTR("Priority of the action set. If multiple action sets bind to the same input, the action set with the highest priority will be updated."));
action_set_priority->set_editing_integer(true);
action_set_priority->set_min(2147483647.0);
action_set_priority->set_min(-2147483648.0);
action_set_priority->set_value_no_signal(action_set->get_priority());
action_set_priority->set_custom_minimum_size(Size2(75.0 * EDSCALE, 0.0));
action_set_priority->connect(SceneStringName(value_changed), callable_mp(this, &OpenXRActionSetEditor::_on_action_set_priority_changed));
action_set_priority->set_accessibility_name(TTRC("Action Set Priority"));
action_set_hb->add_child(action_set_priority);
add_action = memnew(Button);
add_action->set_tooltip_text(TTR("Add action."));
add_action->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionSetEditor::_on_add_action));
add_action->set_flat(true);
action_set_hb->add_child(add_action);
rem_action_set = memnew(Button);
rem_action_set->set_tooltip_text(TTR("Remove action set."));
rem_action_set->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionSetEditor::_on_remove_action_set));
rem_action_set->set_flat(true);
action_set_hb->add_child(rem_action_set);
actions_vb = memnew(VBoxContainer);
actions_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(actions_vb);
// Add our existing actions
Array actions = action_set->get_actions();
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
_add_action_editor(action);
}
}

View File

@@ -0,0 +1,96 @@
/**************************************************************************/
/* openxr_action_set_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_action_set.h"
#include "openxr_action_editor.h"
class EditorSpinSlider;
class BoxContainer;
class Button;
class LineEdit;
class PanelContainer;
class TextEdit;
class OpenXRActionSetEditor : public HBoxContainer {
GDCLASS(OpenXRActionSetEditor, HBoxContainer);
private:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRActionMap> action_map;
Ref<OpenXRActionSet> action_set;
bool is_expanded = true;
PanelContainer *panel = nullptr;
Button *fold_btn = nullptr;
VBoxContainer *main_vb = nullptr;
HBoxContainer *action_set_hb = nullptr;
LineEdit *action_set_name = nullptr;
LineEdit *action_set_localized_name = nullptr;
EditorSpinSlider *action_set_priority = nullptr;
Button *add_action = nullptr;
Button *rem_action_set = nullptr;
VBoxContainer *actions_vb = nullptr;
void _set_fold_icon();
void _theme_changed();
OpenXRActionEditor *_add_action_editor(Ref<OpenXRAction> p_action);
void _on_toggle_expand();
void _on_action_set_name_changed(const String p_new_text);
void _on_action_set_localized_name_changed(const String p_new_text);
void _on_action_set_priority_changed(const double p_new_value);
void _on_add_action();
void _on_remove_action_set();
void _on_remove_action(Object *p_action_editor);
protected:
static void _bind_methods();
void _notification(int p_what);
// used for undo/redo
void _do_set_name(const String p_new_text);
void _do_set_localized_name(const String p_new_text);
void _do_set_priority(int64_t value);
void _do_add_action_editor(OpenXRActionEditor *p_action_editor);
void _do_remove_action_editor(OpenXRActionEditor *p_action_editor);
public:
Ref<OpenXRActionSet> get_action_set() { return action_set; }
void set_focus_on_entry();
void remove_all_actions();
OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set);
};

View File

@@ -0,0 +1,289 @@
/**************************************************************************/
/* openxr_binding_modifier_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_binding_modifier_editor.h"
#include "editor/editor_string_names.h"
#include "scene/gui/option_button.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// EditorPropertyActionSet
void EditorPropertyActionSet::_set_read_only(bool p_read_only) {
options->set_disabled(p_read_only);
}
void EditorPropertyActionSet::_option_selected(int p_which) {
Ref<OpenXRActionSet> val = options->get_item_metadata(p_which);
emit_changed(get_edited_property(), val);
}
void EditorPropertyActionSet::update_property() {
Variant current = get_edited_property_value();
if (current.get_type() == Variant::NIL) {
options->select(-1);
return;
}
Ref<OpenXRActionSet> which = current;
for (int i = 0; i < options->get_item_count(); i++) {
if (which == (Ref<OpenXRActionSet>)options->get_item_metadata(i)) {
options->select(i);
return;
}
}
// Couldn't find it? deselect..
options->select(-1);
}
void EditorPropertyActionSet::setup(const Ref<OpenXRActionMap> &p_action_map) {
options->clear();
Array action_sets = p_action_map->get_action_sets();
for (Ref<OpenXRActionSet> action_set : action_sets) {
options->add_item(action_set->get_localized_name());
options->set_item_metadata(-1, action_set);
}
}
void EditorPropertyActionSet::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
EditorPropertyActionSet::EditorPropertyActionSet() {
options = memnew(OptionButton);
options->set_clip_text(true);
options->set_flat(true);
options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(options);
add_focusable(options);
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyActionSet::_option_selected));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// EditorPropertyBindingPath
void EditorPropertyBindingPath::_set_read_only(bool p_read_only) {
options->set_disabled(p_read_only);
}
void EditorPropertyBindingPath::_option_selected(int p_which) {
String val = options->get_item_metadata(p_which);
emit_changed(get_edited_property(), val);
}
void EditorPropertyBindingPath::update_property() {
Variant current = get_edited_property_value();
if (current.get_type() == Variant::NIL) {
options->select(-1);
return;
}
String which = current;
for (int i = 0; i < options->get_item_count(); i++) {
if (which == (String)options->get_item_metadata(i)) {
options->select(i);
return;
}
}
// Couldn't find it? deselect..
options->select(-1);
}
void EditorPropertyBindingPath::setup(const String &p_interaction_profile_path, Vector<OpenXRAction::ActionType> p_include_action_types) {
options->clear();
const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(p_interaction_profile_path);
// Determine toplevel paths
Vector<String> top_level_paths;
for (const OpenXRInteractionProfileMetadata::IOPath &io_path : profile_def->io_paths) {
if (!top_level_paths.has(io_path.top_level_path)) {
top_level_paths.push_back(io_path.top_level_path);
}
}
for (const String &top_level_path : top_level_paths) {
String top_level_name = OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_name(top_level_path);
for (const OpenXRInteractionProfileMetadata::IOPath &io_path : profile_def->io_paths) {
if (io_path.top_level_path == top_level_path && p_include_action_types.has(io_path.action_type)) {
options->add_item(top_level_name + "/" + io_path.display_name);
options->set_item_metadata(-1, io_path.openxr_path);
}
}
}
}
void EditorPropertyBindingPath::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
EditorPropertyBindingPath::EditorPropertyBindingPath() {
options = memnew(OptionButton);
options->set_clip_text(true);
options->set_flat(true);
options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(options);
add_focusable(options);
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyBindingPath::_option_selected));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRBindingModifierEditor
bool EditorInspectorPluginBindingModifier::can_handle(Object *p_object) {
Ref<OpenXRBindingModifier> binding_modifier(Object::cast_to<OpenXRBindingModifier>(p_object));
return binding_modifier.is_valid();
}
bool EditorInspectorPluginBindingModifier::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
Ref<OpenXRActionBindingModifier> action_binding_modifier(Object::cast_to<OpenXRActionBindingModifier>(p_object));
if (action_binding_modifier.is_valid()) {
if (p_type == Variant::OBJECT && p_hint == PROPERTY_HINT_RESOURCE_TYPE && p_hint_text == OpenXRActionSet::get_class_static()) {
OpenXRIPBinding *ip_binding = action_binding_modifier->get_ip_binding();
ERR_FAIL_NULL_V(ip_binding, false);
OpenXRActionMap *action_map = ip_binding->get_action_map();
ERR_FAIL_NULL_V(action_map, false);
EditorPropertyActionSet *action_set_property = memnew(EditorPropertyActionSet);
action_set_property->setup(action_map);
add_property_editor(p_path, action_set_property);
return true;
}
return false;
}
Ref<OpenXRIPBindingModifier> ip_binding_modifier(Object::cast_to<OpenXRIPBindingModifier>(p_object));
if (ip_binding_modifier.is_valid()) {
if (p_type == Variant::OBJECT && p_hint == PROPERTY_HINT_RESOURCE_TYPE && p_hint_text == OpenXRActionSet::get_class_static()) {
OpenXRInteractionProfile *interaction_profile = ip_binding_modifier->get_interaction_profile();
ERR_FAIL_NULL_V(interaction_profile, false);
OpenXRActionMap *action_map = interaction_profile->get_action_map();
ERR_FAIL_NULL_V(action_map, false);
EditorPropertyActionSet *action_set_property = memnew(EditorPropertyActionSet);
action_set_property->setup(action_map);
add_property_editor(p_path, action_set_property);
return true;
}
if (p_type == Variant::STRING && p_hint == PROPERTY_HINT_TYPE_STRING && p_hint_text == "binding_path") {
EditorPropertyBindingPath *binding_path_property = memnew(EditorPropertyBindingPath);
OpenXRInteractionProfile *interaction_profile = ip_binding_modifier->get_interaction_profile();
ERR_FAIL_NULL_V(interaction_profile, false);
Vector<OpenXRAction::ActionType> action_types;
action_types.push_back(OpenXRAction::OPENXR_ACTION_VECTOR2);
binding_path_property->setup(interaction_profile->get_interaction_profile_path(), action_types);
add_property_editor(p_path, binding_path_property);
return true;
}
return false;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRBindingModifierEditor
void OpenXRBindingModifierEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_binding_modifier"), &OpenXRBindingModifierEditor::get_binding_modifier);
ClassDB::bind_method(D_METHOD("setup", "action_map", "binding_modifier"), &OpenXRBindingModifierEditor::setup);
ADD_SIGNAL(MethodInfo("binding_modifier_removed", PropertyInfo(Variant::OBJECT, "binding_modifier_editor")));
}
void OpenXRBindingModifierEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
rem_binding_modifier_btn->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
} break;
}
}
void OpenXRBindingModifierEditor::_on_remove_binding_modifier() {
// Tell parent to remove us
emit_signal("binding_modifier_removed", this);
}
OpenXRBindingModifierEditor::OpenXRBindingModifierEditor() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
add_child(main_vb);
header_hb = memnew(HBoxContainer);
header_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(header_hb);
binding_modifier_title = memnew(Label);
binding_modifier_title->set_h_size_flags(Control::SIZE_EXPAND_FILL);
header_hb->add_child(binding_modifier_title);
rem_binding_modifier_btn = memnew(Button);
rem_binding_modifier_btn->set_tooltip_text(TTR("Remove this binding modifier."));
rem_binding_modifier_btn->connect(SceneStringName(pressed), callable_mp(this, &OpenXRBindingModifierEditor::_on_remove_binding_modifier));
rem_binding_modifier_btn->set_flat(true);
header_hb->add_child(rem_binding_modifier_btn);
editor_inspector = memnew(EditorInspector);
editor_inspector->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
editor_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
editor_inspector->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(editor_inspector);
}
void OpenXRBindingModifierEditor::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRBindingModifier> p_binding_modifier) {
ERR_FAIL_NULL(binding_modifier_title);
ERR_FAIL_NULL(editor_inspector);
action_map = p_action_map;
binding_modifier = p_binding_modifier;
if (p_binding_modifier.is_valid()) {
binding_modifier_title->set_text(p_binding_modifier->get_description());
editor_inspector->set_object_class(p_binding_modifier->get_class());
editor_inspector->edit(p_binding_modifier.ptr());
}
}

View File

@@ -0,0 +1,110 @@
/**************************************************************************/
/* openxr_binding_modifier_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_action_set.h"
#include "../action_map/openxr_binding_modifier.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector/editor_inspector.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/panel_container.h"
class EditorPropertyActionSet : public EditorProperty {
GDCLASS(EditorPropertyActionSet, EditorProperty);
OptionButton *options = nullptr;
void _option_selected(int p_which);
protected:
virtual void _set_read_only(bool p_read_only) override;
public:
void setup(const Ref<OpenXRActionMap> &p_action_map);
virtual void update_property() override;
void set_option_button_clip(bool p_enable);
EditorPropertyActionSet();
};
class EditorPropertyBindingPath : public EditorProperty {
GDCLASS(EditorPropertyBindingPath, EditorProperty);
OptionButton *options = nullptr;
void _option_selected(int p_which);
protected:
virtual void _set_read_only(bool p_read_only) override;
public:
void setup(const String &p_interaction_profile_path, Vector<OpenXRAction::ActionType> p_include_action_types);
virtual void update_property() override;
void set_option_button_clip(bool p_enable);
EditorPropertyBindingPath();
};
class EditorInspectorPluginBindingModifier : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginBindingModifier, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) override;
};
class OpenXRBindingModifierEditor : public PanelContainer {
GDCLASS(OpenXRBindingModifierEditor, PanelContainer);
private:
HBoxContainer *header_hb = nullptr;
Label *binding_modifier_title = nullptr;
Button *rem_binding_modifier_btn = nullptr;
EditorInspector *editor_inspector = nullptr;
protected:
VBoxContainer *main_vb = nullptr;
EditorUndoRedoManager *undo_redo;
Ref<OpenXRBindingModifier> binding_modifier;
Ref<OpenXRActionMap> action_map;
static void _bind_methods();
void _notification(int p_what);
void _on_remove_binding_modifier();
public:
Ref<OpenXRBindingModifier> get_binding_modifier() const { return binding_modifier; }
virtual void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRBindingModifier> p_binding_modifier);
OpenXRBindingModifierEditor();
};

View File

@@ -0,0 +1,260 @@
/**************************************************************************/
/* openxr_binding_modifiers_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_binding_modifiers_dialog.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "openxr_action_map_editor.h"
#include "editor/themes/editor_scale.h"
void OpenXRBindingModifiersDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_add_binding_modifier_editor", "binding_modifier_editor"), &OpenXRBindingModifiersDialog::_do_add_binding_modifier_editor);
ClassDB::bind_method(D_METHOD("_do_remove_binding_modifier_editor", "binding_modifier_editor"), &OpenXRBindingModifiersDialog::_do_remove_binding_modifier_editor);
}
void OpenXRBindingModifiersDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
_create_binding_modifiers();
} break;
case NOTIFICATION_THEME_CHANGED: {
if (binding_modifier_sc) {
binding_modifier_sc->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
}
} break;
}
}
OpenXRBindingModifierEditor *OpenXRBindingModifiersDialog::_add_binding_modifier_editor(Ref<OpenXRBindingModifier> p_binding_modifier) {
ERR_FAIL_COND_V(p_binding_modifier.is_null(), nullptr);
String class_name = p_binding_modifier->get_class();
ERR_FAIL_COND_V(class_name.is_empty(), nullptr);
String editor_class = OpenXRActionMapEditor::get_binding_modifier_editor_class(class_name);
ERR_FAIL_COND_V(editor_class.is_empty(), nullptr);
OpenXRBindingModifierEditor *new_editor = nullptr;
Object *obj = ClassDB::instantiate(editor_class);
if (obj) {
new_editor = Object::cast_to<OpenXRBindingModifierEditor>(obj);
if (!new_editor) {
// Not of correct type?? Free it.
memfree(obj);
}
}
ERR_FAIL_NULL_V(new_editor, nullptr);
new_editor->setup(action_map, p_binding_modifier);
new_editor->connect("binding_modifier_removed", callable_mp(this, &OpenXRBindingModifiersDialog::_on_remove_binding_modifier));
binding_modifiers_vb->add_child(new_editor);
new_editor->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
return new_editor;
}
void OpenXRBindingModifiersDialog::_create_binding_modifiers() {
Array new_binding_modifiers;
if (ip_binding.is_valid()) {
new_binding_modifiers = ip_binding->get_binding_modifiers();
} else if (interaction_profile.is_valid()) {
new_binding_modifiers = interaction_profile->get_binding_modifiers();
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
for (int i = 0; i < new_binding_modifiers.size(); i++) {
Ref<OpenXRBindingModifier> binding_modifier = new_binding_modifiers[i];
_add_binding_modifier_editor(binding_modifier);
}
}
void OpenXRBindingModifiersDialog::_on_add_binding_modifier() {
create_dialog->popup_create(false);
}
void OpenXRBindingModifiersDialog::_on_remove_binding_modifier(Object *p_binding_modifier_editor) {
if (ip_binding.is_valid()) {
ip_binding->set_edited(true);
} else if (interaction_profile.is_valid()) {
interaction_profile->set_edited(true);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
OpenXRBindingModifierEditor *binding_modifier_editor = Object::cast_to<OpenXRBindingModifierEditor>(p_binding_modifier_editor);
ERR_FAIL_NULL(binding_modifier_editor);
ERR_FAIL_COND(binding_modifier_editor->get_parent() != binding_modifiers_vb);
undo_redo->create_action(TTR("Remove binding modifier"));
undo_redo->add_do_method(this, "_do_remove_binding_modifier_editor", binding_modifier_editor);
undo_redo->add_undo_method(this, "_do_add_binding_modifier_editor", binding_modifier_editor);
undo_redo->commit_action(true);
}
void OpenXRBindingModifiersDialog::_on_dialog_created() {
// Instance new binding modifier object
Variant obj = create_dialog->instantiate_selected();
ERR_FAIL_COND(obj.get_type() != Variant::OBJECT);
Ref<OpenXRBindingModifier> new_binding_modifier = obj;
ERR_FAIL_COND(new_binding_modifier.is_null());
if (ip_binding.is_valid()) {
// Add it to our binding.
ip_binding->add_binding_modifier(new_binding_modifier);
ip_binding->set_edited(true);
} else if (interaction_profile.is_valid()) {
// Add it to our interaction profile.
interaction_profile->add_binding_modifier(new_binding_modifier);
interaction_profile->set_edited(true);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
// Create our editor for this.
OpenXRBindingModifierEditor *binding_modifier_editor = _add_binding_modifier_editor(new_binding_modifier);
ERR_FAIL_NULL(binding_modifier_editor);
// Add undo/redo.
undo_redo->create_action(TTR("Add binding modifier"));
undo_redo->add_do_method(this, "_do_add_binding_modifier_editor", binding_modifier_editor);
undo_redo->add_undo_method(this, "_do_remove_binding_modifier_editor", binding_modifier_editor);
undo_redo->commit_action(false);
}
void OpenXRBindingModifiersDialog::_do_add_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor) {
Ref<OpenXRBindingModifier> binding_modifier = p_binding_modifier_editor->get_binding_modifier();
ERR_FAIL_COND(binding_modifier.is_null());
if (ip_binding.is_valid()) {
// Add it to our binding
ip_binding->add_binding_modifier(binding_modifier);
} else if (interaction_profile.is_valid()) {
// Add it to our interaction profile
interaction_profile->add_binding_modifier(binding_modifier);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
binding_modifiers_vb->add_child(p_binding_modifier_editor);
}
void OpenXRBindingModifiersDialog::_do_remove_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor) {
Ref<OpenXRBindingModifier> binding_modifier = p_binding_modifier_editor->get_binding_modifier();
ERR_FAIL_COND(binding_modifier.is_null());
if (ip_binding.is_valid()) {
// Remove it from our binding.
ip_binding->remove_binding_modifier(binding_modifier);
} else if (interaction_profile.is_valid()) {
// Removed it to from interaction profile.
interaction_profile->remove_binding_modifier(binding_modifier);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
binding_modifiers_vb->remove_child(p_binding_modifier_editor);
}
OpenXRBindingModifiersDialog::OpenXRBindingModifiersDialog() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_transient(true);
binding_modifier_sc = memnew(ScrollContainer);
binding_modifier_sc->set_custom_minimum_size(Size2(350.0 * EDSCALE, 0.0));
binding_modifier_sc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
binding_modifier_sc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
binding_modifier_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
add_child(binding_modifier_sc);
binding_modifiers_vb = memnew(VBoxContainer);
binding_modifiers_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
binding_modifier_sc->add_child(binding_modifiers_vb);
binding_warning_label = memnew(Label);
binding_warning_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
binding_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
binding_warning_label->set_text(TTR("Note: modifiers will only be applied if supported on the host system."));
binding_modifiers_vb->add_child(binding_warning_label);
add_binding_modifier_btn = memnew(Button);
add_binding_modifier_btn->set_text(TTR("Add binding modifier"));
add_binding_modifier_btn->connect("pressed", callable_mp(this, &OpenXRBindingModifiersDialog::_on_add_binding_modifier));
binding_modifiers_vb->add_child(add_binding_modifier_btn);
// TODO may need to create our own dialog for this that can filter on binding modifiers recorded on interaction profiles or on individual bindings.
create_dialog = memnew(CreateDialog);
create_dialog->set_transient(true);
create_dialog->connect("create", callable_mp(this, &OpenXRBindingModifiersDialog::_on_dialog_created));
add_child(create_dialog);
}
void OpenXRBindingModifiersDialog::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile, Ref<OpenXRIPBinding> p_ip_binding) {
OpenXRInteractionProfileMetadata *meta_data = OpenXRInteractionProfileMetadata::get_singleton();
action_map = p_action_map;
interaction_profile = p_interaction_profile;
ip_binding = p_ip_binding;
String profile_path = interaction_profile->get_interaction_profile_path();
if (ip_binding.is_valid()) {
String action_name = "unset";
String path_name = "unset";
Ref<OpenXRAction> action = p_ip_binding->get_action();
if (action.is_valid()) {
action_name = action->get_name_with_set();
}
const OpenXRInteractionProfileMetadata::IOPath *io_path = meta_data->get_io_path(profile_path, p_ip_binding->get_binding_path());
if (io_path != nullptr) {
path_name = io_path->display_name;
}
create_dialog->set_base_type("OpenXRActionBindingModifier");
set_title(TTR("Binding modifiers for:") + " " + action_name + ": " + path_name);
} else if (interaction_profile.is_valid()) {
String profile_name = profile_path;
const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = meta_data->get_profile(profile_path);
if (profile_def != nullptr) {
profile_name = profile_def->display_name;
}
create_dialog->set_base_type("OpenXRIPBindingModifier");
set_title(TTR("Binding modifiers for:") + " " + profile_name);
}
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************/
/* openxr_binding_modifiers_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_interaction_profile.h"
#include "../editor/openxr_binding_modifier_editor.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/create_dialog.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/scroll_container.h"
class OpenXRBindingModifiersDialog : public AcceptDialog {
GDCLASS(OpenXRBindingModifiersDialog, AcceptDialog);
private:
ScrollContainer *binding_modifier_sc = nullptr;
VBoxContainer *binding_modifiers_vb = nullptr;
Label *binding_warning_label = nullptr;
Button *add_binding_modifier_btn = nullptr;
CreateDialog *create_dialog = nullptr;
OpenXRBindingModifierEditor *_add_binding_modifier_editor(Ref<OpenXRBindingModifier> p_binding_modifier);
void _create_binding_modifiers();
void _on_add_binding_modifier();
void _on_remove_binding_modifier(Object *p_binding_modifier_editor);
void _on_dialog_created();
protected:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRActionMap> action_map;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRIPBinding> ip_binding;
static void _bind_methods();
void _notification(int p_what);
// used for undo/redo
void _do_add_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor);
void _do_remove_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor);
public:
OpenXRBindingModifiersDialog();
void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile, Ref<OpenXRIPBinding> p_ip_binding = Ref<OpenXRIPBinding>());
};

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* openxr_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_editor_plugin.h"
#include "../action_map/openxr_action_map.h"
#include "editor/editor_node.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/settings/editor_command_palette.h"
void OpenXREditorPlugin::edit(Object *p_node) {
if (Object::cast_to<OpenXRActionMap>(p_node)) {
String path = Object::cast_to<OpenXRActionMap>(p_node)->get_path();
if (path.is_resource_file()) {
action_map_editor->open_action_map(path);
}
}
}
bool OpenXREditorPlugin::handles(Object *p_node) const {
return (Object::cast_to<OpenXRActionMap>(p_node) != nullptr);
}
void OpenXREditorPlugin::make_visible(bool p_visible) {
}
OpenXREditorPlugin::OpenXREditorPlugin() {
action_map_editor = memnew(OpenXRActionMapEditor);
EditorNode::get_bottom_panel()->add_item(TTRC("OpenXR Action Map"), action_map_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_openxr_action_map_bottom_panel", TTRC("Toggle OpenXR Action Map Bottom Panel")));
binding_modifier_inspector_plugin = Ref<EditorInspectorPluginBindingModifier>(memnew(EditorInspectorPluginBindingModifier));
EditorInspector::add_inspector_plugin(binding_modifier_inspector_plugin);
#ifndef ANDROID_ENABLED
select_runtime = memnew(OpenXRSelectRuntime);
add_control_to_container(CONTAINER_TOOLBAR, select_runtime);
#endif
}

View File

@@ -0,0 +1,56 @@
/**************************************************************************/
/* openxr_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_action_map_editor.h"
#include "openxr_binding_modifier_editor.h"
#include "openxr_select_runtime.h"
#include "editor/plugins/editor_plugin.h"
class OpenXREditorPlugin : public EditorPlugin {
GDCLASS(OpenXREditorPlugin, EditorPlugin);
OpenXRActionMapEditor *action_map_editor = nullptr;
Ref<EditorInspectorPluginBindingModifier> binding_modifier_inspector_plugin = nullptr;
#ifndef ANDROID_ENABLED
OpenXRSelectRuntime *select_runtime = nullptr;
#endif
public:
virtual String get_plugin_name() const override { return "OpenXRPlugin"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_node) override;
virtual bool handles(Object *p_node) const override;
virtual void make_visible(bool p_visible) override;
OpenXREditorPlugin();
};

View File

@@ -0,0 +1,405 @@
/**************************************************************************/
/* openxr_interaction_profile_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_interaction_profile_editor.h"
#include "../openxr_api.h"
#include "editor/editor_string_names.h"
///////////////////////////////////////////////////////////////////////////
// Interaction profile editor base
void OpenXRInteractionProfileEditorBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("setup", "action_map", "interaction_profile"), &OpenXRInteractionProfileEditorBase::setup);
ClassDB::bind_method(D_METHOD("_add_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_add_binding);
ClassDB::bind_method(D_METHOD("_remove_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_remove_binding);
}
void OpenXRInteractionProfileEditorBase::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_interaction_profile();
} break;
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
} break;
}
}
void OpenXRInteractionProfileEditorBase::_do_update_interaction_profile() {
if (!is_dirty) {
is_dirty = true;
callable_mp(this, &OpenXRInteractionProfileEditorBase::_update_interaction_profile).call_deferred();
}
}
void OpenXRInteractionProfileEditorBase::_add_binding(const String p_action, const String p_path) {
ERR_FAIL_COND(action_map.is_null());
ERR_FAIL_COND(interaction_profile.is_null());
Ref<OpenXRAction> action = action_map->get_action(p_action);
ERR_FAIL_COND(action.is_null());
Ref<OpenXRIPBinding> binding = interaction_profile->find_binding(action, p_path);
if (binding.is_null()) {
// create a new binding
binding.instantiate();
binding->set_action(action);
binding->set_binding_path(p_path);
// add it to our interaction profile
interaction_profile->add_binding(binding);
interaction_profile->set_edited(true);
binding->set_edited(true);
}
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
_do_update_interaction_profile();
}
void OpenXRInteractionProfileEditorBase::_remove_binding(const String p_action, const String p_path) {
ERR_FAIL_COND(action_map.is_null());
ERR_FAIL_COND(interaction_profile.is_null());
Ref<OpenXRAction> action = action_map->get_action(p_action);
ERR_FAIL_COND(action.is_null());
Ref<OpenXRIPBinding> binding = interaction_profile->find_binding(action, p_path);
if (binding.is_valid()) {
interaction_profile->remove_binding(binding);
interaction_profile->set_edited(true);
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
_do_update_interaction_profile();
}
}
void OpenXRInteractionProfileEditorBase::_update_interaction_profile() {
if (!is_dirty) {
// no need to update
return;
}
// Nothing to do here for now..
// and we've updated it...
is_dirty = false;
}
void OpenXRInteractionProfileEditorBase::_theme_changed() {
if (binding_modifiers_btn) {
binding_modifiers_btn->set_button_icon(get_theme_icon(SNAME("Modifiers"), EditorStringName(EditorIcons)));
}
}
void OpenXRInteractionProfileEditorBase::remove_all_for_action_set(Ref<OpenXRActionSet> p_action_set) {
// Note, don't need to remove bindings themselves as remove_all_for_action will be called for each before this is called.
// TODO update binding modifiers
}
void OpenXRInteractionProfileEditorBase::remove_all_for_action(Ref<OpenXRAction> p_action) {
Vector<Ref<OpenXRIPBinding>> bindings = interaction_profile->get_bindings_for_action(p_action);
if (bindings.size() > 0) {
String action_name = p_action->get_name_with_set();
// For our undo/redo we process all paths
undo_redo->create_action(TTR("Remove action from interaction profile"));
for (const Ref<OpenXRIPBinding> &binding : bindings) {
undo_redo->add_do_method(this, "_remove_binding", action_name, binding->get_binding_path());
undo_redo->add_undo_method(this, "_add_binding", action_name, binding->get_binding_path());
}
undo_redo->commit_action(false);
// But remove them all in one go so we're more efficient in updating our UI.
for (const Ref<OpenXRIPBinding> &binding : bindings) {
interaction_profile->remove_binding(binding);
}
interaction_profile->set_edited(true);
// Update our toplevel paths
p_action->set_toplevel_paths(action_map->get_top_level_paths(p_action));
_do_update_interaction_profile();
}
}
void OpenXRInteractionProfileEditorBase::_on_open_binding_modifiers() {
binding_modifiers_dialog->popup_centered(Size2i(500, 400));
}
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_h_size_flags(SIZE_EXPAND_FILL);
set_v_size_flags(SIZE_EXPAND_FILL);
interaction_profile_sc = memnew(ScrollContainer);
interaction_profile_sc->set_h_size_flags(SIZE_EXPAND_FILL);
interaction_profile_sc->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(interaction_profile_sc);
binding_modifiers_dialog = memnew(OpenXRBindingModifiersDialog);
add_child(binding_modifiers_dialog);
toolbar_vb = memnew(VBoxContainer);
toolbar_vb->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(toolbar_vb);
binding_modifiers_btn = memnew(Button);
binding_modifiers_btn->set_tooltip_text(TTR("Edit binding modifiers"));
binding_modifiers_btn->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditorBase::_on_open_binding_modifiers));
// TODO show visual difference if there are binding modifiers for this interaction profile
toolbar_vb->add_child(binding_modifiers_btn);
}
void OpenXRInteractionProfileEditorBase::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
ERR_FAIL_NULL(binding_modifiers_dialog);
binding_modifiers_dialog->setup(p_action_map, p_interaction_profile);
action_map = p_action_map;
interaction_profile = p_interaction_profile;
String profile_path = interaction_profile->get_interaction_profile_path();
String profile_name = profile_path;
profile_def = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(profile_path);
if (profile_def != nullptr) {
profile_name = profile_def->display_name;
if (!profile_def->openxr_extension_name.is_empty()) {
profile_name += "*";
tooltip = vformat(TTR("Note: This interaction profile requires extension %s support."), profile_def->openxr_extension_name);
}
}
set_name(profile_name);
// Make sure it is updated when it enters the tree...
is_dirty = true;
}
///////////////////////////////////////////////////////////////////////////
// Default interaction profile editor
void OpenXRInteractionProfileEditor::select_action_for(const String p_io_path) {
selecting_for_io_path = p_io_path;
select_action_dialog->open();
}
void OpenXRInteractionProfileEditor::_on_action_selected(const String p_action) {
undo_redo->create_action(TTR("Add binding"));
undo_redo->add_do_method(this, "_add_binding", p_action, selecting_for_io_path);
undo_redo->add_undo_method(this, "_remove_binding", p_action, selecting_for_io_path);
undo_redo->commit_action(true);
selecting_for_io_path = "";
}
void OpenXRInteractionProfileEditor::_on_remove_pressed(const String p_action, const String p_for_io_path) {
undo_redo->create_action(TTR("Remove binding"));
undo_redo->add_do_method(this, "_remove_binding", p_action, p_for_io_path);
undo_redo->add_undo_method(this, "_add_binding", p_action, p_for_io_path);
undo_redo->commit_action(true);
}
void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::IOPath *p_io_path) {
HBoxContainer *path_hb = memnew(HBoxContainer);
path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
p_container->add_child(path_hb);
Label *path_label = memnew(Label);
path_label->set_focus_mode(FOCUS_ACCESSIBILITY);
if (p_io_path->openxr_extension_name.is_empty()) {
path_label->set_text(p_io_path->display_name);
} else {
path_label->set_text(p_io_path->display_name + "*");
path_hb->set_tooltip_text(vformat(TTR("Note: This binding path requires extension %s support."), p_io_path->openxr_extension_name));
}
path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
path_hb->add_child(path_label);
Label *type_label = memnew(Label);
type_label->set_focus_mode(FOCUS_ACCESSIBILITY);
switch (p_io_path->action_type) {
case OpenXRAction::OPENXR_ACTION_BOOL: {
type_label->set_text(TTR("Boolean"));
} break;
case OpenXRAction::OPENXR_ACTION_FLOAT: {
type_label->set_text(TTR("Float"));
} break;
case OpenXRAction::OPENXR_ACTION_VECTOR2: {
type_label->set_text(TTR("Vector2"));
} break;
case OpenXRAction::OPENXR_ACTION_POSE: {
type_label->set_text(TTR("Pose"));
} break;
case OpenXRAction::OPENXR_ACTION_HAPTIC: {
type_label->set_text(TTR("Haptic"));
} break;
default: {
type_label->set_text(TTR("Unknown"));
} break;
}
type_label->set_custom_minimum_size(Size2(50.0, 0.0));
path_hb->add_child(type_label);
Button *path_add = memnew(Button);
path_add->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
path_add->set_flat(true);
path_add->connect(SceneStringName(pressed), callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for).bind(String(p_io_path->openxr_path)));
path_hb->add_child(path_add);
if (interaction_profile.is_valid()) {
String io_path = String(p_io_path->openxr_path);
Array bindings = interaction_profile->get_bindings();
for (Ref<OpenXRIPBinding> binding : bindings) {
if (binding->get_binding_path() == io_path) {
Ref<OpenXRAction> action = binding->get_action();
HBoxContainer *action_hb = memnew(HBoxContainer);
action_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
p_container->add_child(action_hb);
Control *indent_node = memnew(Control);
indent_node->set_custom_minimum_size(Size2(10.0, 0.0));
action_hb->add_child(indent_node);
Label *action_label = memnew(Label);
action_label->set_focus_mode(FOCUS_ACCESSIBILITY);
action_label->set_text(action->get_name_with_set() + ": " + action->get_localized_name());
action_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_hb->add_child(action_label);
OpenXRBindingModifiersDialog *action_binding_modifiers_dialog = memnew(OpenXRBindingModifiersDialog);
action_binding_modifiers_dialog->setup(action_map, interaction_profile, binding);
action_hb->add_child(action_binding_modifiers_dialog);
Button *action_binding_modifiers_btn = memnew(Button);
action_binding_modifiers_btn->set_flat(true);
action_binding_modifiers_btn->set_button_icon(get_theme_icon(SNAME("Modifiers"), EditorStringName(EditorIcons)));
action_binding_modifiers_btn->connect(SceneStringName(pressed), callable_mp((Window *)action_binding_modifiers_dialog, &Window::popup_centered).bind(Size2i(500, 400)));
action_binding_modifiers_btn->set_accessibility_name(TTRC("Modifiers"));
// TODO change style of button if there are binding modifiers
action_hb->add_child(action_binding_modifiers_btn);
Button *action_rem = memnew(Button);
action_rem->set_flat(true);
action_rem->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
action_rem->connect(SceneStringName(pressed), callable_mp((OpenXRInteractionProfileEditor *)this, &OpenXRInteractionProfileEditor::_on_remove_pressed).bind(action->get_name_with_set(), String(p_io_path->openxr_path)));
action_rem->set_accessibility_name(TTRC("Remove"));
action_hb->add_child(action_rem);
}
}
}
}
void OpenXRInteractionProfileEditor::_update_interaction_profile() {
ERR_FAIL_NULL(profile_def);
if (!is_dirty) {
// no need to update
return;
}
PackedStringArray requested_extensions = OpenXRAPI::get_all_requested_extensions();
// out with the old...
while (interaction_profile_hb->get_child_count() > 0) {
memdelete(interaction_profile_hb->get_child(0));
}
// in with the new...
// Determine toplevel paths
Vector<String> top_level_paths;
for (int i = 0; i < profile_def->io_paths.size(); i++) {
const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[i];
if (!top_level_paths.has(io_path->top_level_path)) {
top_level_paths.push_back(io_path->top_level_path);
}
}
for (int i = 0; i < top_level_paths.size(); i++) {
PanelContainer *panel = memnew(PanelContainer);
panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
interaction_profile_hb->add_child(panel);
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
VBoxContainer *container = memnew(VBoxContainer);
panel->add_child(container);
Label *label = memnew(Label);
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
label->set_text(OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_name(top_level_paths[i]));
container->add_child(label);
for (int j = 0; j < profile_def->io_paths.size(); j++) {
const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[j];
if (io_path->top_level_path == top_level_paths[i] && (io_path->openxr_extension_name.is_empty() || requested_extensions.has(io_path->openxr_extension_name))) {
_add_io_path(container, io_path);
}
}
}
OpenXRInteractionProfileEditorBase::_update_interaction_profile();
}
void OpenXRInteractionProfileEditor::_theme_changed() {
OpenXRInteractionProfileEditorBase::_theme_changed();
interaction_profile_sc->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
for (int i = 0; i < interaction_profile_hb->get_child_count(); i++) {
Control *panel = Object::cast_to<Control>(interaction_profile_hb->get_child(i));
if (panel) {
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
}
}
}
OpenXRInteractionProfileEditor::OpenXRInteractionProfileEditor() {
interaction_profile_hb = memnew(HBoxContainer);
interaction_profile_sc->add_child(interaction_profile_hb);
}
void OpenXRInteractionProfileEditor::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
OpenXRInteractionProfileEditorBase::setup(p_action_map, p_interaction_profile);
select_action_dialog = memnew(OpenXRSelectActionDialog(p_action_map));
select_action_dialog->connect("action_selected", callable_mp(this, &OpenXRInteractionProfileEditor::_on_action_selected));
add_child(select_action_dialog);
}

View File

@@ -0,0 +1,107 @@
/**************************************************************************/
/* openxr_interaction_profile_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_interaction_profile.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../editor/openxr_binding_modifiers_dialog.h"
#include "editor/editor_undo_redo_manager.h"
#include "openxr_select_action_dialog.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
class OpenXRInteractionProfileEditorBase : public HBoxContainer {
GDCLASS(OpenXRInteractionProfileEditorBase, HBoxContainer);
private:
OpenXRBindingModifiersDialog *binding_modifiers_dialog = nullptr;
VBoxContainer *toolbar_vb = nullptr;
Button *binding_modifiers_btn = nullptr;
void _on_open_binding_modifiers();
protected:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRActionMap> action_map;
ScrollContainer *interaction_profile_sc = nullptr;
bool is_dirty = false;
static void _bind_methods();
void _notification(int p_what);
const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = nullptr;
public:
String tooltip; // Tooltip text to show on tab
Ref<OpenXRInteractionProfile> get_interaction_profile() { return interaction_profile; }
virtual void _update_interaction_profile();
virtual void _theme_changed();
void _do_update_interaction_profile();
void _add_binding(const String p_action, const String p_path);
void _remove_binding(const String p_action, const String p_path);
void remove_all_for_action_set(Ref<OpenXRActionSet> p_action_set);
void remove_all_for_action(Ref<OpenXRAction> p_action);
virtual void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
OpenXRInteractionProfileEditorBase();
};
class OpenXRInteractionProfileEditor : public OpenXRInteractionProfileEditorBase {
GDCLASS(OpenXRInteractionProfileEditor, OpenXRInteractionProfileEditorBase);
private:
String selecting_for_io_path;
HBoxContainer *interaction_profile_hb = nullptr;
OpenXRSelectActionDialog *select_action_dialog = nullptr;
void _add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::IOPath *p_io_path);
public:
void select_action_for(const String p_io_path);
void _on_action_selected(const String p_action);
void _on_remove_pressed(const String p_action, const String p_for_io_path);
virtual void _update_interaction_profile() override;
virtual void _theme_changed() override;
virtual void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) override;
OpenXRInteractionProfileEditor();
};

View File

@@ -0,0 +1,135 @@
/**************************************************************************/
/* openxr_select_action_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_select_action_dialog.h"
#include "editor/themes/editor_scale.h"
void OpenXRSelectActionDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("action_selected", PropertyInfo(Variant::STRING, "action")));
}
void OpenXRSelectActionDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
} break;
}
}
void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
if (selected_action != "") {
NodePath button_path = action_buttons[selected_action];
Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(true);
}
}
selected_action = p_action;
if (selected_action != "") {
NodePath button_path = action_buttons[selected_action];
Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(false);
}
}
}
void OpenXRSelectActionDialog::open() {
ERR_FAIL_COND(action_map.is_null());
// Out with the old.
while (main_vb->get_child_count() > 0) {
memdelete(main_vb->get_child(0));
}
selected_action = "";
action_buttons.clear();
// In with the new.
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
Ref<OpenXRActionSet> action_set = action_sets[i];
Label *action_set_label = memnew(Label);
action_set_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
action_set_label->set_text(action_set->get_localized_name());
main_vb->add_child(action_set_label);
Array actions = action_set->get_actions();
for (int j = 0; j < actions.size(); j++) {
Ref<OpenXRAction> action = actions[j];
HBoxContainer *action_hb = memnew(HBoxContainer);
main_vb->add_child(action_hb);
Control *indent_node = memnew(Control);
indent_node->set_custom_minimum_size(Size2(10.0, 0.0));
action_hb->add_child(indent_node);
Button *action_button = memnew(Button);
String action_name = action->get_name_with_set();
action_button->set_flat(true);
action_button->set_text(action->get_name() + ": " + action->get_localized_name());
action_button->connect(SceneStringName(pressed), callable_mp(this, &OpenXRSelectActionDialog::_on_select_action).bind(action_name));
action_hb->add_child(action_button);
action_buttons[action_name] = action_button->get_path();
}
}
popup_centered();
}
void OpenXRSelectActionDialog::ok_pressed() {
if (selected_action == "") {
return;
}
emit_signal("action_selected", selected_action);
hide();
}
OpenXRSelectActionDialog::OpenXRSelectActionDialog(Ref<OpenXRActionMap> p_action_map) {
action_map = p_action_map;
set_title(TTR("Select an action"));
scroll = memnew(ScrollContainer);
scroll->set_custom_minimum_size(Size2(600.0 * EDSCALE, 400.0 * EDSCALE));
add_child(scroll);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scroll->add_child(main_vb);
}

View File

@@ -0,0 +1,65 @@
/**************************************************************************/
/* openxr_select_action_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/separator.h"
#include "scene/gui/text_edit.h"
class OpenXRSelectActionDialog : public ConfirmationDialog {
GDCLASS(OpenXRSelectActionDialog, ConfirmationDialog);
private:
Ref<OpenXRActionMap> action_map;
String selected_action;
Dictionary action_buttons;
VBoxContainer *main_vb = nullptr;
ScrollContainer *scroll = nullptr;
protected:
static void _bind_methods();
void _notification(int p_what);
public:
void _on_select_action(const String p_action);
void open();
virtual void ok_pressed() override;
OpenXRSelectActionDialog(Ref<OpenXRActionMap> p_action_map);
};

View File

@@ -0,0 +1,131 @@
/**************************************************************************/
/* openxr_select_interaction_profile_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_select_interaction_profile_dialog.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../openxr_api.h"
#include "editor/themes/editor_scale.h"
void OpenXRSelectInteractionProfileDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("interaction_profile_selected", PropertyInfo(Variant::STRING, "interaction_profile")));
}
void OpenXRSelectInteractionProfileDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
} break;
}
}
void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const String p_interaction_profile) {
if (selected_interaction_profile != "") {
NodePath button_path = ip_buttons[selected_interaction_profile];
Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(true);
}
}
selected_interaction_profile = p_interaction_profile;
if (selected_interaction_profile != "") {
NodePath button_path = ip_buttons[selected_interaction_profile];
Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(false);
}
}
}
void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_include) {
int available_count = 0;
OpenXRInteractionProfileMetadata *meta_data = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(meta_data);
// Out with the old.
while (main_vb->get_child_count() > 1) {
memdelete(main_vb->get_child(1));
}
PackedStringArray requested_extensions = OpenXRAPI::get_all_requested_extensions();
selected_interaction_profile = "";
ip_buttons.clear();
// In with the new.
PackedStringArray interaction_profiles = meta_data->get_interaction_profile_paths();
for (const String &path : interaction_profiles) {
const String extension = meta_data->get_interaction_profile_extension(path);
if (!p_do_not_include.has(path) && (extension.is_empty() || requested_extensions.has(extension))) {
Button *ip_button = memnew(Button);
ip_button->set_flat(true);
ip_button->set_text(meta_data->get_profile(path)->display_name);
ip_button->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
ip_button->connect(SceneStringName(pressed), callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile).bind(path));
main_vb->add_child(ip_button);
ip_buttons[path] = ip_button->get_path();
available_count++;
}
}
all_selected->set_visible(available_count == 0);
get_cancel_button()->set_visible(available_count > 0);
popup_centered();
}
void OpenXRSelectInteractionProfileDialog::ok_pressed() {
if (selected_interaction_profile != "") {
emit_signal("interaction_profile_selected", selected_interaction_profile);
}
hide();
}
OpenXRSelectInteractionProfileDialog::OpenXRSelectInteractionProfileDialog() {
set_title(TTR("Select an interaction profile"));
scroll = memnew(ScrollContainer);
scroll->set_custom_minimum_size(Size2(600.0 * EDSCALE, 400.0 * EDSCALE));
add_child(scroll);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scroll->add_child(main_vb);
all_selected = memnew(Label);
all_selected->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
all_selected->set_text(TTR("All interaction profiles have been added to the action map."));
main_vb->add_child(all_selected);
}

View File

@@ -0,0 +1,59 @@
/**************************************************************************/
/* openxr_select_interaction_profile_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/scroll_container.h"
class OpenXRSelectInteractionProfileDialog : public ConfirmationDialog {
GDCLASS(OpenXRSelectInteractionProfileDialog, ConfirmationDialog);
private:
String selected_interaction_profile;
Dictionary ip_buttons;
VBoxContainer *main_vb = nullptr;
ScrollContainer *scroll = nullptr;
Label *all_selected = nullptr;
protected:
static void _bind_methods();
void _notification(int p_what);
public:
void _on_select_interaction_profile(const String p_interaction_profile);
void open(PackedStringArray p_do_not_include);
virtual void ok_pressed() override;
OpenXRSelectInteractionProfileDialog();
};

View File

@@ -0,0 +1,128 @@
/**************************************************************************/
/* openxr_select_runtime.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_select_runtime.h"
#include "core/io/dir_access.h"
#include "core/os/os.h"
#include "editor/settings/editor_settings.h"
void OpenXRSelectRuntime::_update_items() {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
OS *os = OS::get_singleton();
Dictionary runtimes = EDITOR_GET("xr/openxr/runtime_paths");
int current_runtime = 0;
int index = 0;
String current_path = os->get_environment("XR_RUNTIME_JSON");
// Parse the user's home folder.
String home_folder = os->get_environment("HOME");
if (home_folder.is_empty()) {
home_folder = os->get_environment("HOMEDRIVE") + os->get_environment("HOMEPATH");
}
clear();
add_item("Default", -1);
set_item_metadata(index, "");
index++;
for (const KeyValue<Variant, Variant> &kv : runtimes) {
const String &key = kv.key;
const String &path = kv.value;
String adj_path = path.replace("~", home_folder);
if (da->file_exists(adj_path)) {
add_item(key, index);
set_item_metadata(index, adj_path);
if (current_path == adj_path) {
current_runtime = index;
}
index++;
}
}
select(current_runtime);
}
void OpenXRSelectRuntime::_on_item_selected(int p_which) {
OS *os = OS::get_singleton();
if (p_which == 0) {
// Return to default runtime
os->set_environment("XR_RUNTIME_JSON", "");
} else {
// Select the runtime we want
String runtime_path = get_item_metadata(p_which);
os->set_environment("XR_RUNTIME_JSON", runtime_path);
}
}
void OpenXRSelectRuntime::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ENTER_TREE: {
// Update dropdown
_update_items();
// Connect signal
connect(SceneStringName(item_selected), callable_mp(this, &OpenXRSelectRuntime::_on_item_selected));
} break;
case NOTIFICATION_EXIT_TREE: {
// Disconnect signal
disconnect(SceneStringName(item_selected), callable_mp(this, &OpenXRSelectRuntime::_on_item_selected));
} break;
}
}
OpenXRSelectRuntime::OpenXRSelectRuntime() {
Dictionary default_runtimes;
// Add known common runtimes by default.
#ifdef WINDOWS_ENABLED
default_runtimes["Meta"] = "C:\\Program Files\\Oculus\\Support\\oculus-runtime\\oculus_openxr_64.json";
default_runtimes["SteamVR"] = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\steamxr_win64.json";
default_runtimes["Varjo"] = "C:\\Program Files\\Varjo\\varjo-openxr\\VarjoOpenXR.json";
default_runtimes["WMR"] = "C:\\WINDOWS\\system32\\MixedRealityRuntime.json";
#endif
#ifdef LINUXBSD_ENABLED
default_runtimes["Monado"] = "/usr/share/openxr/1/openxr_monado.json";
default_runtimes["SteamVR"] = "~/.steam/steam/steamapps/common/SteamVR/steamxr_linux64.json";
#endif
// TODO: Move to editor_settings.cpp
EDITOR_DEF_RST("xr/openxr/runtime_paths", default_runtimes);
set_flat(true);
set_theme_type_variation("TopBarOptionButton");
set_fit_to_longest_item(false);
set_focus_mode(Control::FOCUS_NONE);
set_tooltip_text(TTR("Choose an XR runtime."));
}

View File

@@ -0,0 +1,47 @@
/**************************************************************************/
/* openxr_select_runtime.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "scene/gui/option_button.h"
class OpenXRSelectRuntime : public OptionButton {
GDCLASS(OpenXRSelectRuntime, OptionButton);
public:
OpenXRSelectRuntime();
protected:
void _notification(int p_notification);
private:
void _update_items();
void _on_item_selected(int p_which);
};

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
# These are platform dependent
if env["platform"] == "android":
env_openxr.add_source_files(module_obj, "platform/openxr_android_extension.cpp")
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "platform/openxr_vulkan_extension.cpp")
if env["metal"]:
env_openxr.add_source_files(module_obj, "platform/openxr_metal_extension.mm")
if env["opengl3"] and env["platform"] != "macos":
env_openxr.add_source_files(module_obj, "platform/openxr_opengl_extension.cpp")
if env["d3d12"]:
env_openxr.add_source_files(module_obj, "platform/openxr_d3d12_extension.cpp")
env.modules_sources += module_obj

View File

@@ -0,0 +1,70 @@
/**************************************************************************/
/* openxr_composition_layer_depth_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer_depth_extension.h"
OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::singleton = nullptr;
OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::get_singleton() {
return singleton;
}
OpenXRCompositionLayerDepthExtension::OpenXRCompositionLayerDepthExtension() {
singleton = this;
}
OpenXRCompositionLayerDepthExtension::~OpenXRCompositionLayerDepthExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRCompositionLayerDepthExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRCompositionLayerDepthExtension::is_available() {
return available;
}
int OpenXRCompositionLayerDepthExtension::get_composition_layer_count() {
return 0;
}
XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer(int p_index) {
// Seems this is all done in our base layer... Just in case this changes...
return nullptr;
}
int OpenXRCompositionLayerDepthExtension::get_composition_layer_order(int p_index) {
return 0;
}

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* openxr_composition_layer_depth_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRCompositionLayerDepthExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRCompositionLayerDepthExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRCompositionLayerDepthExtension *get_singleton();
OpenXRCompositionLayerDepthExtension();
virtual ~OpenXRCompositionLayerDepthExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual int get_composition_layer_count() override;
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
private:
static OpenXRCompositionLayerDepthExtension *singleton;
bool available = false;
};

View File

@@ -0,0 +1,538 @@
/**************************************************************************/
/* openxr_composition_layer_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer_extension.h"
#ifdef ANDROID_ENABLED
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#endif
#include "openxr_fb_update_swapchain_extension.h"
#include "platform/android/api/java_class_wrapper.h"
#include "servers/rendering/rendering_server_globals.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRCompositionLayerExtension
OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::singleton = nullptr;
OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::get_singleton() {
return singleton;
}
OpenXRCompositionLayerExtension::OpenXRCompositionLayerExtension() {
singleton = this;
}
OpenXRCompositionLayerExtension::~OpenXRCompositionLayerExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME] = &cylinder_ext_available;
request_extensions[XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME] = &equirect_ext_available;
#ifdef ANDROID_ENABLED
request_extensions[XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME] = &android_surface_ext_available;
#endif
return request_extensions;
}
void OpenXRCompositionLayerExtension::on_instance_created(const XrInstance p_instance) {
#ifdef ANDROID_ENABLED
EXT_INIT_XR_FUNC(xrDestroySwapchain);
EXT_INIT_XR_FUNC(xrCreateSwapchainAndroidSurfaceKHR);
#endif
}
void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_session) {
OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
}
void OpenXRCompositionLayerExtension::on_session_destroyed() {
OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);
#ifdef ANDROID_ENABLED
free_queued_android_surface_swapchains();
#endif
}
void OpenXRCompositionLayerExtension::on_pre_render() {
#ifdef ANDROID_ENABLED
free_queued_android_surface_swapchains();
#endif
for (OpenXRViewportCompositionLayerProvider *composition_layer : composition_layers) {
composition_layer->on_pre_render();
}
}
int OpenXRCompositionLayerExtension::get_composition_layer_count() {
return composition_layers.size();
}
XrCompositionLayerBaseHeader *OpenXRCompositionLayerExtension::get_composition_layer(int p_index) {
ERR_FAIL_INDEX_V(p_index, composition_layers.size(), nullptr);
return composition_layers[p_index]->get_composition_layer();
}
int OpenXRCompositionLayerExtension::get_composition_layer_order(int p_index) {
ERR_FAIL_INDEX_V(p_index, composition_layers.size(), 1);
return composition_layers[p_index]->get_sort_order();
}
void OpenXRCompositionLayerExtension::register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.push_back(p_composition_layer);
}
void OpenXRCompositionLayerExtension::unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.erase(p_composition_layer);
}
bool OpenXRCompositionLayerExtension::is_available(XrStructureType p_which) {
switch (p_which) {
case XR_TYPE_COMPOSITION_LAYER_QUAD: {
// Doesn't require an extension.
return true;
} break;
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {
return cylinder_ext_available;
} break;
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {
return equirect_ext_available;
} break;
default: {
ERR_PRINT(vformat("Unsupported composition layer type: %s", p_which));
return false;
}
}
}
#ifdef ANDROID_ENABLED
bool OpenXRCompositionLayerExtension::create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface) {
if (android_surface_ext_available) {
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, false);
XrResult result = xrCreateSwapchainAndroidSurfaceKHR(openxr_api->get_session(), p_info, r_swapchain, r_surface);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create Android surface swapchain [", openxr_api->get_error_string(result), "]");
return false;
}
return true;
}
return false;
}
void OpenXRCompositionLayerExtension::free_android_surface_swapchain(XrSwapchain p_swapchain) {
android_surface_swapchain_free_queue.push_back(p_swapchain);
}
void OpenXRCompositionLayerExtension::free_queued_android_surface_swapchains() {
for (XrSwapchain swapchain : android_surface_swapchain_free_queue) {
xrDestroySwapchain(swapchain);
}
android_surface_swapchain_free_queue.clear();
}
#endif
////////////////////////////////////////////////////////////////////////////
// OpenXRViewportCompositionLayerProvider
OpenXRViewportCompositionLayerProvider::OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer) {
composition_layer = p_composition_layer;
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
}
OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider() {
for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
extension->on_viewport_composition_layer_destroyed(composition_layer);
}
if (use_android_surface) {
free_swapchain();
} else {
// This will reset the viewport and free the swapchain too.
set_viewport(RID(), Size2i());
}
}
void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend) {
if (alpha_blend != p_alpha_blend) {
alpha_blend = p_alpha_blend;
if (alpha_blend) {
composition_layer->layerFlags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
} else {
composition_layer->layerFlags &= ~XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
}
}
}
void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i p_size) {
ERR_FAIL_COND(use_android_surface);
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
if (subviewport.viewport != p_viewport) {
if (subviewport.viewport.is_valid()) {
RID rt = rs->viewport_get_render_target(subviewport.viewport);
RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID());
}
subviewport.viewport = p_viewport;
if (subviewport.viewport.is_valid()) {
subviewport.viewport_size = p_size;
} else {
free_swapchain();
subviewport.viewport_size = Size2i();
}
}
}
void OpenXRViewportCompositionLayerProvider::set_use_android_surface(bool p_use_android_surface, Size2i p_size) {
#ifdef ANDROID_ENABLED
if (p_use_android_surface == use_android_surface) {
if (use_android_surface && swapchain_size != p_size) {
OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();
if (fb_update_swapchain_ext && fb_update_swapchain_ext->is_android_ext_enabled()) {
swapchain_size = p_size;
fb_update_swapchain_ext->update_swapchain_surface_size(android_surface.swapchain, swapchain_size);
}
}
return;
}
use_android_surface = p_use_android_surface;
if (use_android_surface) {
if (!composition_layer_extension->is_android_surface_swapchain_available()) {
ERR_PRINT_ONCE("OpenXR: Cannot use Android surface for composition layer because the extension isn't available");
}
if (subviewport.viewport.is_valid()) {
set_viewport(RID(), Size2i());
}
swapchain_size = p_size;
} else {
free_swapchain();
}
#endif
}
#ifdef ANDROID_ENABLED
void OpenXRViewportCompositionLayerProvider::create_android_surface() {
ERR_FAIL_COND(android_surface.swapchain != XR_NULL_HANDLE || android_surface.surface.is_valid());
ERR_FAIL_COND(!openxr_api || !openxr_api->is_running());
void *next_pointer = nullptr;
for (OpenXRExtensionWrapper *wrapper : openxr_api->get_registered_extension_wrappers()) {
void *np = wrapper->set_android_surface_swapchain_create_info_and_get_next_pointer(extension_property_values, next_pointer);
if (np != nullptr) {
next_pointer = np;
}
}
// The XR_FB_android_surface_swapchain_create extension mandates that format, sampleCount,
// faceCount, arraySize, and mipCount must be zero.
XrSwapchainCreateInfo info = {
XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
next_pointer, // next
0, // createFlags
XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, // usageFlags
0, // format
0, // sampleCount
(uint32_t)swapchain_size.x, // width
(uint32_t)swapchain_size.y, // height
0, // faceCount
0, // arraySize
0, // mipCount
};
jobject surface;
composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface);
swapchain_state.dirty = true;
if (surface) {
android_surface.surface.instantiate(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface);
}
}
#endif
Ref<JavaObject> OpenXRViewportCompositionLayerProvider::get_android_surface() {
#ifdef ANDROID_ENABLED
if (use_android_surface) {
if (android_surface.surface.is_null()) {
create_android_surface();
}
return android_surface.surface;
}
#endif
return Ref<JavaObject>();
}
void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) {
extension_property_values = p_extension_property_values;
extension_property_values_changed = true;
}
void OpenXRViewportCompositionLayerProvider::on_pre_render() {
#ifdef ANDROID_ENABLED
if (use_android_surface) {
if (android_surface.surface.is_null()) {
create_android_surface();
}
return;
}
#endif
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
if (subviewport.viewport.is_valid() && openxr_api && openxr_api->is_running()) {
RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(subviewport.viewport);
if (update_mode == RS::VIEWPORT_UPDATE_ONCE || update_mode == RS::VIEWPORT_UPDATE_ALWAYS) {
// Update our XR swapchain
if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) {
// Render to our XR swapchain image.
RID rt = rs->viewport_get_render_target(subviewport.viewport);
RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID(), RID());
}
}
}
if (swapchain_state.dirty) {
update_swapchain_state();
swapchain_state.dirty = false;
}
}
XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_composition_layer() {
if (openxr_api == nullptr || composition_layer_extension == nullptr) {
// OpenXR not initialized or we're in the editor?
return nullptr;
}
if (!composition_layer_extension->is_available(composition_layer->type)) {
// Selected type is not supported, ignore our layer.
return nullptr;
}
XrSwapchainSubImage subimage = {
0, // swapchain // NOLINT(modernize-use-nullptr) - 32-bit uses non-pointer uint64
{ { 0, 0 }, { 0, 0 } }, // imageRect
0, // imageArrayIndex
};
update_swapchain_sub_image(subimage);
if (subimage.swapchain == XR_NULL_HANDLE) {
// Don't have a swapchain to display? Ignore our layer.
return nullptr;
}
// Update the layer struct for the swapchain.
switch (composition_layer->type) {
case XR_TYPE_COMPOSITION_LAYER_QUAD: {
XrCompositionLayerQuad *quad_layer = (XrCompositionLayerQuad *)composition_layer;
quad_layer->space = openxr_api->get_play_space();
quad_layer->subImage = subimage;
} break;
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {
XrCompositionLayerCylinderKHR *cylinder_layer = (XrCompositionLayerCylinderKHR *)composition_layer;
cylinder_layer->space = openxr_api->get_play_space();
cylinder_layer->subImage = subimage;
} break;
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {
XrCompositionLayerEquirect2KHR *equirect_layer = (XrCompositionLayerEquirect2KHR *)composition_layer;
equirect_layer->space = openxr_api->get_play_space();
equirect_layer->subImage = subimage;
} break;
default: {
return nullptr;
} break;
}
if (extension_property_values_changed) {
extension_property_values_changed = false;
void *next_pointer = nullptr;
for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
void *np = extension->set_viewport_composition_layer_and_get_next_pointer(composition_layer, extension_property_values, next_pointer);
if (np) {
next_pointer = np;
}
}
composition_layer->next = next_pointer;
}
return composition_layer;
}
void OpenXRViewportCompositionLayerProvider::update_swapchain_state() {
OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();
if (!fb_update_swapchain_ext) {
return;
}
#ifdef ANDROID_ENABLED
if (use_android_surface) {
if (android_surface.swapchain == XR_NULL_HANDLE) {
return;
}
fb_update_swapchain_ext->update_swapchain_state(android_surface.swapchain, &swapchain_state);
} else
#endif
{
if (subviewport.swapchain_info.get_swapchain() == XR_NULL_HANDLE) {
return;
}
fb_update_swapchain_ext->update_swapchain_state(subviewport.swapchain_info.get_swapchain(), &swapchain_state);
}
}
OpenXRViewportCompositionLayerProvider::SwapchainState *OpenXRViewportCompositionLayerProvider::get_swapchain_state() {
return &swapchain_state;
}
void OpenXRViewportCompositionLayerProvider::update_swapchain_sub_image(XrSwapchainSubImage &r_subimage) {
#ifdef ANDROID_ENABLED
if (use_android_surface) {
r_subimage.swapchain = android_surface.swapchain;
} else
#endif
{
XrSwapchain swapchain = subviewport.swapchain_info.get_swapchain();
if (swapchain && subviewport.swapchain_info.is_image_acquired()) {
subviewport.swapchain_info.release();
}
r_subimage.swapchain = swapchain;
}
r_subimage.imageRect.extent.width = swapchain_size.width;
r_subimage.imageRect.extent.height = swapchain_size.height;
}
bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p_static_image) {
ERR_FAIL_COND_V(use_android_surface, false);
if (openxr_api == nullptr || composition_layer_extension == nullptr) {
// OpenXR not initialized or we're in the editor?
return false;
}
if (!composition_layer_extension->is_available(get_openxr_type())) {
// Selected type is not supported?
return false;
}
// See if our current swapchain is outdated.
if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {
// If this swap chain, or the previous one, were static, then we can't reuse it.
if (swapchain_size == subviewport.viewport_size && !p_static_image && !subviewport.static_image) {
// We're all good! Just acquire it.
// We can ignore should_render here, return will be false.
bool should_render = true;
return subviewport.swapchain_info.acquire(should_render);
}
subviewport.swapchain_info.queue_free();
}
// Create our new swap chain
int64_t swapchain_format = openxr_api->get_color_swapchain_format();
const uint32_t sample_count = 1;
const uint32_t array_size = 1;
XrSwapchainCreateFlags create_flags = 0;
if (p_static_image) {
create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;
}
if (!subviewport.swapchain_info.create(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, subviewport.viewport_size.width, subviewport.viewport_size.height, sample_count, array_size)) {
swapchain_size = Size2i();
return false;
}
swapchain_state.dirty = true;
// Acquire our image so we can start rendering into it,
// we can ignore should_render here, ret will be false.
bool should_render = true;
bool ret = subviewport.swapchain_info.acquire(should_render);
swapchain_size = subviewport.viewport_size;
subviewport.static_image = p_static_image;
return ret;
}
void OpenXRViewportCompositionLayerProvider::free_swapchain() {
#ifdef ANDROID_ENABLED
if (use_android_surface) {
if (android_surface.swapchain != XR_NULL_HANDLE) {
composition_layer_extension->free_android_surface_swapchain(android_surface.swapchain);
android_surface.swapchain = XR_NULL_HANDLE;
android_surface.surface.unref();
}
} else
#endif
{
if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {
subviewport.swapchain_info.queue_free();
}
subviewport.static_image = false;
}
swapchain_size = Size2i();
}
RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() {
ERR_FAIL_COND_V(use_android_surface, RID());
if (openxr_api == nullptr) {
return RID();
}
return subviewport.swapchain_info.get_image();
}

View File

@@ -0,0 +1,219 @@
/**************************************************************************/
/* openxr_composition_layer_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
#include "../openxr_api.h"
#ifdef ANDROID_ENABLED
#include <jni.h>
// Copied here from openxr_platform.h, in order to avoid including that whole header,
// which can cause compilation issues on some platforms.
typedef XrResult(XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo *info, XrSwapchain *swapchain, jobject *surface);
#endif
class JavaObject;
class OpenXRViewportCompositionLayerProvider;
// This extension provides access to composition layers for displaying 2D content through the XR compositor.
// OpenXRCompositionLayerExtension enables the extensions related to this functionality
class OpenXRCompositionLayerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRCompositionLayerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRCompositionLayerExtension *get_singleton();
OpenXRCompositionLayerExtension();
virtual ~OpenXRCompositionLayerExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override;
virtual void on_pre_render() override;
virtual int get_composition_layer_count() override;
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
void register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
void unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
bool is_available(XrStructureType p_which);
bool is_android_surface_swapchain_available() { return android_surface_ext_available; }
#ifdef ANDROID_ENABLED
bool create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface);
void free_android_surface_swapchain(XrSwapchain p_swapchain);
#endif
private:
static OpenXRCompositionLayerExtension *singleton;
Vector<OpenXRViewportCompositionLayerProvider *> composition_layers;
bool cylinder_ext_available = false;
bool equirect_ext_available = false;
bool android_surface_ext_available = false;
#ifdef ANDROID_ENABLED
Vector<XrSwapchain> android_surface_swapchain_free_queue;
void free_queued_android_surface_swapchains();
EXT_PROTO_XRRESULT_FUNC1(xrDestroySwapchain, (XrSwapchain), swapchain)
EXT_PROTO_XRRESULT_FUNC4(xrCreateSwapchainAndroidSurfaceKHR, (XrSession), session, (const XrSwapchainCreateInfo *), info, (XrSwapchain *), swapchain, (jobject *), surface)
#endif
};
class OpenXRViewportCompositionLayerProvider {
public:
// Must be identical to Filter enum definition in OpenXRCompositionLayer.
enum Filter {
FILTER_NEAREST,
FILTER_LINEAR,
FILTER_CUBIC,
};
// Must be identical to MipmapMode enum definition in OpenXRCompositionLayer.
enum MipmapMode {
MIPMAP_MODE_DISABLED,
MIPMAP_MODE_NEAREST,
MIPMAP_MODE_LINEAR,
};
// Must be identical to Wrap enum definition in OpenXRCompositionLayer.
enum Wrap {
WRAP_CLAMP_TO_BORDER,
WRAP_CLAMP_TO_EDGE,
WRAP_REPEAT,
WRAP_MIRRORED_REPEAT,
WRAP_MIRROR_CLAMP_TO_EDGE,
};
// Must be identical to Swizzle enum definition in OpenXRCompositionLayer.
enum Swizzle {
SWIZZLE_RED,
SWIZZLE_GREEN,
SWIZZLE_BLUE,
SWIZZLE_ALPHA,
SWIZZLE_ZERO,
SWIZZLE_ONE,
};
struct SwapchainState {
Filter min_filter = Filter::FILTER_LINEAR;
Filter mag_filter = Filter::FILTER_LINEAR;
MipmapMode mipmap_mode = MipmapMode::MIPMAP_MODE_LINEAR;
Wrap horizontal_wrap = Wrap::WRAP_CLAMP_TO_BORDER;
Wrap vertical_wrap = Wrap::WRAP_CLAMP_TO_BORDER;
Swizzle red_swizzle = Swizzle::SWIZZLE_RED;
Swizzle green_swizzle = Swizzle::SWIZZLE_GREEN;
Swizzle blue_swizzle = Swizzle::SWIZZLE_BLUE;
Swizzle alpha_swizzle = Swizzle::SWIZZLE_ALPHA;
float max_anisotropy = 1.0;
Color border_color = { 0.0, 0.0, 0.0, 0.0 };
bool dirty = false;
};
private:
XrCompositionLayerBaseHeader *composition_layer = nullptr;
int sort_order = 1;
bool alpha_blend = false;
Dictionary extension_property_values;
bool extension_property_values_changed = true;
struct {
RID viewport;
Size2i viewport_size;
OpenXRAPI::OpenXRSwapChainInfo swapchain_info;
bool static_image = false;
} subviewport;
#ifdef ANDROID_ENABLED
struct {
XrSwapchain swapchain = XR_NULL_HANDLE;
Ref<JavaObject> surface;
} android_surface;
#endif
bool use_android_surface = false;
Size2i swapchain_size;
OpenXRAPI *openxr_api = nullptr;
OpenXRCompositionLayerExtension *composition_layer_extension = nullptr;
// Only for SubViewports.
bool update_and_acquire_swapchain(bool p_static_image);
RID get_current_swapchain_texture();
void update_swapchain_sub_image(XrSwapchainSubImage &r_swapchain_sub_image);
void free_swapchain();
SwapchainState swapchain_state;
#ifdef ANDROID_ENABLED
void create_android_surface();
#endif
public:
XrStructureType get_openxr_type() { return composition_layer->type; }
void set_sort_order(int p_sort_order) { sort_order = p_sort_order; }
int get_sort_order() const { return sort_order; }
void set_alpha_blend(bool p_alpha_blend);
bool get_alpha_blend() const { return alpha_blend; }
void set_viewport(RID p_viewport, Size2i p_size);
RID get_viewport() const { return subviewport.viewport; }
void set_use_android_surface(bool p_enable, Size2i p_size);
bool get_use_android_surface() const { return use_android_surface; }
Ref<JavaObject> get_android_surface();
void set_extension_property_values(const Dictionary &p_property_values);
void on_pre_render();
XrCompositionLayerBaseHeader *get_composition_layer();
void update_swapchain_state();
SwapchainState *get_swapchain_state();
OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer);
~OpenXRViewportCompositionLayerProvider();
};

View File

@@ -0,0 +1,287 @@
/**************************************************************************/
/* openxr_debug_utils_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_debug_utils_extension.h"
#include "../openxr_api.h"
#include "core/config/project_settings.h"
#include "core/string/print_string.h"
#include <openxr/openxr.h>
OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::singleton = nullptr;
OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::get_singleton() {
return singleton;
}
OpenXRDebugUtilsExtension::OpenXRDebugUtilsExtension() {
singleton = this;
}
OpenXRDebugUtilsExtension::~OpenXRDebugUtilsExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRDebugUtilsExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_EXT_DEBUG_UTILS_EXTENSION_NAME] = &debug_utils_ext;
return request_extensions;
}
void OpenXRDebugUtilsExtension::on_instance_created(const XrInstance p_instance) {
if (debug_utils_ext) {
EXT_INIT_XR_FUNC(xrCreateDebugUtilsMessengerEXT);
EXT_INIT_XR_FUNC(xrDestroyDebugUtilsMessengerEXT);
EXT_INIT_XR_FUNC(xrSetDebugUtilsObjectNameEXT);
EXT_INIT_XR_FUNC(xrSessionBeginDebugUtilsLabelRegionEXT);
EXT_INIT_XR_FUNC(xrSessionEndDebugUtilsLabelRegionEXT);
EXT_INIT_XR_FUNC(xrSessionInsertDebugUtilsLabelEXT);
debug_utils_ext = xrCreateDebugUtilsMessengerEXT_ptr && xrDestroyDebugUtilsMessengerEXT_ptr && xrSetDebugUtilsObjectNameEXT_ptr && xrSessionBeginDebugUtilsLabelRegionEXT_ptr && xrSessionEndDebugUtilsLabelRegionEXT_ptr && xrSessionInsertDebugUtilsLabelEXT_ptr;
} else {
WARN_PRINT("OpenXR: The debug utils extension is not available on this runtime. Debug logging is not enabled!");
}
// On successful init, setup our default messenger.
if (debug_utils_ext) {
int max_severity = GLOBAL_GET("xr/openxr/extensions/debug_utils");
int types = GLOBAL_GET("xr/openxr/extensions/debug_message_types");
XrDebugUtilsMessageSeverityFlagsEXT message_severities = 0;
if (max_severity >= 1) {
message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
}
if (max_severity >= 2) {
message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
}
if (max_severity >= 3) {
message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
}
if (max_severity >= 4) {
message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
}
XrDebugUtilsMessageTypeFlagsEXT message_types = 0;
// These should match up but just to be safe and future proof...
if (types & 1) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
}
if (types & 2) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
}
if (types & 4) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
}
if (types & 8) {
message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
}
XrDebugUtilsMessengerCreateInfoEXT callback_info = {
XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // type
nullptr, // next
message_severities, // messageSeverities
message_types, // messageTypes
&OpenXRDebugUtilsExtension::_debug_callback, // userCallback
nullptr, // userData
};
XrResult result = xrCreateDebugUtilsMessengerEXT(p_instance, &callback_info, &default_messenger);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to create debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
set_object_name(XR_OBJECT_TYPE_INSTANCE, uint64_t(p_instance), "Main Godot OpenXR Instance");
}
}
void OpenXRDebugUtilsExtension::on_instance_destroyed() {
if (default_messenger != XR_NULL_HANDLE) {
XrResult result = xrDestroyDebugUtilsMessengerEXT(default_messenger);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to destroy debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
default_messenger = XR_NULL_HANDLE;
}
xrCreateDebugUtilsMessengerEXT_ptr = nullptr;
xrDestroyDebugUtilsMessengerEXT_ptr = nullptr;
xrSetDebugUtilsObjectNameEXT_ptr = nullptr;
xrSessionBeginDebugUtilsLabelRegionEXT_ptr = nullptr;
xrSessionEndDebugUtilsLabelRegionEXT_ptr = nullptr;
xrSessionInsertDebugUtilsLabelEXT_ptr = nullptr;
debug_utils_ext = false;
}
bool OpenXRDebugUtilsExtension::get_active() {
return debug_utils_ext;
}
void OpenXRDebugUtilsExtension::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name) {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSetDebugUtilsObjectNameEXT_ptr);
const XrDebugUtilsObjectNameInfoEXT space_name_info = {
XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, // type
nullptr, // next
p_object_type, // objectType
p_object_handle, // objectHandle
p_object_name, // objectName
};
XrResult result = xrSetDebugUtilsObjectNameEXT_ptr(OpenXRAPI::get_singleton()->get_instance(), &space_name_info);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to set object name [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
void OpenXRDebugUtilsExtension::begin_debug_label_region(const char *p_label_name) {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSessionBeginDebugUtilsLabelRegionEXT_ptr);
const XrDebugUtilsLabelEXT session_active_region_label = {
XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
nullptr, // next
p_label_name, // labelName
};
XrResult result = xrSessionBeginDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to begin label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
void OpenXRDebugUtilsExtension::end_debug_label_region() {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSessionEndDebugUtilsLabelRegionEXT_ptr);
XrResult result = xrSessionEndDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session());
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to end label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
void OpenXRDebugUtilsExtension::insert_debug_label(const char *p_label_name) {
ERR_FAIL_COND(!debug_utils_ext);
ERR_FAIL_NULL(xrSessionInsertDebugUtilsLabelEXT_ptr);
const XrDebugUtilsLabelEXT session_active_region_label = {
XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
nullptr, // next
p_label_name, // labelName
};
XrResult result = xrSessionInsertDebugUtilsLabelEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to insert label [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
XrBool32 XRAPI_PTR OpenXRDebugUtilsExtension::_debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
if (debug_utils) {
return debug_utils->debug_callback(p_message_severity, p_message_types, p_callback_data, p_user_data);
}
return XR_FALSE;
}
XrBool32 OpenXRDebugUtilsExtension::debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
String msg;
ERR_FAIL_NULL_V(p_callback_data, XR_FALSE);
if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
msg = ", type: General";
} else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
msg = ", type: Validation";
} else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
msg = ", type: Performance";
} else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT) {
msg = ", type: Conformance";
} else {
msg = ", type: Unknown (" + String::num_uint64(p_message_types) + ")";
}
if (p_callback_data->functionName) {
msg += ", function Name: " + String(p_callback_data->functionName);
}
if (p_callback_data->messageId) {
msg += "\nMessage ID: " + String(p_callback_data->messageId);
}
if (p_callback_data->message) {
msg += "\nMessage: " + String(p_callback_data->message);
}
if (p_callback_data->objectCount > 0) {
String objects;
for (uint32_t i = 0; i < p_callback_data->objectCount; i++) {
if (!objects.is_empty()) {
objects += ", ";
}
objects += p_callback_data->objects[i].objectName;
}
msg += "\nObjects: " + objects;
}
if (p_callback_data->sessionLabelCount > 0) {
String labels;
for (uint32_t i = 0; i < p_callback_data->sessionLabelCount; i++) {
if (!labels.is_empty()) {
labels += ", ";
}
labels += p_callback_data->sessionLabels[i].labelName;
}
msg += "\nLabels: " + labels;
}
if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
ERR_PRINT("OpenXR: Severity: Error" + msg);
} else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
WARN_PRINT("OpenXR: Severity: Warning" + msg);
} else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
print_line("OpenXR: Severity: Info" + msg);
} else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
// This is a bit double because we won't output this unless verbose messaging in Godot is on.
print_verbose("OpenXR: Severity: Verbose" + msg);
}
return XR_FALSE;
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************/
/* openxr_debug_utils_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRDebugUtilsExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDebugUtilsExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRDebugUtilsExtension *get_singleton();
OpenXRDebugUtilsExtension();
virtual ~OpenXRDebugUtilsExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
bool get_active();
void set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name);
void begin_debug_label_region(const char *p_label_name);
void end_debug_label_region();
void insert_debug_label(const char *p_label_name);
private:
static OpenXRDebugUtilsExtension *singleton;
// related extensions
bool debug_utils_ext = false;
// debug handlers
XrDebugUtilsMessengerEXT default_messenger = XR_NULL_HANDLE;
static XrBool32 XRAPI_PTR _debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
XrBool32 debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC3(xrCreateDebugUtilsMessengerEXT, (XrInstance), p_instance, (const XrDebugUtilsMessengerCreateInfoEXT *), p_create_info, (XrDebugUtilsMessengerEXT *), p_messenger)
EXT_PROTO_XRRESULT_FUNC1(xrDestroyDebugUtilsMessengerEXT, (XrDebugUtilsMessengerEXT), p_messenger)
EXT_PROTO_XRRESULT_FUNC2(xrSetDebugUtilsObjectNameEXT, (XrInstance), p_instance, (const XrDebugUtilsObjectNameInfoEXT *), p_name_info)
EXT_PROTO_XRRESULT_FUNC2(xrSessionBeginDebugUtilsLabelRegionEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
EXT_PROTO_XRRESULT_FUNC1(xrSessionEndDebugUtilsLabelRegionEXT, (XrSession), p_session)
EXT_PROTO_XRRESULT_FUNC2(xrSessionInsertDebugUtilsLabelEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
};

View File

@@ -0,0 +1,273 @@
/**************************************************************************/
/* openxr_dpad_binding_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_dpad_binding_extension.h"
#include "../openxr_api.h"
#include "core/math/math_funcs.h"
// Implementation for:
// https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_dpad_binding
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRDPadBindingExtension
OpenXRDPadBindingExtension *OpenXRDPadBindingExtension::singleton = nullptr;
OpenXRDPadBindingExtension *OpenXRDPadBindingExtension::get_singleton() {
return singleton;
}
OpenXRDPadBindingExtension::OpenXRDPadBindingExtension() {
singleton = this;
}
OpenXRDPadBindingExtension::~OpenXRDPadBindingExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRDPadBindingExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Note, we're dependent on the binding modifier extension, this may be requested by multiple extension wrappers.
request_extensions[XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME] = &binding_modifier_ext;
request_extensions[XR_EXT_DPAD_BINDING_EXTENSION_NAME] = &dpad_binding_ext;
return request_extensions;
}
bool OpenXRDPadBindingExtension::is_available() {
return binding_modifier_ext && dpad_binding_ext;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRDpadBindingModifier
void OpenXRDpadBindingModifier::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_action_set", "action_set"), &OpenXRDpadBindingModifier::set_action_set);
ClassDB::bind_method(D_METHOD("get_action_set"), &OpenXRDpadBindingModifier::get_action_set);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "action_set", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRActionSet"), "set_action_set", "get_action_set");
ClassDB::bind_method(D_METHOD("set_input_path", "input_path"), &OpenXRDpadBindingModifier::set_input_path);
ClassDB::bind_method(D_METHOD("get_input_path"), &OpenXRDpadBindingModifier::get_input_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_path", PROPERTY_HINT_TYPE_STRING, "binding_path"), "set_input_path", "get_input_path");
ClassDB::bind_method(D_METHOD("set_threshold", "threshold"), &OpenXRDpadBindingModifier::set_threshold);
ClassDB::bind_method(D_METHOD("get_threshold"), &OpenXRDpadBindingModifier::get_threshold);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_threshold", "get_threshold");
ClassDB::bind_method(D_METHOD("set_threshold_released", "threshold_released"), &OpenXRDpadBindingModifier::set_threshold_released);
ClassDB::bind_method(D_METHOD("get_threshold_released"), &OpenXRDpadBindingModifier::get_threshold_released);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "threshold_released", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_threshold_released", "get_threshold_released");
ClassDB::bind_method(D_METHOD("set_center_region", "center_region"), &OpenXRDpadBindingModifier::set_center_region);
ClassDB::bind_method(D_METHOD("get_center_region"), &OpenXRDpadBindingModifier::get_center_region);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "center_region", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_center_region", "get_center_region");
ClassDB::bind_method(D_METHOD("set_wedge_angle", "wedge_angle"), &OpenXRDpadBindingModifier::set_wedge_angle);
ClassDB::bind_method(D_METHOD("get_wedge_angle"), &OpenXRDpadBindingModifier::get_wedge_angle);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wedge_angle", PROPERTY_HINT_RANGE, "1.0,180.0,0.1,radians_as_degrees"), "set_wedge_angle", "get_wedge_angle");
ClassDB::bind_method(D_METHOD("set_is_sticky", "is_sticky"), &OpenXRDpadBindingModifier::set_is_sticky);
ClassDB::bind_method(D_METHOD("get_is_sticky"), &OpenXRDpadBindingModifier::get_is_sticky);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_sticky"), "set_is_sticky", "get_is_sticky");
ClassDB::bind_method(D_METHOD("set_on_haptic", "haptic"), &OpenXRDpadBindingModifier::set_on_haptic);
ClassDB::bind_method(D_METHOD("get_on_haptic"), &OpenXRDpadBindingModifier::get_on_haptic);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "on_haptic", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRHapticBase"), "set_on_haptic", "get_on_haptic");
ClassDB::bind_method(D_METHOD("set_off_haptic", "haptic"), &OpenXRDpadBindingModifier::set_off_haptic);
ClassDB::bind_method(D_METHOD("get_off_haptic"), &OpenXRDpadBindingModifier::get_off_haptic);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "off_haptic", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRHapticBase"), "set_off_haptic", "get_off_haptic");
}
OpenXRDpadBindingModifier::OpenXRDpadBindingModifier() {
ERR_FAIL_COND(dpad_bindings_data.resize_initialized(sizeof(XrInteractionProfileDpadBindingEXT)) != OK);
dpad_bindings = (XrInteractionProfileDpadBindingEXT *)dpad_bindings_data.ptrw();
dpad_bindings->type = XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT;
dpad_bindings->next = nullptr;
dpad_bindings->forceThreshold = 0.6;
dpad_bindings->forceThresholdReleased = 0.4;
dpad_bindings->centerRegion = 0.1;
dpad_bindings->wedgeAngle = Math::deg_to_rad(90.0);
dpad_bindings->isSticky = false;
}
void OpenXRDpadBindingModifier::set_action_set(const Ref<OpenXRActionSet> p_action_set) {
action_set = p_action_set;
}
Ref<OpenXRActionSet> OpenXRDpadBindingModifier::get_action_set() const {
return action_set;
}
void OpenXRDpadBindingModifier::set_input_path(const String &p_input_path) {
input_path = p_input_path;
emit_changed();
}
String OpenXRDpadBindingModifier::get_input_path() const {
return input_path;
}
void OpenXRDpadBindingModifier::set_threshold(float p_threshold) {
ERR_FAIL_NULL(dpad_bindings);
ERR_FAIL_COND(p_threshold < 0.0 || p_threshold > 1.0);
dpad_bindings->forceThreshold = p_threshold;
emit_changed();
}
float OpenXRDpadBindingModifier::get_threshold() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->forceThreshold;
}
void OpenXRDpadBindingModifier::set_threshold_released(float p_threshold) {
ERR_FAIL_NULL(dpad_bindings);
ERR_FAIL_COND(p_threshold < 0.0 || p_threshold > 1.0);
dpad_bindings->forceThresholdReleased = p_threshold;
emit_changed();
}
float OpenXRDpadBindingModifier::get_threshold_released() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->forceThresholdReleased;
}
void OpenXRDpadBindingModifier::set_center_region(float p_center_region) {
ERR_FAIL_NULL(dpad_bindings);
ERR_FAIL_COND(p_center_region < 0.0 || p_center_region > 1.0);
dpad_bindings->centerRegion = p_center_region;
emit_changed();
}
float OpenXRDpadBindingModifier::get_center_region() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->centerRegion;
}
void OpenXRDpadBindingModifier::set_wedge_angle(float p_wedge_angle) {
ERR_FAIL_NULL(dpad_bindings);
dpad_bindings->wedgeAngle = p_wedge_angle;
emit_changed();
}
float OpenXRDpadBindingModifier::get_wedge_angle() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->wedgeAngle;
}
void OpenXRDpadBindingModifier::set_wedge_angle_deg(float p_wedge_angle) {
ERR_FAIL_NULL(dpad_bindings);
dpad_bindings->wedgeAngle = Math::deg_to_rad(p_wedge_angle);
emit_changed();
}
float OpenXRDpadBindingModifier::get_wedge_angle_deg() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return Math::rad_to_deg(dpad_bindings->wedgeAngle);
}
void OpenXRDpadBindingModifier::set_is_sticky(bool p_sticky) {
ERR_FAIL_NULL(dpad_bindings);
dpad_bindings->isSticky = p_sticky;
emit_changed();
}
bool OpenXRDpadBindingModifier::get_is_sticky() const {
ERR_FAIL_NULL_V(dpad_bindings, false);
return dpad_bindings->isSticky;
}
void OpenXRDpadBindingModifier::set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic) {
on_haptic = p_haptic;
emit_changed();
}
Ref<OpenXRHapticBase> OpenXRDpadBindingModifier::get_on_haptic() const {
return on_haptic;
}
void OpenXRDpadBindingModifier::set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic) {
off_haptic = p_haptic;
emit_changed();
}
Ref<OpenXRHapticBase> OpenXRDpadBindingModifier::get_off_haptic() const {
return off_haptic;
}
PackedByteArray OpenXRDpadBindingModifier::get_ip_modification() {
ERR_FAIL_NULL_V(dpad_bindings, PackedByteArray());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, PackedByteArray());
OpenXRDPadBindingExtension *dpad_binding_ext = OpenXRDPadBindingExtension::get_singleton();
if (!dpad_binding_ext || !dpad_binding_ext->is_available()) {
// Extension not enabled!
WARN_PRINT("DPad binding extension is not enabled or available.");
return PackedByteArray();
}
dpad_bindings->binding = openxr_api->get_xr_path(input_path);
ERR_FAIL_COND_V(dpad_bindings->binding == XR_NULL_PATH, PackedByteArray());
// Get our action set
ERR_FAIL_COND_V(action_set.is_null(), PackedByteArray());
RID action_set_rid = openxr_api->find_action_set(action_set->get_name());
ERR_FAIL_COND_V(!action_set_rid.is_valid(), PackedByteArray());
dpad_bindings->actionSet = openxr_api->action_set_get_handle(action_set_rid);
// These are set already:
// - forceThreshold
// - forceThresholdReleased
// - centerRegion
// - wedgeAngle
// - isSticky
if (on_haptic.is_valid()) {
dpad_bindings->onHaptic = on_haptic->get_xr_structure();
} else {
dpad_bindings->onHaptic = nullptr;
}
if (off_haptic.is_valid()) {
dpad_bindings->offHaptic = off_haptic->get_xr_structure();
} else {
dpad_bindings->offHaptic = nullptr;
}
return dpad_bindings_data;
}

View File

@@ -0,0 +1,111 @@
/**************************************************************************/
/* openxr_dpad_binding_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_set.h"
#include "../action_map/openxr_binding_modifier.h"
#include "../action_map/openxr_haptic_feedback.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRDPadBindingExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDPadBindingExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRDPadBindingExtension *get_singleton();
OpenXRDPadBindingExtension();
virtual ~OpenXRDPadBindingExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
private:
static OpenXRDPadBindingExtension *singleton;
bool binding_modifier_ext = false;
bool dpad_binding_ext = false;
};
class OpenXRDpadBindingModifier : public OpenXRIPBindingModifier {
GDCLASS(OpenXRDpadBindingModifier, OpenXRIPBindingModifier);
private:
PackedByteArray dpad_bindings_data;
XrInteractionProfileDpadBindingEXT *dpad_bindings = nullptr;
String input_path;
Ref<OpenXRActionSet> action_set;
Ref<OpenXRHapticBase> on_haptic;
Ref<OpenXRHapticBase> off_haptic;
protected:
static void _bind_methods();
public:
OpenXRDpadBindingModifier();
void set_action_set(const Ref<OpenXRActionSet> p_action_set);
Ref<OpenXRActionSet> get_action_set() const;
void set_input_path(const String &p_input_path);
String get_input_path() const;
void set_threshold(float p_threshold);
float get_threshold() const;
void set_threshold_released(float p_threshold);
float get_threshold_released() const;
void set_center_region(float p_center_region);
float get_center_region() const;
void set_wedge_angle(float p_wedge_angle);
float get_wedge_angle() const;
void set_wedge_angle_deg(float p_wedge_angle);
float get_wedge_angle_deg() const;
void set_is_sticky(bool p_sticky);
bool get_is_sticky() const;
void set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic);
Ref<OpenXRHapticBase> get_on_haptic() const;
void set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic);
Ref<OpenXRHapticBase> get_off_haptic() const;
virtual String get_description() const override { return "DPad modifier"; }
virtual PackedByteArray get_ip_modification() override;
};

View File

@@ -0,0 +1,372 @@
/**************************************************************************/
/* openxr_extension_wrapper.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_extension_wrapper.h"
#include "../openxr_api.h"
#include "../openxr_api_extension.h"
void OpenXRExtensionWrapper::_bind_methods() {
GDVIRTUAL_BIND(_get_requested_extensions);
GDVIRTUAL_BIND(_set_system_properties_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_instance_create_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_hand_joint_locations_and_get_next_pointer, "hand_index", "next_pointer");
GDVIRTUAL_BIND(_set_projection_views_and_get_next_pointer, "view_index", "next_pointer");
GDVIRTUAL_BIND(_set_frame_wait_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_frame_end_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_view_locate_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_reference_space_create_info_and_get_next_pointer, "reference_space_type", "next_pointer");
GDVIRTUAL_BIND(_get_composition_layer_count);
GDVIRTUAL_BIND(_get_composition_layer, "index");
GDVIRTUAL_BIND(_get_composition_layer_order, "index");
GDVIRTUAL_BIND(_get_suggested_tracker_names);
GDVIRTUAL_BIND(_on_register_metadata);
GDVIRTUAL_BIND(_on_before_instance_created);
GDVIRTUAL_BIND(_on_instance_created, "instance");
GDVIRTUAL_BIND(_on_instance_destroyed);
GDVIRTUAL_BIND(_on_session_created, "session");
GDVIRTUAL_BIND(_on_process);
GDVIRTUAL_BIND(_on_sync_actions);
GDVIRTUAL_BIND(_on_pre_render);
GDVIRTUAL_BIND(_on_main_swapchains_created);
GDVIRTUAL_BIND(_on_pre_draw_viewport, "viewport");
GDVIRTUAL_BIND(_on_post_draw_viewport, "viewport");
GDVIRTUAL_BIND(_on_session_destroyed);
GDVIRTUAL_BIND(_on_state_idle);
GDVIRTUAL_BIND(_on_state_ready);
GDVIRTUAL_BIND(_on_state_synchronized);
GDVIRTUAL_BIND(_on_state_visible);
GDVIRTUAL_BIND(_on_state_focused);
GDVIRTUAL_BIND(_on_state_stopping);
GDVIRTUAL_BIND(_on_state_loss_pending);
GDVIRTUAL_BIND(_on_state_exiting);
GDVIRTUAL_BIND(_on_event_polled, "event");
GDVIRTUAL_BIND(_set_viewport_composition_layer_and_get_next_pointer, "layer", "property_values", "next_pointer");
GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_properties);
GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_property_defaults);
GDVIRTUAL_BIND(_on_viewport_composition_layer_destroyed, "layer");
GDVIRTUAL_BIND(_set_android_surface_swapchain_create_info_and_get_next_pointer, "property_values", "next_pointer");
ClassDB::bind_method(D_METHOD("get_openxr_api"), &OpenXRExtensionWrapper::_gdextension_get_openxr_api);
ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapper::_gdextension_register_extension_wrapper);
}
HashMap<String, bool *> OpenXRExtensionWrapper::get_requested_extensions() {
Dictionary request_extension;
if (GDVIRTUAL_CALL(_get_requested_extensions, request_extension)) {
HashMap<String, bool *> result;
for (const KeyValue<Variant, Variant> &kv : request_extension) {
GDExtensionPtr<bool> value = VariantCaster<GDExtensionPtr<bool>>::cast(kv.value);
result.insert(kv.key, value);
}
return result;
}
return HashMap<String, bool *>();
}
void *OpenXRExtensionWrapper::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_system_properties_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_instance_create_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_session_create_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_session_create_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_hand_joint_locations_and_get_next_pointer, p_hand_index, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_projection_views_and_get_next_pointer, p_view_index, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_reference_space_create_info_and_get_next_pointer(int p_reference_space_type, void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_reference_space_create_info_and_get_next_pointer, p_reference_space_type, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_frame_wait_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_frame_wait_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_frame_end_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_frame_end_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_view_locate_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_view_locate_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
PackedStringArray OpenXRExtensionWrapper::get_suggested_tracker_names() {
PackedStringArray ret;
if (GDVIRTUAL_CALL(_get_suggested_tracker_names, ret)) {
return ret;
}
return PackedStringArray();
}
int OpenXRExtensionWrapper::get_composition_layer_count() {
int count = 0;
GDVIRTUAL_CALL(_get_composition_layer_count, count);
return count;
}
XrCompositionLayerBaseHeader *OpenXRExtensionWrapper::get_composition_layer(int p_index) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_get_composition_layer, p_index, pointer)) {
return reinterpret_cast<XrCompositionLayerBaseHeader *>(pointer);
}
return nullptr;
}
int OpenXRExtensionWrapper::get_composition_layer_order(int p_index) {
int order = 0;
GDVIRTUAL_CALL(_get_composition_layer_order, p_index, order);
return order;
}
void OpenXRExtensionWrapper::on_register_metadata() {
GDVIRTUAL_CALL(_on_register_metadata);
}
void OpenXRExtensionWrapper::on_before_instance_created() {
GDVIRTUAL_CALL(_on_before_instance_created);
}
void OpenXRExtensionWrapper::on_instance_created(const XrInstance p_instance) {
uint64_t instance = (uint64_t)p_instance;
GDVIRTUAL_CALL(_on_instance_created, instance);
}
void OpenXRExtensionWrapper::on_instance_destroyed() {
GDVIRTUAL_CALL(_on_instance_destroyed);
}
void OpenXRExtensionWrapper::on_session_created(const XrSession p_session) {
uint64_t session = (uint64_t)p_session;
GDVIRTUAL_CALL(_on_session_created, session);
}
void OpenXRExtensionWrapper::on_process() {
GDVIRTUAL_CALL(_on_process);
}
void OpenXRExtensionWrapper::on_sync_actions() {
GDVIRTUAL_CALL(_on_sync_actions);
}
void OpenXRExtensionWrapper::on_pre_render() {
GDVIRTUAL_CALL(_on_pre_render);
}
void OpenXRExtensionWrapper::on_main_swapchains_created() {
GDVIRTUAL_CALL(_on_main_swapchains_created);
}
void OpenXRExtensionWrapper::on_session_destroyed() {
GDVIRTUAL_CALL(_on_session_destroyed);
}
void OpenXRExtensionWrapper::on_pre_draw_viewport(RID p_render_target) {
GDVIRTUAL_CALL(_on_pre_draw_viewport, p_render_target);
}
void OpenXRExtensionWrapper::on_post_draw_viewport(RID p_render_target) {
GDVIRTUAL_CALL(_on_post_draw_viewport, p_render_target);
}
void OpenXRExtensionWrapper::on_state_idle() {
GDVIRTUAL_CALL(_on_state_idle);
}
void OpenXRExtensionWrapper::on_state_ready() {
GDVIRTUAL_CALL(_on_state_ready);
}
void OpenXRExtensionWrapper::on_state_synchronized() {
GDVIRTUAL_CALL(_on_state_synchronized);
}
void OpenXRExtensionWrapper::on_state_visible() {
GDVIRTUAL_CALL(_on_state_visible);
}
void OpenXRExtensionWrapper::on_state_focused() {
GDVIRTUAL_CALL(_on_state_focused);
}
void OpenXRExtensionWrapper::on_state_stopping() {
GDVIRTUAL_CALL(_on_state_stopping);
}
void OpenXRExtensionWrapper::on_state_loss_pending() {
GDVIRTUAL_CALL(_on_state_loss_pending);
}
void OpenXRExtensionWrapper::on_state_exiting() {
GDVIRTUAL_CALL(_on_state_exiting);
}
bool OpenXRExtensionWrapper::on_event_polled(const XrEventDataBuffer &p_event) {
bool event_polled;
if (GDVIRTUAL_CALL(_on_event_polled, GDExtensionConstPtr<void>(&p_event), event_polled)) {
return event_polled;
}
return false;
}
void *OpenXRExtensionWrapper::set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const Dictionary &p_property_values, void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr<void>(p_layer), p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return p_next_pointer;
}
void OpenXRExtensionWrapper::on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {
GDVIRTUAL_CALL(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>(p_layer));
}
void OpenXRExtensionWrapper::get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) {
TypedArray<Dictionary> properties;
if (GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_properties, properties)) {
for (int i = 0; i < properties.size(); i++) {
p_property_list->push_back(PropertyInfo::from_dict(properties[i]));
}
}
}
Dictionary OpenXRExtensionWrapper::get_viewport_composition_layer_extension_property_defaults() {
Dictionary property_defaults;
GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_property_defaults, property_defaults);
return property_defaults;
}
void *OpenXRExtensionWrapper::set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_android_surface_swapchain_create_info_and_get_next_pointer, p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return p_next_pointer;
}
Ref<OpenXRAPIExtension> OpenXRExtensionWrapper::_gdextension_get_openxr_api() {
static Ref<OpenXRAPIExtension> openxr_api_extension;
if (unlikely(openxr_api_extension.is_null())) {
openxr_api_extension.instantiate();
}
return openxr_api_extension;
}
void OpenXRExtensionWrapper::_gdextension_register_extension_wrapper() {
OpenXRAPI::register_extension_wrapper(this);
}

View File

@@ -0,0 +1,198 @@
/**************************************************************************/
/* openxr_extension_wrapper.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/error/error_macros.h"
#include "core/math/projection.h"
#include "core/object/class_db.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/templates/hash_map.h"
#include "core/templates/rid.h"
#include "core/variant/native_ptr.h"
#include "core/variant/typed_array.h"
#include "core/variant/variant.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRAPIExtension;
class OpenXRActionMap;
// `OpenXRExtensionWrapper` allows us to implement OpenXR extensions.
class OpenXRExtensionWrapper : public Object {
GDCLASS(OpenXRExtensionWrapper, Object);
Ref<OpenXRAPIExtension> _gdextension_get_openxr_api();
void _gdextension_register_extension_wrapper();
protected:
static void _bind_methods();
public:
// `get_requested_extensions` should return a list of OpenXR extensions related to this extension.
// If the bool * is a nullptr this extension is mandatory
// If the bool * points to a boolean, the boolean will be updated
// to true if the extension is enabled.
virtual HashMap<String, bool *> get_requested_extensions();
GDVIRTUAL0R(Dictionary, _get_requested_extensions);
// These functions allow an extension to add entries to a struct chain.
// `p_next_pointer` points to the last struct that was created for this chain
// and should be used as the value for the `pNext` pointer in the first struct you add.
// You should return the pointer to the last struct you define as your result.
// If you are not adding any structs, just return `p_next_pointer`.
// See existing extensions for examples of this implementation.
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we interrogate OpenXRS system abilities.
virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we create our OpenXR instance.
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we create our OpenXR session.
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when creating OpenXR swap chains.
virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer);
virtual void *set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer);
virtual void *set_reference_space_create_info_and_get_next_pointer(int p_reference_space_type, void *p_next_pointer);
// These will only be called for extensions registered via OpenXRApi::register_frame_info_extension().
virtual void *set_frame_wait_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when calling xrWaitFrame
virtual void *set_view_locate_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when calling xrLocateViews
virtual void *set_frame_end_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when calling xrEndFrame
//TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp
GDVIRTUAL1R(uint64_t, _set_system_properties_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_instance_create_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_hand_joint_locations_and_get_next_pointer, int, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_projection_views_and_get_next_pointer, int, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_frame_wait_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_frame_end_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_view_locate_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_reference_space_create_info_and_get_next_pointer, int, GDExtensionPtr<void>);
GDVIRTUAL0R(int, _get_composition_layer_count);
GDVIRTUAL1R(uint64_t, _get_composition_layer, int);
GDVIRTUAL1R(int, _get_composition_layer_order, int);
virtual PackedStringArray get_suggested_tracker_names();
GDVIRTUAL0R(PackedStringArray, _get_suggested_tracker_names);
// `on_register_metadata` allows extensions to register additional controller metadata.
// This function is called even when OpenXRApi is not constructured as the metadata
// needs to be available to the editor.
// Also extensions should provide metadata regardless of whether they are supported
// on the host system as the controller data is used to setup action maps for users
// who may have access to the relevant hardware.
virtual void on_register_metadata();
virtual void on_before_instance_created(); // `on_before_instance_created` is called before we create our OpenXR instance.
virtual void on_instance_created(const XrInstance p_instance); // `on_instance_created` is called right after we've successfully created our OpenXR instance.
virtual void on_instance_destroyed(); // `on_instance_destroyed` is called right before we destroy our OpenXR instance.
virtual void on_session_created(const XrSession p_session); // `on_session_created` is called right after we've successfully created our OpenXR session.
virtual void on_session_destroyed(); // `on_session_destroyed` is called right before we destroy our OpenXR session.
// `on_process` is called as part of our OpenXR process handling,
// this happens right before physics process and normal processing is run.
// This is when controller data is queried and made available to game logic.
virtual void on_process();
virtual void on_sync_actions(); // `on_sync_actions` is called right after we sync our action sets.
virtual void on_pre_render(); // `on_pre_render` is called right before we start rendering our XR viewports.
virtual void on_main_swapchains_created(); // `on_main_swapchains_created` is called right after our main swapchains are (re)created.
virtual void on_pre_draw_viewport(RID p_render_target); // `on_pre_draw_viewport` is called right before we start rendering this viewport
virtual void on_post_draw_viewport(RID p_render_target); // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued)
GDVIRTUAL0(_on_register_metadata);
GDVIRTUAL0(_on_before_instance_created);
GDVIRTUAL1(_on_instance_created, uint64_t);
GDVIRTUAL0(_on_instance_destroyed);
GDVIRTUAL1(_on_session_created, uint64_t);
GDVIRTUAL0(_on_process);
GDVIRTUAL0(_on_sync_actions);
GDVIRTUAL0(_on_pre_render);
GDVIRTUAL0(_on_main_swapchains_created);
GDVIRTUAL0(_on_session_destroyed);
GDVIRTUAL1(_on_pre_draw_viewport, RID);
GDVIRTUAL1(_on_post_draw_viewport, RID);
virtual void on_state_idle(); // `on_state_idle` is called when the OpenXR session state is changed to idle.
virtual void on_state_ready(); // `on_state_ready` is called when the OpenXR session state is changed to ready, this means OpenXR is ready to setup our session.
virtual void on_state_synchronized(); // `on_state_synchronized` is called when the OpenXR session state is changed to synchronized, note that OpenXR also returns to this state when our application looses focus.
virtual void on_state_visible(); // `on_state_visible` is called when the OpenXR session state is changed to visible, OpenXR is now ready to receive frames.
virtual void on_state_focused(); // `on_state_focused` is called when the OpenXR session state is changed to focused, this state is the active state when our game runs.
virtual void on_state_stopping(); // `on_state_stopping` is called when the OpenXR session state is changed to stopping.
virtual void on_state_loss_pending(); // `on_state_loss_pending` is called when the OpenXR session state is changed to loss pending.
virtual void on_state_exiting(); // `on_state_exiting` is called when the OpenXR session state is changed to exiting.
GDVIRTUAL0(_on_state_idle);
GDVIRTUAL0(_on_state_ready);
GDVIRTUAL0(_on_state_synchronized);
GDVIRTUAL0(_on_state_visible);
GDVIRTUAL0(_on_state_focused);
GDVIRTUAL0(_on_state_stopping);
GDVIRTUAL0(_on_state_loss_pending);
GDVIRTUAL0(_on_state_exiting);
// These will only be called on extensions registered via OpenXRAPI::register_composition_layer_provider().
virtual int get_composition_layer_count();
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index);
virtual int get_composition_layer_order(int p_index);
virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const Dictionary &p_property_values, void *p_next_pointer); // Add additional data structures to composition layers created via OpenXRCompositionLayer.
virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer); // `on_viewport_composition_layer_destroyed` is called when a composition layer created via OpenXRCompositionLayer is destroyed.
virtual void get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list); // Get additional property definitions for OpenXRCompositionLayer.
virtual Dictionary get_viewport_composition_layer_extension_property_defaults(); // Get the default values for the additional property definitions for OpenXRCompositionLayer.
virtual void *set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer);
GDVIRTUAL3R(uint64_t, _set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr<void>, Dictionary, GDExtensionPtr<void>);
GDVIRTUAL1(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>);
GDVIRTUAL0R(TypedArray<Dictionary>, _get_viewport_composition_layer_extension_properties);
GDVIRTUAL0R(Dictionary, _get_viewport_composition_layer_extension_property_defaults);
GDVIRTUAL2R(uint64_t, _set_android_surface_swapchain_create_info_and_get_next_pointer, Dictionary, GDExtensionPtr<void>);
// `on_event_polled` is called when there is an OpenXR event to process.
// Should return true if the event was handled, false otherwise.
virtual bool on_event_polled(const XrEventDataBuffer &event);
GDVIRTUAL1R(bool, _on_event_polled, GDExtensionConstPtr<void>);
OpenXRExtensionWrapper() = default;
virtual ~OpenXRExtensionWrapper() = default;
};
// `OpenXRGraphicsExtensionWrapper` implements specific logic for each supported graphics API.
class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper {
public:
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0; // `get_usable_swapchain_formats` should return a list of usable color formats.
virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) = 0; // `get_usable_depth_formats` should return a list of usable depth formats.
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0; // `get_swapchain_format_name` should return the constant name of a given format.
virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) = 0; // `get_swapchain_image_data` extracts image IDs for the swapchain images and stores there in an implementation dependent data structure.
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0; // `cleanup_swapchain_graphics_data` cleans up the data held in our implementation dependent data structure and should free up its memory.
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0; // `create_projection_fov` creates a proper projection matrix based on asymmetric FOV data provided by OpenXR.
virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) = 0; // `get_texture` returns a Godot texture RID for the current active texture in our swapchain.
virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) = 0; // `get_density_map` returns a Godot texture RID for the current active density map in our swapchain (if any).
};

View File

@@ -0,0 +1,37 @@
/**************************************************************************/
/* openxr_extension_wrapper_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRExtensionWrapperExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRExtensionWrapperExtension, OpenXRExtensionWrapper);
};

View File

@@ -0,0 +1,144 @@
/**************************************************************************/
/* openxr_eye_gaze_interaction.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_eye_gaze_interaction.h"
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../openxr_api.h"
OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::singleton = nullptr;
OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::get_singleton() {
ERR_FAIL_NULL_V(singleton, nullptr);
return singleton;
}
OpenXREyeGazeInteractionExtension::OpenXREyeGazeInteractionExtension() {
singleton = this;
}
OpenXREyeGazeInteractionExtension::~OpenXREyeGazeInteractionExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXREyeGazeInteractionExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Only enable this extension when requested.
// We still register our meta data or the action map editor will fail.
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/eye_gaze_interaction") && (!OS::get_singleton()->has_feature("mobile") || OS::get_singleton()->has_feature(XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME))) {
request_extensions[XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME] = &available;
}
return request_extensions;
}
void *OpenXREyeGazeInteractionExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
if (!available) {
return p_next_pointer;
}
properties.type = XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT;
properties.next = p_next_pointer;
properties.supportsEyeGazeInteraction = false;
return &properties;
}
PackedStringArray OpenXREyeGazeInteractionExtension::get_suggested_tracker_names() {
PackedStringArray arr = { "/user/eyes_ext" };
return arr;
}
bool OpenXREyeGazeInteractionExtension::is_available() {
return available;
}
bool OpenXREyeGazeInteractionExtension::supports_eye_gaze_interaction() {
// The extension being available only means that the OpenXR Runtime supports the extension.
// The `supportsEyeGazeInteraction` is set to true if the device also supports this.
// Thus both need to be true.
// In addition, on mobile runtimes, the proper permission needs to be granted.
if (available && properties.supportsEyeGazeInteraction) {
return !OS::get_singleton()->has_feature("mobile") || OS::get_singleton()->has_feature("PERMISSION_XR_EXT_eye_gaze_interaction");
}
return false;
}
void OpenXREyeGazeInteractionExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Eyes top path
openxr_metadata->register_top_level_path("Eye gaze tracker", "/user/eyes_ext", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
// Eye gaze interaction
openxr_metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
}
bool OpenXREyeGazeInteractionExtension::get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose) {
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, false);
if (!init_eye_gaze_pose) {
init_eye_gaze_pose = true;
eye_tracker = openxr_api->find_tracker("/user/eyes_ext");
if (eye_tracker.is_null()) {
WARN_PRINT("Couldn't obtain eye tracker");
}
eye_action = openxr_api->find_action("eye_gaze_pose");
if (eye_action.is_null()) {
WARN_PRINT("Couldn't obtain pose action for `eye_gaze_pose`, make sure to add this to your action map.");
}
}
if (eye_tracker.is_null() || eye_action.is_null()) {
return false;
}
Transform3D eye_transform;
Vector3 linear_velocity;
Vector3 angular_velocity;
XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(eye_action, eye_tracker, eye_transform, linear_velocity, angular_velocity);
if (confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
return false;
}
r_eye_pose = eye_transform.origin + eye_transform.basis[2] * p_dist;
return true;
}

View File

@@ -0,0 +1,68 @@
/**************************************************************************/
/* openxr_eye_gaze_interaction.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXREyeGazeInteractionExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXREyeGazeInteractionExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXREyeGazeInteractionExtension *get_singleton();
OpenXREyeGazeInteractionExtension();
~OpenXREyeGazeInteractionExtension();
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
PackedStringArray get_suggested_tracker_names() override;
bool is_available();
bool supports_eye_gaze_interaction();
virtual void on_register_metadata() override;
bool get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose);
private:
static OpenXREyeGazeInteractionExtension *singleton;
bool available = false;
XrSystemEyeGazeInteractionPropertiesEXT properties;
bool init_eye_gaze_pose = false;
RID eye_tracker;
RID eye_action;
};

View File

@@ -0,0 +1,145 @@
/**************************************************************************/
/* openxr_fb_display_refresh_rate_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_fb_display_refresh_rate_extension.h"
#include "../openxr_interface.h"
OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::singleton = nullptr;
OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::get_singleton() {
return singleton;
}
OpenXRDisplayRefreshRateExtension::OpenXRDisplayRefreshRateExtension() {
singleton = this;
}
OpenXRDisplayRefreshRateExtension::~OpenXRDisplayRefreshRateExtension() {
display_refresh_rate_ext = false;
}
HashMap<String, bool *> OpenXRDisplayRefreshRateExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME] = &display_refresh_rate_ext;
return request_extensions;
}
void OpenXRDisplayRefreshRateExtension::on_instance_created(const XrInstance p_instance) {
if (display_refresh_rate_ext) {
EXT_INIT_XR_FUNC(xrEnumerateDisplayRefreshRatesFB);
EXT_INIT_XR_FUNC(xrGetDisplayRefreshRateFB);
EXT_INIT_XR_FUNC(xrRequestDisplayRefreshRateFB);
}
}
void OpenXRDisplayRefreshRateExtension::on_instance_destroyed() {
display_refresh_rate_ext = false;
}
bool OpenXRDisplayRefreshRateExtension::on_event_polled(const XrEventDataBuffer &event) {
switch (event.type) {
case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB: {
const XrEventDataDisplayRefreshRateChangedFB *event_fb = (XrEventDataDisplayRefreshRateChangedFB *)&event;
OpenXRInterface *xr_interface = OpenXRAPI::get_singleton()->get_xr_interface();
if (xr_interface) {
xr_interface->on_refresh_rate_changes(event_fb->toDisplayRefreshRate);
}
return true;
} break;
default:
return false;
}
}
float OpenXRDisplayRefreshRateExtension::get_refresh_rate() const {
float refresh_rate = 0.0;
if (display_refresh_rate_ext) {
float rate;
XrResult result = xrGetDisplayRefreshRateFB(OpenXRAPI::get_singleton()->get_session(), &rate);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain refresh rate [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
} else {
refresh_rate = rate;
}
}
return refresh_rate;
}
void OpenXRDisplayRefreshRateExtension::set_refresh_rate(float p_refresh_rate) {
if (display_refresh_rate_ext) {
XrResult result = xrRequestDisplayRefreshRateFB(OpenXRAPI::get_singleton()->get_session(), p_refresh_rate);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to set refresh rate [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
}
}
}
Array OpenXRDisplayRefreshRateExtension::get_available_refresh_rates() const {
Array arr;
XrResult result;
if (display_refresh_rate_ext) {
uint32_t display_refresh_rate_count = 0;
result = xrEnumerateDisplayRefreshRatesFB(OpenXRAPI::get_singleton()->get_session(), 0, &display_refresh_rate_count, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain refresh rates count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
}
if (display_refresh_rate_count > 0) {
float *display_refresh_rates = (float *)memalloc(sizeof(float) * display_refresh_rate_count);
if (display_refresh_rates == nullptr) {
print_line("OpenXR: Failed to obtain refresh rates memory buffer [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return arr;
}
result = xrEnumerateDisplayRefreshRatesFB(OpenXRAPI::get_singleton()->get_session(), display_refresh_rate_count, &display_refresh_rate_count, display_refresh_rates);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain refresh rates count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
memfree(display_refresh_rates);
return arr;
}
for (uint32_t i = 0; i < display_refresh_rate_count; i++) {
float refresh_rate = display_refresh_rates[i];
arr.push_back(Variant(refresh_rate));
}
memfree(display_refresh_rates);
}
}
return arr;
}

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* openxr_fb_display_refresh_rate_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
// This extension gives us access to the possible display refresh rates
// supported by the HMD.
// While this is an FB extension it has been adopted by most runtimes and
// will likely become core in the near future.
#include "../openxr_api.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRDisplayRefreshRateExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDisplayRefreshRateExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRDisplayRefreshRateExtension *get_singleton();
OpenXRDisplayRefreshRateExtension();
virtual ~OpenXRDisplayRefreshRateExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
float get_refresh_rate() const;
void set_refresh_rate(float p_refresh_rate);
Array get_available_refresh_rates() const;
private:
static OpenXRDisplayRefreshRateExtension *singleton;
bool display_refresh_rate_ext = false;
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateDisplayRefreshRatesFB, (XrSession), session, (uint32_t), displayRefreshRateCapacityInput, (uint32_t *), displayRefreshRateCountOutput, (float *), displayRefreshRates);
EXT_PROTO_XRRESULT_FUNC2(xrGetDisplayRefreshRateFB, (XrSession), session, (float *), display_refresh_rate);
EXT_PROTO_XRRESULT_FUNC2(xrRequestDisplayRefreshRateFB, (XrSession), session, (float), display_refresh_rate);
};

View File

@@ -0,0 +1,198 @@
/**************************************************************************/
/* openxr_fb_foveation_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_fb_foveation_extension.h"
#include "core/config/project_settings.h"
#include "../openxr_platform_inc.h"
OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::singleton = nullptr;
OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::get_singleton() {
return singleton;
}
OpenXRFBFoveationExtension::OpenXRFBFoveationExtension(const String &p_rendering_driver) {
singleton = this;
rendering_driver = p_rendering_driver;
swapchain_update_state_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();
int fov_level = GLOBAL_GET("xr/openxr/foveation_level");
if (fov_level >= 0 && fov_level < 4) {
foveation_level = XrFoveationLevelFB(fov_level);
}
bool fov_dyn = GLOBAL_GET("xr/openxr/foveation_dynamic");
foveation_dynamic = fov_dyn ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB;
swapchain_create_info_foveation_fb.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
swapchain_create_info_foveation_fb.next = nullptr;
swapchain_create_info_foveation_fb.flags = 0;
if (rendering_driver == "opengl3") {
swapchain_create_info_foveation_fb.flags = XR_SWAPCHAIN_CREATE_FOVEATION_SCALED_BIN_BIT_FB;
} else if (rendering_driver == "vulkan") {
swapchain_create_info_foveation_fb.flags = XR_SWAPCHAIN_CREATE_FOVEATION_FRAGMENT_DENSITY_MAP_BIT_FB;
}
}
OpenXRFBFoveationExtension::~OpenXRFBFoveationExtension() {
singleton = nullptr;
swapchain_update_state_ext = nullptr;
}
HashMap<String, bool *> OpenXRFBFoveationExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_FOVEATION_EXTENSION_NAME] = &fb_foveation_ext;
request_extensions[XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME] = &fb_foveation_configuration_ext;
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (rendering_driver == "vulkan") {
request_extensions[XR_FB_FOVEATION_VULKAN_EXTENSION_NAME] = &fb_foveation_vulkan_ext;
}
#endif // XR_USE_GRAPHICS_API_VULKAN
return request_extensions;
}
void OpenXRFBFoveationExtension::on_instance_created(const XrInstance p_instance) {
if (fb_foveation_ext) {
EXT_INIT_XR_FUNC(xrCreateFoveationProfileFB);
EXT_INIT_XR_FUNC(xrDestroyFoveationProfileFB);
}
if (fb_foveation_configuration_ext) {
// nothing to register here...
}
}
void OpenXRFBFoveationExtension::on_instance_destroyed() {
fb_foveation_ext = false;
fb_foveation_configuration_ext = false;
}
bool OpenXRFBFoveationExtension::is_enabled() const {
bool enabled = swapchain_update_state_ext != nullptr && swapchain_update_state_ext->is_enabled() && fb_foveation_ext && fb_foveation_configuration_ext;
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (rendering_driver == "vulkan") {
enabled = enabled && fb_foveation_vulkan_ext;
}
#endif // XR_USE_GRAPHICS_API_VULKAN
return enabled;
}
void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) {
if (is_enabled()) {
swapchain_create_info_foveation_fb.next = p_next_pointer;
return &swapchain_create_info_foveation_fb;
} else {
return p_next_pointer;
}
}
void OpenXRFBFoveationExtension::on_main_swapchains_created() {
update_profile();
}
XrFoveationLevelFB OpenXRFBFoveationExtension::get_foveation_level() const {
return foveation_level;
}
void OpenXRFBFoveationExtension::set_foveation_level(XrFoveationLevelFB p_foveation_level) {
foveation_level = p_foveation_level;
// Update profile will do nothing if we're not yet initialized.
update_profile();
}
XrFoveationDynamicFB OpenXRFBFoveationExtension::get_foveation_dynamic() const {
return foveation_dynamic;
}
void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic) {
foveation_dynamic = p_foveation_dynamic;
// Update profile will do nothing if we're not yet initialized.
update_profile();
}
void OpenXRFBFoveationExtension::_update_profile() {
// Must be called from rendering thread!
ERR_NOT_ON_RENDER_THREAD;
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
ERR_FAIL_NULL(fov_ext);
if (!fov_ext->is_enabled()) {
return;
}
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
XrSwapchain main_color_swapchain = openxr_api->get_color_swapchain();
if (main_color_swapchain == XR_NULL_HANDLE) {
// Our swapchain hasn't been created yet, we'll call this again once it has.
return;
}
XrFoveationLevelProfileCreateInfoFB level_profile_create_info;
level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB;
level_profile_create_info.next = nullptr;
level_profile_create_info.level = fov_ext->foveation_level;
level_profile_create_info.verticalOffset = 0.0f;
level_profile_create_info.dynamic = fov_ext->foveation_dynamic;
XrFoveationProfileCreateInfoFB profile_create_info;
profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB;
profile_create_info.next = &level_profile_create_info;
XrFoveationProfileFB foveation_profile;
XrResult result = fov_ext->xrCreateFoveationProfileFB(openxr_api->get_session(), &profile_create_info, &foveation_profile);
if (XR_FAILED(result)) {
print_line("OpenXR: Unable to create the foveation profile [", openxr_api->get_error_string(result), "]");
return;
}
XrSwapchainStateFoveationFB foveation_update_state;
foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB;
foveation_update_state.profile = foveation_profile;
result = fov_ext->swapchain_update_state_ext->xrUpdateSwapchainFB(main_color_swapchain, (XrSwapchainStateBaseHeaderFB *)&foveation_update_state);
if (XR_FAILED(result)) {
print_line("OpenXR: Unable to update the swapchain [", openxr_api->get_error_string(result), "]");
// We still want to destroy our profile so keep going...
}
result = fov_ext->xrDestroyFoveationProfileFB(foveation_profile);
if (XR_FAILED(result)) {
print_line("OpenXR: Unable to destroy the foveation profile [", openxr_api->get_error_string(result), "]");
}
}

View File

@@ -0,0 +1,102 @@
/**************************************************************************/
/* openxr_fb_foveation_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
// This extension implements the FB Foveation extension.
// This is an extension Meta added due to VRS being unavailable on Android.
// Other Android based devices are implementing this as well, see:
// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_foveation
#include "../openxr_api.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
#include "openxr_fb_update_swapchain_extension.h"
class OpenXRFBFoveationExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFBFoveationExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRFBFoveationExtension *get_singleton();
OpenXRFBFoveationExtension(const String &p_rendering_driver);
virtual ~OpenXRFBFoveationExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
virtual void on_main_swapchains_created() override;
bool is_enabled() const;
XrFoveationLevelFB get_foveation_level() const;
void set_foveation_level(XrFoveationLevelFB p_foveation_level);
XrFoveationDynamicFB get_foveation_dynamic() const;
void set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic);
private:
static OpenXRFBFoveationExtension *singleton;
// Setup
String rendering_driver;
bool fb_foveation_ext = false;
bool fb_foveation_configuration_ext = false;
bool fb_foveation_vulkan_ext = false;
// Configuration
XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB;
XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB;
static void _update_profile();
void update_profile() {
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL(rendering_server);
rendering_server->call_on_render_thread(callable_mp_static(&OpenXRFBFoveationExtension::_update_profile));
}
// Enable foveation on this swapchain
XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb;
OpenXRFBUpdateSwapchainExtension *swapchain_update_state_ext = nullptr;
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC3(xrCreateFoveationProfileFB, (XrSession), session, (const XrFoveationProfileCreateInfoFB *), create_info, (XrFoveationProfileFB *), profile);
EXT_PROTO_XRRESULT_FUNC1(xrDestroyFoveationProfileFB, (XrFoveationProfileFB), profile);
};

View File

@@ -0,0 +1,347 @@
/**************************************************************************/
/* openxr_fb_update_swapchain_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_fb_update_swapchain_extension.h"
// Always include this as late as possible.
#include "../openxr_platform_inc.h"
#ifndef GL_CUBIC_IMG
#define GL_CUBIC_IMG 0x9139
#endif
#ifndef GL_CUBIC_MIPMAP_LINEAR_IMG
#define GL_CUBIC_MIPMAP_LINEAR_IMG 0x913B
#endif
#ifndef GL_CUBIC_MIPMAP_NEAREST_IMG
#define GL_CUBIC_MIPMAP_NEAREST_IMG 0x913A
#endif
#ifndef GL_CLAMP_TO_BORDER
#define GL_CLAMP_TO_BORDER 0x812D
#endif
OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::singleton = nullptr;
OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::get_singleton() {
return singleton;
}
OpenXRFBUpdateSwapchainExtension::OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver) {
singleton = this;
rendering_driver = p_rendering_driver;
}
OpenXRFBUpdateSwapchainExtension::~OpenXRFBUpdateSwapchainExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRFBUpdateSwapchainExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME] = &fb_swapchain_update_state_ext;
if (rendering_driver == "vulkan") {
#ifdef XR_USE_GRAPHICS_API_VULKAN
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME] = &fb_swapchain_update_state_vulkan_ext;
#endif
} else if (rendering_driver == "opengl3") {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME] = &fb_swapchain_update_state_opengles_ext;
#endif
}
#ifdef ANDROID_ENABLED
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME] = &fb_swapchain_update_state_android_ext;
#endif
return request_extensions;
}
void OpenXRFBUpdateSwapchainExtension::on_instance_created(const XrInstance p_instance) {
if (fb_swapchain_update_state_ext) {
EXT_INIT_XR_FUNC(xrUpdateSwapchainFB);
EXT_INIT_XR_FUNC(xrGetSwapchainStateFB);
}
if (fb_swapchain_update_state_vulkan_ext) {
// nothing to register here...
}
if (fb_swapchain_update_state_opengles_ext) {
// nothing to register here...
}
}
void OpenXRFBUpdateSwapchainExtension::on_instance_destroyed() {
fb_swapchain_update_state_ext = false;
fb_swapchain_update_state_vulkan_ext = false;
fb_swapchain_update_state_opengles_ext = false;
}
bool OpenXRFBUpdateSwapchainExtension::is_enabled() const {
if (rendering_driver == "vulkan") {
return fb_swapchain_update_state_ext && fb_swapchain_update_state_vulkan_ext;
} else if (rendering_driver == "opengl3") {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
return fb_swapchain_update_state_ext && fb_swapchain_update_state_opengles_ext;
#else
return fb_swapchain_update_state_ext;
#endif
}
return false;
}
bool OpenXRFBUpdateSwapchainExtension::is_android_ext_enabled() const {
return fb_swapchain_update_state_android_ext;
}
void OpenXRFBUpdateSwapchainExtension::update_swapchain_state(XrSwapchain p_swapchain, const OpenXRViewportCompositionLayerProvider::SwapchainState *p_swapchain_state) {
if (!p_swapchain_state) {
return;
}
if (rendering_driver == "vulkan") {
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_vulkan_ext) {
return;
}
Color border_color = p_swapchain_state->border_color;
XrSwapchainStateSamplerVulkanFB swapchain_state = {
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB, // type
nullptr, // next
(VkFilter)filter_to_vk(p_swapchain_state->min_filter), // minFilter
(VkFilter)filter_to_vk(p_swapchain_state->mag_filter), // magFilter
(VkSamplerMipmapMode)mipmap_mode_to_vk(p_swapchain_state->mipmap_mode), // mipmapMode
(VkSamplerAddressMode)wrap_to_vk(p_swapchain_state->horizontal_wrap), // wrapModeS;
(VkSamplerAddressMode)wrap_to_vk(p_swapchain_state->vertical_wrap), // wrapModeT
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->red_swizzle), // swizzleRed
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->green_swizzle), // swizzleGreen
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->blue_swizzle), // swizzleBlue
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->alpha_swizzle), // swizzleAlpha
p_swapchain_state->max_anisotropy, // maxAnisotropy
{ border_color.r, border_color.g, border_color.b, border_color.a } // borderColor
};
XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: Failed to update swapchain [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
return;
}
#endif
} else if (rendering_driver == "opengl3") {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_opengles_ext) {
return;
}
Color border_color = p_swapchain_state->border_color;
XrSwapchainStateSamplerOpenGLESFB swapchain_state = {
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB, // type
nullptr, // next
filter_to_gl(p_swapchain_state->min_filter, p_swapchain_state->mipmap_mode), // minFilter
filter_to_gl(p_swapchain_state->mag_filter), // magFilter
wrap_to_gl(p_swapchain_state->horizontal_wrap), // wrapModeS;
wrap_to_gl(p_swapchain_state->vertical_wrap), // wrapModeT
swizzle_to_gl(p_swapchain_state->red_swizzle), // swizzleRed
swizzle_to_gl(p_swapchain_state->green_swizzle), // swizzleGreen
swizzle_to_gl(p_swapchain_state->blue_swizzle), // swizzleBlue
swizzle_to_gl(p_swapchain_state->alpha_swizzle), // swizzleAlpha
p_swapchain_state->max_anisotropy, // maxAnisotropy
{ border_color.r, border_color.g, border_color.b, border_color.a } // borderColor
};
XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: Failed to update swapchain [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
return;
}
#endif
}
}
void OpenXRFBUpdateSwapchainExtension::update_swapchain_surface_size(XrSwapchain p_swapchain, const Size2i &p_size) {
#ifdef ANDROID_ENABLED
if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_android_ext) {
return;
}
XrSwapchainStateAndroidSurfaceDimensionsFB swapchain_state = {
XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB, // type
nullptr, // next
(uint32_t)p_size.width, // width
(uint32_t)p_size.height // height
};
XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: Failed to update swapchain surface size [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
}
#endif
}
uint32_t OpenXRFBUpdateSwapchainExtension::filter_to_gl(OpenXRViewportCompositionLayerProvider::Filter p_filter, OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode) {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
switch (p_mipmap_mode) {
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED:
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return GL_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return GL_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return GL_CUBIC_IMG;
}
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_NEAREST:
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return GL_NEAREST_MIPMAP_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return GL_LINEAR_MIPMAP_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return GL_CUBIC_MIPMAP_NEAREST_IMG;
}
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_LINEAR:
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return GL_NEAREST_MIPMAP_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return GL_LINEAR_MIPMAP_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return GL_CUBIC_MIPMAP_LINEAR_IMG;
}
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::wrap_to_gl(OpenXRViewportCompositionLayerProvider::Wrap p_wrap) {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
switch (p_wrap) {
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_BORDER:
return GL_CLAMP_TO_BORDER;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_REPEAT:
return GL_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRROR_CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::swizzle_to_gl(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle) {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
switch (p_swizzle) {
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_RED:
return GL_RED;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_GREEN:
return GL_GREEN;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_BLUE:
return GL_BLUE;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ALPHA:
return GL_ALPHA;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ZERO:
return GL_ZERO;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ONE:
return GL_ONE;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::filter_to_vk(OpenXRViewportCompositionLayerProvider::Filter p_filter) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return VK_FILTER_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return VK_FILTER_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return VK_FILTER_CUBIC_EXT;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::mipmap_mode_to_vk(OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_mipmap_mode) {
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::wrap_to_vk(OpenXRViewportCompositionLayerProvider::Wrap p_wrap) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_wrap) {
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_BORDER:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRROR_CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::swizzle_to_vk(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_swizzle) {
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_RED:
return VK_COMPONENT_SWIZZLE_R;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_GREEN:
return VK_COMPONENT_SWIZZLE_G;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_BLUE:
return VK_COMPONENT_SWIZZLE_B;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ALPHA:
return VK_COMPONENT_SWIZZLE_A;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ZERO:
return VK_COMPONENT_SWIZZLE_ZERO;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ONE:
return VK_COMPONENT_SWIZZLE_ONE;
}
#endif
return 0;
}

View File

@@ -0,0 +1,91 @@
/**************************************************************************/
/* openxr_fb_update_swapchain_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
// This extension implements the FB update swapchain extension.
// This is an extension Meta added to further configure the swapchain.
// Other Android based devices are implementing this as well, see:
// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_swapchain_update_state
#include "../openxr_api.h"
#include "../util.h"
#include "openxr_composition_layer_extension.h"
#include "openxr_extension_wrapper.h"
class OpenXRFBUpdateSwapchainExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFBUpdateSwapchainExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
friend class OpenXRFBFoveationExtension;
public:
static OpenXRFBUpdateSwapchainExtension *get_singleton();
OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver);
virtual ~OpenXRFBUpdateSwapchainExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
bool is_enabled() const;
bool is_android_ext_enabled() const;
void update_swapchain_state(XrSwapchain p_swapchain, const OpenXRViewportCompositionLayerProvider::SwapchainState *p_swapchain_state);
void update_swapchain_surface_size(XrSwapchain p_swapchain, const Size2i &p_size);
private:
static OpenXRFBUpdateSwapchainExtension *singleton;
// Setup
String rendering_driver;
bool fb_swapchain_update_state_ext = false;
bool fb_swapchain_update_state_vulkan_ext = false;
bool fb_swapchain_update_state_opengles_ext = false;
bool fb_swapchain_update_state_android_ext = false;
uint32_t filter_to_gl(OpenXRViewportCompositionLayerProvider::Filter p_filter, OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode = OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED);
uint32_t wrap_to_gl(OpenXRViewportCompositionLayerProvider::Wrap p_wrap);
uint32_t swizzle_to_gl(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle);
uint32_t filter_to_vk(OpenXRViewportCompositionLayerProvider::Filter p_filter);
uint32_t mipmap_mode_to_vk(OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap);
uint32_t wrap_to_vk(OpenXRViewportCompositionLayerProvider::Wrap p_wrap);
uint32_t swizzle_to_vk(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle);
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC2(xrUpdateSwapchainFB, (XrSwapchain), swapchain, (const XrSwapchainStateBaseHeaderFB *), state);
EXT_PROTO_XRRESULT_FUNC2(xrGetSwapchainStateFB, (XrSwapchain), swapchain, (XrSwapchainStateBaseHeaderFB *), state);
};

View File

@@ -0,0 +1,266 @@
/**************************************************************************/
/* openxr_future_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_future_extension.h"
#include "../openxr_api.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRFutureResult
void OpenXRFutureResult::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_status"), &OpenXRFutureResult::get_status);
ClassDB::bind_method(D_METHOD("get_future"), &OpenXRFutureResult::_get_future);
ClassDB::bind_method(D_METHOD("cancel_future"), &OpenXRFutureResult::cancel_future);
ClassDB::bind_method(D_METHOD("set_result_value", "result_value"), &OpenXRFutureResult::set_result_value);
ClassDB::bind_method(D_METHOD("get_result_value"), &OpenXRFutureResult::get_result_value);
ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::OBJECT, "result", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRFutureResult")));
BIND_ENUM_CONSTANT(RESULT_RUNNING);
BIND_ENUM_CONSTANT(RESULT_FINISHED);
BIND_ENUM_CONSTANT(RESULT_CANCELLED);
}
void OpenXRFutureResult::_mark_as_finished() {
// Update our status
status = RESULT_FINISHED;
// Perform our callback
on_success_callback.call(this); // Note, `this` will be converted to a variant that will be refcounted!
// Emit our signal, we assume our callback has provided us with the correct result value by calling set_result_value.
emit_signal(SNAME("completed"), result_value);
}
void OpenXRFutureResult::_mark_as_cancelled() {
// Update our status
status = RESULT_CANCELLED;
// There is no point in doing a callback for cancellation as its always user invoked.
// But we do emit our signal to make sure any await finishes.
Variant no_result;
emit_signal(SNAME("completed"), no_result);
}
OpenXRFutureResult::ResultStatus OpenXRFutureResult::get_status() const {
return status;
}
XrFutureEXT OpenXRFutureResult::get_future() const {
return future;
}
uint64_t OpenXRFutureResult::_get_future() const {
return (uint64_t)future;
}
void OpenXRFutureResult::set_result_value(const Variant &p_result_value) {
result_value = p_result_value;
}
Variant OpenXRFutureResult::get_result_value() const {
return result_value;
}
void OpenXRFutureResult::cancel_future() {
ERR_FAIL_COND(status != RESULT_RUNNING);
OpenXRFutureExtension *future_extension = OpenXRFutureExtension::get_singleton();
ERR_FAIL_NULL(future_extension);
future_extension->cancel_future(future);
}
OpenXRFutureResult::OpenXRFutureResult(XrFutureEXT p_future, const Callable &p_on_success) {
future = p_future;
on_success_callback = p_on_success;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRFutureExtension
OpenXRFutureExtension *OpenXRFutureExtension::singleton = nullptr;
OpenXRFutureExtension *OpenXRFutureExtension::get_singleton() {
return singleton;
}
OpenXRFutureExtension::OpenXRFutureExtension() {
singleton = this;
}
OpenXRFutureExtension::~OpenXRFutureExtension() {
singleton = nullptr;
}
void OpenXRFutureExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_active"), &OpenXRFutureExtension::is_active);
ClassDB::bind_method(D_METHOD("register_future", "future", "on_success"), &OpenXRFutureExtension::_register_future, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("cancel_future", "future"), &OpenXRFutureExtension::_cancel_future);
}
HashMap<String, bool *> OpenXRFutureExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_EXT_FUTURE_EXTENSION_NAME] = &future_ext;
return request_extensions;
}
void OpenXRFutureExtension::on_instance_created(const XrInstance p_instance) {
if (future_ext) {
EXT_INIT_XR_FUNC(xrPollFutureEXT);
EXT_INIT_XR_FUNC(xrCancelFutureEXT);
future_ext = xrPollFutureEXT_ptr && xrCancelFutureEXT_ptr;
}
}
void OpenXRFutureExtension::on_instance_destroyed() {
xrPollFutureEXT_ptr = nullptr;
xrCancelFutureEXT_ptr = nullptr;
}
void OpenXRFutureExtension::on_session_destroyed() {
if (!is_active()) {
return;
}
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Cancel any running futures.
for (const KeyValue<XrFutureEXT, Ref<OpenXRFutureResult>> &element : futures) {
XrFutureCancelInfoEXT cancel_info = {
XR_TYPE_FUTURE_CANCEL_INFO_EXT, // type
nullptr, // next
element.key // future
};
XrResult result = xrCancelFutureEXT_ptr(openxr_api->get_instance(), &cancel_info);
if (XR_FAILED(result)) {
WARN_PRINT("OpenXR: Failed to cancel future [" + openxr_api->get_error_string(result) + "]");
}
// Make sure we mark our future result as cancelled
element.value->_mark_as_cancelled();
}
futures.clear();
}
bool OpenXRFutureExtension::is_active() const {
return future_ext;
}
Ref<OpenXRFutureResult> OpenXRFutureExtension::register_future(XrFutureEXT p_future, const Callable &p_on_success) {
ERR_FAIL_COND_V(futures.has(p_future), nullptr);
Ref<OpenXRFutureResult> future_result;
future_result.instantiate(p_future, p_on_success);
futures[p_future] = future_result;
return future_result;
}
Ref<OpenXRFutureResult> OpenXRFutureExtension::_register_future(uint64_t p_future, const Callable &p_on_success) {
return register_future((XrFutureEXT)p_future, p_on_success);
}
void OpenXRFutureExtension::cancel_future(XrFutureEXT p_future) {
ERR_FAIL_COND(!futures.has(p_future));
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
XrFutureCancelInfoEXT cancel_info = {
XR_TYPE_FUTURE_CANCEL_INFO_EXT, // type
nullptr, // next
p_future // future
};
XrResult result = xrCancelFutureEXT_ptr(openxr_api->get_instance(), &cancel_info);
if (XR_FAILED(result)) {
WARN_PRINT("OpenXR: Failed to cancel future [" + openxr_api->get_error_string(result) + "]");
}
// Make sure we mark our future result as cancelled
futures[p_future]->_mark_as_cancelled();
// And erase it from the futures we track
futures.erase(p_future);
}
void OpenXRFutureExtension::_cancel_future(uint64_t p_future) {
cancel_future((XrFutureEXT)p_future);
}
void OpenXRFutureExtension::on_process() {
if (!is_active()) {
return;
}
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Process futures
Vector<XrFutureEXT> completed;
for (const KeyValue<XrFutureEXT, Ref<OpenXRFutureResult>> &element : futures) {
XrFuturePollInfoEXT poll_info = {
XR_TYPE_FUTURE_POLL_INFO_EXT, // type
nullptr, // next
element.key // future
};
XrFuturePollResultEXT poll_result = {
XR_TYPE_FUTURE_POLL_RESULT_EXT, // type
nullptr, // next
XR_FUTURE_STATE_MAX_ENUM_EXT // state
};
XrResult result = xrPollFutureEXT_ptr(openxr_api->get_instance(), &poll_info, &poll_result);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to obtain future status [" + openxr_api->get_error_string(result) + "]");
// Maybe remove this depending on the error?
continue;
}
if (poll_result.state == XR_FUTURE_STATE_READY_EXT) {
// Mark our future result as finished (this will invoke our callback).
element.value->_mark_as_finished();
// Queue removing this
completed.push_back(element.key);
}
}
// Now clean up completed futures.
for (const XrFutureEXT &future : completed) {
futures.erase(future);
}
}

View File

@@ -0,0 +1,136 @@
/**************************************************************************/
/* openxr_future_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
/*
The OpenXR future extension forms the basis of OpenXRs ability to
execute logic asynchronously.
Asynchronous functions will return a future object which can be
polled each frame to determine if the asynchronous function has
been completed.
If so the future can be used to obtain final return values.
The API call for this is often part of the extension that utilizes
the future.
We will be using Godot Callables to drive responses on futures.
*/
#include "../util.h"
#include "core/object/ref_counted.h"
#include "openxr_extension_wrapper.h"
#include <openxr/openxr.h>
class OpenXRFutureExtension;
class OpenXRFutureResult : public RefCounted {
GDCLASS(OpenXRFutureResult, RefCounted);
friend class OpenXRFutureExtension;
protected:
static void _bind_methods();
void _mark_as_finished();
void _mark_as_cancelled();
public:
enum ResultStatus {
RESULT_RUNNING,
RESULT_FINISHED,
RESULT_CANCELLED,
};
ResultStatus get_status() const;
XrFutureEXT get_future() const;
void set_result_value(const Variant &p_result_value);
Variant get_result_value() const;
void cancel_future();
OpenXRFutureResult(XrFutureEXT p_future, const Callable &p_on_success);
private:
ResultStatus status = RESULT_RUNNING;
XrFutureEXT future;
Variant result_value;
Callable on_success_callback;
uint64_t _get_future() const;
};
VARIANT_ENUM_CAST(OpenXRFutureResult::ResultStatus);
class OpenXRFutureExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFutureExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods();
public:
static OpenXRFutureExtension *get_singleton();
OpenXRFutureExtension();
virtual ~OpenXRFutureExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
virtual void on_session_destroyed() override;
virtual void on_process() override;
bool is_active() const;
Ref<OpenXRFutureResult> register_future(XrFutureEXT p_future, const Callable &p_on_success = Callable());
void cancel_future(XrFutureEXT p_future);
private:
static OpenXRFutureExtension *singleton;
bool future_ext = false;
HashMap<XrFutureEXT, Ref<OpenXRFutureResult>> futures;
// Make these accessible from GDExtension and/or GDScript
Ref<OpenXRFutureResult> _register_future(uint64_t p_future, const Callable &p_on_success = Callable());
void _cancel_future(uint64_t p_future);
// OpenXR API call wrappers
// Futures
EXT_PROTO_XRRESULT_FUNC3(xrPollFutureEXT, (XrInstance), instance, (const XrFuturePollInfoEXT *), poll_info, (XrFuturePollResultEXT *), poll_result);
EXT_PROTO_XRRESULT_FUNC2(xrCancelFutureEXT, (XrInstance), instance, (const XrFutureCancelInfoEXT *), cancel_info);
};

View File

@@ -0,0 +1,97 @@
/**************************************************************************/
/* openxr_hand_interaction_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_hand_interaction_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "core/config/project_settings.h"
OpenXRHandInteractionExtension *OpenXRHandInteractionExtension::singleton = nullptr;
OpenXRHandInteractionExtension *OpenXRHandInteractionExtension::get_singleton() {
return singleton;
}
OpenXRHandInteractionExtension::OpenXRHandInteractionExtension() {
singleton = this;
}
OpenXRHandInteractionExtension::~OpenXRHandInteractionExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRHandInteractionExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Only enable this extension when requested.
// We still register our meta data or the action map editor will fail.
if (GLOBAL_GET("xr/openxr/extensions/hand_interaction_profile")) {
request_extensions[XR_EXT_HAND_INTERACTION_EXTENSION_NAME] = &available;
}
return request_extensions;
}
bool OpenXRHandInteractionExtension::is_available() {
return available;
}
void OpenXRHandInteractionExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Hand interaction profile
openxr_metadata->register_interaction_profile("Hand interaction", "/interaction_profiles/ext/hand_interaction_ext", XR_EXT_HAND_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch pose", "/user/hand/left", "/user/hand/left/input/pinch_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch pose", "/user/hand/right", "/user/hand/right/input/pinch_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Poke pose", "/user/hand/left", "/user/hand/left/input/poke_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Poke pose", "/user/hand/right", "/user/hand/right/input/poke_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch", "/user/hand/left", "/user/hand/left/input/pinch_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch", "/user/hand/right", "/user/hand/right/input/pinch_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch ready", "/user/hand/left", "/user/hand/left/input/pinch_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch ready", "/user/hand/right", "/user/hand/right/input/pinch_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate", "/user/hand/left", "/user/hand/left/input/aim_activate_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate", "/user/hand/right", "/user/hand/right/input/aim_activate_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate ready", "/user/hand/left", "/user/hand/left/input/aim_activate_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate ready", "/user/hand/right", "/user/hand/right/input/aim_activate_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp", "/user/hand/left", "/user/hand/left/input/grasp_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp", "/user/hand/right", "/user/hand/right/input/grasp_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp ready", "/user/hand/left", "/user/hand/left/input/grasp_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp ready", "/user/hand/right", "/user/hand/right/input/grasp_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
}

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* openxr_hand_interaction_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
// When supported the hand interaction extension introduces an interaction
// profile that becomes active when the user either lets go of their
// controllers or isn't using controllers at all.
//
// The OpenXR specification states that all XR runtimes that support this
// interaction profile should also allow it's controller to use this
// interaction profile.
// This means that if you only supply this interaction profile in your
// action map, it should work both when the player is holding a controller
// or using visual hand tracking.
//
// This allows easier portability between games that use controller
// tracking or hand tracking.
//
// See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_interaction
// for more information.
class OpenXRHandInteractionExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHandInteractionExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRHandInteractionExtension *get_singleton();
OpenXRHandInteractionExtension();
virtual ~OpenXRHandInteractionExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual void on_register_metadata() override;
private:
static OpenXRHandInteractionExtension *singleton;
bool available = false;
};

View File

@@ -0,0 +1,479 @@
/**************************************************************************/
/* openxr_hand_tracking_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_hand_tracking_extension.h"
#include "../openxr_api.h"
#include "core/config/project_settings.h"
#include "core/string/print_string.h"
#include "servers/xr_server.h"
#include <openxr/openxr.h>
OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::singleton = nullptr;
OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::get_singleton() {
return singleton;
}
OpenXRHandTrackingExtension::OpenXRHandTrackingExtension() {
singleton = this;
// Make sure this is cleared until we actually request it
handTrackingSystemProperties.supportsHandTracking = false;
}
OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
unobstructed_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_unobstructed_data_source");
controller_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_controller_data_source");
request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext;
request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext;
if (unobstructed_data_source || controller_data_source) {
request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext;
}
return request_extensions;
}
void OpenXRHandTrackingExtension::on_instance_created(const XrInstance p_instance) {
if (hand_tracking_ext) {
EXT_INIT_XR_FUNC(xrCreateHandTrackerEXT);
EXT_INIT_XR_FUNC(xrDestroyHandTrackerEXT);
EXT_INIT_XR_FUNC(xrLocateHandJointsEXT);
hand_tracking_ext = xrCreateHandTrackerEXT_ptr && xrDestroyHandTrackerEXT_ptr && xrLocateHandJointsEXT_ptr;
}
}
void OpenXRHandTrackingExtension::on_session_destroyed() {
cleanup_hand_tracking();
}
void OpenXRHandTrackingExtension::on_instance_destroyed() {
xrCreateHandTrackerEXT_ptr = nullptr;
xrDestroyHandTrackerEXT_ptr = nullptr;
xrLocateHandJointsEXT_ptr = nullptr;
}
void *OpenXRHandTrackingExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
if (!hand_tracking_ext) {
// not supported...
return p_next_pointer;
}
handTrackingSystemProperties = {
XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, // type
p_next_pointer, // next
false, // supportsHandTracking
};
return &handTrackingSystemProperties;
}
void OpenXRHandTrackingExtension::on_state_ready() {
if (!handTrackingSystemProperties.supportsHandTracking) {
// not supported...
return;
}
// Setup our hands and reset data
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
// we'll do this later
hand_trackers[i].is_initialized = false;
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
hand_trackers[i].locations.isActive = false;
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; j++) {
hand_trackers[i].joint_locations[j] = { 0, { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }, 0.0 };
hand_trackers[i].joint_velocities[j] = { 0, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
}
}
}
void OpenXRHandTrackingExtension::on_process() {
if (!handTrackingSystemProperties.supportsHandTracking) {
// not supported...
return;
}
// process our hands
const XrTime time = OpenXRAPI::get_singleton()->get_predicted_display_time();
if (time == 0) {
// we don't have timing info yet, or we're skipping a frame...
return;
}
XrResult result;
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
if (hand_trackers[i].hand_tracker == XR_NULL_HANDLE) {
void *next_pointer = nullptr;
// Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking.
// With this extension we can indicate we wish to accept input from either or both sources.
// This functionality is subject to the abilities of the XR runtime and requires the data source extension.
// Note: If the data source extension is not available, no guarantees can be made on what the XR runtime supports.
uint32_t data_source_count = 0;
XrHandTrackingDataSourceEXT data_sources[2];
if (unobstructed_data_source) {
data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT;
}
if (controller_data_source) {
data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT;
}
XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, data_source_count, data_sources };
if (hand_tracking_source_ext) {
// If supported include this info
next_pointer = &data_source_info;
}
XrHandTrackerCreateInfoEXT create_info = {
XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, // type
next_pointer, // next
i == 0 ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT, // hand
XR_HAND_JOINT_SET_DEFAULT_EXT, // handJointSet
};
result = xrCreateHandTrackerEXT(OpenXRAPI::get_singleton()->get_session(), &create_info, &hand_trackers[i].hand_tracker);
if (XR_FAILED(result)) {
// not successful? then we do nothing.
print_line("OpenXR: Failed to obtain hand tracking information [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
hand_trackers[i].is_initialized = false;
} else {
next_pointer = nullptr;
hand_trackers[i].velocities.type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
hand_trackers[i].velocities.next = next_pointer;
hand_trackers[i].velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
hand_trackers[i].velocities.jointVelocities = hand_trackers[i].joint_velocities;
next_pointer = &hand_trackers[i].velocities;
if (hand_tracking_source_ext) {
hand_trackers[i].data_source.type = XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT;
hand_trackers[i].data_source.next = next_pointer;
hand_trackers[i].data_source.isActive = false;
hand_trackers[i].data_source.dataSource = XrHandTrackingDataSourceEXT(0);
next_pointer = &hand_trackers[i].data_source;
}
// Needed for vendor hand tracking extensions implemented from GDExtension.
for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {
void *np = wrapper->set_hand_joint_locations_and_get_next_pointer(i, next_pointer);
if (np != nullptr) {
next_pointer = np;
}
}
hand_trackers[i].locations.type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
hand_trackers[i].locations.next = next_pointer;
hand_trackers[i].locations.isActive = false;
hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations;
Ref<XRHandTracker> godot_tracker;
godot_tracker.instantiate();
godot_tracker->set_tracker_hand(i == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
godot_tracker->set_tracker_name(i == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right");
XRServer::get_singleton()->add_tracker(godot_tracker);
hand_trackers[i].godot_tracker = godot_tracker;
hand_trackers[i].is_initialized = true;
}
}
if (hand_trackers[i].is_initialized) {
Ref<XRHandTracker> godot_tracker = hand_trackers[i].godot_tracker;
void *next_pointer = nullptr;
XrHandJointsMotionRangeInfoEXT motion_range_info = { XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, next_pointer, hand_trackers[i].motion_range };
if (hand_motion_range_ext) {
next_pointer = &motion_range_info;
}
XrHandJointsLocateInfoEXT locateInfo = {
XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, // type
next_pointer, // next
OpenXRAPI::get_singleton()->get_play_space(), // baseSpace
time, // time
};
result = xrLocateHandJointsEXT(hand_trackers[i].hand_tracker, &locateInfo, &hand_trackers[i].locations);
if (XR_FAILED(result)) {
// not successful? then we do nothing.
print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]");
godot_tracker->set_hand_tracking_source(XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default");
continue;
}
// For some reason an inactive controller isn't coming back as inactive but has coordinates either as NAN or very large
const XrPosef &palm = hand_trackers[i].joint_locations[XR_HAND_JOINT_PALM_EXT].pose;
if (!hand_trackers[i].locations.isActive || std::isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive
}
if (hand_trackers[i].locations.isActive) {
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
const Quaternion bone_adjustment(0.0, -Math::SQRT12, Math::SQRT12, 0.0);
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint];
const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint];
const XrPosef &pose = location.pose;
Transform3D transform;
Vector3 linear_velocity;
Vector3 angular_velocity;
BitField<XRHandTracker::HandJointFlags> flags = {};
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID);
transform.basis = Basis(Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * bone_adjustment);
}
}
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_VALID);
transform.origin = Vector3(pose.position.x, pose.position.y, pose.position.z);
}
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_TRACKED);
}
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_TRACKED);
}
if (location.locationFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID);
linear_velocity = Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
godot_tracker->set_hand_joint_linear_velocity((XRHandTracker::HandJoint)joint, linear_velocity);
}
if (location.locationFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID);
angular_velocity = Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
godot_tracker->set_hand_joint_angular_velocity((XRHandTracker::HandJoint)joint, angular_velocity);
}
godot_tracker->set_hand_joint_flags((XRHandTracker::HandJoint)joint, flags);
godot_tracker->set_hand_joint_transform((XRHandTracker::HandJoint)joint, transform);
godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
if (joint == XR_HAND_JOINT_PALM_EXT) {
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
if (hand_tracking_source_ext) {
if (!data_source.isActive) {
source = XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED;
} else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED;
} else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER;
} else {
// Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
WARN_PRINT_ONCE("Unknown active data source found!");
source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
}
}
godot_tracker->set_hand_tracking_source(source);
godot_tracker->set_has_tracking_data(true);
godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
} else {
godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default");
}
}
}
} else {
godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default");
}
}
}
}
void OpenXRHandTrackingExtension::on_state_stopping() {
// cleanup
cleanup_hand_tracking();
}
void OpenXRHandTrackingExtension::cleanup_hand_tracking() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
if (hand_trackers[i].hand_tracker != XR_NULL_HANDLE) {
xrDestroyHandTrackerEXT(hand_trackers[i].hand_tracker);
hand_trackers[i].is_initialized = false;
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
XRServer::get_singleton()->remove_tracker(hand_trackers[i].godot_tracker);
}
}
}
bool OpenXRHandTrackingExtension::get_active() {
return handTrackingSystemProperties.supportsHandTracking;
}
const OpenXRHandTrackingExtension::HandTracker *OpenXRHandTrackingExtension::get_hand_tracker(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, nullptr);
return &hand_trackers[p_hand];
}
XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT);
return hand_trackers[p_hand].motion_range;
}
OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN);
if (hand_tracking_source_ext) {
if (!hand_trackers[p_hand].data_source.isActive) {
return OPENXR_SOURCE_NOT_TRACKED;
} else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
return OPENXR_SOURCE_UNOBSTRUCTED;
} else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
return OPENXR_SOURCE_CONTROLLER;
} else {
// Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
WARN_PRINT_ONCE("Unknown active data source found!");
return OPENXR_SOURCE_UNKNOWN;
}
}
return OPENXR_SOURCE_UNKNOWN;
}
void OpenXRHandTrackingExtension::set_motion_range(HandTrackedHands p_hand, XrHandJointsMotionRangeEXT p_motion_range) {
ERR_FAIL_UNSIGNED_INDEX(p_hand, OPENXR_MAX_TRACKED_HANDS);
hand_trackers[p_hand].motion_range = p_motion_range;
}
XrSpaceLocationFlags OpenXRHandTrackingExtension::get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceLocationFlags(0));
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceLocationFlags(0));
if (!hand_trackers[p_hand].is_initialized) {
return XrSpaceLocationFlags(0);
}
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
return location.locationFlags;
}
Quaternion OpenXRHandTrackingExtension::get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Quaternion());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Quaternion());
if (!hand_trackers[p_hand].is_initialized) {
return Quaternion();
}
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
return Quaternion(location.pose.orientation.x, location.pose.orientation.y, location.pose.orientation.z, location.pose.orientation.w);
}
Vector3 OpenXRHandTrackingExtension::get_hand_joint_position(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
if (!hand_trackers[p_hand].is_initialized) {
return Vector3();
}
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
return Vector3(location.pose.position.x, location.pose.position.y, location.pose.position.z);
}
float OpenXRHandTrackingExtension::get_hand_joint_radius(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, 0.0);
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, 0.0);
if (!hand_trackers[p_hand].is_initialized) {
return 0.0;
}
return hand_trackers[p_hand].joint_locations[p_joint].radius;
}
XrSpaceVelocityFlags OpenXRHandTrackingExtension::get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceVelocityFlags(0));
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceVelocityFlags(0));
if (!hand_trackers[p_hand].is_initialized) {
return XrSpaceVelocityFlags(0);
}
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
return velocity.velocityFlags;
}
Vector3 OpenXRHandTrackingExtension::get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
if (!hand_trackers[p_hand].is_initialized) {
return Vector3();
}
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
return Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
}
Vector3 OpenXRHandTrackingExtension::get_hand_joint_angular_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
if (!hand_trackers[p_hand].is_initialized) {
return Vector3();
}
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
return Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
}

View File

@@ -0,0 +1,128 @@
/**************************************************************************/
/* openxr_hand_tracking_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "../util.h"
#include "core/math/quaternion.h"
#include "openxr_extension_wrapper.h"
#include "servers/xr/xr_hand_tracker.h"
class OpenXRHandTrackingExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHandTrackingExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
enum HandTrackedHands {
OPENXR_TRACKED_LEFT_HAND,
OPENXR_TRACKED_RIGHT_HAND,
OPENXR_MAX_TRACKED_HANDS
};
enum HandTrackedSource {
OPENXR_SOURCE_UNKNOWN,
OPENXR_SOURCE_UNOBSTRUCTED,
OPENXR_SOURCE_CONTROLLER,
OPENXR_SOURCE_NOT_TRACKED,
OPENXR_SOURCE_MAX
};
struct HandTracker {
bool is_initialized = false;
Ref<XRHandTracker> godot_tracker;
XrHandJointsMotionRangeEXT motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
HandTrackedSource source = OPENXR_SOURCE_UNKNOWN;
XrHandTrackerEXT hand_tracker = XR_NULL_HANDLE;
XrHandJointLocationEXT joint_locations[XR_HAND_JOINT_COUNT_EXT];
XrHandJointVelocityEXT joint_velocities[XR_HAND_JOINT_COUNT_EXT];
XrHandJointVelocitiesEXT velocities;
XrHandJointLocationsEXT locations;
XrHandTrackingDataSourceStateEXT data_source;
};
static OpenXRHandTrackingExtension *get_singleton();
OpenXRHandTrackingExtension();
virtual ~OpenXRHandTrackingExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void on_instance_destroyed() override;
virtual void on_session_destroyed() override;
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
virtual void on_state_ready() override;
virtual void on_process() override;
virtual void on_state_stopping() override;
bool get_active();
const HandTracker *get_hand_tracker(HandTrackedHands p_hand) const;
XrHandJointsMotionRangeEXT get_motion_range(HandTrackedHands p_hand) const;
void set_motion_range(HandTrackedHands p_hand, XrHandJointsMotionRangeEXT p_motion_range);
HandTrackedSource get_hand_tracking_source(HandTrackedHands p_hand) const;
XrSpaceLocationFlags get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Quaternion get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_position(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
float get_hand_joint_radius(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
XrSpaceVelocityFlags get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_angular_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
private:
static OpenXRHandTrackingExtension *singleton;
// state
XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties;
HandTracker hand_trackers[OPENXR_MAX_TRACKED_HANDS]; // Fixed for left and right hand
// related extensions
bool hand_tracking_ext = false;
bool hand_motion_range_ext = false;
bool hand_tracking_source_ext = false;
bool unobstructed_data_source = false;
bool controller_data_source = false;
// functions
void cleanup_hand_tracking();
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC3(xrCreateHandTrackerEXT, (XrSession), p_session, (const XrHandTrackerCreateInfoEXT *), p_createInfo, (XrHandTrackerEXT *), p_handTracker)
EXT_PROTO_XRRESULT_FUNC1(xrDestroyHandTrackerEXT, (XrHandTrackerEXT), p_handTracker)
EXT_PROTO_XRRESULT_FUNC3(xrLocateHandJointsEXT, (XrHandTrackerEXT), p_handTracker, (const XrHandJointsLocateInfoEXT *), p_locateInfo, (XrHandJointLocationsEXT *), p_locations)
};

View File

@@ -0,0 +1,149 @@
/**************************************************************************/
/* openxr_htc_controller_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_htc_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRHTCControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available[HTC_VIVE_COSMOS];
request_extensions[XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available[HTC_VIVE_FOCUS3];
request_extensions[XR_HTC_HAND_INTERACTION_EXTENSION_NAME] = &available[HTC_HAND_INTERACTION];
return request_extensions;
}
PackedStringArray OpenXRHTCControllerExtension::get_suggested_tracker_names() {
PackedStringArray arr = {
"/user/hand_htc/left",
"/user/hand_htc/right",
};
return arr;
}
bool OpenXRHTCControllerExtension::is_available(HTCControllers p_type) {
return available[p_type];
}
void OpenXRHTCControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
openxr_metadata->register_top_level_path("HTC left hand tracker", "/user/hand_htc/left", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("HTC right hand tracker", "/user/hand_htc/right", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
{ // HTC Vive Cosmos controller
const String profile_path = "/interaction_profiles/htc/vive_cosmos_controller";
openxr_metadata->register_interaction_profile("Vive Cosmos controller", profile_path, XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Shoulder click", user_path, user_path + "/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // HTC Vive Focus 3 controller
const String profile_path = "/interaction_profiles/htc/vive_focus3_controller";
openxr_metadata->register_interaction_profile("Vive Focus 3 controller", profile_path, XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze touch", user_path, user_path + "/input/squeeze/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbrest touch", user_path, user_path + "/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // HTC Hand interaction profile
const String profile_path = "/interaction_profiles/htc/hand_interaction";
openxr_metadata->register_interaction_profile("HTC Hand interaction", profile_path, XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand_htc/left", "/user/hand_htc/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Select (pinch)", user_path, user_path + "/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze (grab)", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
}
}
}

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* openxr_htc_controller_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRHTCControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHTCControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
enum HTCControllers {
// Note, HTC Vive Wand controllers are part of the core spec and not part of our extension.
HTC_VIVE_COSMOS,
HTC_VIVE_FOCUS3,
HTC_HAND_INTERACTION,
HTC_MAX_CONTROLLERS
};
virtual HashMap<String, bool *> get_requested_extensions() override;
PackedStringArray get_suggested_tracker_names() override;
bool is_available(HTCControllers p_type);
virtual void on_register_metadata() override;
private:
bool available[HTC_MAX_CONTROLLERS] = { false, false };
};

View File

@@ -0,0 +1,151 @@
/**************************************************************************/
/* openxr_htc_vive_tracker_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_htc_vive_tracker_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "core/string/print_string.h"
HashMap<String, bool *> OpenXRHTCViveTrackerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME] = &available;
return request_extensions;
}
PackedStringArray OpenXRHTCViveTrackerExtension::get_suggested_tracker_names() {
PackedStringArray arr = {
"/user/vive_tracker_htcx/role/handheld_object",
"/user/vive_tracker_htcx/role/left_foot",
"/user/vive_tracker_htcx/role/right_foot",
"/user/vive_tracker_htcx/role/left_shoulder",
"/user/vive_tracker_htcx/role/right_shoulder",
"/user/vive_tracker_htcx/role/left_elbow",
"/user/vive_tracker_htcx/role/right_elbow",
"/user/vive_tracker_htcx/role/left_knee",
"/user/vive_tracker_htcx/role/right_knee",
"/user/vive_tracker_htcx/role/waist",
"/user/vive_tracker_htcx/role/chest",
"/user/vive_tracker_htcx/role/camera",
"/user/vive_tracker_htcx/role/keyboard",
"/user/vive_tracker_htcx/role/left_wrist",
"/user/vive_tracker_htcx/role/right_wrist",
"/user/vive_tracker_htcx/role/left_ankle",
"/user/vive_tracker_htcx/role/right_ankle",
};
return arr;
}
bool OpenXRHTCViveTrackerExtension::is_available() {
return available;
}
void OpenXRHTCViveTrackerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// register_top_level_path("Handheld object tracker", "/user/vive_tracker_htcx/role/handheld_object", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left foot tracker", "/user/vive_tracker_htcx/role/left_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right foot tracker", "/user/vive_tracker_htcx/role/right_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left shoulder tracker", "/user/vive_tracker_htcx/role/left_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right shoulder tracker", "/user/vive_tracker_htcx/role/right_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left elbow tracker", "/user/vive_tracker_htcx/role/left_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right elbow tracker", "/user/vive_tracker_htcx/role/right_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left knee tracker", "/user/vive_tracker_htcx/role/left_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right knee tracker", "/user/vive_tracker_htcx/role/right_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Waist tracker", "/user/vive_tracker_htcx/role/waist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Chest tracker", "/user/vive_tracker_htcx/role/chest", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Camera tracker", "/user/vive_tracker_htcx/role/camera", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Keyboard tracker", "/user/vive_tracker_htcx/role/keyboard", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left wrist tracker", "/user/vive_tracker_htcx/role/left_wrist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right wrist tracker", "/user/vive_tracker_htcx/role/right_wrist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left ankle tracker", "/user/vive_tracker_htcx/role/left_ankle", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right ankle tracker", "/user/vive_tracker_htcx/role/right_ankle", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
{ // HTC Vive tracker
// Interestingly enough trackers don't have buttons or inputs, yet these are defined in the spec.
// I think this can be supported through attachments on the trackers.
const String profile_path = "/interaction_profiles/htc/vive_tracker_htcx";
openxr_metadata->register_interaction_profile("HTC Vive tracker", profile_path, XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
for (const String user_path : {
/* "/user/vive_tracker_htcx/role/handheld_object", */
"/user/vive_tracker_htcx/role/left_foot",
"/user/vive_tracker_htcx/role/right_foot",
"/user/vive_tracker_htcx/role/left_shoulder",
"/user/vive_tracker_htcx/role/right_shoulder",
"/user/vive_tracker_htcx/role/left_elbow",
"/user/vive_tracker_htcx/role/right_elbow",
"/user/vive_tracker_htcx/role/left_knee",
"/user/vive_tracker_htcx/role/right_knee",
"/user/vive_tracker_htcx/role/waist",
"/user/vive_tracker_htcx/role/chest",
"/user/vive_tracker_htcx/role/camera",
"/user/vive_tracker_htcx/role/keyboard",
"/user/vive_tracker_htcx/role/left_wrist",
"/user/vive_tracker_htcx/role/right_wrist",
"/user/vive_tracker_htcx/role/left_ankle",
"/user/vive_tracker_htcx/role/right_ankle",
}) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
}
bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &event) {
switch (event.type) {
case XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX: {
// Investigate if we need to do more here
print_verbose("OpenXR EVENT: VIVE tracker connected");
return true;
} break;
default: {
return false;
} break;
}
}

View File

@@ -0,0 +1,53 @@
/**************************************************************************/
/* openxr_htc_vive_tracker_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRHTCViveTrackerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHTCViveTrackerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
PackedStringArray get_suggested_tracker_names() override;
bool is_available();
virtual void on_register_metadata() override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
private:
bool available = false;
};

View File

@@ -0,0 +1,80 @@
/**************************************************************************/
/* openxr_huawei_controller_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_huawei_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRHuaweiControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRHuaweiControllerExtension::is_available() {
return available;
}
void OpenXRHuaweiControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
{ // Huawei controller
const String profile_path = "/interaction_profiles/huawei/controller";
openxr_metadata->register_interaction_profile("Huawei controller", profile_path, XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Home click", user_path, user_path + "/input/home/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Back click", user_path, user_path + "/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Volume up click", user_path, user_path + "/input/volume_up/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Volume down click", user_path, user_path + "/input/volume_down/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
}

Some files were not shown because too many files have changed in this diff Show More