Add action map editor for OpenXR

This commit is contained in:
Bastiaan Olij 2022-03-17 10:24:47 +11:00
parent 7bb963efe9
commit 9b7b9de0e5
39 changed files with 2739 additions and 58 deletions

View file

@ -1923,7 +1923,7 @@
</member>
<member name="rendering/vulkan/staging_buffer/texture_upload_region_size_px" type="int" setter="" getter="" default="64">
</member>
<member name="xr/openxr/default_action_map" type="String" setter="" getter="" default="&quot;res://default_action_map.tres&quot;">
<member name="xr/openxr/default_action_map" type="String" setter="" getter="" default="&quot;res://openxr_action_map.tres&quot;">
Action map configuration to load by default.
</member>
<member name="xr/openxr/enabled" type="bool" setter="" getter="" default="false">

View file

@ -30,6 +30,13 @@
Returns the previously active tab index.
</description>
</method>
<method name="get_tab_button_icon" qualifiers="const">
<return type="Texture2D" />
<argument index="0" name="tab_idx" type="int" />
<description>
Returns the button icon from the tab at index [code]tab_idx[/code].
</description>
</method>
<method name="get_tab_control" qualifiers="const">
<return type="Control" />
<argument index="0" name="tab_idx" type="int" />
@ -92,6 +99,14 @@
If set on a [Popup] node instance, a popup menu icon appears in the top-right corner of the [TabContainer] (setting it to [code]null[/code] will make it go away). Clicking it will expand the [Popup] node.
</description>
</method>
<method name="set_tab_button_icon">
<return type="void" />
<argument index="0" name="tab_idx" type="int" />
<argument index="1" name="icon" type="Texture2D" />
<description>
Sets the button icon from the tab at index [code]tab_idx[/code].
</description>
</method>
<method name="set_tab_disabled">
<return type="void" />
<argument index="0" name="tab_idx" type="int" />
@ -158,6 +173,12 @@
Emitted when the [TabContainer]'s [Popup] button is clicked. See [method set_popup] for details.
</description>
</signal>
<signal name="tab_button_pressed">
<argument index="0" name="tab" type="int" />
<description>
Emitted when the user clicks on the button icon on this tab.
</description>
</signal>
<signal name="tab_changed">
<argument index="0" name="tab" type="int" />
<description>

View file

@ -1489,8 +1489,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF("input_devices/pointing/ios/touch_delay", 0.150);
// XR project settings.
GLOBAL_DEF_BASIC("xr/openxr/enabled", false);
GLOBAL_DEF_BASIC("xr/openxr/default_action_map", "res://default_action_map.tres");
GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false);
GLOBAL_DEF_BASIC("xr/openxr/default_action_map", "res://openxr_action_map.tres");
ProjectSettings::get_singleton()->set_custom_property_info("xr/openxr/default_action_map", PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"));
GLOBAL_DEF_BASIC("xr/openxr/form_factor", "0");

View file

@ -83,5 +83,9 @@ if env["vulkan"]:
env.modules_sources += module_obj
if env["tools"]:
SConscript("editor/SCsub")
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

View file

@ -29,6 +29,7 @@
/*************************************************************************/
#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);
@ -62,6 +63,16 @@ Ref<OpenXRAction> OpenXRAction::new_action(const char *p_name, const char *p_loc
return action;
}
String OpenXRAction::get_name_with_set() const {
String name = get_name();
if (action_set != nullptr) {
name = action_set->get_name() + "/" + name;
}
return name;
}
void OpenXRAction::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
}
@ -86,6 +97,18 @@ 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);
}
}
void OpenXRAction::rem_toplevel_path(const String p_toplevel_path) {
if (toplevel_paths.has(p_toplevel_path)) {
toplevel_paths.erase(p_toplevel_path);
}
}
void OpenXRAction::parse_toplevel_paths(const String p_toplevel_paths) {
toplevel_paths = p_toplevel_paths.split(",", false);
}

View file

@ -33,6 +33,8 @@
#include "core/io/resource.h"
class OpenXRActionSet;
class OpenXRAction : public Resource {
GDCLASS(OpenXRAction, Resource);
@ -43,6 +45,7 @@ public:
OPENXR_ACTION_VECTOR2,
OPENXR_ACTION_POSE,
OPENXR_ACTION_HAPTIC,
OPENXR_ACTION_MAX
};
private:
@ -52,21 +55,31 @@ private:
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);
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
void set_localized_name(const String p_localized_name);
String get_localized_name() const;
String get_name_with_set() const; // Retrieve the name of this action as <action_set>/<action>
void set_action_type(const ActionType p_action_type);
ActionType get_action_type() const;
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_toplevel_paths(const PackedStringArray p_toplevel_paths);
PackedStringArray get_toplevel_paths() const;
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 parse_toplevel_paths(const String p_toplevel_paths);
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

