Skip to content

Commit

Permalink
OpenXR: change bindings to 'flatten' source paths
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiaanOlij committed Oct 14, 2024
1 parent 92e51fc commit df53941
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 83 deletions.
23 changes: 9 additions & 14 deletions modules/openxr/action_map/openxr_action_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,20 +576,15 @@ PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p
const OpenXRInteractionProfileMetadata::InteractionProfile *profile = OpenXRInteractionProfileMetadata::get_singleton()->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 OpenXRInteractionProfileMetadata::IOPath *io_path = profile->get_io_path(paths[k]);
if (io_path != nullptr) {
String top_path = io_path->top_level_path;

if (!arr.has(top_path)) {
arr.push_back(top_path);
}
}
Vector<Ref<OpenXRIPBinding>> bindings = ip->get_bindings_for_action(p_action);
for (const Ref<OpenXRIPBinding> &binding : bindings) {
String source_path = binding->get_source_path();
const OpenXRInteractionProfileMetadata::IOPath *io_path = profile->get_io_path(source_path);
if (io_path != nullptr) {
String top_path = io_path->top_level_path;

if (!arr.has(top_path)) {
arr.push_back(top_path);
}
}
}
Expand Down
114 changes: 84 additions & 30 deletions modules/openxr/action_map/openxr_interaction_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,27 @@ 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_source_path", "source_path"), &OpenXRIPBinding::set_source_path);
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");

// Deprecated
ClassDB::bind_method(D_METHOD("set_paths", "paths"), &OpenXRIPBinding::set_paths);
ClassDB::bind_method(D_METHOD("get_paths"), &OpenXRIPBinding::get_paths);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths"), "set_paths", "get_paths");

ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_paths", "get_paths");
ClassDB::bind_method(D_METHOD("get_path_count"), &OpenXRIPBinding::get_path_count);
ClassDB::bind_method(D_METHOD("has_path", "path"), &OpenXRIPBinding::has_path);
ClassDB::bind_method(D_METHOD("add_path", "path"), &OpenXRIPBinding::add_path);
ClassDB::bind_method(D_METHOD("remove_path", "path"), &OpenXRIPBinding::remove_path);
}

