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:
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);