@ -35,6 +35,9 @@ void OpenXRActionMap::_bind_methods() {
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);
@ -42,6 +45,9 @@ void OpenXRActionMap::_bind_methods() {
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);
@ -56,6 +62,27 @@ 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());
@ -79,6 +106,27 @@ 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 (int i = 0; i < interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
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());
@ -255,6 +303,69 @@ 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) {
Ref<OpenXRAction> action = get_action(p_path);
if (action.is_valid()) {
OpenXRActionSet *action_set = action->get_action_set();
if (action_set != nullptr) {
// Remove the action from this action set
action_set->remove_action(action);
}
for (int i = 0; i < interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
// Remove any bindings for this action
interaction_profile->remove_binding_for_action(action);
}
}
}
PackedStringArray OpenXRActionMap::get_top_level_paths(Ref<OpenXRAction> p_action) {
PackedStringArray arr;
for (int i = 0; i < interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> ip = interaction_profiles[i];
const OpenXRDefs::InteractionProfile *profile = OpenXRDefs::get_profile(ip->get_interaction_profile_path());
if (profile != nullptr) {
for (int j = 0; j < ip->get_binding_count(); j++) {
Ref<OpenXRIPBinding> binding = ip->get_binding(j);
if (binding->get_action() == p_action) {
PackedStringArray paths = binding->get_paths();
for (int k = 0; k < paths.size(); k++) {
const OpenXRDefs::IOPath *io_path = profile->get_io_path(paths[k]);
if (io_path != nullptr) {
String top_path = String(io_path->top_level_path->openxr_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();
interaction_profiles.clear();

View file

@ -33,6 +33,7 @@
#include "core/io/resource.h"
#include "openxr_action.h"
#include "openxr_action_set.h"
#include "openxr_interaction_profile.h"
@ -47,20 +48,33 @@ protected:
static void _bind_methods();
public:
void set_action_sets(Array p_action_sets);
Array get_action_sets() const;
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)
void add_action_set(Ref<OpenXRActionSet> p_action_set);
void remove_action_set(Ref<OpenXRActionSet> p_action_set);
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 set_interaction_profiles(Array p_interaction_profiles);
Array get_interaction_profiles() const;
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)
void add_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile);
void remove_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile);
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();
void create_editor_action_sets();
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); // Remove action from action set, also removes it from interaction profiles
PackedStringArray get_top_level_paths(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

@ -39,6 +39,7 @@ void OpenXRActionSet::_bind_methods() {
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");
@ -75,18 +76,54 @@ 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
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
action->action_set = nullptr;
}
actions.clear();
}
void OpenXRActionSet::set_actions(Array p_actions) {
actions = 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.find(p_action) == -1) {
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);
}
}
@ -95,6 +132,9 @@ 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;
}
}
@ -107,5 +147,5 @@ Ref<OpenXRAction> OpenXRActionSet::add_new_action(const char *p_name, const char
}
OpenXRActionSet::~OpenXRActionSet() {
actions.clear();
clear_actions();
}

View file

