-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OpenXR: Add support for binding modifiers #97140
OpenXR: Add support for binding modifiers #97140
Conversation
45f85cf
to
17a009e
Compare
Back working on this after dealing with a bout of the flu. Starting to get an interface together but need to change direction slightly. |
e3aeb3b
to
a419e71
Compare
0b77271
to
577c7ef
Compare
While some of this is a bit of an unknown (but likely) future, I changed our action map structure slightly so we can record binding modifiers both on interaction profiles and on individual bindings. Right now the dpad modifier is a bit of the odd duckling being the only modifier that is linked to an action set/source path combination and is thus recorded on the interaction profile level. Right now they are all added to the next chain or |
577c7ef
to
924f137
Compare
924f137
to
050d3cf
Compare
Some interesting feedback concerning the DPad modifier extension that we need to ensure gets documented well. The extension actually does two things when enabled.
|
050d3cf
to
3db345a
Compare
All the new paths have been added to the meta data database. In doing this I've cleaned up a lot of the code to make it easier to manage. Still planning on adding a bit of extra logic in the UI when editing the action map so we only show bindings that are available based on which extensions have been selected in project settings so we don't get a whole bunch the user isn't using. |
4fb4d9a
to
00c39d2
Compare
Other then needing to do some more testing, this should now all work. My only remaining issue is that I had to use the These sections make no sense in this context so I hope there is a way to remove them. |
00c39d2
to
ff9ffb0
Compare
@@ -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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to call p_interaction_profile->action_map->remove_interaction_profile(p_interaction_profile)
before you reset the action_map
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the design is that an interaction profile always belongs to a single action map. The action_map
member is only null momentarily in 2 conditions:
- when we're creating a new interaction profile right before it's added to an action map
- when we're destructing an interaction profile where it's remove from the action map just before.
The check to see if the actionmap pointer is different is just to catch errors in usage, someone who later on adds code that would attempt to add an interaction profile to multiple action maps, and these checks just erroring out.
Array OpenXRIPBinding::get_binding_modifiers() const { | ||
Array ret; | ||
for (const Ref<OpenXRBindingModifier> &binding_modifier : binding_modifiers) { | ||
ret.push_back(binding_modifier); | ||
} | ||
return ret; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not directly returning the binding_modifiers
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we're converting from a Vector<Ref<OpenXRBindingModifier>>
to an Array
. I don't think there is an implicit conversion or was that added at some time?
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment; should p_binding_modifier
be removed from ip_binding
before resetting it?
Return [code]false[/code] if binding modifiers of this type are recorded on an interaction profile. | ||
Return [code]true[/code] if binding modifiers of this type are recorded on bindings within an interaction profile. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description is a bit confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically there are two types of binding modifiers, those recorded on the interaction profile, and those recorded against a specific binding on the interaction profile. This flag simply indicates which type we're dealing with.
This really won't get a lot cleared until more binding modifiers become public.
If [code]false[/code], when the joystick enters a new dpad zone this becomes true. | ||
If [code]true[/code], when the joystick remains in active dpad zone, this remains true even if we overlap with another zone. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also hard to understand what this field is for at first read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had real difficulty describing this in text, you really need to look at the diagrams in the API spec and even there the description is overly technical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are those diagrams publicly available? Can you link to them in this description?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you scroll to the top of the help page you'll notice that on both modifiers, I link to the specification for the modifiers.
For the DPad modifier its this one: https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_dpad_binding
Totally open to suggestions on how to work that more user friendly and embed that into our documentation.
7d8bef8
to
ba417e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic looks good, although I'd like us to iterate on the documentation as in their current state, they're hard to parse.
@BastiaanOlij Is there a test project that can be used to help validate those changes?
ba417e9
to
5ff3002
Compare
Did some more testing with this while addressing some of the feedback, found out that when both modifier extensions are enabled in the settings, one wouldn't work because both extensions require the base modifier extension. Fixed that up so Godot will react properly if multiple wrappers ask for the same extension to be enabled. |
@m4gr3d, I added a demo project on godot-demo-projects, see link in OP. |
5ff3002
to
a304355
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I skimmed the latest code, and mostly just found more little nitpicky stuff.
I haven't tested the binding modifiers at runtime, but I did re-test the editor stuff again. I'm able to add an OpenXRAnalogThresholdModifier
, and that seems to be working fine. However, when I try to add an OpenXRDpadBindingModifier
I get this error:
ERROR: Condition "!new_binding_modifier->record_on_binding()" is true.
at: _on_dialog_created (modules/openxr/editor/openxr_binding_modifiers_dialog.cpp:134)
Is that expected? If so, how should dpad modifiers be added?
modules/openxr/extensions/openxr_valve_analog_threshold_extension.h
Outdated
Show resolved
Hide resolved
modules/openxr/extensions/openxr_valve_analog_threshold_extension.h
Outdated
Show resolved
Hide resolved
DPad modifiers aren't added to individual bindings, they are added to the interaction profile with the binding button on the right. |
a304355
to
2f60056
Compare
I think even just having a more user-friendly error message would be OK. Right now, I'm getting a very programmer-y error and the modifier just fails to be added. Maybe we just need a popup with an explanation? |
2f60056
to
da92172
Compare
I decided to go down the subclass path after all. So there is now an Right now it seems a little overkill but once more binding modifiers become available it will start to make a whole lot more sense I recon. |
da92172
to
f863461
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
I re-tested in the editor, adding both threshold and dpad modifiers with haptics in the right places for those. Having the two base classes seems like a nice way to go, both for the editor and making the API simpler (removing the somewhat confusing record_on_binding
virtual method).
I skimmed the code again and it looks good to me, but it's a big PR and having reviewed it so many times already, my brain can't pick up on all that much anymore. :-)
I think this is ready to go!
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path | ||
Ref<OpenXRActionBindingModifier> get_binding_modifier(int p_index) const; | ||
void clear_binding_modifiers(); // Remove all binding modifiers | ||
void set_binding_modifiers(const Array &p_bindings); // Set the binding modifiers (for loading from a resource) | ||
Array get_binding_modifiers() const; // Get the binding modifiers (for saving to a resource) | ||
|
||
void add_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Add a binding modifier object | ||
void remove_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Remove a binding modifier object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path | |
Ref<OpenXRActionBindingModifier> get_binding_modifier(int p_index) const; | |
void clear_binding_modifiers(); // Remove all binding modifiers | |
void set_binding_modifiers(const Array &p_bindings); // Set the binding modifiers (for loading from a resource) | |
Array get_binding_modifiers() const; // Get the binding modifiers (for saving to a resource) | |
void add_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Add a binding modifier object | |
void remove_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Remove a binding modifier object | |
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path | |
Ref<OpenXRActionBindingModifier> get_binding_modifier(int p_index) const; | |
void clear_binding_modifiers(); // Remove all binding modifiers. | |
void set_binding_modifiers(const Array &p_bindings); // Set the binding modifiers (for loading from a resource) | |
Array get_binding_modifiers() const; // Get the binding modifiers (for saving to a resource) | |
void add_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Add a binding modifier object. | |
void remove_binding_modifier(const Ref<OpenXRActionBindingModifier> &p_binding_modifier); // Remove a binding modifier object. |
Ref<OpenXRIPBinding> find_binding(const Ref<OpenXRAction> &p_action, const String &p_binding_path) const; // Get our binding record | ||
Vector<Ref<OpenXRIPBinding>> get_bindings_for_action(const Ref<OpenXRAction> &p_action) const; // Get our binding record for a given action | ||
void add_binding(const Ref<OpenXRIPBinding> &p_binding); // Add a binding object | ||
void remove_binding(const Ref<OpenXRIPBinding> &p_binding); // Remove a binding object | ||
|
||
void add_new_binding(const Ref<OpenXRAction> &p_action, const String &p_paths); // Create a new binding for this profile | ||
void remove_binding_for_action(const Ref<OpenXRAction> &p_action); // Remove all bindings for this action | ||
bool has_binding_for_action(const Ref<OpenXRAction> &p_action); // Returns true if we have a binding for this action | ||
|
||
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path | ||
Ref<OpenXRIPBindingModifier> get_binding_modifier(int p_index) const; | ||
void clear_binding_modifiers(); // Remove all binding modifiers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ref<OpenXRIPBinding> find_binding(const Ref<OpenXRAction> &p_action, const String &p_binding_path) const; // Get our binding record | |
Vector<Ref<OpenXRIPBinding>> get_bindings_for_action(const Ref<OpenXRAction> &p_action) const; // Get our binding record for a given action | |
void add_binding(const Ref<OpenXRIPBinding> &p_binding); // Add a binding object | |
void remove_binding(const Ref<OpenXRIPBinding> &p_binding); // Remove a binding object | |
void add_new_binding(const Ref<OpenXRAction> &p_action, const String &p_paths); // Create a new binding for this profile | |
void remove_binding_for_action(const Ref<OpenXRAction> &p_action); // Remove all bindings for this action | |
bool has_binding_for_action(const Ref<OpenXRAction> &p_action); // Returns true if we have a binding for this action | |
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path | |
Ref<OpenXRIPBindingModifier> get_binding_modifier(int p_index) const; | |
void clear_binding_modifiers(); // Remove all binding modifiers | |
Ref<OpenXRIPBinding> find_binding(const Ref<OpenXRAction> &p_action, const String &p_binding_path) const; // Get our binding record. | |
Vector<Ref<OpenXRIPBinding>> get_bindings_for_action(const Ref<OpenXRAction> &p_action) const; // Get our binding record for a given action. | |
void add_binding(const Ref<OpenXRIPBinding> &p_binding); // Add a binding object. | |
void remove_binding(const Ref<OpenXRIPBinding> &p_binding); // Remove a binding object. | |
void add_new_binding(const Ref<OpenXRAction> &p_action, const String &p_paths); // Create a new binding for this profile. | |
void remove_binding_for_action(const Ref<OpenXRAction> &p_action); // Remove all bindings for this action. | |
bool has_binding_for_action(const Ref<OpenXRAction> &p_action); // Returns true if we have a binding for this action. | |
int get_binding_modifier_count() const; // Retrieve the number of binding modifiers in this profile path. | |
Ref<OpenXRIPBindingModifier> get_binding_modifier(int p_index) const; | |
void clear_binding_modifiers(); // Remove all binding modifiers. |
void add_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Add a binding modifier object | ||
void remove_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Remove a binding modifier object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void add_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Add a binding modifier object | |
void remove_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Remove a binding modifier object | |
void add_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Add a binding modifier object. | |
void remove_binding_modifier(const Ref<OpenXRIPBindingModifier> &p_binding_modifier); // Remove a binding modifier object. |
static HashMap<String, String> interaction_profile_editors; // interaction profile path, interaction profile editor | ||
static HashMap<String, String> binding_modifier_editors; // binding modifier class, binding modifiers editor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static HashMap<String, String> interaction_profile_editors; // interaction profile path, interaction profile editor | |
static HashMap<String, String> binding_modifier_editors; // binding modifier class, binding modifiers editor | |
static HashMap<String, String> interaction_profile_editors; // interaction profile path, interaction profile editor. | |
static HashMap<String, String> binding_modifier_editors; // binding modifier class, binding modifiers editor. |
for (int i = 0; i < new_binding_modifiers.size(); i++) { | ||
Ref<OpenXRBindingModifier> binding_modifier = new_binding_modifiers[i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (int i = 0; i < new_binding_modifiers.size(); i++) { | |
Ref<OpenXRBindingModifier> binding_modifier = new_binding_modifiers[i]; | |
for (Ref<OpenXRBindingModifier> binding_modifier : new_binding_modifiers) { |
@dsnopek Thanks for checking it again though, I do think it's time we merge this and any further things we find we can deal with later. It's a big PR but for people who don't use binding modifiers it's fairly low risk. @AThousandShips dang :) That's a lot of cleanup of existing code. Worth doing I guess, I'll go through it later today :) |
f863461
to
0a61ebd
Compare
Limited it largely to new code 😅 |
Thanks! |
Adds support for OpenXR binding modifiers to the action map.
This allows you to add modifiers to inputs such as applying thresholds or offsetting values, etc.
Binding modifiers can add additional input paths or change the type of an input path.
Todos:
Depends on:
There is now a demo for this project that can be found here:
godotengine/godot-demo-projects#1137
Some improvements that can be done (possibly in follow up PRs):
Contributed by Khronos Group through the Godot Integration Project