Skip to content

Commit

Permalink
OpenXR: Add support for binding modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiaanOlij committed Oct 14, 2024
1 parent df53941 commit 0b77271
Show file tree
Hide file tree
Showing 33 changed files with 1,783 additions and 45 deletions.
6 changes: 6 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2952,6 +2952,12 @@
<member name="threading/worker_pool/max_threads" type="int" setter="" getter="" default="-1">
Maximum number of threads to be used by [WorkerThreadPool]. Value of [code]-1[/code] means no limit.
</member>
<member name="xr/openxr/binding_modifiers/analog_threshold" type="bool" setter="" getter="" default="false">
Enables the analog threshold binding modifier if supported by the XR runtime.
</member>
<member name="xr/openxr/binding_modifiers/dpad_binding" type="bool" setter="" getter="" default="false">
Enabled the dpad binding modifier if supported by the XR runtime.
</member>
<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>
Expand Down
4 changes: 4 additions & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2604,6 +2604,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/hand_interaction_profile", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/eye_gaze_interaction", false);

// OpenXR Binding modifier settings
GLOBAL_DEF_BASIC("xr/openxr/binding_modifiers/analog_threshold", false);
GLOBAL_DEF_BASIC("xr/openxr/binding_modifiers/dpad_binding", false);

#ifdef TOOLS_ENABLED
// Disabled for now, using XR inside of the editor we'll be working on during the coming months.

Expand Down
35 changes: 29 additions & 6 deletions modules/openxr/action_map/openxr_action_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,26 @@ void OpenXRActionMap::remove_action_set(Ref<OpenXRActionSet> p_action_set) {
}
}

