1/**************************************************************************/
2/* openxr_action_map.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "openxr_action_map.h"
32
33void OpenXRActionMap::_bind_methods() {
34 ClassDB::bind_method(D_METHOD("set_action_sets", "action_sets"), &OpenXRActionMap::set_action_sets);
35 ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRActionMap::get_action_sets);
36 ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "action_sets", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRActionSet", PROPERTY_USAGE_NO_EDITOR), "set_action_sets", "get_action_sets");
37
38 ClassDB::bind_method(D_METHOD("get_action_set_count"), &OpenXRActionMap::get_action_set_count);
39 ClassDB::bind_method(D_METHOD("find_action_set", "name"), &OpenXRActionMap::find_action_set);
40 ClassDB::bind_method(D_METHOD("get_action_set", "idx"), &OpenXRActionMap::get_action_set);
41 ClassDB::bind_method(D_METHOD("add_action_set", "action_set"), &OpenXRActionMap::add_action_set);
42 ClassDB::bind_method(D_METHOD("remove_action_set", "action_set"), &OpenXRActionMap::remove_action_set);
43
44 ClassDB::bind_method(D_METHOD("set_interaction_profiles", "interaction_profiles"), &OpenXRActionMap::set_interaction_profiles);
45 ClassDB::bind_method(D_METHOD("get_interaction_profiles"), &OpenXRActionMap::get_interaction_profiles);
46 ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "interaction_profiles", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRInteractionProfile", PROPERTY_USAGE_NO_EDITOR), "set_interaction_profiles", "get_interaction_profiles");
47
48 ClassDB::bind_method(D_METHOD("get_interaction_profile_count"), &OpenXRActionMap::get_interaction_profile_count);
49 ClassDB::bind_method(D_METHOD("find_interaction_profile", "name"), &OpenXRActionMap::find_interaction_profile);
50 ClassDB::bind_method(D_METHOD("get_interaction_profile", "idx"), &OpenXRActionMap::get_interaction_profile);
51 ClassDB::bind_method(D_METHOD("add_interaction_profile", "interaction_profile"), &OpenXRActionMap::add_interaction_profile);
52 ClassDB::bind_method(D_METHOD("remove_interaction_profile", "interaction_profile"), &OpenXRActionMap::remove_interaction_profile);
53
54 ClassDB::bind_method(D_METHOD("create_default_action_sets"), &OpenXRActionMap::create_default_action_sets);
55}
56
57void OpenXRActionMap::set_action_sets(Array p_action_sets) {
58 action_sets.clear();
59
60 for (int i = 0; i < p_action_sets.size(); i++) {
61 Ref<OpenXRActionSet> action_set = p_action_sets[i];
62 if (action_set.is_valid() && action_sets.find(action_set) == -1) {
63 action_sets.push_back(action_set);
64 }
65 }
66}
67
68Array OpenXRActionMap::get_action_sets() const {
69 return action_sets;
70}
71
72int OpenXRActionMap::get_action_set_count() const {
73 return action_sets.size();
74}
75
76Ref<OpenXRActionSet> OpenXRActionMap::find_action_set(String p_name) const {
77 for (int i = 0; i < action_sets.size(); i++) {
78 Ref<OpenXRActionSet> action_set = action_sets[i];
79 if (action_set->get_name() == p_name) {
80 return action_set;
81 }
82 }
83
84 return Ref<OpenXRActionSet>();
85}
86
87Ref<OpenXRActionSet> OpenXRActionMap::get_action_set(int p_idx) const {
88 ERR_FAIL_INDEX_V(p_idx, action_sets.size(), Ref<OpenXRActionSet>());
89
90 return action_sets[p_idx];
91}
92
93void OpenXRActionMap::add_action_set(Ref<OpenXRActionSet> p_action_set) {
94 ERR_FAIL_COND(p_action_set.is_null());
95
96 if (action_sets.find(p_action_set) == -1) {
97 action_sets.push_back(p_action_set);
98 emit_changed();
99 }
100}
101
102void OpenXRActionMap::remove_action_set(Ref<OpenXRActionSet> p_action_set) {
103 int idx = action_sets.find(p_action_set);
104 if (idx != -1) {
105 action_sets.remove_at(idx);
106 emit_changed();
107 }
108}
109
110void OpenXRActionMap::set_interaction_profiles(Array p_interaction_profiles) {
111 interaction_profiles.clear();
112
113 for (int i = 0; i < p_interaction_profiles.size(); i++) {
114 Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profiles[i];
115 if (interaction_profile.is_valid() && interaction_profiles.find(interaction_profile) == -1) {
116 interaction_profiles.push_back(interaction_profile);
117 }
118 }
119}
120
121Array OpenXRActionMap::get_interaction_profiles() const {
122 return interaction_profiles;
123}
124
125int OpenXRActionMap::get_interaction_profile_count() const {
126 return interaction_profiles.size();
127}
128
129Ref<OpenXRInteractionProfile> OpenXRActionMap::find_interaction_profile(String p_path) const {
130 for (int i = 0; i < interaction_profiles.size(); i++) {
131 Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
132 if (interaction_profile->get_interaction_profile_path() == p_path) {
133 return interaction_profile;
134 }
135 }
136
137 return Ref<OpenXRInteractionProfile>();
138}
139
140Ref<OpenXRInteractionProfile> OpenXRActionMap::get_interaction_profile(int p_idx) const {
141 ERR_FAIL_INDEX_V(p_idx, interaction_profiles.size(), Ref<OpenXRInteractionProfile>());
142
143 return interaction_profiles[p_idx];
144}
145
146void OpenXRActionMap::add_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile) {
147 ERR_FAIL_COND(p_interaction_profile.is_null());
148
149 if (interaction_profiles.find(p_interaction_profile) == -1) {
150 interaction_profiles.push_back(p_interaction_profile);
151 emit_changed();
152 }
153}
154
155void OpenXRActionMap::remove_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile) {
156 int idx = interaction_profiles.find(p_interaction_profile);
157 if (idx != -1) {
158 interaction_profiles.remove_at(idx);
159 emit_changed();
160 }
161}
162
163void OpenXRActionMap::create_default_action_sets() {
164 // Note:
165 // - if you make changes here make sure to delete your default_action_map.tres file of it will load an old version.
166 // - our palm pose is only available if the relevant extension is supported,
167 // we still want it to be part of our action map as we may deploy the same game to platforms that do and don't support it.
168 // - the same applies for interaction profiles that are only supported if the relevant extension is supported.
169
170 // Create our Godot action set
171 Ref<OpenXRActionSet> action_set = OpenXRActionSet::new_action_set("godot", "Godot action set");
172 add_action_set(action_set);
173
174 // Create our actions
175 Ref<OpenXRAction> trigger = action_set->add_new_action("trigger", "Trigger", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
176 Ref<OpenXRAction> trigger_click = action_set->add_new_action("trigger_click", "Trigger click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
177 Ref<OpenXRAction> trigger_touch = action_set->add_new_action("trigger_touch", "Trigger touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
178 Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
179 Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
180 Ref<OpenXRAction> grip_touch = action_set->add_new_action("grip_touch", "Grip touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
181 Ref<OpenXRAction> grip_force = action_set->add_new_action("grip_force", "Grip force", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
182 Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
183 Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
184 Ref<OpenXRAction> primary_touch = action_set->add_new_action("primary_touch", "Primary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
185 Ref<OpenXRAction> secondary = action_set->add_new_action("secondary", "Secondary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
186 Ref<OpenXRAction> secondary_click = action_set->add_new_action("secondary_click", "Secondary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
187 Ref<OpenXRAction> secondary_touch = action_set->add_new_action("secondary_touch", "Secondary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
188 Ref<OpenXRAction> menu_button = action_set->add_new_action("menu_button", "Menu button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
189 Ref<OpenXRAction> select_button = action_set->add_new_action("select_button", "Select button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
190 Ref<OpenXRAction> ax_button = action_set->add_new_action("ax_button", "A/X button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
191 Ref<OpenXRAction> ax_touch = action_set->add_new_action("ax_touch", "A/X touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
192 Ref<OpenXRAction> by_button = action_set->add_new_action("by_button", "B/Y button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
193 Ref<OpenXRAction> by_touch = action_set->add_new_action("by_touch", "B/Y touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
194 Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE,
195 "/user/hand/left,"
196 "/user/hand/right,"
197 // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
198 "/user/vive_tracker_htcx/role/left_foot,"
199 "/user/vive_tracker_htcx/role/right_foot,"
200 "/user/vive_tracker_htcx/role/left_shoulder,"
201 "/user/vive_tracker_htcx/role/right_shoulder,"
202 "/user/vive_tracker_htcx/role/left_elbow,"
203 "/user/vive_tracker_htcx/role/right_elbow,"
204 "/user/vive_tracker_htcx/role/left_knee,"
205 "/user/vive_tracker_htcx/role/right_knee,"
206 "/user/vive_tracker_htcx/role/waist,"
207 "/user/vive_tracker_htcx/role/chest,"
208 "/user/vive_tracker_htcx/role/camera,"
209 "/user/vive_tracker_htcx/role/keyboard");
210 Ref<OpenXRAction> aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
211 Ref<OpenXRAction> grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
212 Ref<OpenXRAction> palm_pose = action_set->add_new_action("palm_pose", "Palm pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
213 Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC,
214 "/user/hand/left,"
215 "/user/hand/right,"
216 // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
217 "/user/vive_tracker_htcx/role/left_foot,"
218 "/user/vive_tracker_htcx/role/right_foot,"
219 "/user/vive_tracker_htcx/role/left_shoulder,"
220 "/user/vive_tracker_htcx/role/right_shoulder,"
221 "/user/vive_tracker_htcx/role/left_elbow,"
222 "/user/vive_tracker_htcx/role/right_elbow,"
223 "/user/vive_tracker_htcx/role/left_knee,"
224 "/user/vive_tracker_htcx/role/right_knee,"
225 "/user/vive_tracker_htcx/role/waist,"
226 "/user/vive_tracker_htcx/role/chest,"
227 "/user/vive_tracker_htcx/role/camera,"
228 "/user/vive_tracker_htcx/role/keyboard");
229
230 // Create our interaction profiles
231 Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
232 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
233 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
234 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
235 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
236 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
237 profile->add_new_binding(select_button, "/user/hand/left/input/select/click,/user/hand/right/input/select/click");
238 // generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs
239 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
240 add_interaction_profile(profile);
241
242 // Create our Vive controller profile
243 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller");
244 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
245 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
246 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
247 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
248 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
249 profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
250 // wmr controller has no a/b/x/y buttons
251 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
252 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
253 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float
254 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
255 // primary on our vive controller is our trackpad
256 profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
257 profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
258 profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
259 // vive controllers have no secondary input
260 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
261 add_interaction_profile(profile);
262
263 // Create our WMR controller profile
264 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller");
265 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
266 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
267 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
268 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
269 // wmr controllers have no select button we can use
270 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
271 // wmr controller has no a/b/x/y buttons
272 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
273 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // OpenXR will convert float to bool
274 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float
275 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
276 // primary on our wmr controller is our thumbstick, no touch
277 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
278 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
279 // secondary on our wmr controller is our trackpad
280 profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
281 profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
282 profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
283 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
284 add_interaction_profile(profile);
285
286 // Create our Meta touch controller profile
287 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller");
288 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
289 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
290 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
291 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
292 // touch controllers have no select button we can use
293 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available
294 profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
295 profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
296 profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
297 profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
298 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
299 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
300 profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
301 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
302 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
303 // primary on our touch controller is our thumbstick
304 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
305 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
306 profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
307 // touch controller has no secondary input
308 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
309 add_interaction_profile(profile);
310
311 // Create our Pico 4 controller profile.
312 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/bytedance/pico4_controller");
313 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
314 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
315 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
316 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
317 profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available
318 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
319 profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
320 profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
321 profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
322 profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
323 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
324 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
325 profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
326 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
327 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
328 // primary on our pico controller is our thumbstick
329 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
330 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
331 profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
332 // pico controller has no secondary input
333 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
334 add_interaction_profile(profile);
335
336 // Create our Valve index controller profile
337 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
338 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
339 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
340 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
341 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
342 // index controllers have no select button we can use
343 profile->add_new_binding(menu_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
344 profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers
345 profile->add_new_binding(ax_touch, "/user/hand/left/input/a/touch,/user/hand/right/input/a/touch");
346 profile->add_new_binding(by_button, "/user/hand/left/input/b/click,/user/hand/right/input/b/click"); // b on both controllers
347 profile->add_new_binding(by_touch, "/user/hand/left/input/b/touch,/user/hand/right/input/b/touch");
348 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
349 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
350 profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
351 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
352 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion
353 profile->add_new_binding(grip_force, "/user/hand/left/input/squeeze/force,/user/hand/right/input/squeeze/force"); // grip force seems to be unique to the Valve Index
354 // primary on our index controller is our thumbstick
355 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
356 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
357 profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
358 // secondary on our index controller is our trackpad
359 profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
360 profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/force,/user/hand/right/input/trackpad/force"); // not sure if this will work but doesn't seem to support click...
361 profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
362 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
363 add_interaction_profile(profile);
364
365 // Create our HP MR controller profile
366 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller");
367 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
368 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
369 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
370 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
371 // hpmr controllers have no select button we can use
372 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
373 // hpmr controllers only register click, not touch, on our a/b/x/y buttons
374 profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
375 profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
376 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
377 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
378 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
379 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
380 // primary on our hpmr controller is our thumbstick
381 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
382 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
383 // No secondary on our hpmr controller
384 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
385 add_interaction_profile(profile);
386
387 // Create our Samsung Odyssey controller profile,
388 // Note that this controller is only identified specifically on WMR, on SteamVR this is identified as a normal WMR controller.
389 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/samsung/odyssey_controller");
390 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
391 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
392 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
393 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
394 // Odyssey controllers have no select button we can use
395 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
396 // Odyssey controller has no a/b/x/y buttons
397 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
398 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
399 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
400 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
401 // primary on our Odyssey controller is our thumbstick, no touch
402 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
403 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
404 // secondary on our Odyssey controller is our trackpad
405 profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
406 profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
407 profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
408 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
409 add_interaction_profile(profile);
410
411 // Create our Vive Cosmos controller
412 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller");
413 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
414 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
415 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
416 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
417 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
418 profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select
419 profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
420 profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
421 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
422 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
423 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
424 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
425 // primary on our Cosmos controller is our thumbstick
426 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
427 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
428 profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
429 // No secondary on our cosmos controller
430 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
431 add_interaction_profile(profile);
432
433 // Create our Vive Focus 3 controller
434 // Note, Vive Focus 3 currently is not yet supported as a stand alone device
435 // however HTC currently has a beta OpenXR runtime in testing we may support in the near future
436 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller");
437 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
438 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
439 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
440 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
441 profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
442 profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select
443 profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
444 profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
445 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
446 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
447 profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
448 profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
449 profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
450 // primary on our Focus 3 controller is our thumbstick
451 profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
452 profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
453 profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
454 // We only have a thumb rest
455 profile->add_new_binding(secondary_touch, "/user/hand/left/input/thumbrest/touch,/user/hand/right/input/thumbrest/touch");
456 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
457 add_interaction_profile(profile);
458
459 // Create our Huawei controller
460 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller");
461 profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
462 profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
463 profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
464 profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
465 profile->add_new_binding(menu_button, "/user/hand/left/input/home/click,/user/hand/right/input/home/click");
466 profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
467 profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
468 // primary on our Huawei controller is our trackpad
469 profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
470 profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
471 profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
472 profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
473 add_interaction_profile(profile);
474
475 // Create our HTC Vive tracker profile
476 profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_tracker_htcx");
477 profile->add_new_binding(default_pose,
478 // "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose," <-- getting errors on this one
479 "/user/vive_tracker_htcx/role/left_foot/input/grip/pose,"
480 "/user/vive_tracker_htcx/role/right_foot/input/grip/pose,"
481 "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose,"
482 "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose,"
483 "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose,"
484 "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose,"
485 "/user/vive_tracker_htcx/role/left_knee/input/grip/pose,"
486 "/user/vive_tracker_htcx/role/right_knee/input/grip/pose,"
487 "/user/vive_tracker_htcx/role/waist/input/grip/pose,"
488 "/user/vive_tracker_htcx/role/chest/input/grip/pose,"
489 "/user/vive_tracker_htcx/role/camera/input/grip/pose,"
490 "/user/vive_tracker_htcx/role/keyboard/input/grip/pose");
491 profile->add_new_binding(haptic,
492 // "/user/vive_tracker_htcx/role/handheld_object/output/haptic," <-- getting errors on this one
493 "/user/vive_tracker_htcx/role/left_foot/output/haptic,"
494 "/user/vive_tracker_htcx/role/right_foot/output/haptic,"
495 "/user/vive_tracker_htcx/role/left_shoulder/output/haptic,"
496 "/user/vive_tracker_htcx/role/right_shoulder/output/haptic,"
497 "/user/vive_tracker_htcx/role/left_elbow/output/haptic,"
498 "/user/vive_tracker_htcx/role/right_elbow/output/haptic,"
499 "/user/vive_tracker_htcx/role/left_knee/output/haptic,"
500 "/user/vive_tracker_htcx/role/right_knee/output/haptic,"
501 "/user/vive_tracker_htcx/role/waist/output/haptic,"
502 "/user/vive_tracker_htcx/role/chest/output/haptic,"
503 "/user/vive_tracker_htcx/role/camera/output/haptic,"
504 "/user/vive_tracker_htcx/role/keyboard/output/haptic");
505 add_interaction_profile(profile);
506}
507
508void OpenXRActionMap::create_editor_action_sets() {
509 // TODO implement
510}
511
512Ref<OpenXRAction> OpenXRActionMap::get_action(const String p_path) const {
513 PackedStringArray paths = p_path.split("/", false);
514 ERR_FAIL_COND_V(paths.size() != 2, Ref<OpenXRAction>());
515
516 Ref<OpenXRActionSet> action_set = find_action_set(paths[0]);
517 if (action_set.is_valid()) {
518 return action_set->get_action(paths[1]);
519 }
520
521 return Ref<OpenXRAction>();
522}
523
524void OpenXRActionMap::remove_action(const String p_path, bool p_remove_interaction_profiles) {
525 Ref<OpenXRAction> action = get_action(p_path);
526 if (action.is_valid()) {
527 for (int i = 0; i < interaction_profiles.size(); i++) {
528 Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
529
530 if (p_remove_interaction_profiles) {
531 // Remove any bindings for this action
532 interaction_profile->remove_binding_for_action(action);
533 } else {
534 ERR_FAIL_COND(interaction_profile->has_binding_for_action(action));
535 }
536 }
537
538 OpenXRActionSet *action_set = action->get_action_set();
539 if (action_set != nullptr) {
540 // Remove the action from this action set
541 action_set->remove_action(action);
542 }
543 }
544}
545
546PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p_action) {
547 PackedStringArray arr;
548
549 for (int i = 0; i < interaction_profiles.size(); i++) {
550 Ref<OpenXRInteractionProfile> ip = interaction_profiles[i];
551 const OpenXRInteractionProfileMetadata::InteractionProfile *profile = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(ip->get_interaction_profile_path());
552
553 if (profile != nullptr) {
554 for (int j = 0; j < ip->get_binding_count(); j++) {
555 Ref<OpenXRIPBinding> binding = ip->get_binding(j);
556 if (binding->get_action() == p_action) {
557 PackedStringArray paths = binding->get_paths();
558
559 for (int k = 0; k < paths.size(); k++) {
560 const OpenXRInteractionProfileMetadata::IOPath *io_path = profile->get_io_path(paths[k]);
561 if (io_path != nullptr) {
562 String top_path = io_path->top_level_path;
563
564 if (!arr.has(top_path)) {
565 arr.push_back(top_path);
566 }
567 }
568 }
569 }
570 }
571 }
572 }
573
574 // print_line("Toplevel paths for", p_action->get_name_with_set(), "are", arr);
575
576 return arr;
577}
578
579OpenXRActionMap::~OpenXRActionMap() {
580 action_sets.clear();
581 interaction_profiles.clear();
582}
583