@ -43,26 +43,31 @@ private:
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);
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);
String get_localized_name() const;
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);
int get_priority() const;
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
void set_actions(Array p_actions);
Array get_actions() const;
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)
void add_action(Ref<OpenXRAction> p_action);
void remove_action(Ref<OpenXRAction> p_action);
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);
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,325 @@
/*************************************************************************/
/* openxr_defs.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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_defs.h"
// Our top level paths to which devices can be bound
OpenXRDefs::TopLevelPath OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_TOP_LEVEL_PATH_MAX] = {
{ "Left hand controller", "/user/hand/left" },
{ "Right hand controller", "/user/hand/right" },
};
// Fallback Khronos simple controller
OpenXRDefs::IOPath OpenXRDefs::simple_io_paths[] = {
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Select click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/select/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Select click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/select/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
// Original HTC Vive wands
OpenXRDefs::IOPath OpenXRDefs::vive_io_paths[] = {
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
// Microsoft motion controller (original WMR controllers)
OpenXRDefs::IOPath OpenXRDefs::motion_io_paths[] = {
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
// HP MR controller (newer G2 controllers)
OpenXRDefs::IOPath OpenXRDefs::hpmr_io_paths[] = {
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "X click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Y click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
// Meta touch controller (original touch controllers, Quest 1 and Quest 2 controllers)
OpenXRDefs::IOPath OpenXRDefs::touch_io_paths[] = {
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "X click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "X touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Y click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Y touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
// Valve index controller
OpenXRDefs::IOPath OpenXRDefs::index_io_paths[] = {
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
{ "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/a/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "A touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/b/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "B touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Trackpad force", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/force", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
{ "Trackpad force", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/force", OpenXRAction::OPENXR_ACTION_FLOAT },
{ "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
OpenXRDefs::InteractionProfile OpenXRDefs::available_interaction_profiles[] = {
{
"Simple controller", // display_name
"/interaction_profiles/khr/simple_controller", // openxr_path
simple_io_paths, // io_paths
sizeof(simple_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
{
"HTC Vive wand", // display_name
"/interaction_profiles/htc/vive_controller", // openxr_path
vive_io_paths, // io_paths
sizeof(vive_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
{
"MS Motion controller", // display_name
"/interaction_profiles/microsoft/motion_controller", // openxr_path
motion_io_paths, // io_paths
sizeof(motion_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
{
"HPMR controller", // display_name
"/interaction_profiles/hp/mixed_reality_controller", // openxr_path
hpmr_io_paths, // io_paths
sizeof(hpmr_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
{
"Touch controller", // display_name
"/interaction_profiles/oculus/touch_controller", // openxr_path
touch_io_paths, // io_paths
sizeof(touch_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
{
"Index controller", // display_name
"/interaction_profiles/valve/index_controller", // openxr_path
index_io_paths, // io_paths
sizeof(index_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
};
int OpenXRDefs::available_interaction_profile_count = sizeof(OpenXRDefs::available_interaction_profiles) / sizeof(OpenXRDefs::InteractionProfile);
const OpenXRDefs::TopLevelPath *OpenXRDefs::get_top_level_path(const String p_top_level_path) {
for (int i = 0; i < OPENXR_TOP_LEVEL_PATH_MAX; i++) {
if (available_top_level_paths[i].openxr_path == p_top_level_path) {
return &OpenXRDefs::available_top_level_paths[i];
}
}
return nullptr;
}
const OpenXRDefs::InteractionProfile *OpenXRDefs::get_profile(const String p_interaction_profile_path) {
for (int i = 0; i < available_interaction_profile_count; i++) {
if (available_interaction_profiles[i].openxr_path == p_interaction_profile_path) {
return &available_interaction_profiles[i];
}
}
return nullptr;
}
const OpenXRDefs::IOPath *OpenXRDefs::InteractionProfile::get_io_path(const String p_io_path) const {
for (int i = 0; i < available_interaction_profiles[i].io_path_count; i++) {
if (io_paths[i].openxr_path == p_io_path) {
return &io_paths[i];
}
}
return nullptr;
}
const OpenXRDefs::IOPath *OpenXRDefs::get_io_path(const String p_interaction_profile_path, const String p_io_path) {
const OpenXRDefs::InteractionProfile *profile = OpenXRDefs::get_profile(p_interaction_profile_path);
if (profile != nullptr) {
return profile->get_io_path(p_io_path);
}
return nullptr;
}
PackedStringArray OpenXRDefs::get_interaction_profile_paths() {
PackedStringArray arr;
for (int i = 0; i < available_interaction_profile_count; i++) {
arr.push_back(available_interaction_profiles[i].openxr_path);
}
return arr;
}

View file

@ -0,0 +1,99 @@
/*************************************************************************/
/* openxr_defs.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_DEFS_H
#define OPENXR_DEFS_H
#include "openxr_action.h"
///////////////////////////////////////////////////////////////////////////
// Stores available interaction profiles
//
// 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 extensions that need to be enabled
// until they are 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 on action type that automatic conversions between boolean and float
// are supported but otherwise action types should match between action and
// input/output paths.
class OpenXRDefs {
public:
enum TOP_LEVEL_PATH {
OPENXR_LEFT_HAND,
OPENXR_RIGHT_HAND,
OPENXR_TOP_LEVEL_PATH_MAX
};
struct TopLevelPath {
const char *display_name; // User friendly display name (i.e. Left controller)
const char *openxr_path; // Path in OpenXR (i.e. /user/hand/left)
};
struct IOPath {
const char *display_name; // User friendly display name (i.e. Grip pose (left controller))
const TopLevelPath *top_level_path; // Top level path identifying the usage of the device in relation to this input/output
const char *openxr_path; // Path in OpenXR (i.e. /user/hand/left/input/grip/pose)
const OpenXRAction::ActionType action_type; // Type of input/output
};
struct InteractionProfile {
const char *display_name; // User friendly display name (i.e. Simple controller)
const char *openxr_path; // Path in OpenXR (i.e. /interaction_profiles/khr/simple_controller)
const IOPath *io_paths; // Inputs and outputs for this device
const int io_path_count; // Number of inputs and outputs for this device
const IOPath *get_io_path(const String p_io_path) const;
};
private:
static TopLevelPath available_top_level_paths[OPENXR_TOP_LEVEL_PATH_MAX];
static IOPath simple_io_paths[];
static IOPath vive_io_paths[];
static IOPath motion_io_paths[];
static IOPath hpmr_io_paths[];
static IOPath touch_io_paths[];
static IOPath index_io_paths[];
static InteractionProfile available_interaction_profiles[];
static int available_interaction_profile_count;
public:
static const TopLevelPath *get_top_level_path(const String p_top_level_path);
static const InteractionProfile *get_profile(const String p_interaction_profile_path);
static const IOPath *get_io_path(const String p_interaction_profile_path, const String p_io_path);
static PackedStringArray get_interaction_profile_paths();
};
#endif // !OPENXR_DEFS_H

View file

@ -35,9 +35,14 @@ void OpenXRIPBinding::_bind_methods() {
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("get_path_count"), &OpenXRIPBinding::get_path_count);
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::ARRAY, "paths", PROPERTY_HINT_ARRAY_TYPE, "STRING"), "set_paths", "get_paths");
ClassDB::bind_method(D_METHOD("has_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);
}
Ref<OpenXRIPBinding> OpenXRIPBinding::new_binding(const Ref<OpenXRAction> p_action, const char *p_paths) {
@ -59,6 +64,10 @@ Ref<OpenXRAction> OpenXRIPBinding::get_action() const {
return action;
}
int OpenXRIPBinding::get_path_count() const {
return paths.size();
}
void OpenXRIPBinding::set_paths(const PackedStringArray p_paths) {
paths = p_paths;
}
@ -71,6 +80,22 @@ void OpenXRIPBinding::parse_paths(const String p_paths) {
paths = p_paths.split(",", false);
}
bool OpenXRIPBinding::has_path(const String p_path) const {
return paths.has(p_path);
}
void OpenXRIPBinding::add_path(const String p_path) {
if (!paths.has(p_path)) {
paths.push_back(p_path);
}
}
void OpenXRIPBinding::remove_path(const String p_path) {
if (paths.has(p_path)) {
paths.erase(p_path);
}
}
OpenXRIPBinding::~OpenXRIPBinding() {
action.unref();
}
@ -80,6 +105,8 @@ void OpenXRInteractionProfile::_bind_methods() {
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");
@ -101,18 +128,43 @@ 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(Array p_bindings) {
bindings = p_bindings;
// TODO add check here that our bindings don't contain duplicate actions
}
Array OpenXRInteractionProfile::get_bindings() const {
return bindings;
}
Ref<OpenXRIPBinding> OpenXRInteractionProfile::get_binding_for_action(const Ref<OpenXRAction> p_action) const {
for (int i = 0; i < bindings.size(); i++) {
Ref<OpenXRIPBinding> binding = bindings[i];
if (binding->get_action() == p_action) {
return binding;
}
}
return Ref<OpenXRIPBinding>();
}
void OpenXRInteractionProfile::add_binding(Ref<OpenXRIPBinding> p_binding) {
ERR_FAIL_COND(p_binding.is_null());
if (bindings.find(p_binding) == -1) {
ERR_FAIL_COND_MSG(get_binding_for_action(p_binding->get_action()).is_valid(), "There is already a binding for this action in this interaction profile");
bindings.push_back(p_binding);
}
}
@ -131,6 +183,15 @@ void OpenXRInteractionProfile::add_new_binding(const Ref<OpenXRAction> p_action,
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);
}
}
}
OpenXRInteractionProfile::~OpenXRInteractionProfile() {
bindings.clear();
}

View file

@ -34,6 +34,7 @@
#include "core/io/resource.h"
#include "openxr_action.h"
#include "openxr_defs.h"
class OpenXRIPBinding : public Resource {
GDCLASS(OpenXRIPBinding, Resource);
@ -46,15 +47,22 @@ protected:
static void _bind_methods();
public:
static Ref<OpenXRIPBinding> new_binding(const Ref<OpenXRAction> p_action, const char *p_paths);
static Ref<OpenXRIPBinding> new_binding(const Ref<OpenXRAction> p_action, const char *p_paths); // Helper function for adding a new binding
void set_action(const Ref<OpenXRAction> p_action);
Ref<OpenXRAction> get_action() const;
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_paths(const PackedStringArray p_paths);
PackedStringArray get_paths() const;
int get_path_count() const; // Get the number of io paths
void set_paths(const PackedStringArray p_paths); // Set our paths (for loading from resource)
PackedStringArray get_paths() const; // Get our paths (for saving to resource)
void parse_paths(const String p_paths);
void parse_paths(const String p_paths); // Parse a comma separated string 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
// TODO add validation that we can display in the interface that checks if no two paths belong to the same top level path
~OpenXRIPBinding();
};
@ -70,18 +78,22 @@ protected:
static void _bind_methods();
public:
static Ref<OpenXRInteractionProfile> new_profile(const char *p_input_profile_path);
static Ref<OpenXRInteractionProfile> new_profile(const char *p_input_profile_path); // Helper function to create a new interaction profile
void set_interaction_profile_path(const String p_input_profile_path);
String get_interaction_profile_path() const;
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
void set_bindings(Array p_bindings);
Array get_bindings() const;
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(Array p_bindings); // Set the bindings (for loading from a resource)
Array get_bindings() const; // Get the bindings (for saving to a resource)
void add_binding(Ref<OpenXRIPBinding> p_binding);
void remove_binding(Ref<OpenXRIPBinding> p_binding);
Ref<OpenXRIPBinding> get_binding_for_action(const Ref<OpenXRAction> p_action) const; // Get our binding record for a given action
void add_binding(Ref<OpenXRIPBinding> p_binding); // Add a binding object
void remove_binding(Ref<OpenXRIPBinding> p_binding); // Remove a binding object
void add_new_binding(const Ref<OpenXRAction> p_action, const char *p_paths);
void add_new_binding(const Ref<OpenXRAction> p_action, const char *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
~OpenXRInteractionProfile();
};

View file

@ -31,6 +31,46 @@
Setup this action set with our default actions.
</description>
</method>
<method name="find_action_set" qualifiers="const">
<return type="OpenXRActionSet" />
<argument 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" />
<argument 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" />
<argument 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" />
<argument 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" />
<argument index="0" name="action_set" type="OpenXRActionSet" />
@ -48,8 +88,10 @@
</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

@ -5,8 +5,7 @@
</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 actions, or actions 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.
Note that the name of the resource is used to identify the action set within OpenXR.
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>
@ -18,6 +17,12 @@
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" />
<argument index="0" name="action" type="OpenXRAction" />

View file

@ -4,13 +4,42 @@
Defines a binding between an [OpenXRAction] and an XR input or output.
</brief_description>
<description>
This binding resource binds an OpenXR action to inputs or outputs. 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 binding resource binds an [OpenXRAction] to inputs or outputs. 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".
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_path">
<return type="void" />
<argument index="0" name="path" type="String" />
<description>
Add an input/output path to this binding.
</description>
</method>
<method name="get_path_count" qualifiers="const">
<return type="int" />
<description>
Get the number of input/output paths in this binding.
</description>
</method>
<method name="has_path" qualifiers="const">
<return type="bool" />
<argument index="0" name="arg0" type="String" />
<description>
Returns [code]true[/code] if this input/output path is part of this binding.
</description>
</method>
<method name="remove_path">
<return type="void" />
<argument 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">
Action that is bound to these paths.
[OpenXRAction] that is bound to these paths.
</member>
<member name="paths" type="PackedStringArray" setter="set_paths" getter="get_paths" default="PackedStringArray()">
Paths that define the inputs or outputs bound on the device.

View file

@ -9,6 +9,21 @@
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_binding" qualifiers="const">
<return type="OpenXRIPBinding" />
<argument 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>
</methods>
<members>
<member name="bindings" type="Array" setter="set_bindings" getter="get_bindings" default="[]">
Action bindings for this interaction profile.

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.modules_sources, "*.cpp")

View file

@ -0,0 +1,112 @@
/*************************************************************************/
/* openxr_action_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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"
void OpenXRActionEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_editor")));
}
void OpenXRActionEditor::_theme_changed() {
rem_action->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
}
void OpenXRActionEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
} break;
}
}
void OpenXRActionEditor::_on_action_name_changed(const String p_new_text) {
// TODO validate if entry is allowed
// If our localized name matches our action name, set this too
if (action->get_name() == action->get_localized_name()) {
action->set_localized_name(p_new_text);
action_localized_name->set_text(p_new_text);
}
action->set_name(p_new_text);
}
void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_text) {
action->set_localized_name(p_new_text);
}
void OpenXRActionEditor::_on_item_selected(int p_idx) {
ERR_FAIL_COND(p_idx < 0);
ERR_FAIL_COND(p_idx >= OpenXRAction::OPENXR_ACTION_MAX);
action->set_action_type(OpenXRAction::ActionType(p_idx));
}
void OpenXRActionEditor::_on_remove_action() {
emit_signal("remove", this);
}
OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
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_custom_minimum_size(Size2(150.0, 0.0));
action_name->connect("text_changed", callable_mp(this, &OpenXRActionEditor::_on_action_name_changed));
add_child(action_name);
action_localized_name = memnew(LineEdit);
action_localized_name->set_text(action->get_localized_name());
action_localized_name->set_custom_minimum_size(Size2(150.0, 0.0));
action_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_localized_name->connect("text_changed", callable_mp(this, &OpenXRActionEditor::_on_action_localized_name_changed));
add_child(action_localized_name);
action_type = memnew(OptionButton);
action_type->add_item("Bool", OpenXRAction::OPENXR_ACTION_BOOL);
action_type->add_item("Float", OpenXRAction::OPENXR_ACTION_FLOAT);
action_type->add_item("Vector2", OpenXRAction::OPENXR_ACTION_VECTOR2);
action_type->add_item("Pose", OpenXRAction::OPENXR_ACTION_POSE);
action_type->add_item("Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC);
action_type->select(int(action->get_action_type()));
action_type->set_custom_minimum_size(Size2(100.0, 0.0));
action_type->connect("item_selected", callable_mp(this, &OpenXRActionEditor::_on_item_selected));
add_child(action_type);
// 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(TTR("Remove action"));
rem_action->connect("pressed", callable_mp(this, &OpenXRActionEditor::_on_remove_action));
rem_action->set_flat(true);
add_child(rem_action);
}

View file

@ -0,0 +1,67 @@
/*************************************************************************/
/* openxr_action_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_ACTION_EDITOR_H
#define OPENXR_ACTION_EDITOR_H
#include "../action_map/openxr_action.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:
Ref<OpenXRAction> action;
LineEdit *action_name = nullptr;
LineEdit *action_localized_name = nullptr;
OptionButton *action_type = 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);
public:
Ref<OpenXRAction> get_action() { return action; };
OpenXRActionEditor(Ref<OpenXRAction> p_action);
};
#endif // !OPENXR_ACTION_EDITOR_H

View file

@ -0,0 +1,370 @@
/*************************************************************************/
/* openxr_action_map_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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_file_dialog.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
// TODO implement redo/undo system
void OpenXRActionMapEditor::_bind_methods() {
ClassDB::bind_method("_add_action_set_editor", &OpenXRActionMapEditor::_add_action_set_editor);
ClassDB::bind_method("_update_action_sets", &OpenXRActionMapEditor::_update_action_sets);
ClassDB::bind_method("_add_interaction_profile_editor", &OpenXRActionMapEditor::_add_interaction_profile_editor);
ClassDB::bind_method("_update_interaction_profiles", &OpenXRActionMapEditor::_update_interaction_profiles);
ClassDB::bind_method(D_METHOD("_add_action_set", "name"), &OpenXRActionMapEditor::_add_action_set);
ClassDB::bind_method(D_METHOD("_set_focus_on_action_set", "action_set"), &OpenXRActionMapEditor::_set_focus_on_action_set);
ClassDB::bind_method(D_METHOD("_remove_action_set", "name"), &OpenXRActionMapEditor::_remove_action_set);
}
void OpenXRActionMapEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
for (int i = 0; i < tabs->get_child_count(); i++) {
Control *tab = static_cast<Control *>(tabs->get_child(i));
if (tab) {
tab->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
}
}
} break;
case NOTIFICATION_READY: {
_update_action_sets();
_update_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::_update_action_sets() {
// out with the old...
while (actionsets_vb->get_child_count() > 0) {
memdelete(actionsets_vb->get_child(0));
}
// in with the new...
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 (profile_path == "placeholder_text") {
// instance specific editor for this type
} else {
// instance generic editor
new_profile_editor = memnew(OpenXRInteractionProfileEditor(action_map, p_interaction_profile));
}
// now add it in..
ERR_FAIL_NULL_V(new_profile_editor, nullptr);
tabs->add_child(new_profile_editor);
new_profile_editor->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
interaction_profiles.push_back(new_profile_editor);
return new_profile_editor;
}
void OpenXRActionMapEditor::_update_interaction_profiles() {
// out with the old...
while (interaction_profiles.size() > 0) {
Node *interaction_profile = interaction_profiles[0];
interaction_profiles.remove_at(0);
tabs->remove_child(interaction_profile);
interaction_profile->queue_delete();
}
// in with the new...
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);
// update our editor right away
return _add_action_set_editor(new_action_set);
}
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());
if (action_set->get_action_count() > 0) {
// we should remove these and add to our redo/undo step before calling _remove_action_set
WARN_PRINT("Action set still has associated actions before being removed!");
}
// now we remove it
action_map->remove_action_set(action_set);
}
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);
call_deferred("_set_focus_on_action_set", 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());
action_map->remove_action_set(action_set);
actionsets_vb->remove_child(action_set_editor);
action_set_editor->queue_delete();
}
void OpenXRActionMapEditor::_on_action_removed() {
// make sure our interaction profiles are updated
_update_interaction_profiles();
}
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);
_add_interaction_profile_editor(new_profile);
tabs->set_current_tab(tabs->get_tab_count() - 1);
}
void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) {
action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
if (action_map.is_null()) {
if (p_create_new_if_missing) {
action_map.instantiate();
action_map->create_default_action_sets();
} else {
EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an OpenXR action map."));
edited_path = "";
header_label->set_text("");
return;
}
}
edited_path = p_path;
header_label->set_text(TTR("OpenXR Action map:") + " " + p_path.get_file());
}
void OpenXRActionMapEditor::_on_save_action_map() {
Error err = ResourceSaver::save(edited_path, action_map);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), edited_path));
return;
}
_update_action_sets();
_update_interaction_profiles();
}
void OpenXRActionMapEditor::_on_reset_to_default_layout() {
// create a new one
action_map.unref();
action_map.instantiate();
action_map->create_default_action_sets();
_update_action_sets();
_update_interaction_profiles();
}
void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) {
}
void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) {
OpenXRInteractionProfileEditorBase *profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(p_tab));
ERR_FAIL_NULL(profile_editor);
Ref<OpenXRInteractionProfile> interaction_profile = profile_editor->get_interaction_profile();
ERR_FAIL_COND(interaction_profile.is_null());
action_map->remove_interaction_profile(interaction_profile);
tabs->remove_child(profile_editor);
profile_editor->queue_delete();
}
void OpenXRActionMapEditor::open_action_map(String p_path) {
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
_load_action_map(p_path);
_update_action_sets();
_update_interaction_profiles();
}
OpenXRActionMapEditor::OpenXRActionMapEditor() {
set_custom_minimum_size(Size2(0.0, 300.0));
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(TTR("Add an action set."));
add_action_set->connect("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(TTR("Add an interaction profile."));
add_interaction_profile->connect("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(TTR("Save this OpenXR action map."));
save_as->connect("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(TTR("Reset to default OpenXR action map."));
_default->connect("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->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);
_load_action_map(ProjectSettings::get_singleton()->get("xr/openxr/default_action_map"));
}
OpenXRActionMapEditor::~OpenXRActionMapEditor() {
}

View file

@ -0,0 +1,100 @@
/*************************************************************************/
/* openxr_action_map_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_ACTION_MAP_EDITOR_H
#define OPENXR_ACTION_MAP_EDITOR_H
#include "../action_map/openxr_action_map.h"
#include "../editor/openxr_action_set_editor.h"
#include "../editor/openxr_interaction_profile_editor.h"
#include "../editor/openxr_select_interaction_profile_dialog.h"
#include "editor/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:
String edited_path;
Ref<OpenXRActionMap> action_map;
Vector<Node *> interaction_profiles;
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 _update_action_sets();
OpenXRInteractionProfileEditorBase *_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile);
void _update_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();
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);
public:
void open_action_map(String p_path);
OpenXRActionMapEditor();
~OpenXRActionMapEditor();
};
#endif // !OPENXR_ACTION_MAP_EDITOR_H

View file

@ -0,0 +1,218 @@
/*************************************************************************/
/* openxr_action_set_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "openxr_action_editor.h"
void OpenXRActionSetEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_set_editor")));
ADD_SIGNAL(MethodInfo("action_removed"));
}
void OpenXRActionSetEditor::_set_fold_icon() {
if (is_expanded) {
fold_btn->set_icon(get_theme_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")));
} else {
fold_btn->set_icon(get_theme_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")));
}
}
void OpenXRActionSetEditor::_theme_changed() {
_set_fold_icon();
add_action->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
rem_action_set->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
}
void OpenXRActionSetEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("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::_update_actions() {
// out with the old...
while (actions_vb->get_child_count() > 0) {
memdelete(actions_vb->get_child(0));
}
// in with the new...
Array actions = action_set->get_actions();
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
_add_action_editor(action);
}
}
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) {
// TODO validate if entry is allowed
// If our localized name matches our action set name, set this too
if (action_set->get_name() == action_set->get_localized_name()) {
action_set->set_localized_name(p_new_text);
action_set_localized_name->set_text(p_new_text);
}
action_set->set_name(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_localized_name_changed(const String p_new_text) {
action_set->set_localized_name(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_priority_changed(const String p_new_text) {
int64_t value = p_new_text.to_int();
action_set->set_priority(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);
_add_action_editor(new_action);
// 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());
// TODO add undo/redo action
// TODO find where this action is used by our interaction profiles and remove it there
// And remove it....
action_map->remove_action(action->get_name_with_set()); // remove it from the set and any interaction profile it relates to
actions_vb->remove_child(action_editor);
action_editor->queue_delete();
// Let action map editor know so we can update our interaction profiles
emit_signal("action_removed");
}
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) {
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("pressed", callable_mp(this, &OpenXRActionSetEditor::_on_toggle_expand));
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_custom_minimum_size(Size2(150.0, 0.0));
action_set_name->connect("text_changed", callable_mp(this, &OpenXRActionSetEditor::_on_action_set_name_changed));
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_custom_minimum_size(Size2(150.0, 0.0));
action_set_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_set_localized_name->connect("text_changed", callable_mp(this, &OpenXRActionSetEditor::_on_action_set_localized_name_changed));
action_set_hb->add_child(action_set_localized_name);
action_set_priority = memnew(TextEdit);
action_set_priority->set_text(itos(action_set->get_priority()));
action_set_priority->set_custom_minimum_size(Size2(50.0, 0.0));
action_set_priority->connect("text_changed", callable_mp(this, &OpenXRActionSetEditor::_on_action_set_priority_changed));
action_set_hb->add_child(action_set_priority);
add_action = memnew(Button);
add_action->set_tooltip("Add Action.");
add_action->connect("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("Remove Action Set.");
rem_action_set->connect("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);
_update_actions();
}

View file

@ -0,0 +1,88 @@
/*************************************************************************/
/* openxr_action_set_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_ACTION_SET_EDITOR_H
#define OPENXR_ACTION_SET_EDITOR_H
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_action_set.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"
class OpenXRActionSetEditor : public HBoxContainer {
GDCLASS(OpenXRActionSetEditor, HBoxContainer);
private:
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;
TextEdit *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 _update_actions();
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 String p_new_text);
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);
public:
Ref<OpenXRActionSet> get_action_set() { return action_set; };
void set_focus_on_entry();
OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set);
};
#endif // !OPENXR_ACTION_SET_EDITOR_H

View file

@ -0,0 +1,58 @@
/*************************************************************************/
/* openxr_editor_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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"
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_singleton()->add_bottom_panel_item(TTR("OpenXR Action Map"), action_map_editor);
}
OpenXREditorPlugin::~OpenXREditorPlugin() {
}

View file

@ -0,0 +1,53 @@
/*************************************************************************/
/* openxr_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_EDITOR_PLUGIN_H
#define OPENXR_EDITOR_PLUGIN_H
#include "editor/editor_plugin.h"
#include "openxr_action_map_editor.h"
class OpenXREditorPlugin : public EditorPlugin {
GDCLASS(OpenXREditorPlugin, EditorPlugin);
OpenXRActionMapEditor *action_map_editor;
public:
virtual String get_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();
~OpenXREditorPlugin();
};
#endif // !OPENXR_EDITOR_PLUGIN_H

View file

@ -0,0 +1,272 @@
/*************************************************************************/
/* openxr_interaction_profile_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/separator.h"
#include "scene/gui/text_edit.h"
///////////////////////////////////////////////////////////////////////////
// Interaction profile editor base
void OpenXRInteractionProfileEditorBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("_add_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_add_binding);
ClassDB::bind_method(D_METHOD("_remove_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_remove_binding);
ClassDB::bind_method(D_METHOD("_update_interaction_profile"), &OpenXRInteractionProfileEditorBase::_update_interaction_profile);
}
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::_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->get_binding_for_action(action);
if (binding.is_null()) {
// create a new binding
binding.instantiate();
binding->set_action(action);
interaction_profile->add_binding(binding);
}
binding->add_path(p_path);
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
call_deferred("_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->get_binding_for_action(action);
if (binding.is_valid()) {
binding->remove_path(p_path);
if (binding->get_path_count() == 0) {
interaction_profile->remove_binding(binding);
}
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
call_deferred("_update_interaction_profile");
}
}
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> 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 = OpenXRDefs::get_profile(profile_path);
if (profile_def != nullptr) {
profile_name = profile_def->display_name;
}
set_name(profile_name);
set_h_size_flags(SIZE_EXPAND_FILL);
set_v_size_flags(SIZE_EXPAND_FILL);
}
///////////////////////////////////////////////////////////////////////////
// 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::action_selected(const String p_action) {
_add_binding(p_action, selecting_for_io_path);
selecting_for_io_path = "";
}
void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRDefs::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_text(p_io_path->display_name);
path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
path_hb->add_child(path_label);
Label *type_label = memnew(Label);
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_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
path_add->set_flat(true);
Vector<Variant> add_binds;
add_binds.push_back(String(p_io_path->openxr_path));
path_add->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for), add_binds);
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 (int i = 0; i < bindings.size(); i++) {
Ref<OpenXRIPBinding> binding = bindings[i];
if (binding->has_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_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);
Button *action_rem = memnew(Button);
action_rem->set_flat(true);
action_rem->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
Vector<Variant> remove_binds;
remove_binds.push_back(action->get_name_with_set());
remove_binds.push_back(String(p_io_path->openxr_path));
action_rem->connect("pressed", callable_mp((OpenXRInteractionProfileEditorBase *)this, &OpenXRInteractionProfileEditorBase::_remove_binding), remove_binds);
action_hb->add_child(action_rem);
}
}
}
}
void OpenXRInteractionProfileEditor::_update_interaction_profile() {
// out with the old...
while (main_hb->get_child_count() > 0) {
memdelete(main_hb->get_child(0));
}
// in with the new...
// Determine toplevel paths
Vector<const OpenXRDefs::TopLevelPath *> top_level_paths;
for (int i = 0; i < profile_def->io_path_count; i++) {
const OpenXRDefs::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);
main_hb->add_child(panel);
panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
VBoxContainer *container = memnew(VBoxContainer);
panel->add_child(container);
Label *label = memnew(Label);
label->set_text(top_level_paths[i]->display_name);
container->add_child(label);
for (int j = 0; j < profile_def->io_path_count; j++) {
const OpenXRDefs::IOPath *io_path = &profile_def->io_paths[j];
if (io_path->top_level_path == top_level_paths[i]) {
_add_io_path(container, io_path);
}
}
}
}
void OpenXRInteractionProfileEditor::_theme_changed() {
for (int i = 0; i < main_hb->get_child_count(); i++) {
Control *panel = static_cast<Control *>(main_hb->get_child(i));
if (panel) {
panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
}
}
}
OpenXRInteractionProfileEditor::OpenXRInteractionProfileEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) :
OpenXRInteractionProfileEditorBase(p_action_map, p_interaction_profile) {
// TODO background of scrollbox should be darker with our VBoxContainers we're adding in _update_interaction_profile the normal color
main_hb = memnew(HBoxContainer);
add_child(main_hb);
select_action_dialog = memnew(OpenXRSelectActionDialog(p_action_map));
select_action_dialog->connect("action_selected", callable_mp(this, &OpenXRInteractionProfileEditor::action_selected));
add_child(select_action_dialog);
_update_interaction_profile();
}

View file

@ -0,0 +1,83 @@
/*************************************************************************/
/* openxr_interaction_profile_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_INTERACTION_PROFILE_EDITOR_H
#define OPENXR_INTERACTION_PROFILE_EDITOR_H
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_defs.h"
#include "../action_map/openxr_interaction_profile.h"
#include "scene/gui/scroll_container.h"
#include "openxr_select_action_dialog.h"
class OpenXRInteractionProfileEditorBase : public ScrollContainer {
GDCLASS(OpenXRInteractionProfileEditorBase, ScrollContainer);
protected:
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRActionMap> action_map;
static void _bind_methods();
void _notification(int p_what);
const OpenXRDefs::InteractionProfile *profile_def = nullptr;
public:
Ref<OpenXRInteractionProfile> get_interaction_profile() { return interaction_profile; }
virtual void _update_interaction_profile() {}
virtual void _theme_changed() {}
void _add_binding(const String p_action, const String p_path);
void _remove_binding(const String p_action, const String p_path);
OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
};
class OpenXRInteractionProfileEditor : public OpenXRInteractionProfileEditorBase {
GDCLASS(OpenXRInteractionProfileEditor, OpenXRInteractionProfileEditorBase);
private:
String selecting_for_io_path;
HBoxContainer *main_hb = nullptr;
OpenXRSelectActionDialog *select_action_dialog = nullptr;
void _add_io_path(VBoxContainer *p_container, const OpenXRDefs::IOPath *p_io_path);
public:
void select_action_for(const String p_io_path);
void action_selected(const String p_action);
virtual void _update_interaction_profile() override;
virtual void _theme_changed() override;
OpenXRInteractionProfileEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
};
#endif // !OPENXR_INTERACTION_PROFILE_EDITOR_H

View file

@ -0,0 +1,135 @@
/*************************************************************************/
/* openxr_select_action_dialog.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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/editor_node.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_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
} break;
}
}
void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
if (selected_action != "") {
NodePath button_path = action_buttons[selected_action];
Button *button = static_cast<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 = static_cast<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();
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_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();
Vector<Variant> binds;
binds.push_back(action_name);
action_button->set_flat(true);
action_button->set_text(action->get_name() + ": " + action->get_localized_name());
action_button->connect("pressed", callable_mp(this, &OpenXRSelectActionDialog::_on_select_action), binds);
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, 400.0));
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,67 @@
/*************************************************************************/
/* openxr_select_action_dialog.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_SELECT_ACTION_DIALOG_H
#define OPENXR_SELECT_ACTION_DIALOG_H
#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);
};
#endif // !OPENXR_SELECT_ACTION_DIALOG_H

View file

@ -0,0 +1,125 @@
/*************************************************************************/
/* openxr_select_interaction_profile_dialog.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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"
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_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), 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 = static_cast<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 = static_cast<Button *>(get_node(button_path));
if (button != nullptr) {
button->set_flat(false);
}
}
}
void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_include) {
int available_count = 0;
// out with the old...
while (main_vb->get_child_count() > 0) {
memdelete(main_vb->get_child(0));
}
selected_interaction_profile = "";
ip_buttons.clear();
// in with the new
PackedStringArray interaction_profiles = OpenXRDefs::get_interaction_profile_paths();
for (int i = 0; i < interaction_profiles.size(); i++) {
String path = interaction_profiles[i];
if (!p_do_not_include.has(path)) {
Button *ip_button = memnew(Button);
Vector<Variant> binds;
binds.push_back(path);
ip_button->set_flat(true);
ip_button->set_text(OpenXRDefs::get_profile(path)->display_name);
ip_button->connect("pressed", callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile), binds);
main_vb->add_child(ip_button);
ip_buttons[path] = ip_button->get_path();
available_count++;
}
}
if (available_count == 0) {
// give warning that we have all profiles selected
} else {
// TODO maybe if we only have one, auto select it?
popup_centered();
}
}
void OpenXRSelectInteractionProfileDialog::ok_pressed() {
if (selected_interaction_profile == "") {
return;
}
emit_signal("interaction_profile_selected", selected_interaction_profile);
hide();
}
OpenXRSelectInteractionProfileDialog::OpenXRSelectInteractionProfileDialog() {
set_title("Select an interaction profile");
scroll = memnew(ScrollContainer);
scroll->set_custom_minimum_size(Size2(600.0, 400.0));
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,66 @@
/*************************************************************************/
/* openxr_select_interaction_profile_dialog.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
#define OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
#include "../action_map/openxr_defs.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 OpenXRSelectInteractionProfileDialog : public ConfirmationDialog {
GDCLASS(OpenXRSelectInteractionProfileDialog, ConfirmationDialog);
private:
String selected_interaction_profile;
Dictionary ip_buttons;
VBoxContainer *main_vb = nullptr;
ScrollContainer *scroll = 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();
};
#endif // !OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H

View file

@ -30,9 +30,9 @@
#include "core/string/print_string.h"
#include "modules/openxr/extensions/openxr_vulkan_extension.h"
#include "modules/openxr/openxr_api.h"
#include "modules/openxr/openxr_util.h"
#include "../extensions/openxr_vulkan_extension.h"
#include "../openxr_api.h"
#include "../openxr_util.h"
#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"

View file

@ -48,14 +48,14 @@
#include "extensions/openxr_vulkan_extension.h"
#endif
#include "modules/openxr/openxr_interface.h"
#include "openxr_interface.h"
OpenXRAPI *OpenXRAPI::singleton = nullptr;
bool OpenXRAPI::openxr_is_enabled() {
bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) {
// @TODO we need an overrule switch so we can force enable openxr, i.e run "godot --openxr_enabled"
if (Engine::get_singleton()->is_editor_hint()) {
if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) {
#ifdef TOOLS_ENABLED
// Disabled for now, using XR inside of the editor we'll be working on during the coming months.
return false;

View file

@ -222,7 +222,7 @@ protected:
void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity);
public:
static bool openxr_is_enabled();
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
static OpenXRAPI *get_singleton();
String get_error_string(XrResult result);

View file

@ -38,6 +38,22 @@
#include "action_map/openxr_action_set.h"
#include "action_map/openxr_interaction_profile.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "editor/openxr_editor_plugin.h"
static void _editor_init() {
if (OpenXRAPI::openxr_is_enabled(false)) {
// Only add our OpenXR action map editor if OpenXR is enabled for our project
OpenXREditorPlugin *openxr_plugin = memnew(OpenXREditorPlugin());
EditorNode::get_singleton()->add_editor_plugin(openxr_plugin);
}
}
#endif
OpenXRAPI *openxr_api = nullptr;
Ref<OpenXRInterface> openxr_interface;
@ -74,6 +90,10 @@ void register_openxr_types() {
openxr_interface->initialize();
}
}
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(_editor_init);
#endif
}
void unregister_openxr_types() {

View file

@ -472,6 +472,10 @@ void TabContainer::_on_tab_selected(int p_tab) {
emit_signal(SNAME("tab_selected"), p_tab);
}
void TabContainer::_on_tab_button_pressed(int p_tab) {
emit_signal(SNAME("tab_button_pressed"), p_tab);
}
void TabContainer::_refresh_tab_names() {
Vector<Control *> controls = _get_tab_controls();
for (int i = 0; i < controls.size(); i++) {
@ -733,6 +737,17 @@ bool TabContainer::is_tab_hidden(int p_tab) const {
return tab_bar->is_tab_hidden(p_tab);
}
void TabContainer::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
tab_bar->set_tab_button_icon(p_tab, p_icon);
_update_margins();
_repaint();
}
Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const {
return tab_bar->get_tab_button_icon(p_tab);
}
void TabContainer::get_translatable_strings(List<String> *p_strings) const {
Vector<Control *> controls = _get_tab_controls();
for (int i = 0; i < controls.size(); i++) {
@ -877,6 +892,8 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled);
ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden);
ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabContainer::is_tab_hidden);
ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabContainer::set_tab_button_icon);
ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabContainer::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point);
ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
@ -896,6 +913,7 @@ void TabContainer::_bind_methods() {
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("pre_popup_pressed"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
@ -915,6 +933,7 @@ TabContainer::TabContainer() {
tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);
tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed));
tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected));
tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed));
connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}

View file

@ -56,6 +56,7 @@ class TabContainer : public Container {
void _on_mouse_exited();
void _on_tab_changed(int p_tab);
void _on_tab_selected(int p_tab);
void _on_tab_button_pressed(int p_tab);
Variant _get_drag_data_fw(const Point2 &p_point, Control *p_from_control);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const;
@ -97,6 +98,9 @@ public:
void set_tab_hidden(int p_tab, bool p_hidden);
bool is_tab_hidden(int p_tab) const;
void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_tab_button_icon(int p_tab) const;
int get_tab_count() const;
void set_current_tab(int p_current);
int get_current_tab() const;