void OpenXRActionMap::set_interaction_profiles(Array p_interaction_profiles) {
void OpenXRActionMap::clear_interaction_profiles() {
// Interaction profiles held within our action map set should be released and destroyed but just in case they are still used some where else
if (interaction_profiles.size() == 0) {
return;
}

for (int i = 0; i < interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
interaction_profile->action_map = nullptr;
}
interaction_profiles.clear();
emit_changed();
}

void OpenXRActionMap::set_interaction_profiles(Array p_interaction_profiles) {
clear_interaction_profiles();

for (int i = 0; i < p_interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profiles[i];
if (interaction_profile.is_valid() && !interaction_profiles.has(interaction_profile)) {
interaction_profiles.push_back(interaction_profile);
}
// add them anew so we verify our interaction profile pointer
add_interaction_profile(p_interaction_profiles[i]);
}
}

Expand Down Expand Up @@ -147,6 +159,13 @@ void OpenXRActionMap::add_interaction_profile(Ref<OpenXRInteractionProfile> p_in
ERR_FAIL_COND(p_interaction_profile.is_null());

if (!interaction_profiles.has(p_interaction_profile)) {
if (p_interaction_profile->action_map && p_interaction_profile->action_map != this) {
// interaction profiles should only relate to our action map
p_interaction_profile->action_map->remove_interaction_profile(p_interaction_profile);
}

p_interaction_profile->action_map = this;

interaction_profiles.push_back(p_interaction_profile);
emit_changed();
}
Expand All @@ -156,6 +175,10 @@ void OpenXRActionMap::remove_interaction_profile(Ref<OpenXRInteractionProfile> p
int idx = interaction_profiles.find(p_interaction_profile);
if (idx != -1) {
interaction_profiles.remove_at(idx);

ERR_FAIL_COND_MSG(p_interaction_profile->action_map != this, "Removing interaction profile that belongs to this action map but had incorrect action map pointer."); // this should never happen!
p_interaction_profile->action_map = nullptr;

emit_changed();
}
}
Expand Down Expand Up @@ -598,5 +621,5 @@ PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p

OpenXRActionMap::~OpenXRActionMap() {
action_sets.clear();
interaction_profiles.clear();
clear_interaction_profiles();
}
1 change: 1 addition & 0 deletions modules/openxr/action_map/openxr_action_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class OpenXRActionMap : public Resource {
void add_action_set(Ref<OpenXRActionSet> p_action_set); // Add an action set to our action map
void remove_action_set(Ref<OpenXRActionSet> p_action_set); // Remove an action set from our action map

void clear_interaction_profiles(); // Remove all our interaction profiles
void set_interaction_profiles(Array p_interaction_profiles); // Set our interaction profiles by providing an array (for loading from resource)
Array get_interaction_profiles() const; // Get our interaction profiles as an array (for saving to resource)

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

#include "openxr_binding_modifier.h"

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

#ifndef OPENXR_BINDING_MODIFIER_H
#define OPENXR_BINDING_MODIFIER_H

#include "../action_map/openxr_action.h"
#include "core/io/resource.h"

// Part of implementation for:
// https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_KHR_binding_modification

struct XrBindingModificationBaseHeaderKHR;
class OpenXRInteractionProfile;
class OpenXRIPBinding;

class OpenXRBindingModifier : public Resource {
GDCLASS(OpenXRBindingModifier, Resource);

private:
protected:
friend class OpenXRInteractionProfile;
friend class OpenXRIPBinding;

static void _bind_methods();

OpenXRInteractionProfile *interaction_profile = nullptr; // action belongs to this interaction profile (should only be set if record_on_binding() == false).
OpenXRIPBinding *ip_binding = nullptr; // action belongs to this binding (should only be set if record_on_binding() == true).

public:
virtual bool record_on_binding() const { return true; } // If true, this binding modifier is recorded on a specific binding. If false, this binding modifier is recorded on the interaction profile.

virtual String get_description() const = 0; // Returns the description shown in the editor
virtual int get_binding_modification_struct_size() const = 0; // Return the size of the struct returned by get_binding_modification
virtual const XrBindingModificationBaseHeaderKHR *get_binding_modification() = 0; // Return the binding modifier struct used when calling xrSuggestInteractionProfileBindings
};

#endif // OPENXR_BINDING_MODIFIER_H
153 changes: 153 additions & 0 deletions modules/openxr/action_map/openxr_interaction_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ void OpenXRIPBinding::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_source_path"), &OpenXRIPBinding::get_source_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_path"), "set_source_path", "get_source_path");

ClassDB::bind_method(D_METHOD("get_binding_modifier_count"), &OpenXRIPBinding::get_binding_modifier_count);
ClassDB::bind_method(D_METHOD("get_binding_modifier", "index"), &OpenXRIPBinding::get_binding_modifier);
ClassDB::bind_method(D_METHOD("set_binding_modifiers", "binding_modifiers"), &OpenXRIPBinding::set_binding_modifiers);
ClassDB::bind_method(D_METHOD("get_binding_modifiers"), &OpenXRIPBinding::get_binding_modifiers);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "binding_modifiers", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRBindingModifier", PROPERTY_USAGE_NO_EDITOR), "set_binding_modifiers", "get_binding_modifiers");

// Deprecated
ClassDB::bind_method(D_METHOD("set_paths", "paths"), &OpenXRIPBinding::set_paths);
ClassDB::bind_method(D_METHOD("get_paths"), &OpenXRIPBinding::get_paths);
Expand Down Expand Up @@ -78,6 +84,76 @@ String OpenXRIPBinding::get_source_path() const {
return source_path;
}

int OpenXRIPBinding::get_binding_modifier_count() const {
return binding_modifiers.size();
}

Ref<OpenXRBindingModifier> OpenXRIPBinding::get_binding_modifier(int p_index) const {
ERR_FAIL_INDEX_V(p_index, binding_modifiers.size(), nullptr);

return binding_modifiers[p_index];
}

void OpenXRIPBinding::clear_binding_modifiers() {
// Binding modifiers held within our interaction profile set should be released and destroyed but just in case they are still used some where else
if (binding_modifiers.size() == 0) {
return;
}

for (int i = 0; i < binding_modifiers.size(); i++) {
Ref<OpenXRBindingModifier> binding_modifier = binding_modifiers[i];
binding_modifier->ip_binding = nullptr;
}
binding_modifiers.clear();
emit_changed();
}

void OpenXRIPBinding::set_binding_modifiers(Array p_binding_modifiers) {
// Any binding modifier not retained in p_binding_modifiers should be freed automatically, those held within our Array will have be relinked to our interaction profile.
clear_binding_modifiers();

for (int i = 0; i < p_binding_modifiers.size(); i++) {
// add them anew so we verify our binding modifier pointer
add_binding_modifier(p_binding_modifiers[i]);
}
}

Array OpenXRIPBinding::get_binding_modifiers() const {
Array ret;
for (const Ref<OpenXRBindingModifier> &binding_modifier : binding_modifiers) {
ret.push_back(binding_modifier);
}
return ret;
}

void OpenXRIPBinding::add_binding_modifier(Ref<OpenXRBindingModifier> p_binding_modifier) {
ERR_FAIL_COND(p_binding_modifier.is_null());
ERR_FAIL_COND_MSG(!p_binding_modifier->record_on_binding(), "This binding modifier must be added to an interaction profile.");

if (!binding_modifiers.has(p_binding_modifier)) {
if (p_binding_modifier->ip_binding && p_binding_modifier->ip_binding != this) {
// binding modifier should only relate to our binding
p_binding_modifier->ip_binding->remove_binding_modifier(p_binding_modifier);
}

p_binding_modifier->ip_binding = this;
binding_modifiers.push_back(p_binding_modifier);
emit_changed();
}
}

void OpenXRIPBinding::remove_binding_modifier(Ref<OpenXRBindingModifier> p_binding_modifier) {
int idx = binding_modifiers.find(p_binding_modifier);
if (idx != -1) {
binding_modifiers.remove_at(idx);

ERR_FAIL_COND_MSG(p_binding_modifier->ip_binding != this, "Removing binding modifier that belongs to this binding but had incorrect binding pointer."); // this should never happen!
p_binding_modifier->ip_binding = nullptr;

emit_changed();
}
}

void OpenXRIPBinding::set_paths(const PackedStringArray p_paths) { // deprecated
// Fallback logic, this should ONLY be called when loading older action maps.
// We'll parse this momentarily and extract individual bindings.
Expand Down Expand Up @@ -141,6 +217,12 @@ void OpenXRInteractionProfile::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bindings", "bindings"), &OpenXRInteractionProfile::set_bindings);
ClassDB::bind_method(D_METHOD("get_bindings"), &OpenXRInteractionProfile::get_bindings);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bindings", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRIPBinding", PROPERTY_USAGE_NO_EDITOR), "set_bindings", "get_bindings");

