evan 2026.1.4
Evan
Loading...
Searching...
No Matches
XrManageActions.cpp
1/*
2** ETIB PROJECT, 2026
3** evan
4** File description:
5** XrManageActions
6*/
7
8#include "evan/openxr/XrManageActions.hpp"
9#include "evan/openxr/XrDeviceBackend.hpp"
10
12{
13 this->getLogger().info() << "Initializing XrManageActions";
14
15 createActionSet(deviceBackend);
16
18 std::make_unique<XrHandsMotionActions>(_actionSet, deviceBackend);
20 std::make_unique<XrManageButtonsActions>(_actionSet, deviceBackend);
22 std::make_unique<XrManageThumbStickActions>(_actionSet, deviceBackend);
23
24 bindActionSets(deviceBackend);
25
26 // WARNING: The order of these operations is critical. All spaces must be
27 // created before attaching the action set to the session, otherwise the
28 // runtime will not recognize the spaces and they will not function
29 // correctly.
30 _handsMotionActions->createHandsMotionSpaces(deviceBackend);
31
32 attachSessionActionSet(deviceBackend);
33}
34
35evan::XrManageActions::~XrManageActions()
36{
37 this->getLogger().info() << "Destroying XrManageActions";
38
39 if (_actionSet != XR_NULL_HANDLE)
40 xrDestroyActionSet(_actionSet);
41}
42
43std::vector<std::shared_ptr<utility::event::Event>>
45{
46 this->getLogger().debug() << "Polling actions from OpenXR runtime";
47
48 std::vector<std::shared_ptr<utility::event::Event>> events;
49
52 syncInfo.countActiveActionSets = 1;
53 syncInfo.activeActionSets = &activeActionSet;
54
56 if (result != XR_SUCCESS) {
57 this->getLogger().error() << "Failed to sync actions: " << result;
58 return events;
59 }
60 auto handEvents = _handsMotionActions->getEvents(deviceBackend);
61 events.insert(events.end(), handEvents.begin(), handEvents.end());
62
63 auto buttonEvents = _manageButtonsActions->getEvents(
64 deviceBackend, _handsMotionActions->_leftHandEvent,
65 _handsMotionActions->_rightHandEvent);
66 events.insert(events.end(), buttonEvents.begin(), buttonEvents.end());
67
68 auto thumbStickEvents = _manageThumbStickActions->getEvents(deviceBackend);
69 events.insert(events.end(), thumbStickEvents.begin(),
70 thumbStickEvents.end());
71 return events;
72}
73
74void evan::XrManageActions::createActionSet(XrDeviceBackend &deviceBackend)
75{
76 this->getLogger().info() << "Creating main action set for XrManageActions";
77
79 std::strncpy(actionSetInfo.actionSetName, "gameplay",
80 XR_MAX_ACTION_SET_NAME_SIZE);
81 std::strncpy(actionSetInfo.localizedActionSetName, "Gameplay",
82 XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE);
83 actionSetInfo.priority = 0;
84
85 XrResult result = xrCreateActionSet(deviceBackend._XrInstance,
86 &actionSetInfo, &_actionSet);
87 if (result != XR_SUCCESS) {
88 this->getLogger().error() << "Failed to create action set: " << result;
89 return;
90 }
91 this->getLogger().info()
92 << "Successfully created main action set for XrManageActions";
93}
94
95void evan::XrManageActions::attachSessionActionSet(
96 XrDeviceBackend &deviceBackend)
97{
98 this->getLogger().info() << "Attaching action set to OpenXR session";
99
100 XrSessionActionSetsAttachInfo attachInfo {
101 XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO
102 };
103 attachInfo.countActionSets = 1;
104 attachInfo.actionSets = &_actionSet;
105
106 XrResult result =
107 xrAttachSessionActionSets(deviceBackend._session, &attachInfo);
108 if (result != XR_SUCCESS) {
109 this->getLogger().error() << "Failed to attach action sets: " << result;
110 return;
111 }
112 this->getLogger().info()
113 << "Successfully attached action set to OpenXR session";
114}
115
116void evan::XrManageActions::bindActionSets(XrDeviceBackend &deviceBackend)
117{
118 this->getLogger().info()
119 << "Binding action sets to OpenXR interaction profile";
120
121 const auto &options = deviceBackend.getOpenXrOptions();
122 const auto &candidateProfiles = options.interactionProfileCandidates.empty()
124 : options.interactionProfileCandidates;
125
126 std::vector<XrActionSuggestedBinding> coreBindings {
127 { _handsMotionActions->_handAimAction,
128 InteractionProfile::stringToPath(deviceBackend._XrInstance,
129 "/user/hand/left/input/aim/pose") },
130 { _handsMotionActions->_handAimAction,
131 InteractionProfile::stringToPath(deviceBackend._XrInstance,
132 "/user/hand/right/input/aim/pose") },
133
134 { _handsMotionActions->_handGripAction,
135 InteractionProfile::stringToPath(deviceBackend._XrInstance,
136 "/user/hand/left/input/grip/pose") },
137 { _handsMotionActions->_handGripAction,
139 deviceBackend._XrInstance, "/user/hand/right/input/grip/pose") },
140
141 { _manageButtonsActions->_buttonAAction->getAction(),
142 InteractionProfile::stringToPath(deviceBackend._XrInstance,
143 "/user/hand/right/input/a/click") },
144 { _manageButtonsActions->_buttonBAction->getAction(),
145 InteractionProfile::stringToPath(deviceBackend._XrInstance,
146 "/user/hand/right/input/b/click") },
147 { _manageButtonsActions->_buttonXAction->getAction(),
148 InteractionProfile::stringToPath(deviceBackend._XrInstance,
149 "/user/hand/left/input/x/click") },
150 { _manageButtonsActions->_buttonYAction->getAction(),
151 InteractionProfile::stringToPath(deviceBackend._XrInstance,
152 "/user/hand/left/input/y/click") },
153
154 { _manageThumbStickActions->_leftHandThumbStickActions->getAction(),
156 deviceBackend._XrInstance, "/user/hand/left/input/thumbstick") },
157 { _manageThumbStickActions->_rightHandThumbStickActions->getAction(),
159 deviceBackend._XrInstance, "/user/hand/right/input/thumbstick") },
160 };
161
162 // Menu/system bindings are optional because some runtimes reserve these
163 // inputs. They are appended to the suggestion so that, when a profile
164 // rejects them, the core bindings can be re-suggested on their own.
165
166 const auto suggestForProfile =
167 [&deviceBackend](
168 const std::string &profilePath,
169 const std::vector<XrActionSuggestedBinding> &bindings) -> XrResult {
170 XrInteractionProfileSuggestedBinding suggestedBindings {
171 XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING
172 };
173 suggestedBindings.interactionProfile = InteractionProfile::stringToPath(
174 deviceBackend._XrInstance, profilePath);
175 suggestedBindings.countSuggestedBindings =
176 static_cast<uint32_t>(bindings.size());
177 suggestedBindings.suggestedBindings = bindings.data();
178 return xrSuggestInteractionProfileBindings(deviceBackend._XrInstance,
179 &suggestedBindings);
180 };
181
182 for (const auto &candidateProfile: candidateProfiles) {
183 std::vector<XrActionSuggestedBinding> bindings = coreBindings;
184 if (options.enableMenuSystemBindings) {
185 bindings.push_back(
186 { _manageButtonsActions->_buttonMenuAction->getAction(),
188 deviceBackend._XrInstance,
189 "/user/hand/left/input/menu/click") });
190 bindings.push_back(
191 { _manageButtonsActions->_buttonSystemAction->getAction(),
193 deviceBackend._XrInstance,
194 "/user/hand/right/input/system/click") });
195 }
196
197 XrResult result = suggestForProfile(candidateProfile, bindings);
198 if (result != XR_SUCCESS && options.enableMenuSystemBindings) {
199 this->getLogger().warning()
200 << "Failed to suggest bindings for profile " << candidateProfile
201 << " including menu/system, retrying without them: " << result;
202 result = suggestForProfile(candidateProfile, coreBindings);
203 }
204 if (result == XR_SUCCESS) {
205 this->getLogger().info()
206 << "Successfully suggested bindings for interaction profile: "
207 << candidateProfile;
208 return;
209 }
210 this->getLogger().warning()
211 << "Interaction profile rejected the suggested bindings: "
212 << candidateProfile << " (" << result << ")";
213 }
214
215 this->getLogger().error()
216 << "Failed to suggest interaction profile bindings on any candidate "
217 "profile";
218}
static XrPath stringToPath(XrInstance instance, const std::string &pathString)
Converts a string to an OpenXR path.
static const std::vector< std::string > & getDefaultProfileCandidates()
Retrieves the default interaction profile candidates.
The XrDeviceBackend class is responsible for managing the OpenXR device backend, including instance c...
std::unique_ptr< XrHandsMotionActions > _handsMotionActions
Action manager for hand tracking actions.
std::unique_ptr< XrManageButtonsActions > _manageButtonsActions
Action manager for button actions.
XrManageActions(XrDeviceBackend &deviceBackend)
Initializes XrManageActions with OpenXR action management.
XrActionSet _actionSet
The primary OpenXR action set for the application.
std::unique_ptr< XrManageThumbStickActions > _manageThumbStickActions
Action manager for thumb stick analog inputs.
std::vector< std::shared_ptr< utility::event::Event > > pollActions(XrDeviceBackend &deviceBackend)
Polls for input events from the OpenXR runtime.
Small result wrapper used by helpers that must return both a status and a value (e....
Definition Error.hpp:89