Ref<OpenXRIPBinding> OpenXRIPBinding::new_binding(const Ref<OpenXRAction> p_action, const char *p_paths) {
Ref<OpenXRIPBinding> OpenXRIPBinding::new_binding(const Ref<OpenXRAction> p_action, const String &p_source_path) {
// This is a helper function to help build our default action sets

Ref<OpenXRIPBinding> binding;
binding.instantiate();
binding->set_action(p_action);
binding->parse_paths(String(p_paths));
binding->set_source_path(p_source_path);

return binding;
}
Expand All @@ -65,38 +69,60 @@ Ref<OpenXRAction> OpenXRIPBinding::get_action() const {
return action;
}

int OpenXRIPBinding::get_path_count() const {
return paths.size();
void OpenXRIPBinding::set_source_path(const String &path) {
source_path = path;
emit_changed();
}

void OpenXRIPBinding::set_paths(const PackedStringArray p_paths) {
paths = p_paths;
emit_changed();
String OpenXRIPBinding::get_source_path() const {
return source_path;
}

PackedStringArray OpenXRIPBinding::get_paths() const {
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.
source_path = "";
for (const String &path : p_paths) {
if (!source_path.is_empty()) {
source_path += ",";
}
source_path += path;
}
}

PackedStringArray OpenXRIPBinding::get_paths() const { // deprecated
// Fallback logic, return an array.
// If we just loaded an old action map from disc, this will be a comma separated list of actions.
// Once parsed there should be only one path in our array.
PackedStringArray paths = source_path.split(",", false);

return paths;
}

void OpenXRIPBinding::parse_paths(const String p_paths) {
paths = p_paths.split(",", false);
emit_changed();
int OpenXRIPBinding::get_path_count() const { // deprecated
// Fallback logic, we only have one entry.
return source_path.is_empty() ? 0 : 1;
}

bool OpenXRIPBinding::has_path(const String p_path) const {
return paths.has(p_path);
bool OpenXRIPBinding::has_path(const String p_path) const { // deprecated
// Fallback logic, return true if this is our path.
return source_path == p_path;
}

void OpenXRIPBinding::add_path(const String p_path) {
if (!paths.has(p_path)) {
paths.push_back(p_path);
void OpenXRIPBinding::add_path(const String p_path) { // deprecated
// Fallback logic, only assign first time this is called.
if (source_path != p_path) {
ERR_FAIL_COND(!source_path.is_empty());

source_path = p_path;
emit_changed();
}
}

void OpenXRIPBinding::remove_path(const String p_path) {
if (paths.has(p_path)) {
paths.erase(p_path);
void OpenXRIPBinding::remove_path(const String p_path) { // deprecated
// Fallback logic, clear if this is our path.
if (source_path == p_path) {
source_path = p_path;
emit_changed();
}
}
Expand Down Expand Up @@ -151,32 +177,56 @@ Ref<OpenXRIPBinding> OpenXRInteractionProfile::get_binding(int p_index) const {
}

void OpenXRInteractionProfile::set_bindings(Array p_bindings) {
// TODO add check here that our bindings don't contain duplicate actions
bindings.clear();

for (int i = 0; i < p_bindings.size(); i++) {
Ref<OpenXRIPBinding> binding = p_bindings[i];

String source_path = binding->get_source_path();
if (source_path.find_char(',') >= 0) {
// Convert old binding approach to new...
add_new_binding(binding->get_action(), source_path);
} else {
add_binding(binding);
}
}

bindings = p_bindings;
emit_changed();
}

Array OpenXRInteractionProfile::get_bindings() const {
return bindings;
}

Ref<OpenXRIPBinding> OpenXRInteractionProfile::get_binding_for_action(const Ref<OpenXRAction> p_action) const {
Ref<OpenXRIPBinding> OpenXRInteractionProfile::find_binding(const Ref<OpenXRAction> p_action, const String &p_source_path) const {
for (int i = 0; i < bindings.size(); i++) {
Ref<OpenXRIPBinding> binding = bindings[i];
if (binding->get_action() == p_action) {
if (binding->get_action() == p_action && binding->get_source_path() == p_source_path) {
return binding;
}
}

return Ref<OpenXRIPBinding>();
}

Vector<Ref<OpenXRIPBinding>> OpenXRInteractionProfile::get_bindings_for_action(const Ref<OpenXRAction> p_action) const {
Vector<Ref<OpenXRIPBinding>> ret_bindings;

for (int i = 0; i < bindings.size(); i++) {
Ref<OpenXRIPBinding> binding = bindings[i];
if (binding->get_action() == p_action) {
ret_bindings.push_back(binding);
}
}

return ret_bindings;
}

void OpenXRInteractionProfile::add_binding(Ref<OpenXRIPBinding> p_binding) {
ERR_FAIL_COND(p_binding.is_null());

if (!bindings.has(p_binding)) {
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");
ERR_FAIL_COND_MSG(find_binding(p_binding->get_action(), p_binding->get_source_path()).is_valid(), "There is already a binding for this action and source path in this interaction profile");

bindings.push_back(p_binding);
emit_changed();
Expand All @@ -191,11 +241,15 @@ void OpenXRInteractionProfile::remove_binding(Ref<OpenXRIPBinding> p_binding) {
}
}

void OpenXRInteractionProfile::add_new_binding(const Ref<OpenXRAction> p_action, const char *p_paths) {
void OpenXRInteractionProfile::add_new_binding(const Ref<OpenXRAction> p_action, const String &p_paths) {
// This is a helper function to help build our default action sets

Ref<OpenXRIPBinding> binding = OpenXRIPBinding::new_binding(p_action, p_paths);
add_binding(binding);
PackedStringArray paths = p_paths.split(",", false);

for (const String &path : paths) {
Ref<OpenXRIPBinding> binding = OpenXRIPBinding::new_binding(p_action, path);
add_binding(binding);
}
}

void OpenXRInteractionProfile::remove_binding_for_action(const Ref<OpenXRAction> p_action) {
Expand Down
21 changes: 13 additions & 8 deletions modules/openxr/action_map/openxr_interaction_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,30 @@ class OpenXRIPBinding : public Resource {

private:
Ref<OpenXRAction> action;
PackedStringArray paths;
String source_path;

// PackedStringArray paths;

protected:
static void _bind_methods();

public:
static Ref<OpenXRIPBinding> new_binding(const Ref<OpenXRAction> p_action, const char *p_paths); // Helper function for adding a new binding
static Ref<OpenXRIPBinding> new_binding(const Ref<OpenXRAction> p_action, const String &p_source_path); // Helper function for adding a new binding

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

int get_path_count() const; // Get the number of io paths
void set_source_path(const String &path);
String get_source_path() const;

// Deprecated
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); // Parse a comma separated string of io paths.

int get_path_count() const; // Get the number of io paths
bool has_path(const String p_path) const; // Has this io path
void add_path(const String p_path); // Add an io path
void remove_path(const String p_path); // Remove an io path
// End deprecated

// TODO add validation that we can display in the interface that checks if no two paths belong to the same top level path

Expand Down Expand Up @@ -88,11 +92,12 @@ class OpenXRInteractionProfile : public Resource {
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)

Ref<OpenXRIPBinding> get_binding_for_action(const Ref<OpenXRAction> p_action) const; // Get our binding record for a given action
Ref<OpenXRIPBinding> find_binding(const Ref<OpenXRAction> p_action, const String &p_source_path) const; // Get our binding record
Vector<Ref<OpenXRIPBinding>> get_bindings_for_action(const Ref<OpenXRAction> p_action) const; // Get our binding record for a given action
void add_binding(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); // Create a new binding for this profile
void add_new_binding(const Ref<OpenXRAction> p_action, const String &p_paths); // Create a new binding for this profile
void remove_binding_for_action(const Ref<OpenXRAction> p_action); // Remove all bindings for this action
bool has_binding_for_action(const Ref<OpenXRAction> p_action); // Returns true if we have a binding for this action

Expand Down
17 changes: 10 additions & 7 deletions modules/openxr/doc_classes/OpenXRIPBinding.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,32 @@
Defines a binding between an [OpenXRAction] and an XR input or output.
</brief_description>
<description>
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".
This binding resource binds an [OpenXRAction] to a input or output. As most controllers have left hand and right versions that are handled by the same interaction profile we can specify multiple bindings. For instance an action "Fire" could be bound to both "/user/hand/left/input/trigger" and "/user/hand/right/input/trigger". This would require two binding entries.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_path">
<method name="add_path" deprecated="Binding is for a single path.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Add an input/output path to this binding.
</description>
</method>
<method name="get_path_count" qualifiers="const">
<method name="get_path_count" qualifiers="const" deprecated="Binding is for a single path.">
<return type="int" />
<description>
Get the number of input/output paths in this binding.
</description>
</method>
<method name="has_path" qualifiers="const">
<method name="has_path" qualifiers="const" deprecated="Binding is for a single path.">
<return type="bool" />
<param index="0" name="path" type="String" />
<description>
Returns [code]true[/code] if this input/output path is part of this binding.
</description>
</method>
<method name="remove_path">
<method name="remove_path" deprecated="Binding is for a single path.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Expand All @@ -39,10 +39,13 @@
</methods>
<members>
<member name="action" type="OpenXRAction" setter="set_action" getter="get_action">
[OpenXRAction] that is bound to these paths.
[OpenXRAction] that is bound to [member source_path].
</member>
<member name="paths" type="PackedStringArray" setter="set_paths" getter="get_paths" default="PackedStringArray()">
<member name="paths" type="PackedStringArray" setter="set_paths" getter="get_paths" deprecated="Use source_path instead.">
Paths that define the inputs or outputs bound on the device.
</member>
<member name="source_path" type="String" setter="set_source_path" getter="get_source_path" default="&quot;&quot;">
Source path that defines the input or output bound to [member action].
</member>
</members>
</class>
Loading

0 comments on commit df53941

Please sign in to comment.