ClassDB::bind_method(D_METHOD("get_binding_modifier_count"), &OpenXRInteractionProfile::get_binding_modifier_count);
ClassDB::bind_method(D_METHOD("get_binding_modifier", "index"), &OpenXRInteractionProfile::get_binding_modifier);
ClassDB::bind_method(D_METHOD("set_binding_modifiers", "binding_modifiers"), &OpenXRInteractionProfile::set_binding_modifiers);
ClassDB::bind_method(D_METHOD("get_binding_modifiers"), &OpenXRInteractionProfile::get_binding_modifiers);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "binding_modifiers", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRBindingModifier", PROPERTY_USAGE_NO_EDITOR), "set_binding_modifiers", "get_binding_modifiers");
}

Ref<OpenXRInteractionProfile> OpenXRInteractionProfile::new_profile(const char *p_input_profile_path) {
Expand Down Expand Up @@ -272,6 +354,77 @@ bool OpenXRInteractionProfile::has_binding_for_action(const Ref<OpenXRAction> p_
return false;
}

int OpenXRInteractionProfile::get_binding_modifier_count() const {
return binding_modifiers.size();
}

Ref<OpenXRBindingModifier> OpenXRInteractionProfile::get_binding_modifier(int p_index) const {
ERR_FAIL_INDEX_V(p_index, binding_modifiers.size(), nullptr);

return binding_modifiers[p_index];
}

void OpenXRInteractionProfile::clear_binding_modifiers() {
// Binding modifiers held within our interaction profile set should be released and destroyed but just in case they are still used some where else
if (binding_modifiers.size() == 0) {
return;
}

for (int i = 0; i < binding_modifiers.size(); i++) {
Ref<OpenXRBindingModifier> binding_modifier = binding_modifiers[i];
binding_modifier->interaction_profile = nullptr;
}
binding_modifiers.clear();
emit_changed();
}

void OpenXRInteractionProfile::set_binding_modifiers(Array p_binding_modifiers) {
// Any binding modifier not retained in p_binding_modifiers should be freed automatically, those held within our Array will have be relinked to our interaction profile.
clear_binding_modifiers();

for (int i = 0; i < p_binding_modifiers.size(); i++) {
// add them anew so we verify our binding modifier pointer
add_binding_modifier(p_binding_modifiers[i]);
}
}

Array OpenXRInteractionProfile::get_binding_modifiers() const {
Array ret;
for (const Ref<OpenXRBindingModifier> &binding_modifier : binding_modifiers) {
ret.push_back(binding_modifier);
}
return ret;
}

void OpenXRInteractionProfile::add_binding_modifier(Ref<OpenXRBindingModifier> p_binding_modifier) {
ERR_FAIL_COND(p_binding_modifier.is_null());
ERR_FAIL_COND_MSG(p_binding_modifier->record_on_binding(), "This binding modifier must be added to a binding.");

if (!binding_modifiers.has(p_binding_modifier)) {
if (p_binding_modifier->interaction_profile && p_binding_modifier->interaction_profile != this) {
// binding modifier should only relate to our interaction profile
p_binding_modifier->interaction_profile->remove_binding_modifier(p_binding_modifier);
}

p_binding_modifier->interaction_profile = this;
binding_modifiers.push_back(p_binding_modifier);
emit_changed();
}
}

void OpenXRInteractionProfile::remove_binding_modifier(Ref<OpenXRBindingModifier> p_binding_modifier) {
int idx = binding_modifiers.find(p_binding_modifier);
if (idx != -1) {
binding_modifiers.remove_at(idx);

ERR_FAIL_COND_MSG(p_binding_modifier->interaction_profile != this, "Removing binding modifier that belongs to this interaction profile but had incorrect interaction profile pointer."); // this should never happen!
p_binding_modifier->interaction_profile = nullptr;

emit_changed();
}
}

OpenXRInteractionProfile::~OpenXRInteractionProfile() {
bindings.clear();
clear_binding_modifiers();
}
Loading

0 comments on commit 0b77271

Please sign in to comment.