-
-
Notifications
You must be signed in to change notification settings - Fork 21.3k
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
base: master
Are you sure you want to change the base?
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
cb7dd7a
to
2129833
Compare
One more extra thing, this PR also introduces the I'm thinking of replacing |
Maybe you can figure out why the tests fail.
|
2129833
to
cb20fb6
Compare
Looks like a failure to check for NULL parameters with standard unit tests. Sadly on a part of the API that is completely unrelated to this work, so I don't know why it's suddenly failing. |
Hmm I wonder, the line of code that fails is |
cb20fb6
to
3ac09bc
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!
This PR is epic in size, but it looks great! All my code review comments are very nitpick-y things.
I tested the new editor stuff, and it all seemed to work fine. I didn't attempt to test it functionally - I see that SteamVR and Monado reportedly support it, so I may attempt to give it a try later.
Since PR #98163 has been merged, this should be rebased. That'll make reviewing a tiny bit easier.
5654653
to
7d8bef8
Compare
@@ -4095,6 +4095,7 @@ void EditorInspector::_notification(int p_what) { | |||
} break; | |||
|
|||
case NOTIFICATION_READY: { | |||
ERR_FAIL_NULL(EditorFeatureProfileManager::get_singleton()); |
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.
Now that CI testing runs through this, and we're not fully setting up the editor, we need to exit here as the EditorFeatureProfileManager
singleton does not get constructed.
@@ -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?
// Append the extensions requested by the registered extension wrappers. | ||
HashMap<String, bool *> requested_extensions; | ||
// We can request an extension multiple times if there are dependencies | ||
struct request_extension { |
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.
Even though this is a very local struct, I think we always use camel case for struct names - using snake case feels a little weird
GDVIRTUAL0RC(bool, _record_on_binding) | ||
GDVIRTUAL0RC(String, _get_description) | ||
GDVIRTUAL0R(PackedByteArray, _get_ip_modification) |
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.
These feel like they should be required? So:
GDVIRTUAL0RC(bool, _record_on_binding) | |
GDVIRTUAL0RC(String, _get_description) | |
GDVIRTUAL0R(PackedByteArray, _get_ip_modification) | |
GDVIRTUAL0RC_REQUIRED(bool, _record_on_binding) | |
GDVIRTUAL0RC_REQUIRED(String, _get_description) | |
GDVIRTUAL0R_REQUIRED(PackedByteArray, _get_ip_modification) |
This will make it an error if GDVIRTUAL_CALL()
can't find the method, and mark them as required in the extension_api.json
.
This won't have any effect on the sub-classes within Godot that override these methods; only for GDExtension classes that do.
class OpenXRBindingModifier : public Resource { | ||
GDCLASS(OpenXRBindingModifier, Resource); | ||
|
||
private: |
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.
This superfluous private:
could be removed:
private: |
#include "../action_map/openxr_action_set.h" | ||
#include "../action_map/openxr_binding_modifier.h" | ||
#include "editor/editor_inspector.h" | ||
// #include "editor/editor_properties.h" |
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.
Delete this?
// #include "editor/editor_properties.h" |
void set_off_threshold(float p_threshold); | ||
float get_off_threshold() const; | ||
|
||
void set_on_haptic(const Ref<OpenXRHapticBase> p_haptic); |
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 think this might have meant to be a const reference? So:
void set_on_haptic(const Ref<OpenXRHapticBase> p_haptic); | |
void set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic); |
void set_on_haptic(const Ref<OpenXRHapticBase> p_haptic); | ||
Ref<OpenXRHapticBase> get_on_haptic() const; | ||
|
||
void set_off_haptic(const Ref<OpenXRHapticBase> p_haptic); |
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.
This one too:
void set_off_haptic(const Ref<OpenXRHapticBase> p_haptic); | |
void set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic); |
void set_is_sticky(bool p_sticky); | ||
bool get_is_sticky() const; | ||
|
||
void set_on_haptic(const Ref<OpenXRHapticBase> p_haptic); |
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.
And this:
void set_on_haptic(const Ref<OpenXRHapticBase> p_haptic); | |
void set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic); |
void set_on_haptic(const Ref<OpenXRHapticBase> p_haptic); | ||
Ref<OpenXRHapticBase> get_on_haptic() const; | ||
|
||
void set_off_haptic(const Ref<OpenXRHapticBase> p_haptic); |
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.
And here:
void set_off_haptic(const Ref<OpenXRHapticBase> p_haptic); | |
void set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic); |
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