Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Kim <pk15950@gmail.com>2021-08-05 17:40:17 +0300
committerPeter Kim <pk15950@gmail.com>2021-08-05 17:40:17 +0300
commite844e9e8f3bb6814e24749316003814156e2e2ce (patch)
tree3c3ef939b10a84b2de5c2f5fb2e713a0b49e3dca /source/blender/windowmanager
parent0cff7c2a228589505bd2d66e966e5e84613b2786 (diff)
XR Controller Support Step 2: Action Maps
Addresses the remaining portions of T77137 (Python API for Controller Interaction), which was partially completed by D10942. Adds an XR "action maps" system for loading XR action data from a Python script. Action maps are accessible via the Python API, and are used to pass default actions to the VR session during the xr_session_start_pre() callback. Since action maps are stored only as runtime data, they will be cleaned up with the rest of the VR runtime data on file read or exit. Reviewed By: Julian Eisel, Hans Goudey Differential Revision: https://developer.blender.org/D10943
Diffstat (limited to 'source/blender/windowmanager')
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h51
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c1
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_action.c99
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actionmap.c565
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h59
6 files changed, 733 insertions, 43 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 8b0ceb02b5f..b7fbb9bb82b 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -204,6 +204,7 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
xr/intern/wm_xr_action.c
+ xr/intern/wm_xr_actionmap.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 7e26d921bd7..02e8d42e0ff 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -74,8 +74,7 @@ struct wmNDOFMotionData;
#endif
#ifdef WITH_XR_OPENXR
-struct wmXrActionState;
-struct wmXrPose;
+struct wmXrRuntimeData;
#endif
typedef struct wmGizmo wmGizmo;
@@ -988,7 +987,13 @@ bool WM_xr_action_create(wmXrData *xr,
const char **subaction_paths,
struct wmOperatorType *ot,
struct IDProperty *op_properties,
- eXrOpFlag op_flag);
+ const char **haptic_name,
+ const int64_t *haptic_duration,
+ const float *haptic_frequency,
+ const float *haptic_amplitude,
+ eXrOpFlag op_flag,
+ eXrActionFlag action_flag,
+ eXrHapticFlag haptic_flag);
void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
@@ -1022,10 +1027,48 @@ bool WM_xr_action_state_get(const wmXrData *xr,
bool WM_xr_haptic_action_apply(wmXrData *xr,
const char *action_set_name,
const char *action_name,
+ const char **subaction_path,
const int64_t *duration,
const float *frequency,
const float *amplitude);
-void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name);
+void WM_xr_haptic_action_stop(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char **subaction_path);
+
+/* wm_xr_actionmap.c */
+XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime,
+ const char *name,
+ bool replace_existing);
+void WM_xr_actionmap_ensure_unique(struct wmXrRuntimeData *runtime, XrActionMap *actionmap);
+XrActionMap *WM_xr_actionmap_add_copy(struct wmXrRuntimeData *runtime, XrActionMap *am_src);
+bool WM_xr_actionmap_remove(struct wmXrRuntimeData *runtime, XrActionMap *actionmap);
+XrActionMap *WM_xr_actionmap_find(struct wmXrRuntimeData *runtime, const char *name);
+void WM_xr_actionmap_clear(XrActionMap *actionmap);
+void WM_xr_actionmaps_clear(struct wmXrRuntimeData *runtime);
+ListBase *WM_xr_actionmaps_get(struct wmXrRuntimeData *runtime);
+short WM_xr_actionmap_active_index_get(const struct wmXrRuntimeData *runtime);
+void WM_xr_actionmap_active_index_set(struct wmXrRuntimeData *runtime, short idx);
+short WM_xr_actionmap_selected_index_get(const struct wmXrRuntimeData *runtime);
+void WM_xr_actionmap_selected_index_set(struct wmXrRuntimeData *runtime, short idx);
+
+XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
+ const char *name,
+ bool replace_existing);
+void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami);
+XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src);
+bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami);
+XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name);
+void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami);
+
+XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami,
+ const char *name,
+ bool replace_existing);
+void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb);
+XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami,
+ XrActionMapBinding *amb_src);
+bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb);
+XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name);
#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 2a67c2bee9f..716a0936a24 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -115,6 +115,7 @@ bool wm_xr_init(wmWindowManager *wm)
void wm_xr_exit(wmWindowManager *wm)
{
if (wm->xr.runtime != NULL) {
+ WM_xr_actionmaps_clear(wm->xr.runtime);
wm_xr_runtime_data_free(&wm->xr.runtime);
}
if (wm->xr.session_settings.shading.prop) {
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c
index 8f2de4bbbad..2712fde51a8 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_action.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c
@@ -23,6 +23,7 @@
* All functions are designed to be usable by RNA / the Python API.
*/
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "GHOST_C-api.h"
@@ -56,6 +57,9 @@ static void action_set_destroy(void *val)
MEM_SAFE_FREE(action_set->name);
+ BLI_freelistN(&action_set->active_modal_actions);
+ BLI_freelistN(&action_set->active_haptic_actions);
+
MEM_freeN(action_set);
}
@@ -70,7 +74,13 @@ static wmXrAction *action_create(const char *action_name,
const char **subaction_paths,
wmOperatorType *ot,
IDProperty *op_properties,
- eXrOpFlag op_flag)
+ const char **haptic_name,
+ const int64_t *haptic_duration,
+ const float *haptic_frequency,
+ const float *haptic_amplitude,
+ eXrOpFlag op_flag,
+ eXrActionFlag action_flag,
+ eXrHapticFlag haptic_flag)
{
wmXrAction *action = MEM_callocN(sizeof(*action), __func__);
action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name");
@@ -121,7 +131,19 @@ static wmXrAction *action_create(const char *action_name,
action->ot = ot;
action->op_properties = op_properties;
+
+ if (haptic_name) {
+ BLI_assert(is_button_action);
+ action->haptic_name = MEM_mallocN(strlen(*haptic_name) + 1, "XrAction_HapticName");
+ strcpy(action->haptic_name, *haptic_name);
+ action->haptic_duration = *haptic_duration;
+ action->haptic_frequency = *haptic_frequency;
+ action->haptic_amplitude = *haptic_amplitude;
+ }
+
action->op_flag = op_flag;
+ action->action_flag = action_flag;
+ action->haptic_flag = haptic_flag;
return action;
}
@@ -147,6 +169,8 @@ static void action_destroy(void *val)
MEM_SAFE_FREE(action->float_thresholds);
MEM_SAFE_FREE(action->axis_flags);
+ MEM_SAFE_FREE(action->haptic_name);
+
MEM_freeN(action);
}
@@ -190,9 +214,10 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
wm_xr_session_controller_data_clear(session_state);
action_set->controller_grip_action = action_set->controller_aim_action = NULL;
}
- if (action_set->active_modal_action) {
- action_set->active_modal_action = NULL;
- }
+
+ BLI_freelistN(&action_set->active_modal_actions);
+ BLI_freelistN(&action_set->active_haptic_actions);
+
session_state->active_action_set = NULL;
}
@@ -207,14 +232,31 @@ bool WM_xr_action_create(wmXrData *xr,
const char **subaction_paths,
wmOperatorType *ot,
IDProperty *op_properties,
- eXrOpFlag op_flag)
+ const char **haptic_name,
+ const int64_t *haptic_duration,
+ const float *haptic_frequency,
+ const float *haptic_amplitude,
+ eXrOpFlag op_flag,
+ eXrActionFlag action_flag,
+ eXrHapticFlag haptic_flag)
{
if (action_find(xr, action_set_name, action_name)) {
return false;
}
- wmXrAction *action = action_create(
- action_name, type, count_subaction_paths, subaction_paths, ot, op_properties, op_flag);
+ wmXrAction *action = action_create(action_name,
+ type,
+ count_subaction_paths,
+ subaction_paths,
+ ot,
+ op_properties,
+ haptic_name,
+ haptic_duration,
+ haptic_frequency,
+ haptic_amplitude,
+ op_flag,
+ action_flag,
+ haptic_flag);
GHOST_XrActionInfo info = {
.name = action_name,
@@ -274,9 +316,18 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char
action_set->controller_grip_action = action_set->controller_aim_action = NULL;
}
- if (action_set->active_modal_action &&
- STREQ(action_set->active_modal_action->name, action_name)) {
- action_set->active_modal_action = NULL;
+ LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) {
+ wmXrAction *active_modal_action = ld->data;
+ if (STREQ(active_modal_action->name, action_name)) {
+ BLI_freelinkN(&action_set->active_modal_actions, ld);
+ break;
+ }
+ }
+
+ LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, &action_set->active_haptic_actions) {
+ if (STREQ(ha->action->name, action_name)) {
+ BLI_freelinkN(&action_set->active_haptic_actions, ha);
+ }
}
GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name);
@@ -342,16 +393,11 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
}
{
- /* Unset active modal action (if any). */
+ /* Clear any active modal/haptic actions. */
wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
if (active_action_set) {
- wmXrAction *active_modal_action = active_action_set->active_modal_action;
- if (active_modal_action) {
- if (active_modal_action->active_modal_path) {
- active_modal_action->active_modal_path = NULL;
- }
- active_action_set->active_modal_action = NULL;
- }
+ BLI_freelistN(&active_action_set->active_modal_actions);
+ BLI_freelistN(&active_action_set->active_haptic_actions);
}
}
@@ -456,19 +502,28 @@ bool WM_xr_action_state_get(const wmXrData *xr,
bool WM_xr_haptic_action_apply(wmXrData *xr,
const char *action_set_name,
const char *action_name,
+ const char **subaction_path,
const int64_t *duration,
const float *frequency,
const float *amplitude)
{
- return GHOST_XrApplyHapticAction(
- xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ?
+ return GHOST_XrApplyHapticAction(xr->runtime->context,
+ action_set_name,
+ action_name,
+ subaction_path,
+ duration,
+ frequency,
+ amplitude) ?
true :
false;
}
-void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name)
+void WM_xr_haptic_action_stop(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char **subaction_path)
{
- GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name);
+ GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path);
}
/** \} */ /* XR-Action API */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
new file mode 100644
index 00000000000..7673f2aa212
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
@@ -0,0 +1,565 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ *
+ * \name Window-Manager XR Action Maps
+ *
+ * XR actionmap API, similar to WM keymap API.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "GHOST_Types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap"
+#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action"
+#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding"
+
+/* -------------------------------------------------------------------- */
+/** \name Action Map Binding
+ *
+ * Binding in an XR action map item, that maps an action to an XR input.
+ * \{ */
+
+XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami,
+ const char *name,
+ bool replace_existing)
+{
+ XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name);
+ if (amb_prev && replace_existing) {
+ return amb_prev;
+ }
+
+ XrActionMapBinding *amb = MEM_callocN(sizeof(XrActionMapBinding), __func__);
+ BLI_strncpy(amb->name, name, MAX_NAME);
+ if (amb_prev) {
+ WM_xr_actionmap_binding_ensure_unique(ami, amb);
+ }
+
+ BLI_addtail(&ami->bindings, amb);
+
+ /* Set non-zero threshold by default. */
+ amb->float_threshold = 0.3f;
+
+ return amb;
+}
+
+static XrActionMapBinding *wm_xr_actionmap_binding_find_except(XrActionMapItem *ami,
+ const char *name,
+ XrActionMapBinding *ambexcept)
+{
+ LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) {
+ if (STREQLEN(name, amb->name, MAX_NAME) && (amb != ambexcept)) {
+ return amb;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Ensure unique name among all action map bindings.
+ */
+void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb)
+{
+ char name[MAX_NAME];
+ char *suffix;
+ size_t baselen;
+ size_t idx = 0;
+
+ BLI_strncpy(name, amb->name, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+
+ while (wm_xr_actionmap_binding_find_except(ami, name, amb)) {
+ if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
+ /* Use default base name. */
+ BLI_strncpy(name, WM_XR_ACTIONMAP_BINDING_STR_DEFAULT, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+ idx = 0;
+ }
+ else {
+ BLI_snprintf(suffix, MAX_NAME, "%zu", idx);
+ }
+ }
+
+ BLI_strncpy(amb->name, name, MAX_NAME);
+}
+
+static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_src)
+{
+ XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src);
+
+ amb_dst->prev = amb_dst->next = NULL;
+
+ return amb_dst;
+}
+
+XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami,
+ XrActionMapBinding *amb_src)
+{
+ XrActionMapBinding *amb_dst = wm_xr_actionmap_binding_copy(amb_src);
+
+ WM_xr_actionmap_binding_ensure_unique(ami, amb_dst);
+
+ BLI_addtail(&ami->bindings, amb_dst);
+
+ return amb_dst;
+}
+
+bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb)
+{
+ int idx = BLI_findindex(&ami->bindings, amb);
+
+ if (idx != -1) {
+ BLI_freelinkN(&ami->bindings, amb);
+
+ if (BLI_listbase_is_empty(&ami->bindings)) {
+ ami->selbinding = -1;
+ }
+ else {
+ if (idx <= ami->selbinding) {
+ if (--ami->selbinding < 0) {
+ ami->selbinding = 0;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name)
+{
+ LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) {
+ if (STREQLEN(name, amb->name, MAX_NAME)) {
+ return amb;
+ }
+ }
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Action Map Item
+ *
+ * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output.
+ * \{ */
+
+static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami)
+{
+ WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op);
+ WM_operator_properties_sanitize(ami->op_properties_ptr, 1);
+}
+
+static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami)
+{
+ if (ami->op_properties_ptr) {
+ WM_operator_properties_free(ami->op_properties_ptr);
+ MEM_freeN(ami->op_properties_ptr);
+ ami->op_properties_ptr = NULL;
+ ami->op_properties = NULL;
+ }
+ else {
+ BLI_assert(ami->op_properties == NULL);
+ }
+}
+
+/**
+ * Similar to #wm_xr_actionmap_item_properties_set()
+ * but checks for the #eXrActionType and #wmOperatorType having changed.
+ */
+void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami)
+{
+ switch (ami->type) {
+ case XR_BOOLEAN_INPUT:
+ case XR_FLOAT_INPUT:
+ case XR_VECTOR2F_INPUT:
+ break;
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ wm_xr_actionmap_item_properties_free(ami);
+ memset(ami->op, 0, sizeof(ami->op));
+ return;
+ }
+
+ if (ami->op[0] == 0) {
+ wm_xr_actionmap_item_properties_free(ami);
+ return;
+ }
+
+ if (ami->op_properties_ptr == NULL) {
+ wm_xr_actionmap_item_properties_set(ami);
+ }
+ else {
+ wmOperatorType *ot = WM_operatortype_find(ami->op, 0);
+ if (ot) {
+ if (ot->srna != ami->op_properties_ptr->type) {
+ /* Matches wm_xr_actionmap_item_properties_set() but doesn't alloc new ptr. */
+ WM_operator_properties_create_ptr(ami->op_properties_ptr, ot);
+ if (ami->op_properties) {
+ ami->op_properties_ptr->data = ami->op_properties;
+ }
+ WM_operator_properties_sanitize(ami->op_properties_ptr, 1);
+ }
+ }
+ else {
+ wm_xr_actionmap_item_properties_free(ami);
+ }
+ }
+}
+
+XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
+ const char *name,
+ bool replace_existing)
+{
+ XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name);
+ if (ami_prev && replace_existing) {
+ wm_xr_actionmap_item_properties_free(ami_prev);
+ return ami_prev;
+ }
+
+ XrActionMapItem *ami = MEM_callocN(sizeof(XrActionMapItem), __func__);
+ BLI_strncpy(ami->name, name, MAX_NAME);
+ if (ami_prev) {
+ WM_xr_actionmap_item_ensure_unique(actionmap, ami);
+ }
+
+ BLI_addtail(&actionmap->items, ami);
+
+ /* Set type to float (button) input by default. */
+ ami->type = XR_FLOAT_INPUT;
+
+ return ami;
+}
+
+static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap,
+ const char *name,
+ const XrActionMapItem *amiexcept)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ if (STREQLEN(name, ami->name, MAX_NAME) && (ami != amiexcept)) {
+ return ami;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Ensure unique name among all action map items.
+ */
+void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami)
+{
+ char name[MAX_NAME];
+ char *suffix;
+ size_t baselen;
+ size_t idx = 0;
+
+ BLI_strncpy(name, ami->name, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+
+ while (wm_xr_actionmap_item_find_except(actionmap, name, ami)) {
+ if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
+ /* Use default base name. */
+ BLI_strncpy(name, WM_XR_ACTIONMAP_ITEM_STR_DEFAULT, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+ idx = 0;
+ }
+ else {
+ BLI_snprintf(suffix, MAX_NAME, "%zu", idx);
+ }
+ }
+
+ BLI_strncpy(ami->name, name, MAX_NAME);
+}
+
+static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami)
+{
+ XrActionMapItem *amin = MEM_dupallocN(ami);
+
+ amin->prev = amin->next = NULL;
+
+ if (amin->op_properties) {
+ amin->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
+ WM_operator_properties_create(amin->op_properties_ptr, amin->op);
+
+ amin->op_properties = IDP_CopyProperty(amin->op_properties);
+ amin->op_properties_ptr->data = amin->op_properties;
+ }
+ else {
+ amin->op_properties = NULL;
+ amin->op_properties_ptr = NULL;
+ }
+
+ return amin;
+}
+
+XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src)
+{
+ XrActionMapItem *ami_dst = wm_xr_actionmap_item_copy(ami_src);
+
+ WM_xr_actionmap_item_ensure_unique(actionmap, ami_dst);
+
+ BLI_addtail(&actionmap->items, ami_dst);
+
+ return ami_dst;
+}
+
+bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
+{
+ int idx = BLI_findindex(&actionmap->items, ami);
+
+ if (idx != -1) {
+ if (ami->op_properties_ptr) {
+ WM_operator_properties_free(ami->op_properties_ptr);
+ MEM_freeN(ami->op_properties_ptr);
+ }
+ BLI_freelinkN(&actionmap->items, ami);
+
+ if (BLI_listbase_is_empty(&actionmap->items)) {
+ actionmap->selitem = -1;
+ }
+ else {
+ if (idx <= actionmap->selitem) {
+ if (--actionmap->selitem < 0) {
+ actionmap->selitem = 0;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ if (STREQLEN(name, ami->name, MAX_NAME)) {
+ return ami;
+ }
+ }
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Action Map
+ *
+ * List of XR action map items.
+ * \{ */
+
+XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing)
+{
+ XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name);
+ if (am_prev && replace_existing) {
+ WM_xr_actionmap_clear(am_prev);
+ return am_prev;
+ }
+
+ XrActionMap *am = MEM_callocN(sizeof(struct XrActionMap), __func__);
+ BLI_strncpy(am->name, name, MAX_NAME);
+ if (am_prev) {
+ WM_xr_actionmap_ensure_unique(runtime, am);
+ }
+
+ BLI_addtail(&runtime->actionmaps, am);
+
+ return am;
+}
+
+static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime,
+ const char *name,
+ const XrActionMap *am_except)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) {
+ return am;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Ensure unique name among all action maps.
+ */
+void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap)
+{
+ char name[MAX_NAME];
+ char *suffix;
+ size_t baselen;
+ size_t idx = 0;
+
+ BLI_strncpy(name, actionmap->name, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+
+ while (wm_xr_actionmap_find_except(runtime, name, actionmap)) {
+ if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
+ /* Use default base name. */
+ BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+ idx = 0;
+ }
+ else {
+ BLI_snprintf(suffix, MAX_NAME, "%zu", idx);
+ }
+ }
+
+ BLI_strncpy(actionmap->name, name, MAX_NAME);
+}
+
+static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src)
+{
+ XrActionMap *am_dst = MEM_dupallocN(am_src);
+
+ am_dst->prev = am_dst->next = NULL;
+ BLI_listbase_clear(&am_dst->items);
+
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &am_src->items) {
+ XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(ami);
+ BLI_addtail(&am_dst->items, ami_new);
+ }
+
+ return am_dst;
+}
+
+XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src)
+{
+ XrActionMap *am_dst = wm_xr_actionmap_copy(am_src);
+
+ WM_xr_actionmap_ensure_unique(runtime, am_dst);
+
+ BLI_addtail(&runtime->actionmaps, am_dst);
+
+ return am_dst;
+}
+
+bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap)
+{
+ int idx = BLI_findindex(&runtime->actionmaps, actionmap);
+
+ if (idx != -1) {
+ WM_xr_actionmap_clear(actionmap);
+ BLI_freelinkN(&runtime->actionmaps, actionmap);
+
+ if (BLI_listbase_is_empty(&runtime->actionmaps)) {
+ runtime->actactionmap = runtime->selactionmap = -1;
+ }
+ else {
+ if (idx <= runtime->actactionmap) {
+ if (--runtime->actactionmap < 0) {
+ runtime->actactionmap = 0;
+ }
+ }
+ if (idx <= runtime->selactionmap) {
+ if (--runtime->selactionmap < 0) {
+ runtime->selactionmap = 0;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ if (STREQLEN(name, am->name, MAX_NAME)) {
+ return am;
+ }
+ }
+ return NULL;
+}
+
+void WM_xr_actionmap_clear(XrActionMap *actionmap)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ wm_xr_actionmap_item_properties_free(ami);
+ }
+
+ BLI_freelistN(&actionmap->items);
+
+ actionmap->selitem = -1;
+}
+
+void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ WM_xr_actionmap_clear(am);
+ }
+
+ BLI_freelistN(&runtime->actionmaps);
+
+ runtime->actactionmap = runtime->selactionmap = -1;
+}
+
+ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime)
+{
+ return &runtime->actionmaps;
+}
+
+short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime)
+{
+ return runtime->actactionmap;
+}
+
+void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx)
+{
+ BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps));
+ runtime->actactionmap = idx;
+}
+
+short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime)
+{
+ return runtime->selactionmap;
+}
+
+void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx)
+{
+ BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps));
+ runtime->selactionmap = idx;
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index b6aff1f71f9..4b1605d0f68 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -70,6 +70,10 @@ typedef struct wmXrRuntimeData {
/** Although this struct is internal, RNA gets a handle to this for state information queries. */
wmXrSessionState session_state;
wmXrSessionExitFn exit_fn;
+
+ ListBase actionmaps; /* XrActionMap */
+ short actactionmap;
+ short selactionmap;
} wmXrRuntimeData;
typedef struct wmXrViewportPair {
@@ -99,6 +103,22 @@ typedef struct wmXrDrawData {
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef struct wmXrController {
+ struct wmXrController *next, *prev;
+ /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
+ This subaction path will later be combined with a component path, and that combined path should
+ also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
+ /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
+ */
+ char subaction_path[64];
+ /* Pose (in world space) that represents the user's hand when holding the controller.*/
+ GHOST_XrPose grip_pose;
+ float grip_mat[4][4];
+ /* Pose (in world space) that represents the controller's aiming source. */
+ GHOST_XrPose aim_pose;
+ float aim_mat[4][4];
+} wmXrController;
+
typedef struct wmXrAction {
char *name;
eXrActionType type;
@@ -119,32 +139,37 @@ typedef struct wmXrAction {
/** Operator to be called on XR events. */
struct wmOperatorType *ot;
IDProperty *op_properties;
+
+ /** Haptics. */
+ char *haptic_name;
+ int64_t haptic_duration;
+ float haptic_frequency;
+ float haptic_amplitude;
+
+ /** Flags. */
eXrOpFlag op_flag;
+ eXrActionFlag action_flag;
+ eXrHapticFlag haptic_flag;
} wmXrAction;
-typedef struct wmXrController {
- struct wmXrController *next, *prev;
- /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
- This subaction path will later be combined with a component path, and that combined path should
- also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
- /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
- */
- char subaction_path[64];
- /* Pose (in world space) that represents the user's hand when holding the controller.*/
- GHOST_XrPose grip_pose;
- float grip_mat[4][4];
- /* Pose (in world space) that represents the controller's aiming source. */
- GHOST_XrPose aim_pose;
- float aim_mat[4][4];
-} wmXrController;
+typedef struct wmXrHapticAction {
+ struct wmXrHapticAction *next, *prev;
+ wmXrAction *action;
+ const char **subaction_path;
+ int64_t time_start;
+} wmXrHapticAction;
typedef struct wmXrActionSet {
char *name;
+
/** XR pose actions that determine the controller grip/aim transforms. */
wmXrAction *controller_grip_action;
wmXrAction *controller_aim_action;
- /** The currently active modal action (if any). */
- wmXrAction *active_modal_action;
+
+ /** Currently active modal actions. */
+ ListBase active_modal_actions;
+ /** Currently active haptic actions. */
+ ListBase active_haptic_actions;
} wmXrActionSet;
wmXrRuntimeData *wm_xr_runtime_data_create(void);