diff options
33 files changed, 2428 insertions, 372 deletions
diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index 8222f2ff0b9..6bfb0125e95 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -5,7 +5,7 @@ update-code: git: submodules: - - branch: master + - branch: xr-dev commit_id: HEAD path: release/scripts/addons - branch: master diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 78f2b24ea78..01a0a7652aa 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -661,6 +661,10 @@ enum { * (use DirectX fallback instead). */ GHOST_kXrContextGpuNVIDIA = (1 << 2), # endif + /* Needed to selectively enable the #XR_HTCX_vive_tracker_interaction extension, as it causes + incorrect controller poses with SteamVR (1.21). Although not ideal, either trackers or + controllers (but not both) can be used during a given session. */ + GHOST_kXrContextEnableViveTrackerExtension = (1 << 3), }; typedef struct { diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 2ac3d9ec2a5..4a7f833a25b 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -82,7 +82,7 @@ void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info) determineGraphicsBindingTypesToEnable(create_info); assert(m_oxr->instance == XR_NULL_HANDLE); - createOpenXRInstance(graphics_binding_types); + createOpenXRInstance(graphics_binding_types, create_info); storeInstanceProperties(); /* Multiple bindings may be enabled. Now that we know the runtime in use, settle for one. */ @@ -95,7 +95,8 @@ void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info) } void GHOST_XrContext::createOpenXRInstance( - const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types) + const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types, + const GHOST_XrContextCreateInfo *ctx_create_info) { XrInstanceCreateInfo create_info = {XR_TYPE_INSTANCE_CREATE_INFO}; @@ -104,7 +105,7 @@ void GHOST_XrContext::createOpenXRInstance( create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; getAPILayersToEnable(m_enabled_layers); - getExtensionsToEnable(graphics_binding_types, m_enabled_extensions); + getExtensionsToEnable(graphics_binding_types, ctx_create_info, m_enabled_extensions); create_info.enabledApiLayerCount = m_enabled_layers.size(); create_info.enabledApiLayerNames = m_enabled_layers.data(); create_info.enabledExtensionCount = m_enabled_extensions.size(); @@ -400,6 +401,7 @@ static const char *openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding */ void GHOST_XrContext::getExtensionsToEnable( const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types, + const GHOST_XrContextCreateInfo *create_info, std::vector<const char *> &r_ext_names) { std::vector<std::string_view> try_ext; @@ -415,6 +417,11 @@ void GHOST_XrContext::getExtensionsToEnable( #ifdef XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME try_ext.push_back(XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME); #endif + if ((create_info->context_flag & GHOST_kXrContextEnableViveTrackerExtension) != 0) { +#ifdef XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME + try_ext.push_back(XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME); +#endif + } try_ext.push_back(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME); /* Controller model extension. */ diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h index 6eac4284e43..435454397cf 100644 --- a/intern/ghost/intern/GHOST_XrContext.h +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -108,7 +108,8 @@ class GHOST_XrContext : public GHOST_IXrContext { bool m_debug = false; bool m_debug_time = false; - void createOpenXRInstance(const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types); + void createOpenXRInstance(const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types, + const GHOST_XrContextCreateInfo *create_info); void storeInstanceProperties(); void initDebugMessenger(); @@ -122,6 +123,7 @@ class GHOST_XrContext : public GHOST_IXrContext { void initExtensionsEx(std::vector<XrExtensionProperties> &extensions, const char *layer_name); void getAPILayersToEnable(std::vector<const char *> &r_ext_names); void getExtensionsToEnable(const std::vector<GHOST_TXrGraphicsBinding> &graphics_binding_types, + const GHOST_XrContextCreateInfo *create_info, std::vector<const char *> &r_ext_names); std::vector<GHOST_TXrGraphicsBinding> determineGraphicsBindingTypesToEnable( const GHOST_XrContextCreateInfo *create_info); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 973965ada50..a34ba753912 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2441,6 +2441,10 @@ static void lib_link_wm_xr_data_restore(struct IDNameLib_Map *id_map, wmXrData * { xr_data->session_settings.base_pose_object = restore_pointer_by_name( id_map, (ID *)xr_data->session_settings.base_pose_object, USER_REAL); + + LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob, &xr_data->session_settings.mocap_objects) { + mocap_ob->ob = restore_pointer_by_name(id_map, (ID *)mocap_ob->ob, USER_REAL); + } } static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, ViewLayer *view_layer) diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 82cc518f029..1cebdfafd74 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -13,10 +13,13 @@ extern "C" { /* ******************* Registration Function ********************** */ +struct ID; struct Object; struct bContext; +struct ViewLayer; struct wmKeyConfig; struct wmOperatorType; +struct wmTimer; void ED_keymap_transform(struct wmKeyConfig *keyconf); void transform_operatortypes(void); @@ -202,6 +205,19 @@ int ED_transform_calc_gizmo_stats(const struct bContext *C, const struct TransformCalcParams *params, struct TransformBounds *tbounds); +/* transform_convert.c */ +void ED_transform_animrecord_check_state(struct Scene *scene, + struct wmTimer *animtimer, + struct ID *id); + +/* transform_convert_object.c */ +void ED_transform_autokeyframe_object(struct bContext *C, + struct Scene *scene, + struct ViewLayer *view_layer, + struct Object *ob, + int tmode); +bool ED_transform_motionpath_need_update_object(struct Scene *scene, struct Object *ob); + /** * Iterates over all the strips and finds the closest snapping candidate of either \a frame_1 or \a * frame_2. The closest snapping candidate will be the closest start or end frame of an existing diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 0298983ed26..ddd0cb252cb 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -974,6 +974,10 @@ void ED_view3d_viewcontext_init(struct bContext *C, */ void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact); /** + * Deselect all except b. + */ +bool ED_view3d_object_deselect_all_except(struct ViewLayer *view_layer, struct Base *b); +/** * Use this call when executing an operator, * event system doesn't set for each event the OpenGL drawing context. */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b2ec2102ddd..984d3409554 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2417,6 +2417,7 @@ void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C); void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C); void uiTemplateInputStatus(uiLayout *layout, struct bContext *C); void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr); +void uiTemplateXrActionmapItemProperties(uiLayout *layout, struct PointerRNA *ptr); bool uiTemplateEventFromKeymapItem(struct uiLayout *layout, const char *text, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 2a1852bd6e7..a140ee27d3c 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -106,6 +106,9 @@ if(WIN32 OR APPLE) endif() endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 05ae5299e58..4b3937dabce 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6367,6 +6367,44 @@ void uiTemplateKeymapItemProperties(uiLayout *layout, PointerRNA *ptr) /** \} */ /* -------------------------------------------------------------------- */ +/** \name XR Actionmap Template + * \{ */ + +#ifdef WITH_XR_OPENXR +static void xr_actionmap_item_modified(bContext *UNUSED(C), + void *UNUSED(ami_p), + void *UNUSED(unused)) +{ +} +#endif + +void uiTemplateXrActionmapItemProperties(uiLayout *layout, PointerRNA *ptr) +{ +#ifdef WITH_XR_OPENXR + PointerRNA propptr = RNA_pointer_get(ptr, "op_properties"); + + if (propptr.data) { + uiBut *but = uiLayoutGetBlock(layout)->buttons.last; + + WM_operator_properties_sanitize(&propptr, false); + /* Use same template as keymap item properties. */ + template_keymap_item_properties(layout, NULL, &propptr); + + for (; but; but = but->next) { + if (but->rnaprop) { + UI_but_func_set(but, xr_actionmap_item_modified, ptr->data, NULL); + UI_but_flag_enable(but, UI_BUT_UPDATE_DELAY); + } + } + } +#else + UNUSED_VARS(layout, ptr); +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Event Icon Template * \{ */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 8eff9ee472f..01f94bcab55 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -134,18 +134,12 @@ void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) } } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Internal Object Utilities - * \{ */ - -static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d) +bool ED_view3d_object_deselect_all_except(ViewLayer *view_layer, Base *b) { bool changed = false; LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (base->flag & BASE_SELECTED) { - if (BASE_SELECTABLE(v3d, base)) { + if (b != base) { ED_object_base_select(base, BA_DESELECT); changed = true; } @@ -154,13 +148,18 @@ static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d) return changed; } -/* deselect all except b */ -static bool object_deselect_all_except(ViewLayer *view_layer, Base *b) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Object Utilities + * \{ */ + +static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d) { bool changed = false; LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (base->flag & BASE_SELECTED) { - if (b != base) { + if (BASE_SELECTABLE(v3d, base)) { ED_object_base_select(base, BA_DESELECT); changed = true; } @@ -1494,7 +1493,7 @@ static int object_select_menu_exec(bContext *C, wmOperator *op) } } else { - object_deselect_all_except(view_layer, basact); + ED_view3d_object_deselect_all_except(view_layer, basact); ED_object_base_select(basact, BA_SELECT); changed = true; } @@ -2705,7 +2704,7 @@ static bool ed_object_select_pick(bContext *C, else if (found || params->deselect_all) { /* Deselect everything. */ /* `basact` may be NULL. */ - if (object_deselect_all_except(view_layer, basact)) { + if (ED_view3d_object_deselect_all_except(view_layer, basact)) { changed_object = true; } } @@ -2717,7 +2716,7 @@ static bool ed_object_select_pick(bContext *C, if (vc.obedit) { /* Only do the select (use for setting vertex parents & hooks). * In edit-mode do not activate. */ - object_deselect_all_except(view_layer, basact); + ED_view3d_object_deselect_all_except(view_layer, basact); ED_object_base_select(basact, BA_SELECT); changed_object = true; @@ -2748,7 +2747,7 @@ static bool ed_object_select_pick(bContext *C, break; } case SEL_OP_SET: { - object_deselect_all_except(view_layer, basact); + ED_view3d_object_deselect_all_except(view_layer, basact); ED_object_base_select(basact, BA_SELECT); break; } diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d9b971c5478..267a519ebee 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1607,10 +1607,8 @@ void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc) } } -void animrecord_check_state(TransInfo *t, struct ID *id) +void ED_transform_animrecord_check_state(Scene *scene, wmTimer *animtimer, struct ID *id) { - Scene *scene = t->scene; - wmTimer *animtimer = t->animtimer; ScreenAnimData *sad = (animtimer) ? animtimer->customdata : NULL; /* sanity checks */ diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 037fbe26c77..3aeea049150 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -92,10 +92,6 @@ char transform_convert_frame_side_dir_get(TransInfo *t, float cframe); */ bool FrameOnMouseSide(char side, float frame, float cframe); void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc); -/** - * For the realtime animation recording feature, handle overlapping data. - */ -void animrecord_check_state(TransInfo *t, struct ID *id); /* transform_convert_action.c */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index e1b25acb21e..485425c644a 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1471,7 +1471,7 @@ void recalcData_pose(TransInfo *t) /* XXX: this currently doesn't work, since flags aren't set yet! */ int targetless_ik = (t->flag & T_AUTOIK); - animrecord_check_state(t, &ob->id); + ED_transform_animrecord_check_state(t->scene, t->animtimer, &ob->id); autokeyframe_pose(t->context, t->scene, ob, t->mode, targetless_ik); } diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 5879a65eb4b..02549390c6a 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -718,7 +718,7 @@ void createTransObject(bContext *C, TransInfo *t) * \note Context may not always be available, * so must check before using it as it's a luxury for a few cases. */ -static void autokeyframe_object( +void ED_transform_autokeyframe_object( bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int tmode) { Main *bmain = CTX_data_main(C); @@ -840,7 +840,7 @@ static void autokeyframe_object( /* Return if we need to update motion paths, only if they already exist, * and we will insert a keyframe at the end of transform. */ -static bool motionpath_need_update_object(Scene *scene, Object *ob) +bool ED_transform_motionpath_need_update_object(Scene *scene, Object *ob) { /* XXX: there's potential here for problems with unkeyed rotations/scale, * but for now (until proper data-locality for baking operations), @@ -884,11 +884,11 @@ void recalcData_objects(TransInfo *t) /* TODO: autokeyframe calls need some setting to specify to add samples * (FPoints) instead of keyframes? */ if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) { - animrecord_check_state(t, &ob->id); - autokeyframe_object(t->context, t->scene, t->view_layer, ob, t->mode); + ED_transform_animrecord_check_state(t->scene, t->animtimer, &ob->id); + ED_transform_autokeyframe_object(t->context, t->scene, t->view_layer, ob, t->mode); } - motionpath_update |= motionpath_need_update_object(t->scene, ob); + motionpath_update |= ED_transform_motionpath_need_update_object(t->scene, ob); /* sets recalc flags fully, instead of flushing existing ones * otherwise proxies don't function correctly @@ -961,10 +961,10 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) /* Set auto-key if necessary. */ if (!canceled) { - autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode); + ED_transform_autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode); } - motionpath_update |= motionpath_need_update_object(t->scene, ob); + motionpath_update |= ED_transform_motionpath_need_update_object(t->scene, ob); /* Restore rigid body transform. */ if (ob->rigidbody_object && canceled) { diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index 76c6632039a..741b1e35838 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -238,7 +238,7 @@ void recalcData_sequencer_image(TransInfo *t) } if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) { - animrecord_check_state(t, &t->scene->id); + ED_transform_animrecord_check_state(t->scene, t->animtimer, &t->scene->id); autokeyframe_sequencer_image(t->context, t->scene, transform, t->mode); } diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index 44419c9763f..fa590c69728 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -40,11 +40,22 @@ typedef struct XrSessionSettings { /** Object type settings to apply to VR view (unlike shading, not shared with window 3D-View). */ int object_type_exclude_viewport; int object_type_exclude_select; + + ListBase actionmaps; /* XrActionMap */ + short act_actionmap; + short sel_actionmap; + char _pad3[2]; + + /** Objects to bind to headset/controller poses. */ + short sel_mocap_object; + ListBase mocap_objects; /* #XrMotionCaptureObject */ } XrSessionSettings; typedef enum eXrSessionFlag { XR_SESSION_USE_POSITION_TRACKING = (1 << 0), XR_SESSION_USE_ABSOLUTE_TRACKING = (1 << 1), + XR_SESSION_ENABLE_VIVE_TRACKER_EXTENSION = + (1 << 2), /* See #GHOST_kXrContextEnableViveTrackerExtension. */ } eXrSessionFlag; typedef enum eXRSessionBasePoseType { @@ -108,8 +119,15 @@ typedef enum eXrPoseFlag { /* Pose represents controller grip/aim. */ XR_POSE_GRIP = (1 << 0), XR_POSE_AIM = (1 << 1), + /* Pose represents VR tracker. */ + XR_POSE_TRACKER = (1 << 2), } eXrPoseFlag; +typedef enum eXrMotionCaptureFlag { + XR_MOCAP_OBJECT_ENABLE = (1 << 0), + XR_MOCAP_OBJECT_AUTOKEY = (1 << 1), +} eXrMotionCaptureFlag; + /** * The following user and component path lengths are dependent on OpenXR's XR_MAX_PATH_LENGTH * (256). A user path will be combined with a component path to identify an action binding, and @@ -141,7 +159,8 @@ typedef struct XrActionMapBinding { /** Input threshold/region. */ float float_threshold; short axis_flag; /* eXrAxisFlag */ - char _pad[2]; + + short sel_component_path; /** Pose action properties. */ float pose_location[3]; @@ -187,8 +206,8 @@ typedef struct XrActionMapItem { float haptic_frequency; float haptic_amplitude; - short selbinding; - char _pad3[2]; + short sel_user_path; + short sel_binding; ListBase bindings; /* XrActionMapBinding */ } XrActionMapItem; @@ -201,12 +220,30 @@ typedef struct XrActionMap { char name[64]; /* MAX_NAME */ ListBase items; /* XrActionMapItem */ - short selitem; + short sel_item; char _pad[6]; } XrActionMap; /* -------------------------------------------------------------------- */ +typedef struct XrMotionCaptureObject { + struct XrMotionCaptureObject *next, *prev; + + /** Object to bind to a VR device. Used as struct identifier. */ + Object *ob; + /** OpenXR user path, identifies the target headset/controller. */ + char user_path[64]; /* XR_MAX_USER_PATH_LENGTH */ + + /** Location/rotation offsets. */ + float location_offset[3]; + float rotation_offset[3]; + + short flag; /* eXrMotionCaptureFlag */ + char _pad[6]; +} XrMotionCaptureObject; + +/* -------------------------------------------------------------------- */ + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 1b416e4b6e5..e05e4a03f40 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1793,6 +1793,11 @@ void RNA_api_ui_layout(StructRNA *srna) parm = RNA_def_pointer(func, "item", "KeyMapItem", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + func = RNA_def_function( + srna, "template_xr_actionmap_item_properties", "uiTemplateXrActionmapItemProperties"); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + func = RNA_def_function(srna, "template_component_menu", "uiTemplateComponentMenu"); RNA_def_function_ui_description(func, "Item. Display expanded property in a popup menu"); parm = RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property"); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index dcfa1bbca51..f01bfbe4fee 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -66,6 +66,12 @@ static void rna_XrComponentPath_remove(XrActionMapBinding *amb, PointerRNA *comp int idx = BLI_findindex(&amb->component_paths, component_path); if (idx != -1) { BLI_freelinkN(&amb->component_paths, component_path); + + if (idx <= amb->sel_component_path) { + if (--amb->sel_component_path < 0) { + amb->sel_component_path = 0; + } + } } RNA_POINTER_INVALIDATE(component_path_ptr); # else @@ -216,12 +222,11 @@ static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene) { # ifdef WITH_XR_OPENXR wmWindowManager *wm = bmain->wm.first; - if (wm && wm->xr.runtime) { - ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); - short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); - XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (wm) { + XrActionMap *actionmap = BLI_findlink(&wm->xr.session_settings.actionmaps, + wm->xr.session_settings.sel_actionmap); if (actionmap) { - XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem); + XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->sel_item); if (ami) { XrActionMapBinding *amb = ptr->data; WM_xr_actionmap_binding_ensure_unique(ami, amb); @@ -253,6 +258,12 @@ static void rna_XrUserPath_remove(XrActionMapItem *ami, PointerRNA *user_path_pt int idx = BLI_findindex(&ami->user_paths, user_path); if (idx != -1) { BLI_freelinkN(&ami->user_paths, user_path); + + if (idx <= ami->sel_user_path) { + if (--ami->sel_user_path < 0) { + ami->sel_user_path = 0; + } + } } RNA_POINTER_INVALIDATE(user_path_ptr); # else @@ -512,6 +523,29 @@ static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool # endif } +static bool rna_XrActionMapItem_pose_is_tracker_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_TRACKER) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_tracker_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_TRACKER); +# else + UNUSED_VARS(ptr, value); +# endif +} + static void rna_XrActionMapItem_bindings_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -537,10 +571,9 @@ static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), P { # ifdef WITH_XR_OPENXR wmWindowManager *wm = bmain->wm.first; - if (wm && wm->xr.runtime) { - ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); - short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); - XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (wm) { + XrActionMap *actionmap = BLI_findlink(&wm->xr.session_settings.actionmaps, + wm->xr.session_settings.sel_actionmap); if (actionmap) { XrActionMapItem *ami = ptr->data; WM_xr_actionmap_item_ensure_unique(actionmap, ami); @@ -561,50 +594,51 @@ static void rna_XrActionMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene) # endif } -static XrActionMap *rna_XrActionMap_new(PointerRNA *ptr, const char *name, bool replace_existing) +static XrActionMap *rna_XrActionMap_new(XrSessionSettings *settings, + const char *name, + bool replace_existing) { # ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return WM_xr_actionmap_new(xr->runtime, name, replace_existing); + return WM_xr_actionmap_new(settings, name, replace_existing); # else - UNUSED_VARS(ptr, name, replace_existing); + UNUSED_VARS(settings, name, replace_existing); return NULL; # endif } -static XrActionMap *rna_XrActionMap_new_from_actionmap(PointerRNA *ptr, XrActionMap *am_src) +static XrActionMap *rna_XrActionMap_new_from_actionmap(XrSessionSettings *settings, + XrActionMap *am_src) { # ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return WM_xr_actionmap_add_copy(xr->runtime, am_src); + return WM_xr_actionmap_add_copy(settings, am_src); # else - UNUSED_VARS(ptr, am_src); + UNUSED_VARS(settings, am_src); return NULL; # endif } -static void rna_XrActionMap_remove(ReportList *reports, PointerRNA *ptr, PointerRNA *actionmap_ptr) +static void rna_XrActionMap_remove(XrSessionSettings *settings, + ReportList *reports, + PointerRNA *actionmap_ptr) { # ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); XrActionMap *actionmap = actionmap_ptr->data; - if (WM_xr_actionmap_remove(xr->runtime, actionmap) == false) { + if (WM_xr_actionmap_remove(settings, actionmap) == false) { BKE_reportf(reports, RPT_ERROR, "ActionMap '%s' cannot be removed", actionmap->name); return; } RNA_POINTER_INVALIDATE(actionmap_ptr); # else - UNUSED_VARS(ptr, reports, actionmap_ptr); + UNUSED_VARS(settings, reports, actionmap_ptr); # endif } -static XrActionMap *rna_XrActionMap_find(PointerRNA *ptr, const char *name) +static XrActionMap *rna_XrActionMap_find(XrSessionSettings *settings, const char *name) { # ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return WM_xr_actionmap_find(xr->runtime, name); + return WM_xr_actionmap_find(settings, name); # else - UNUSED_VARS(ptr, name); + UNUSED_VARS(settings, name); return NULL; # endif } @@ -634,9 +668,9 @@ static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), Point { # ifdef WITH_XR_OPENXR wmWindowManager *wm = bmain->wm.first; - if (wm && wm->xr.runtime) { + if (wm) { XrActionMap *actionmap = ptr->data; - WM_xr_actionmap_ensure_unique(wm->xr.runtime, actionmap); + WM_xr_actionmap_ensure_unique(&wm->xr.session_settings, actionmap); } # else UNUSED_VARS(bmain, ptr); @@ -646,6 +680,126 @@ static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), Point /** \} */ /* -------------------------------------------------------------------- */ +/** \name XR Motion Capture + * \{ */ + +static XrMotionCaptureObject *rna_XrMotionCaptureObject_new(XrSessionSettings *settings, + PointerRNA *ob_ptr) +{ +# ifdef WITH_XR_OPENXR + Object *ob = ob_ptr->data; + return WM_xr_mocap_object_new(settings, ob); +# else + UNUSED_VARS(settings, ob_ptr); + return NULL; +# endif +} + +static void rna_XrMotionCaptureObject_remove(XrSessionSettings *settings, PointerRNA *mocap_ob_ptr) +{ +# ifdef WITH_XR_OPENXR + XrMotionCaptureObject *mocap_ob = mocap_ob_ptr->data; + WM_xr_mocap_object_remove(settings, mocap_ob); + RNA_POINTER_INVALIDATE(mocap_ob_ptr); +# else + UNUSED_VARS(settings, mocap_ob_ptr); +# endif +} + +static XrMotionCaptureObject *rna_XrMotionCaptureObject_find(XrSessionSettings *settings, + PointerRNA *ob_ptr) +{ +# ifdef WITH_XR_OPENXR + Object *ob = ob_ptr->data; + return WM_xr_mocap_object_find(settings, ob); +# else + UNUSED_VARS(settings, ob_ptr); + return NULL; +# endif +} + +static void rna_XrMotionCaptureObject_object_update(Main *bmain, + Scene *UNUSED(scene), + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm) { + XrMotionCaptureObject *mocap_ob = ptr->data; + WM_xr_mocap_object_ensure_unique(&wm->xr.session_settings, mocap_ob); + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static bool rna_XrMotionCaptureObject_enable_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const XrMotionCaptureObject *mocap_ob = ptr->data; + return (mocap_ob->flag & XR_MOCAP_OBJECT_ENABLE) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrMotionCaptureObject_enable_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrMotionCaptureObject *mocap_ob = ptr->data; + SET_FLAG_FROM_TEST(mocap_ob->flag, value, XR_MOCAP_OBJECT_ENABLE); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrMotionCaptureObject_enable_update(Main *bmain, + Scene *UNUSED(scene), + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm) { + XrMotionCaptureObject *mocap_ob = ptr->data; + if ((mocap_ob->flag & XR_MOCAP_OBJECT_ENABLE) != 0) { + /* Store object's original pose. */ + WM_xr_session_state_mocap_pose_set(&wm->xr, mocap_ob); + } + else { + /* Restore object's original pose. */ + WM_xr_session_state_mocap_pose_get(&wm->xr, mocap_ob); + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static bool rna_XrMotionCaptureObject_autokey_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const XrMotionCaptureObject *mocap_ob = ptr->data; + return (mocap_ob->flag & XR_MOCAP_OBJECT_AUTOKEY) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrMotionCaptureObject_autokey_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrMotionCaptureObject *mocap_ob = ptr->data; + SET_FLAG_FROM_TEST(mocap_ob->flag, value, XR_MOCAP_OBJECT_AUTOKEY); +# else + UNUSED_VARS(ptr, value); +# endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name XR Session Settings * \{ */ @@ -691,6 +845,27 @@ static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, boo # endif } +static bool rna_XrSessionSettings_enable_vive_tracker_extension_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return (xr->session_settings.flag & XR_SESSION_ENABLE_VIVE_TRACKER_EXTENSION) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrSessionSettings_enable_vive_tracker_extension_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_ENABLE_VIVE_TRACKER_EXTENSION); +# else + UNUSED_VARS(ptr, value); +# endif +} + static int rna_XrSessionSettings_icon_from_show_object_viewport_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -710,6 +885,50 @@ static int rna_XrSessionSettings_icon_from_show_object_viewport_get(PointerRNA * # endif } +static void rna_XrSessionSettings_actionmaps_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + rna_iterator_listbase_begin(iter, &xr->session_settings.actionmaps, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrSessionSettings_actionmaps_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return BLI_listbase_count(&xr->session_settings.actionmaps); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrSessionSettings_mocap_objects_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + rna_iterator_listbase_begin(iter, &xr->session_settings.mocap_objects, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrSessionSettings_mocap_objects_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return BLI_listbase_count(&xr->session_settings.mocap_objects); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -871,6 +1090,32 @@ bool rna_XrSessionState_controller_pose_actions_set(bContext *C, # endif } +bool rna_XrSessionState_tracker_pose_action_add(bContext *C, + const char *action_set_name, + const char *tracker_action_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_tracker_pose_action_add(&wm->xr, action_set_name, tracker_action_name); +# else + UNUSED_VARS(C, action_set_name, tracker_action_name); + return false; +# endif +} + +bool rna_XrSessionState_tracker_pose_action_remove(bContext *C, + const char *action_set_name, + const char *tracker_action_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_tracker_pose_action_remove(&wm->xr, action_set_name, tracker_action_name); +# else + UNUSED_VARS(C, action_set_name, tracker_action_name); + return false; +# endif +} + void rna_XrSessionState_action_state_get(bContext *C, const char *action_set_name, const char *action_name, @@ -994,6 +1239,32 @@ static void rna_XrSessionState_controller_aim_rotation_get(bContext *C, # endif } +static void rna_XrSessionState_tracker_location_get(bContext *C, + const char *user_path, + float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_tracker_location_get(&wm->xr, user_path, r_values); +# else + UNUSED_VARS(C, user_path); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_tracker_rotation_get(bContext *C, + const char *user_path, + float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_tracker_rotation_get(&wm->xr, user_path, r_values); +# else + UNUSED_VARS(C, user_path); + unit_qt(r_values); +# endif +} + static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR @@ -1081,71 +1352,6 @@ static void rna_XrSessionState_nav_scale_set(PointerRNA *ptr, float value) # endif } -static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ -# ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - ListBase *lb = WM_xr_actionmaps_get(xr->runtime); - rna_iterator_listbase_begin(iter, lb, NULL); -# else - UNUSED_VARS(iter, ptr); -# endif -} - -static int rna_XrSessionState_actionmaps_length(PointerRNA *ptr) -{ -# ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - ListBase *lb = WM_xr_actionmaps_get(xr->runtime); - return BLI_listbase_count(lb); -# else - UNUSED_VARS(ptr); - return 0; -# endif -} - -static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) -{ -# ifdef WITH_XR_OPENXR - const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return WM_xr_actionmap_active_index_get(xr->runtime); -# else - UNUSED_VARS(ptr); - return -1; -# endif -} - -static void rna_XrSessionState_active_actionmap_set(PointerRNA *ptr, int value) -{ -# ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - WM_xr_actionmap_active_index_set(xr->runtime, (short)value); -# else - UNUSED_VARS(ptr, value); -# endif -} - -static int rna_XrSessionState_selected_actionmap_get(PointerRNA *ptr) -{ -# ifdef WITH_XR_OPENXR - const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return WM_xr_actionmap_selected_index_get(xr->runtime); -# else - UNUSED_VARS(ptr); - return -1; -# endif -} - -static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value) -{ -# ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - WM_xr_actionmap_selected_index_set(xr->runtime, (short)value); -# else - UNUSED_VARS(ptr, value); -# endif -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -1600,12 +1806,10 @@ static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_srna(cprop, "XrActionMaps"); srna = RNA_def_struct(brna, "XrActionMaps", NULL); + RNA_def_struct_sdna(srna, "XrSessionSettings"); RNA_def_struct_ui_text(srna, "XR Action Maps", "Collection of XR action maps"); func = RNA_def_function(srna, "new", "rna_XrActionMap_new"); - RNA_def_function_flag(func, FUNC_NO_SELF); - parm = RNA_def_pointer(func, "xr_session_state", "XrSessionState", "XR Session State", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_boolean(func, @@ -1618,9 +1822,6 @@ static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "new_from_actionmap", "rna_XrActionMap_new_from_actionmap"); - RNA_def_function_flag(func, FUNC_NO_SELF); - parm = RNA_def_pointer(func, "xr_session_state", "XrSessionState", "XR Session State", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); parm = RNA_def_pointer( func, "actionmap", "XrActionMap", "Action Map", "Action map to use as a reference"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); @@ -1628,17 +1829,12 @@ static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "remove", "rna_XrActionMap_remove"); - RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "xr_session_state", "XrSessionState", "XR Session State", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Removed action map"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); func = RNA_def_function(srna, "find", "rna_XrActionMap_find"); - RNA_def_function_flag(func, FUNC_NO_SELF); - parm = RNA_def_pointer(func, "xr_session_state", "XrSessionState", "XR Session State", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer( @@ -1680,7 +1876,7 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) rna_def_xr_actionmap_items(brna, prop); prop = RNA_def_property(srna, "selected_item", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "selitem"); + RNA_def_property_int_sdna(prop, NULL, "sel_item"); RNA_def_property_ui_text(prop, "Selected Item", ""); /* XrUserPath */ @@ -1721,6 +1917,10 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "User Paths", "OpenXR user paths"); rna_def_xr_user_paths(brna, prop); + prop = RNA_def_property(srna, "selected_user_path", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "sel_user_path"); + RNA_def_property_ui_text(prop, "Selected User Path", "Currently selected user path"); + prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE); RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); RNA_def_property_ui_text(prop, "Operator", "Identifier of operator to call on action event"); @@ -1765,6 +1965,11 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Is Controller Aim", "The action poses will be used for the VR controller aims"); + prop = RNA_def_property(srna, "pose_is_tracker", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs( + prop, "rna_XrActionMapItem_pose_is_tracker_get", "rna_XrActionMapItem_pose_is_tracker_set"); + RNA_def_property_ui_text(prop, "Is Tracker", "The action poses represent a VR tracker"); + prop = RNA_def_property(srna, "haptic_name", PROP_STRING, PROP_NONE); RNA_def_property_ui_text( prop, "Haptic Name", "Name of the haptic action to apply when executing this action"); @@ -1818,7 +2023,7 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) rna_def_xr_actionmap_bindings(brna, prop); prop = RNA_def_property(srna, "selected_binding", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "selbinding"); + RNA_def_property_int_sdna(prop, NULL, "sel_binding"); RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding"); /* XrComponentPath */ @@ -1858,6 +2063,10 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Component Paths", "OpenXR component paths"); rna_def_xr_component_paths(brna, prop); + prop = RNA_def_property(srna, "selected_component_path", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "sel_component_path"); + RNA_def_property_ui_text(prop, "Selected Component Path", "Currently selected component path"); + prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "float_threshold"); RNA_def_property_range(prop, 0.0, 1.0); @@ -1891,6 +2100,97 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) /** \} */ /* -------------------------------------------------------------------- */ +/** \name XR Motion Capture + * \{ */ + +static void rna_def_xr_motioncapture_objects(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrMotionCaptureObjects"); + srna = RNA_def_struct(brna, "XrMotionCaptureObjects", NULL); + RNA_def_struct_sdna(srna, "XrSessionSettings"); + RNA_def_struct_ui_text( + srna, "XR Motion Capture Objects", "Collection of XR motion capture objects"); + + func = RNA_def_function(srna, "new", "rna_XrMotionCaptureObject_new"); + parm = RNA_def_pointer(func, + "object", + "Object", + "Object", + "Blender object used to identify the motion capture object"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_pointer(func, + "mocap_object", + "XrMotionCaptureObject", + "Motion Capture Object", + "Added motion capture object"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrMotionCaptureObject_remove"); + parm = RNA_def_pointer(func, + "mocap_object", + "XrMotionCaptureObject", + "Motion Capture Object", + "Removed motion capture object"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrMotionCaptureObject_find"); + parm = RNA_def_pointer( + func, "object", "Object", "Object", "Blender object identifying the motion capture object"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_pointer(func, + "mocap_object", + "XrMotionCaptureObject", + "Motion Capture Object", + "The motion capture object with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_motioncapture_object(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "XrMotionCaptureObject", NULL); + RNA_def_struct_sdna(srna, "XrMotionCaptureObject"); + RNA_def_struct_ui_text(srna, "XR Motion Capture Object", ""); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "ob"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object to bind to a VR device"); + RNA_def_property_update(prop, 0, "rna_XrMotionCaptureObject_object_update"); + + prop = RNA_def_property(srna, "user_path", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path", "OpenXR user path identifying the target VR device"); + + prop = RNA_def_property(srna, "location_offset", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_ui_text(prop, "Location Offset", "Location offset in device space"); + + prop = RNA_def_property(srna, "rotation_offset", PROP_FLOAT, PROP_EULER); + RNA_def_property_ui_text(prop, "Rotation Offset", "Rotation offset in device space"); + + prop = RNA_def_property(srna, "enable", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs( + prop, "rna_XrMotionCaptureObject_enable_get", "rna_XrMotionCaptureObject_enable_set"); + RNA_def_property_ui_text(prop, "Enable", "Bind object to target VR device"); + RNA_def_property_update(prop, 0, "rna_XrMotionCaptureObject_enable_update"); + + prop = RNA_def_property(srna, "autokey", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs( + prop, "rna_XrMotionCaptureObject_autokey_get", "rna_XrMotionCaptureObject_autokey_set"); + RNA_def_property_ui_text(prop, "Auto Key", "Auto-insert keyframes on animation playback"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name XR Session Settings * \{ */ @@ -2052,6 +2352,16 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "Allow the VR tracking origin to be defined independently of the headset location"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "enable_vive_tracker_extension", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrSessionSettings_enable_vive_tracker_extension_get", + "rna_XrSessionSettings_enable_vive_tracker_extension_set"); + RNA_def_property_ui_text(prop, + "Enable Vive Tracker Extension", + "Enable bindings for the HTC Vive Trackers. Note that this may not be " + "supported by all OpenXR runtimes"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + rna_def_object_type_visibility_flags_common(srna, NC_WM | ND_XR_DATA_CHANGED); /* Helper for drawing the icon. */ @@ -2060,6 +2370,48 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) prop, "rna_XrSessionSettings_icon_from_show_object_viewport_get", NULL, NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Visibility Icon", ""); + + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_XrSessionSettings_actionmaps_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrSessionSettings_actionmaps_length", + NULL, + NULL, + NULL); + RNA_def_property_struct_type(prop, "XrActionMap"); + RNA_def_property_ui_text(prop, "XR Action Maps", ""); + rna_def_xr_actionmaps(brna, prop); + + prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "act_actionmap"); + RNA_def_property_ui_text(prop, "Active Action Map", ""); + + prop = RNA_def_property(srna, "selected_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "sel_actionmap"); + RNA_def_property_ui_text(prop, "Selected Action Map", ""); + + prop = RNA_def_property(srna, "mocap_objects", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrMotionCaptureObject"); + RNA_def_property_collection_funcs(prop, + "rna_XrSessionSettings_mocap_objects_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrSessionSettings_mocap_objects_length", + NULL, + NULL, + NULL); + RNA_def_property_ui_text( + prop, "XR Motion Capture Objects", "Objects to bind to headset/controller poses"); + rna_def_xr_motioncapture_objects(brna, prop); + + prop = RNA_def_property(srna, "selected_mocap_object", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "sel_mocap_object"); + RNA_def_property_ui_text( + prop, "Selected Motion Capture Object", "Currently selected motion capture object"); } /** \} */ @@ -2165,6 +2517,42 @@ static void rna_def_xr_session_state(BlenderRNA *brna) parm = RNA_def_boolean(func, "result", 0, "Result", ""); RNA_def_function_return(func, parm); + func = RNA_def_function( + srna, "tracker_pose_action_add", "rna_XrSessionState_tracker_pose_action_add"); + RNA_def_function_ui_description(func, "Add a VR tracker pose to the session"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "tracker_action", + NULL, + MAX_NAME, + "Tracker Action", + "Name of the action representing the VR tracker"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "tracker_pose_action_remove", "rna_XrSessionState_tracker_pose_action_remove"); + RNA_def_function_ui_description(func, "Remove a VR tracker pose from the session"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "tracker_action", + NULL, + MAX_NAME, + "Tracker Action", + "Name of the action representing the VR tracker"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "action_state_get", "rna_XrSessionState_action_state_get"); RNA_def_function_ui_description(func, "Get the current state of a VR action"); RNA_def_function_flag(func, FUNC_NO_SELF); @@ -2347,6 +2735,49 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + func = RNA_def_function(srna, "tracker_location_get", "rna_XrSessionState_tracker_location_get"); + RNA_def_function_ui_description(func, "Get the last known tracker location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, "user_path", NULL, XR_MAX_USER_PATH_LENGTH, "User Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Tracker location", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function(srna, "tracker_rotation_get", "rna_XrSessionState_tracker_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known tracker rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, "user_path", NULL, XR_MAX_USER_PATH_LENGTH, "User Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Tracker quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 3); RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL); @@ -2390,34 +2821,6 @@ static void rna_def_xr_session_state(BlenderRNA *brna) prop, "Navigation Scale", "Additional scale multiplier to apply to base scale when determining viewer scale"); - - prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "XrActionMap"); - RNA_def_property_collection_funcs(prop, - "rna_XrSessionState_actionmaps_begin", - "rna_iterator_listbase_next", - "rna_iterator_listbase_end", - "rna_iterator_listbase_get", - "rna_XrSessionState_actionmaps_length", - NULL, - NULL, - NULL); - RNA_def_property_ui_text(prop, "XR Action Maps", ""); - rna_def_xr_actionmaps(brna, prop); - - prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE); - RNA_def_property_int_funcs(prop, - "rna_XrSessionState_active_actionmap_get", - "rna_XrSessionState_active_actionmap_set", - NULL); - RNA_def_property_ui_text(prop, "Active Action Map", ""); - - prop = RNA_def_property(srna, "selected_actionmap", PROP_INT, PROP_NONE); - RNA_def_property_int_funcs(prop, - "rna_XrSessionState_selected_actionmap_get", - "rna_XrSessionState_selected_actionmap_set", - NULL); - RNA_def_property_ui_text(prop, "Selected Action Map", ""); } /** \} */ @@ -2528,6 +2931,7 @@ void RNA_def_xr(BlenderRNA *brna) RNA_define_animate_sdna(false); rna_def_xr_actionmap(brna); + rna_def_xr_motioncapture_object(brna); rna_def_xr_session_settings(brna); rna_def_xr_session_state(brna); rna_def_xr_eventdata(brna); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 4c553d0edfe..3cc643a7ac7 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -176,6 +176,8 @@ if(WITH_XR_OPENXR) list(APPEND INC xr + # For wm_xr_operators.c: BKE_editmesh.h + ../bmesh ) list(APPEND SRC @@ -183,6 +185,7 @@ if(WITH_XR_OPENXR) xr/intern/wm_xr_action.c xr/intern/wm_xr_actionmap.c xr/intern/wm_xr_draw.c + xr/intern/wm_xr_mocap.c xr/intern/wm_xr_operators.c xr/intern/wm_xr_session.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 60cded3b869..e60d812e5b6 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1667,6 +1667,12 @@ bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +bool WM_xr_session_state_tracker_location_get(const wmXrData *xr, + const char *subaction_path, + float r_location[3]); +bool WM_xr_session_state_tracker_rotation_get(const wmXrData *xr, + const char *subaction_path, + float r_rotation[4]); bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]); void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]); bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]); @@ -1722,6 +1728,12 @@ bool WM_xr_controller_pose_actions_set(wmXrData *xr, const char *action_set_name, const char *grip_action_name, const char *aim_action_name); +bool WM_xr_tracker_pose_action_add(wmXrData *xr, + const char *action_set_name, + const char *tracker_action_name); +bool WM_xr_tracker_pose_action_remove(wmXrData *xr, + const char *action_set_name, + const char *tracker_action_name); /** * XR action functions to be called post-XR session start. @@ -1745,23 +1757,17 @@ void WM_xr_haptic_action_stop(wmXrData *xr, /* wm_xr_actionmap.c */ -XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime, +XrActionMap *WM_xr_actionmap_new(XrSessionSettings *settings, const char *name, bool replace_existing); /** * Ensure unique name among all action maps. */ -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); +void WM_xr_actionmap_ensure_unique(XrSessionSettings *settings, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_add_copy(XrSessionSettings *settings, XrActionMap *am_src); +bool WM_xr_actionmap_remove(XrSessionSettings *settings, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_find(XrSessionSettings *settings, const char *name); +void WM_xr_actionmaps_free(XrSessionSettings *settings); XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, const char *name, @@ -1790,6 +1796,18 @@ 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); + +/* wm_xr_mocap.c */ + +XrMotionCaptureObject *WM_xr_mocap_object_new(XrSessionSettings *settings, Object *ob); +void WM_xr_mocap_object_ensure_unique(XrSessionSettings *settings, + XrMotionCaptureObject *mocap_ob); +void WM_xr_mocap_object_remove(XrSessionSettings *settings, XrMotionCaptureObject *mocap_ob); +XrMotionCaptureObject *WM_xr_mocap_object_find(const XrSessionSettings *settings, + const Object *ob); + +bool WM_xr_session_state_mocap_pose_get(const wmXrData *xr, XrMotionCaptureObject *mocap_ob); +void WM_xr_session_state_mocap_pose_set(wmXrData *xr, const XrMotionCaptureObject *mocap_ob); #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 40d9b0b9a35..d113669a998 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -95,6 +95,33 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) static void write_wm_xr_data(BlendWriter *writer, wmXrData *xr_data) { BKE_screen_view3d_shading_blend_write(writer, &xr_data->session_settings.shading); + + LISTBASE_FOREACH (XrActionMap *, am, &xr_data->session_settings.actionmaps) { + BLO_write_struct(writer, XrActionMap, am); + + LISTBASE_FOREACH (XrActionMapItem *, ami, &am->items) { + BLO_write_struct(writer, XrActionMapItem, ami); + if (ami->op[0] && ami->op_properties) { + IDP_BlendWrite(writer, ami->op_properties); + } + + LISTBASE_FOREACH (XrUserPath *, user_path, &ami->user_paths) { + BLO_write_struct(writer, XrUserPath, user_path); + } + + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + BLO_write_struct(writer, XrActionMapBinding, amb); + + LISTBASE_FOREACH (XrComponentPath *, component_path, &amb->component_paths) { + BLO_write_struct(writer, XrComponentPath, component_path); + } + } + } + } + + LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob, &xr_data->session_settings.mocap_objects) { + BLO_write_struct(writer, XrMotionCaptureObject, mocap_ob); + } } static void window_manager_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -123,6 +150,36 @@ static void window_manager_blend_write(BlendWriter *writer, ID *id, const void * static void direct_link_wm_xr_data(BlendDataReader *reader, wmXrData *xr_data) { BKE_screen_view3d_shading_blend_read_data(reader, &xr_data->session_settings.shading); + + BLO_read_list(reader, &xr_data->session_settings.actionmaps); + + LISTBASE_FOREACH (XrActionMap *, am, &xr_data->session_settings.actionmaps) { + BLO_read_list(reader, &am->items); + + LISTBASE_FOREACH (XrActionMapItem *, ami, &am->items) { + if (ami->op[0] && ami->op_properties) { + BLO_read_data_address(reader, &ami->op_properties); + IDP_BlendDataRead(reader, &ami->op_properties); + + ami->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); + WM_operator_properties_create(ami->op_properties_ptr, ami->op); + ami->op_properties_ptr->data = ami->op_properties; + } + else { + ami->op_properties = NULL; + ami->op_properties_ptr = NULL; + } + + BLO_read_list(reader, &ami->user_paths); + BLO_read_list(reader, &ami->bindings); + + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + BLO_read_list(reader, &amb->component_paths); + } + } + } + + BLO_read_list(reader, &xr_data->session_settings.mocap_objects); } static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) @@ -216,6 +273,10 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) static void lib_link_wm_xr_data(BlendLibReader *reader, ID *parent_id, wmXrData *xr_data) { BLO_read_id_address(reader, parent_id->lib, &xr_data->session_settings.base_pose_object); + + LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob, &xr_data->session_settings.mocap_objects) { + BLO_read_id_address(reader, parent_id->lib, &mocap_ob->ob); + } } static void lib_link_workspace_instance_hook(BlendLibReader *reader, diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index a170fa9902b..45d192d227c 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -201,6 +201,12 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) WM_msgbus_destroy(wm->message_bus); wm->message_bus = NULL; } + +#ifdef WITH_XR_OPENXR + /* Free XR action maps and motion capture objects. */ + WM_xr_actionmaps_free(&wm->xr.session_settings); + BLI_freelistN(&wm->xr.session_settings.mocap_objects); +#endif } BLI_listbase_clear(&G_MAIN->wm); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 91ec45da6d4..104eda220cc 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1519,7 +1519,7 @@ void wm_window_process_events(const bContext *C) #ifdef WITH_XR_OPENXR /* XR events don't use the regular window queues. So here we don't only trigger * processing/dispatching but also handling. */ - has_event |= wm_xr_events_handle(CTX_wm_manager(C)); + has_event |= wm_xr_events_handle(C); #endif /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle. diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index e46af0657a5..c4427898d3a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -8,6 +8,9 @@ * representation of the OpenXR runtime connection within the application. */ +#include "BLI_listbase.h" + +#include "BKE_context.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_main.h" @@ -87,6 +90,9 @@ bool wm_xr_init(wmWindowManager *wm) create_info.context_flag |= GHOST_kXrContextGpuNVIDIA; } #endif + if ((wm->xr.session_settings.flag & XR_SESSION_ENABLE_VIVE_TRACKER_EXTENSION) != 0) { + create_info.context_flag |= GHOST_kXrContextEnableViveTrackerExtension; + } if (!(context = GHOST_XrContextCreate(&create_info))) { return false; @@ -117,16 +123,20 @@ void wm_xr_exit(wmWindowManager *wm) IDP_FreeProperty(wm->xr.session_settings.shading.prop); wm->xr.session_settings.shading.prop = NULL; } + WM_xr_actionmaps_free(&wm->xr.session_settings); + BLI_freelistN(&wm->xr.session_settings.mocap_objects); } -bool wm_xr_events_handle(wmWindowManager *wm) +bool wm_xr_events_handle(const bContext *C) { + wmWindowManager *wm = CTX_wm_manager(C); + if (wm->xr.runtime && wm->xr.runtime->context) { GHOST_XrEventsHandle(wm->xr.runtime->context); /* Process OpenXR action events. */ if (WM_xr_session_is_ready(&wm->xr)) { - wm_xr_session_actions_update(wm); + wm_xr_session_actions_update(C); } /* wm_window_process_events() uses the return value to determine if it can put the main thread @@ -168,7 +178,6 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime) (*runtime)->area = NULL; } wm_xr_session_data_free(&(*runtime)->session_state); - WM_xr_actionmaps_clear(*runtime); GHOST_XrContextDestroy(context); } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index a83415c98af..3dc354e148d 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -43,6 +43,7 @@ static void action_set_destroy(void *val) MEM_SAFE_FREE(action_set->name); + BLI_freelistN(&action_set->tracker_actions); BLI_freelistN(&action_set->active_modal_actions); BLI_freelistN(&action_set->active_haptic_actions); @@ -200,6 +201,11 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) action_set->controller_grip_action = action_set->controller_aim_action = NULL; } + if (!BLI_listbase_is_empty(&action_set->tracker_actions)) { + wm_xr_session_tracker_data_clear(session_state); + BLI_freelistN(&action_set->tracker_actions); + } + BLI_freelistN(&action_set->active_modal_actions); BLI_freelistN(&action_set->active_haptic_actions); @@ -309,6 +315,14 @@ 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; } + LISTBASE_FOREACH (LinkData *, ld, &action_set->tracker_actions) { + wmXrAction *tracker_action = ld->data; + if (STREQ(tracker_action->name, action_name)) { + WM_xr_tracker_pose_action_remove(xr, action_set_name, action_name); + break; + } + } + LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) { wmXrAction *active_modal_action = ld->data; if (STREQ(active_modal_action->name, action_name)) { @@ -416,6 +430,7 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool xr->runtime->session_state.active_action_set = action_set; + /* Update controller/tracker data. */ if (action_set->controller_grip_action && action_set->controller_aim_action) { wm_xr_session_controller_data_populate( action_set->controller_grip_action, action_set->controller_aim_action, xr); @@ -424,6 +439,13 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool wm_xr_session_controller_data_clear(&xr->runtime->session_state); } + if (!BLI_listbase_is_empty(&action_set->tracker_actions)) { + wm_xr_session_tracker_data_populate(&action_set->tracker_actions, xr); + } + else { + wm_xr_session_tracker_data_clear(&xr->runtime->session_state); + } + return true; } @@ -469,6 +491,66 @@ bool WM_xr_controller_pose_actions_set(wmXrData *xr, return true; } +bool WM_xr_tracker_pose_action_add(wmXrData *xr, + const char *action_set_name, + const char *tracker_action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + wmXrAction *tracker_action = action_find(xr, action_set_name, tracker_action_name); + if (!tracker_action) { + return false; + } + + LinkData *ld = MEM_callocN(sizeof(*ld), __func__); + ld->data = tracker_action; + BLI_addtail(&action_set->tracker_actions, ld); + + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_tracker_data_populate(&action_set->tracker_actions, xr); + } + + return true; +} + +bool WM_xr_tracker_pose_action_remove(wmXrData *xr, + const char *action_set_name, + const char *tracker_action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + wmXrAction *tracker_action = action_find(xr, action_set_name, tracker_action_name); + if (!tracker_action) { + return false; + } + + { + LinkData *ld = BLI_findptr( + &action_set->tracker_actions, tracker_action, offsetof(LinkData, data)); + if (!ld) { + return false; + } + BLI_freelinkN(&action_set->tracker_actions, ld); + } + + if (action_set == xr->runtime->session_state.active_action_set) { + if (BLI_listbase_is_empty(&action_set->tracker_actions)) { + wm_xr_session_tracker_data_clear(&xr->runtime->session_state); + } + else { + wm_xr_session_tracker_data_populate(&action_set->tracker_actions, xr); + } + } + + return true; +} + bool WM_xr_action_state_get(const wmXrData *xr, const char *action_set_name, const char *action_name, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c index 8a1982fa8b5..20d77c8bdcf 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -127,6 +127,7 @@ XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, static void wm_xr_actionmap_binding_clear(XrActionMapBinding *amb) { BLI_freelistN(&amb->component_paths); + amb->sel_component_path = 0; } bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb) @@ -137,9 +138,9 @@ bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *am wm_xr_actionmap_binding_clear(amb); BLI_freelinkN(&ami->bindings, amb); - if (idx <= ami->selbinding) { - if (--ami->selbinding < 0) { - ami->selbinding = 0; + if (idx <= ami->sel_binding) { + if (--ami->sel_binding < 0) { + ami->sel_binding = 0; } } @@ -192,11 +193,12 @@ static void wm_xr_actionmap_item_clear(XrActionMapItem *ami) wm_xr_actionmap_binding_clear(amb); } BLI_freelistN(&ami->bindings); - ami->selbinding = 0; - - wm_xr_actionmap_item_properties_free(ami); + ami->sel_binding = 0; BLI_freelistN(&ami->user_paths); + ami->sel_user_path = 0; + + wm_xr_actionmap_item_properties_free(ami); } void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami) @@ -313,6 +315,12 @@ static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src) BLI_addtail(&ami_dst->bindings, amb_new); } + BLI_listbase_clear(&ami_dst->user_paths); + LISTBASE_FOREACH (XrUserPath *, path, &ami_src->user_paths) { + XrUserPath *path_new = MEM_dupallocN(path); + BLI_addtail(&ami_dst->user_paths, path_new); + } + if (ami_dst->op_properties) { ami_dst->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); WM_operator_properties_create(ami_dst->op_properties_ptr, ami_dst->op); @@ -324,12 +332,6 @@ static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src) ami_dst->op_properties_ptr = NULL; } - BLI_listbase_clear(&ami_dst->user_paths); - LISTBASE_FOREACH (XrUserPath *, path, &ami_src->user_paths) { - XrUserPath *path_new = MEM_dupallocN(path); - BLI_addtail(&ami_dst->user_paths, path_new); - } - return ami_dst; } @@ -352,9 +354,9 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) wm_xr_actionmap_item_clear(ami); BLI_freelinkN(&actionmap->items, ami); - if (idx <= actionmap->selitem) { - if (--actionmap->selitem < 0) { - actionmap->selitem = 0; + if (idx <= actionmap->sel_item) { + if (--actionmap->sel_item < 0) { + actionmap->sel_item = 0; } } @@ -382,39 +384,49 @@ XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *n * List of XR action map items. * \{ */ -XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing) +static void wm_xr_actionmap_clear(XrActionMap *actionmap) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + wm_xr_actionmap_item_clear(ami); + } + BLI_freelistN(&actionmap->items); + actionmap->sel_item = 0; +} + +XrActionMap *WM_xr_actionmap_new(XrSessionSettings *settings, + const char *name, + bool replace_existing) { - XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name); + XrActionMap *am_prev = WM_xr_actionmap_find(settings, name); if (am_prev && replace_existing) { - WM_xr_actionmap_clear(am_prev); + 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); + WM_xr_actionmap_ensure_unique(settings, am); } - BLI_addtail(&runtime->actionmaps, am); + BLI_addtail(&settings->actionmaps, am); return am; } -static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime, +static XrActionMap *wm_xr_actionmap_find_except(XrSessionSettings *settings, const char *name, const XrActionMap *am_except) { - LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + LISTBASE_FOREACH (XrActionMap *, am, &settings->actionmaps) { if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) { return am; } } - return NULL; } -void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap) +void WM_xr_actionmap_ensure_unique(XrSessionSettings *settings, XrActionMap *actionmap) { char name[MAX_NAME]; char *suffix; @@ -425,7 +437,7 @@ void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *action baselen = BLI_strnlen(name, MAX_NAME); suffix = &name[baselen]; - while (wm_xr_actionmap_find_except(runtime, name, actionmap)) { + while (wm_xr_actionmap_find_except(settings, name, actionmap)) { if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { /* Use default base name. */ BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME); @@ -455,33 +467,33 @@ static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src) return am_dst; } -XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src) +XrActionMap *WM_xr_actionmap_add_copy(XrSessionSettings *settings, XrActionMap *am_src) { XrActionMap *am_dst = wm_xr_actionmap_copy(am_src); - WM_xr_actionmap_ensure_unique(runtime, am_dst); + WM_xr_actionmap_ensure_unique(settings, am_dst); - BLI_addtail(&runtime->actionmaps, am_dst); + BLI_addtail(&settings->actionmaps, am_dst); return am_dst; } -bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap) +bool WM_xr_actionmap_remove(XrSessionSettings *settings, XrActionMap *actionmap) { - int idx = BLI_findindex(&runtime->actionmaps, actionmap); + int idx = BLI_findindex(&settings->actionmaps, actionmap); if (idx != -1) { - WM_xr_actionmap_clear(actionmap); - BLI_freelinkN(&runtime->actionmaps, actionmap); + wm_xr_actionmap_clear(actionmap); + BLI_freelinkN(&settings->actionmaps, actionmap); - if (idx <= runtime->actactionmap) { - if (--runtime->actactionmap < 0) { - runtime->actactionmap = 0; + if (idx <= settings->act_actionmap) { + if (--settings->act_actionmap < 0) { + settings->act_actionmap = 0; } } - if (idx <= runtime->selactionmap) { - if (--runtime->selactionmap < 0) { - runtime->selactionmap = 0; + if (idx <= settings->sel_actionmap) { + if (--settings->sel_actionmap < 0) { + settings->sel_actionmap = 0; } } @@ -491,9 +503,9 @@ bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap) return false; } -XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) +XrActionMap *WM_xr_actionmap_find(XrSessionSettings *settings, const char *name) { - LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + LISTBASE_FOREACH (XrActionMap *, am, &settings->actionmaps) { if (STREQLEN(name, am->name, MAX_NAME)) { return am; } @@ -501,47 +513,13 @@ XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) return NULL; } -void WM_xr_actionmap_clear(XrActionMap *actionmap) -{ - LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { - wm_xr_actionmap_item_clear(ami); - } - BLI_freelistN(&actionmap->items); - actionmap->selitem = 0; -} - -void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) +void WM_xr_actionmaps_free(XrSessionSettings *settings) { - LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { - WM_xr_actionmap_clear(am); + LISTBASE_FOREACH (XrActionMap *, am, &settings->actionmaps) { + wm_xr_actionmap_clear(am); } - BLI_freelistN(&runtime->actionmaps); - runtime->actactionmap = runtime->selactionmap = 0; -} - -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) -{ - 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) -{ - runtime->selactionmap = idx; + BLI_freelistN(&settings->actionmaps); + settings->act_actionmap = settings->sel_actionmap = 0; } /** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 3b1acee2b99..d17d132708d 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -227,7 +227,7 @@ static GPUBatch *wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_co static void wm_xr_controller_model_draw(const XrSessionSettings *settings, GHOST_XrContextHandle xr_context, - wmXrSessionState *state) + ListBase *controllers) { GHOST_XrControllerModelData model_data; @@ -246,7 +246,7 @@ static void wm_xr_controller_model_draw(const XrSessionSettings *settings, GPU_depth_test(GPU_DEPTH_NONE); GPU_blend(GPU_BLEND_ALPHA); - LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + LISTBASE_FOREACH (wmXrController *, controller, controllers) { GPUBatch *model = controller->model; if (!model) { model = controller->model = wm_xr_controller_model_batch_create(xr_context, @@ -289,7 +289,7 @@ static void wm_xr_controller_model_draw(const XrSessionSettings *settings, } } -static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state) +static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, ListBase *controllers) { bool draw_ray; switch (settings->controller_draw_style) { @@ -322,9 +322,9 @@ static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSes GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPU_blend(GPU_BLEND_ALPHA); - immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 2); + immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(controllers) * 2); - LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + LISTBASE_FOREACH (wmXrController *, controller, controllers) { const float(*mat)[4] = controller->aim_mat; madd_v3_v3v3fl(ray, mat[3], mat[2], -scale); @@ -346,9 +346,9 @@ static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSes GPU_depth_test(GPU_DEPTH_NONE); GPU_blend(GPU_BLEND_NONE); - immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 6); + immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(controllers) * 6); - LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + LISTBASE_FOREACH (wmXrController *, controller, controllers) { const float(*mat)[4] = controller->aim_mat; madd_v3_v3v3fl(x_axis, mat[3], mat[0], scale); madd_v3_v3v3fl(y_axis, mat[3], mat[1], scale); @@ -381,8 +381,19 @@ void wm_xr_draw_controllers(const bContext *UNUSED(C), ARegion *UNUSED(region), wmXrData *xr = customdata; const XrSessionSettings *settings = &xr->session_settings; GHOST_XrContextHandle xr_context = xr->runtime->context; - wmXrSessionState *state = &xr->runtime->session_state; + ListBase *controllers = &xr->runtime->session_state.controllers; - wm_xr_controller_model_draw(settings, xr_context, state); - wm_xr_controller_aim_draw(settings, state); + wm_xr_controller_model_draw(settings, xr_context, controllers); + wm_xr_controller_aim_draw(settings, controllers); +} + +void wm_xr_draw_trackers(const bContext *UNUSED(C), ARegion *UNUSED(region), void *customdata) +{ + wmXrData *xr = customdata; + const XrSessionSettings *settings = &xr->session_settings; + GHOST_XrContextHandle xr_context = xr->runtime->context; + ListBase *trackers = &xr->runtime->session_state.trackers; + + wm_xr_controller_model_draw(settings, xr_context, trackers); + wm_xr_controller_aim_draw(settings, trackers); } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index bb24514457d..f450fc91e89 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -52,6 +52,8 @@ typedef struct wmXrSessionState { /** Last known controller data. */ ListBase controllers; /* #wmXrController */ + /** Last known tracker data. */ + ListBase trackers; /* #wmXrController */ /** The currently active action set that will be updated on calls to * #wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and @@ -59,6 +61,10 @@ typedef struct wmXrSessionState { struct wmXrActionSet *active_action_set; /* Name of the action set (if any) to activate before the next actions sync. */ char active_action_set_next[64]; /* MAX_NAME */ + + /* Original poses for motion capture objects. Used to restore object transforms on session end. + */ + ListBase mocap_orig_poses; /* #wmXrMotionCapturePose */ } wmXrSessionState; typedef struct wmXrRuntimeData { @@ -74,10 +80,6 @@ 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 { @@ -94,6 +96,8 @@ typedef struct { struct ARegionType *controller_art; /** Controller draw callback handle. */ void *controller_draw_handle; + /** Tracker draw callback handle. */ + void *tracker_draw_handle; } wmXrSurfaceData; typedef struct wmXrDrawData { @@ -178,13 +182,21 @@ typedef struct wmXrActionSet { /** XR pose actions that determine the controller grip/aim transforms. */ wmXrAction *controller_grip_action; wmXrAction *controller_aim_action; + /** XR pose actions that determine tracker transforms. */ + ListBase tracker_actions; /* #LinkData */ /** Currently active modal actions. */ - ListBase active_modal_actions; + ListBase active_modal_actions; /* #LinkData */ /** Currently active haptic actions. */ - ListBase active_haptic_actions; + ListBase active_haptic_actions; /* wmXrHapticAction */ } wmXrActionSet; +typedef struct wmXrMotionCapturePose { + struct wmXrMotionCapturePose *next, *prev; + const Object *ob; + GHOST_XrPose pose; +} wmXrMotionCapturePose; + /* wm_xr.c */ wmXrRuntimeData *wm_xr_runtime_data_create(void); @@ -214,14 +226,15 @@ void *wm_xr_session_gpu_binding_context_create(void); void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context); void wm_xr_session_actions_init(wmXrData *xr); -void wm_xr_session_actions_update(wmWindowManager *wm); +void wm_xr_session_actions_update(const struct bContext *C); void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, const wmXrAction *aim_action, wmXrData *xr); void wm_xr_session_controller_data_clear(wmXrSessionState *state); +void wm_xr_session_tracker_data_populate(const ListBase *tracker_actions, wmXrData *xr); +void wm_xr_session_tracker_data_clear(wmXrSessionState *state); /* wm_xr_draw.c */ - void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]); void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); @@ -234,3 +247,23 @@ void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_ima */ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata); +void wm_xr_draw_trackers(const struct bContext *C, struct ARegion *region, void *customdata); + +/* wm_xr_mocap.c */ +void wm_xr_mocap_orig_poses_store(const XrSessionSettings *settings, wmXrSessionState *state); +void wm_xr_mocap_orig_poses_restore(const wmXrSessionState *state, XrSessionSettings *settings); +void wm_xr_mocap_object_autokey(struct bContext *C, + struct Scene *scene, + struct ViewLayer *view_layer, + wmWindow *win, + Object *ob, + bool notify); +void wm_xr_mocap_objects_update(const char *user_path, + const GHOST_XrPose *pose, + struct bContext *C, + XrSessionSettings *settings, + struct Scene *scene, + struct ViewLayer *view_layer, + wmWindow *win, + struct bScreen *screen_anim, + bool *notify); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_mocap.c b/source/blender/windowmanager/xr/intern/wm_xr_mocap.c new file mode 100644 index 00000000000..a7086a3cfa8 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_mocap.c @@ -0,0 +1,272 @@ +/* + * 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 Motion Capture + * + * API for XR motion capture objects. + */ + +#include "BKE_context.h" +#include "BKE_layer.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DEG_depsgraph.h" + +#include "DNA_object_types.h" + +#include "ED_keyframing.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_transform.h" + +#include "GHOST_C-api.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name XR Motion Capture Objects + * + * List of XR motion capture objects. Stored in session settings and can be written to files. + * \{ */ + +XrMotionCaptureObject *WM_xr_mocap_object_new(XrSessionSettings *settings, Object *ob) +{ + XrMotionCaptureObject *mocap_ob = WM_xr_mocap_object_find(settings, ob); + + if (!mocap_ob) { + mocap_ob = MEM_callocN(sizeof(XrMotionCaptureObject), __func__); + mocap_ob->ob = ob; + BLI_addtail(&settings->mocap_objects, mocap_ob); + } + + return mocap_ob; +} + +void WM_xr_mocap_object_ensure_unique(XrSessionSettings *settings, XrMotionCaptureObject *mocap_ob) +{ + LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob_other, &settings->mocap_objects) { + if ((mocap_ob_other != mocap_ob) && (mocap_ob_other->ob == mocap_ob->ob)) { + mocap_ob->ob = NULL; + return; + } + } +} + +void WM_xr_mocap_object_remove(XrSessionSettings *settings, XrMotionCaptureObject *mocap_ob) +{ + int idx = BLI_findindex(&settings->mocap_objects, mocap_ob); + + if (idx != -1) { + BLI_freelinkN(&settings->mocap_objects, mocap_ob); + + if (idx <= settings->sel_mocap_object) { + if (--settings->sel_mocap_object < 0) { + settings->sel_mocap_object = 0; + } + } + } +} + +XrMotionCaptureObject *WM_xr_mocap_object_find(const XrSessionSettings *settings, const Object *ob) +{ + return ob ? BLI_findptr(&settings->mocap_objects, ob, offsetof(XrMotionCaptureObject, ob)) : + NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Motion Capture Runtime + * + * Runtime functions for motion capture object poses and autokeying. + * \{ */ + +static void wm_xr_mocap_object_pose_get(const XrMotionCaptureObject *mocap_ob, GHOST_XrPose *pose) +{ + BLI_assert(mocap_ob->ob); + copy_v3_v3(pose->position, mocap_ob->ob->loc); + eul_to_quat(pose->orientation_quat, mocap_ob->ob->rot); +} + +static void wm_xr_mocap_object_pose_set(const GHOST_XrPose *pose, + XrMotionCaptureObject *mocap_ob, + bool apply_offset) +{ + BLI_assert(mocap_ob->ob); + + if (apply_offset) { + /* Convert offsets to pose (device) space. */ + float loc_ofs[3], rot_ofs[4]; + copy_v3_v3(loc_ofs, mocap_ob->location_offset); + mul_qt_v3(pose->orientation_quat, loc_ofs); + + eul_to_quat(rot_ofs, mocap_ob->rotation_offset); + normalize_qt(rot_ofs); + invert_qt_normalized(rot_ofs); + mul_qt_qtqt(rot_ofs, pose->orientation_quat, rot_ofs); + normalize_qt(rot_ofs); + + add_v3_v3v3(mocap_ob->ob->loc, pose->position, loc_ofs); + quat_to_eul(mocap_ob->ob->rot, rot_ofs); + } + else { + copy_v3_v3(mocap_ob->ob->loc, pose->position); + quat_to_eul(mocap_ob->ob->rot, pose->orientation_quat); + } + + DEG_id_tag_update(&mocap_ob->ob->id, ID_RECALC_TRANSFORM); +} + +void wm_xr_mocap_orig_poses_store(const XrSessionSettings *settings, wmXrSessionState *state) +{ + ListBase *mocap_orig_poses = &state->mocap_orig_poses; + + LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob, &settings->mocap_objects) { + wmXrMotionCapturePose *mocap_pose = MEM_callocN(sizeof(wmXrMotionCapturePose), __func__); + mocap_pose->ob = mocap_ob->ob; + if (mocap_ob->ob) { + wm_xr_mocap_object_pose_get(mocap_ob, &mocap_pose->pose); + } + BLI_addtail(mocap_orig_poses, mocap_pose); + } +} + +void wm_xr_mocap_orig_poses_restore(const wmXrSessionState *state, XrSessionSettings *settings) +{ + ListBase *mocap_objects = &settings->mocap_objects; + + LISTBASE_FOREACH (wmXrMotionCapturePose *, mocap_pose, &state->mocap_orig_poses) { + XrMotionCaptureObject *mocap_ob = BLI_findptr( + mocap_objects, mocap_pose->ob, offsetof(XrMotionCaptureObject, ob)); + if (mocap_ob && mocap_ob->ob) { + wm_xr_mocap_object_pose_set(&mocap_pose->pose, mocap_ob, false); + } + } +} + +void wm_xr_mocap_object_autokey( + bContext *C, Scene *scene, ViewLayer *view_layer, wmWindow *win, Object *ob, bool notify) +{ + /* Poll functions in keyingsets_utils.py require an active window and object. */ + wmWindow *win_prev = win ? CTX_wm_window(C) : NULL; + if (win) { + CTX_wm_window_set(C, win); + } + + Object *obact = CTX_data_active_object(C); + if (!obact) { + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base) { + ED_object_base_select(base, BA_SELECT); + ED_object_base_activate(C, base); + } + } + + bScreen *screen = CTX_wm_screen(C); + if (screen && screen->animtimer && (IS_AUTOKEY_FLAG(scene, INSERTAVAIL) == 0) && + ((scene->toolsettings->autokey_flag & ANIMRECORD_FLAG_WITHNLA) != 0)) { + ED_transform_animrecord_check_state(scene, screen->animtimer, &ob->id); + } + + ED_transform_autokeyframe_object(C, scene, view_layer, ob, TFM_TRANSLATION); + if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) { + ED_transform_autokeyframe_object(C, scene, view_layer, ob, TFM_ROTATION); + } + + if (ED_transform_motionpath_need_update_object(scene, ob)) { + ListBase lb = {NULL, NULL}; + BLI_addtail(&lb, BLI_genericNodeN(ob)); + + ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME, &lb); + + BLI_freelistN(&lb); + } + + if (notify) { + WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); + WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + } + + if (win) { + CTX_wm_window_set(C, win_prev); + } +} + +void wm_xr_mocap_objects_update(const char *user_path, + const GHOST_XrPose *pose, + bContext *C, + XrSessionSettings *settings, + Scene *scene, + ViewLayer *view_layer, + wmWindow *win, + bScreen *screen_anim, + bool *notify) +{ + LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob, &settings->mocap_objects) { + if (mocap_ob->ob && ((mocap_ob->flag & XR_MOCAP_OBJECT_ENABLE) != 0) && + STREQ(mocap_ob->user_path, user_path)) { + wm_xr_mocap_object_pose_set(pose, mocap_ob, true); + + if (((mocap_ob->flag & XR_MOCAP_OBJECT_AUTOKEY) != 0) && screen_anim && + autokeyframe_cfra_can_key(scene, &mocap_ob->ob->id)) { + wm_xr_mocap_object_autokey(C, scene, view_layer, win, mocap_ob->ob, *notify); + *notify = false; + } + } + } +} + +bool WM_xr_session_state_mocap_pose_get(const wmXrData *xr, XrMotionCaptureObject *mocap_ob) +{ + if (!WM_xr_session_exists(xr)) { + return false; + } + + if (mocap_ob->ob) { + wmXrMotionCapturePose *mocap_pose = BLI_findptr(&xr->runtime->session_state.mocap_orig_poses, + mocap_ob->ob, + offsetof(wmXrMotionCapturePose, ob)); + if (!mocap_pose) { + return false; + } + wm_xr_mocap_object_pose_set(&mocap_pose->pose, mocap_ob, false); + } + + return true; +} + +void WM_xr_session_state_mocap_pose_set(wmXrData *xr, const XrMotionCaptureObject *mocap_ob) +{ + if (WM_xr_session_exists(xr) && mocap_ob->ob) { + wmXrMotionCapturePose *mocap_pose = BLI_findptr(&xr->runtime->session_state.mocap_orig_poses, + mocap_ob->ob, + offsetof(wmXrMotionCapturePose, ob)); + if (mocap_pose) { + wm_xr_mocap_object_pose_get(mocap_ob, &mocap_pose->pose); + } + } +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c index 3f0c72a4a05..8fbf0964e2d 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_operators.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -13,14 +13,21 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_screen.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" +#include "ED_keyframing.h" +#include "ED_mesh.h" +#include "ED_object.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_space_api.h" #include "ED_transform_snap_object_context.h" #include "ED_view3d.h" @@ -194,6 +201,30 @@ static void wm_xr_grab_update(wmOperator *op, const wmXrActionData *actiondata) } } +static bool wm_xr_grab_can_do_bimanual(const wmXrActionData *actiondata, const XrGrabData *data) +{ + /* Returns true if: 1) Bimanual interaction is currently occurring (i.e. inputs on both + * controllers are pressed) and 2) bimanual interaction occurred on the last update. This second + * part is needed to avoid "jumpy" navigation/transform changes when transitioning from + * one-handed to two-handed interaction (see #wm_xr_grab_compute/compute_bimanual() for how + * navigation/transform deltas are calculated). */ + return (actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_grab_is_bimanual_ending(const wmXrActionData *actiondata, const XrGrabData *data) +{ + return (!actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_grab_is_locked(const XrGrabData *data, const bool bimanual) +{ + if (bimanual) { + return data->loc_lock && data->rot_lock && data->scale_lock; + } + /* Ignore scale lock, as one-handed interaction cannot change navigation/transform scale. */ + return data->loc_lock && data->rot_lock; +} + static void orient_mat_z_normalized(float R[4][4], const float z_axis[3]) { const float scale = len_v3(R[0]); @@ -254,6 +285,7 @@ static void wm_xr_grab_compute(const wmXrActionData *actiondata, const XrGrabData *data, const float nav_mat[4][4], const float nav_inv[4][4], + const float ob_inv[4][4], bool reverse, float r_delta[4][4]) { @@ -280,6 +312,11 @@ static void wm_xr_grab_compute(const wmXrActionData *actiondata, nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); } + if (ob_inv) { + mul_m4_m4m4(prev, ob_inv, prev); + mul_m4_m4m4(curr, ob_inv, curr); + } + if (reverse) { invert_m4(curr); mul_m4_m4m4(r_delta, prev, curr); @@ -306,6 +343,7 @@ static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata, const XrGrabData *data, const float nav_mat[4][4], const float nav_inv[4][4], + const float ob_inv[4][4], bool reverse, float r_delta[4][4]) { @@ -375,6 +413,11 @@ static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata, nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); } + if (ob_inv) { + mul_m4_m4m4(prev, ob_inv, prev); + mul_m4_m4m4(curr, ob_inv, curr); + } + if (reverse) { invert_m4(curr); mul_m4_m4m4(r_delta, prev, curr); @@ -414,32 +457,6 @@ static int wm_xr_navigation_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op return OPERATOR_CANCELLED; } -static bool wm_xr_navigation_grab_can_do_bimanual(const wmXrActionData *actiondata, - const XrGrabData *data) -{ - /* Returns true if: 1) Bimanual interaction is currently occurring (i.e. inputs on both - * controllers are pressed) and 2) bimanual interaction occurred on the last update. This second - * part is needed to avoid "jumpy" navigation changes when transitioning from one-handed to - * two-handed interaction (see #wm_xr_grab_compute/compute_bimanual() for how navigation deltas - * are calculated). */ - return (actiondata->bimanual && data->bimanual_prev); -} - -static bool wm_xr_navigation_grab_is_bimanual_ending(const wmXrActionData *actiondata, - const XrGrabData *data) -{ - return (!actiondata->bimanual && data->bimanual_prev); -} - -static bool wm_xr_navigation_grab_is_locked(const XrGrabData *data, const bool bimanual) -{ - if (bimanual) { - return data->loc_lock && data->rot_lock && data->scale_lock; - } - /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */ - return data->loc_lock && data->rot_lock; -} - static void wm_xr_navigation_grab_apply(wmXrData *xr, const wmXrActionData *actiondata, const XrGrabData *data, @@ -461,12 +478,22 @@ static void wm_xr_navigation_grab_apply(wmXrData *xr, } if (bimanual) { - wm_xr_grab_compute_bimanual( - actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + wm_xr_grab_compute_bimanual(actiondata, + data, + need_navinv ? nav_mat : NULL, + need_navinv ? nav_inv : NULL, + NULL, + true, + delta); } else { - wm_xr_grab_compute( - actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + wm_xr_grab_compute(actiondata, + data, + need_navinv ? nav_mat : NULL, + need_navinv ? nav_inv : NULL, + NULL, + true, + delta); } mul_m4_m4m4(out, delta, nav_mat); @@ -520,7 +547,7 @@ static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEven wmWindowManager *wm = CTX_wm_manager(C); wmXrData *xr = &wm->xr; - const bool do_bimanual = wm_xr_navigation_grab_can_do_bimanual(actiondata, data); + const bool do_bimanual = wm_xr_grab_can_do_bimanual(actiondata, data); data->loc_lock = RNA_boolean_get(op->ptr, "lock_location"); data->locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); @@ -529,10 +556,10 @@ static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEven data->scale_lock = RNA_boolean_get(op->ptr, "lock_scale"); /* Check if navigation is locked. */ - if (!wm_xr_navigation_grab_is_locked(data, do_bimanual)) { + if (!wm_xr_grab_is_locked(data, do_bimanual)) { /* Prevent unwanted snapping (i.e. "jumpy" navigation changes when transitioning from * two-handed to one-handed interaction) at the end of a bimanual interaction. */ - if (!wm_xr_navigation_grab_is_bimanual_ending(actiondata, data)) { + if (!wm_xr_grab_is_bimanual_ending(actiondata, data)) { wm_xr_navigation_grab_apply(xr, actiondata, data, do_bimanual); } } @@ -714,10 +741,10 @@ static void wm_xr_raycast_update(wmOperator *op, static void wm_xr_raycast(Scene *scene, Depsgraph *depsgraph, + eSnapTargetSelect snap_target_select, const float origin[3], const float direction[3], float *ray_dist, - bool selectable_only, float r_location[3], float r_normal[3], int *r_index, @@ -731,9 +758,7 @@ static void wm_xr_raycast(Scene *scene, sctx, depsgraph, NULL, - &(const struct SnapObjectParams){.snap_target_select = (selectable_only ? - SCE_SNAP_TARGET_ONLY_SELECTABLE : - SCE_SNAP_TARGET_ALL)}, + &(const struct SnapObjectParams){.snap_target_select = snap_target_select}, origin, direction, ray_dist, @@ -1225,10 +1250,10 @@ static void wm_xr_navigation_teleport(bContext *C, wm_xr_raycast(scene, depsgraph, + selectable_only ? SCE_SNAP_TARGET_ONLY_SELECTABLE : SCE_SNAP_TARGET_ALL, origin, direction, ray_dist, - selectable_only, location, normal, &index, @@ -1277,7 +1302,7 @@ static int wm_xr_navigation_teleport_invoke(bContext *C, wmOperator *op, const w wm_xr_raycast_init(op); - int retval = op->type->modal(C, op, event); + const int retval = op->type->modal(C, op, event); if ((retval & OPERATOR_RUNNING_MODAL) != 0) { WM_event_add_modal_handler(C, op); @@ -1509,6 +1534,845 @@ static void WM_OT_xr_navigation_reset(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name XR Raycast Select + * + * Casts a ray from an XR controller's pose and selects any hit geometry. + * \{ */ + +typedef enum eXrSelectElem { + XR_SEL_BASE = 0, + XR_SEL_VERTEX = 1, + XR_SEL_EDGE = 2, + XR_SEL_FACE = 3, +} eXrSelectElem; + +static void wm_xr_select_op_apply(void *elem, + BMesh *bm, + eXrSelectElem select_elem, + eSelectOp select_op, + bool *r_changed, + bool *r_set) +{ + const bool selected_prev = (select_elem == XR_SEL_BASE) ? + (((Base *)elem)->flag & BASE_SELECTED) != 0 : + (((BMElem *)elem)->head.hflag & BM_ELEM_SELECT) != 0; + + if (selected_prev) { + switch (select_op) { + case SEL_OP_SUB: + case SEL_OP_XOR: { + switch (select_elem) { + case XR_SEL_BASE: + ED_object_base_select((Base *)elem, BA_DESELECT); + *r_changed = true; + break; + case XR_SEL_VERTEX: + BM_vert_select_set(bm, (BMVert *)elem, false); + *r_changed = true; + break; + case XR_SEL_EDGE: + BM_edge_select_set(bm, (BMEdge *)elem, false); + *r_changed = true; + break; + case XR_SEL_FACE: + BM_face_select_set(bm, (BMFace *)elem, false); + *r_changed = true; + break; + } + break; + } + default: { + break; + } + } + } + else { + switch (select_op) { + case SEL_OP_SET: + case SEL_OP_ADD: + case SEL_OP_XOR: { + switch (select_elem) { + case XR_SEL_BASE: + ED_object_base_select((Base *)elem, BA_SELECT); + *r_changed = true; + break; + case XR_SEL_VERTEX: + BM_vert_select_set(bm, (BMVert *)elem, true); + *r_changed = true; + break; + case XR_SEL_EDGE: + BM_edge_select_set(bm, (BMEdge *)elem, true); + *r_changed = true; + break; + case XR_SEL_FACE: + BM_face_select_set(bm, (BMFace *)elem, true); + *r_changed = true; + break; + } + } + default: { + break; + } + } + + if (select_op == SEL_OP_SET) { + *r_set = true; + } + } +} + +static bool wm_xr_select_raycast(bContext *C, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + eSelectOp select_op, + bool deselect_all) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + vc.em = (vc.obedit && (vc.obedit->type == OB_MESH)) ? BKE_editmesh_from_object(vc.obedit) : NULL; + + float location[3]; + float normal[3]; + int index; + Object *ob = NULL; + float obmat[4][4]; + + wm_xr_raycast(vc.scene, + depsgraph, + selectable_only ? SCE_SNAP_TARGET_ONLY_SELECTABLE : SCE_SNAP_TARGET_ALL, + origin, + direction, + ray_dist, + location, + normal, + &index, + &ob, + obmat); + + /* Select. */ + bool hit = false; + bool changed = false; + + if (ob && vc.em && + ((ob == vc.obedit) || (ob->id.orig_id == &vc.obedit->id))) { /* TODO_XR: Non-mesh objects. */ + BMesh *bm = vc.em->bm; + BMFace *f = NULL; + BMEdge *e = NULL; + BMVert *v = NULL; + + if (index != -1) { + ToolSettings *ts = vc.scene->toolsettings; + float co[3]; + f = BM_face_at_index(bm, index); + + if ((ts->selectmode & SCE_SELECT_VERTEX) != 0) { + /* Find nearest vertex. */ + float dist_max = *ray_dist; + float dist; + BMLoop *l = f->l_first; + for (int i = 0; i < f->len; ++i, l = l->next) { + mul_v3_m4v3(co, obmat, l->v->co); + if ((dist = len_manhattan_v3v3(location, co)) < dist_max) { + v = l->v; + dist_max = dist; + } + } + if (v) { + hit = true; + } + } + if ((ts->selectmode & SCE_SELECT_EDGE) != 0) { + /* Find nearest edge. */ + float dist_max = *ray_dist; + float dist; + BMLoop *l = f->l_first; + for (int i = 0; i < f->len; ++i, l = l->next) { + add_v3_v3v3(co, l->e->v1->co, l->e->v2->co); + mul_v3_fl(co, 0.5f); + mul_m4_v3(obmat, co); + if ((dist = len_manhattan_v3v3(location, co)) < dist_max) { + e = l->e; + dist_max = dist; + } + } + if (e) { + hit = true; + } + } + if ((ts->selectmode & SCE_SELECT_FACE) != 0) { + hit = true; + } + else { + f = NULL; + } + } + + if (!hit) { + if (deselect_all) { + changed = EDBM_mesh_deselect_all_multi(C); + } + } + else { + bool set_v = false; + bool set_e = false; + bool set_f = false; + + if (v) { + wm_xr_select_op_apply(v, bm, XR_SEL_VERTEX, select_op, &changed, &set_v); + } + if (e) { + wm_xr_select_op_apply(e, bm, XR_SEL_EDGE, select_op, &changed, &set_e); + } + if (f) { + wm_xr_select_op_apply(f, bm, XR_SEL_FACE, select_op, &changed, &set_f); + } + + if (set_v || set_e || set_f) { + EDBM_mesh_deselect_all_multi(C); + if (set_v) { + BM_vert_select_set(bm, v, true); + } + if (set_e) { + BM_edge_select_set(bm, e, true); + } + if (set_f) { + BM_face_select_set(bm, f, true); + } + } + } + + if (changed) { + DEG_id_tag_update((ID *)vc.obedit->data, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + } + else if (vc.em) { + if (deselect_all) { + changed = EDBM_mesh_deselect_all_multi(C); + } + + if (changed) { + DEG_id_tag_update((ID *)vc.obedit->data, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + } + else { + if (ob) { + hit = true; + } + + if (!hit) { + if (deselect_all) { + changed = ED_view3d_object_deselect_all_except(vc.view_layer, NULL); + } + } + else { + Base *base = BKE_view_layer_base_find(vc.view_layer, DEG_get_original_object(ob)); + if (base && BASE_SELECTABLE(vc.v3d, base)) { + bool set = false; + wm_xr_select_op_apply(base, NULL, XR_SEL_BASE, select_op, &changed, &set); + if (set) { + ED_view3d_object_deselect_all_except(vc.view_layer, base); + } + } + } + + if (changed) { + DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + } + } + + return changed; +} + +static int wm_xr_select_raycast_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wm_xr_raycast_init(op); + + const int retval = op->type->modal(C, op, event); + + if ((retval & OPERATOR_RUNNING_MODAL) != 0) { + WM_event_add_modal_handler(C, op); + } + + return retval; +} + +static int wm_xr_select_raycast_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_select_raycast_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + wm_xr_raycast_update(op, xr, actiondata); + + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: { + XrRaycastData *data = op->customdata; + eSelectOp select_op = SEL_OP_SET; + bool deselect_all, selectable_only; + float ray_dist; + + if (RNA_boolean_get(op->ptr, "toggle")) { + select_op = SEL_OP_XOR; + } + if (RNA_boolean_get(op->ptr, "deselect")) { + select_op = SEL_OP_SUB; + } + if (RNA_boolean_get(op->ptr, "extend")) { + select_op = SEL_OP_ADD; + } + deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + selectable_only = RNA_boolean_get(op->ptr, "selectable_only"); + ray_dist = RNA_float_get(op->ptr, "distance"); + + const bool changed = wm_xr_select_raycast( + C, data->origin, data->direction, &ray_dist, selectable_only, select_op, deselect_all); + + wm_xr_raycast_uninit(op); + + return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + default: + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_raycast_uninit(op); + return OPERATOR_CANCELLED; + } +} + +static void WM_OT_xr_select_raycast(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Raycast Select"; + ot->idname = "WM_OT_xr_select_raycast"; + ot->description = "Raycast select with a VR controller"; + + /* callbacks */ + ot->invoke = wm_xr_select_raycast_invoke; + ot->exec = wm_xr_select_raycast_exec; + ot->modal = wm_xr_select_raycast_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_mouse_select(ot); + + /* Override "deselect_all" default value. */ + PropertyRNA *prop = RNA_struct_type_find_property(ot->srna, "deselect_all"); + BLI_assert(prop != NULL); + RNA_def_property_boolean_default(prop, true); + + RNA_def_boolean(ot->srna, + "selectable_only", + true, + "Selectable Only", + "Only allow selectable objects to influence raycast result"); + RNA_def_float(ot->srna, + "distance", + BVH_RAYCAST_DIST_MAX, + 0.0, + BVH_RAYCAST_DIST_MAX, + "", + "Maximum raycast distance", + 0.0, + BVH_RAYCAST_DIST_MAX); + RNA_def_boolean( + ot->srna, "from_viewer", false, "From Viewer", "Use viewer pose as raycast origin"); + RNA_def_float_vector(ot->srna, + "axis", + 3, + g_xr_default_raycast_axis, + -1.0f, + 1.0f, + "Axis", + "Raycast axis in controller/viewer space", + -1.0f, + 1.0f); + RNA_def_float_color(ot->srna, + "color", + 4, + g_xr_default_raycast_color, + 0.0f, + 1.0f, + "Color", + "Raycast color", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Transform Grab + * + * Transforms selected objects relative to an XR controller's pose. + * \{ */ + +static int wm_xr_transform_grab_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + bool loc_lock, rot_lock, scale_lock; + float loc_t, rot_t, loc_ofs_orig[3], rot_ofs_orig[4]; + bool loc_ofs_set = false; + bool rot_ofs_set = false; + + loc_lock = RNA_boolean_get(op->ptr, "location_lock"); + if (!loc_lock) { + loc_t = RNA_float_get(op->ptr, "location_interpolation"); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "location_offset"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, loc_ofs_orig); + loc_ofs_set = true; + } + } + + rot_lock = RNA_boolean_get(op->ptr, "rotation_lock"); + if (!rot_lock) { + rot_t = RNA_float_get(op->ptr, "rotation_interpolation"); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "rotation_offset"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + float eul[3]; + RNA_property_float_get_array(op->ptr, prop, eul); + eul_to_quat(rot_ofs_orig, eul); + normalize_qt(rot_ofs_orig); + rot_ofs_set = true; + } + } + + scale_lock = RNA_boolean_get(op->ptr, "scale_lock"); + + if (loc_lock && rot_lock && scale_lock) { + return OPERATOR_CANCELLED; + } + + const wmXrActionData *actiondata = event->customdata; + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = (obedit && (obedit->type == OB_MESH)) ? BKE_editmesh_from_object(obedit) : NULL; + bool selected = false; + + if (em) { /* TODO_XR: Non-mesh objects. */ + /* Check for selection. */ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + BMesh *bm = em->bm; + BMIter iter; + if ((ts->selectmode & SCE_SELECT_FACE) != 0) { + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + selected = true; + break; + } + } + } + if (!selected) { + if ((ts->selectmode & SCE_SELECT_EDGE) != 0) { + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + selected = true; + break; + } + } + } + if (!selected) { + if ((ts->selectmode & SCE_SELECT_VERTEX) != 0) { + BMVert *v; + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + selected = true; + break; + } + } + } + } + } + } + else { + float controller_loc[3], controller_rot[4], controller_mat[4][4]; + float loc_ofs[3], loc_ofs_controller[3], rot_ofs[4], rot_ofs_controller[4], + rot_ofs_orig_inv[4]; + float q0[4], q1[4], q2[4], m0[4][4], m1[4][4], m2[4][4]; + + quat_to_mat4(controller_mat, actiondata->controller_rot); + copy_v3_v3(controller_mat[3], actiondata->controller_loc); + + /* Convert offsets to controller space. */ + if (loc_ofs_set) { + copy_v3_v3(loc_ofs_controller, loc_ofs_orig); + mul_qt_v3(actiondata->controller_rot, loc_ofs_controller); + } + if (rot_ofs_set) { + invert_qt_qt_normalized(rot_ofs_orig_inv, rot_ofs_orig); + mul_qt_qtqt(rot_ofs_controller, actiondata->controller_rot, rot_ofs_orig_inv); + normalize_qt(rot_ofs_controller); + } + + /* Apply interpolation and offsets. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + bool update = false; + + if (ob->parent) { + invert_m4_m4(m0, ob->parentinv); + mul_m4_m4m4(m1, ob->parent->imat, controller_mat); + mul_m4_m4m4(m2, m0, m1); + mat4_to_loc_quat(controller_loc, controller_rot, m2); + + if (loc_ofs_set) { + copy_v3_v3(loc_ofs, loc_ofs_orig); + mul_qt_v3(controller_rot, loc_ofs); + } + if (rot_ofs_set) { + mul_qt_qtqt(rot_ofs, controller_rot, rot_ofs_orig_inv); + normalize_qt(rot_ofs); + } + } + else { + copy_v3_v3(controller_loc, actiondata->controller_loc); + copy_qt_qt(controller_rot, actiondata->controller_rot); + + if (loc_ofs_set) { + copy_v3_v3(loc_ofs, loc_ofs_controller); + } + if (rot_ofs_set) { + copy_qt_qt(rot_ofs, rot_ofs_controller); + } + } + + if (!loc_lock) { + if (loc_t > 0.0f) { + ob->loc[0] += loc_t * (controller_loc[0] - ob->loc[0]); + ob->loc[1] += loc_t * (controller_loc[1] - ob->loc[1]); + ob->loc[2] += loc_t * (controller_loc[2] - ob->loc[2]); + update = true; + } + if (loc_ofs_set) { + add_v3_v3(ob->loc, loc_ofs); + update = true; + } + } + + if (!rot_lock) { + if (rot_t > 0.0f) { + eul_to_quat(q1, ob->rot); + interp_qt_qtqt(q0, q1, controller_rot, rot_t); + if (!rot_ofs_set) { + quat_to_eul(ob->rot, q0); + } + update = true; + } + else if (rot_ofs_set) { + eul_to_quat(q0, ob->rot); + } + if (rot_ofs_set) { + rotation_between_quats_to_quat(q1, rot_ofs, q0); + mul_qt_qtqt(q0, rot_ofs, q1); + normalize_qt(q0); + mul_qt_qtqt(q2, controller_rot, q1); + normalize_qt(q2); + rotation_between_quats_to_quat(q1, q0, q2); + + mul_qt_qtqt(q2, q0, q1); + normalize_qt(q2); + quat_to_eul(ob->rot, q2); + update = true; + } + } + + if (update) { + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + + selected = true; + } + CTX_DATA_END; + } + + if (!selected) { + return OPERATOR_CANCELLED; + } + + wm_xr_grab_init(op); + wm_xr_grab_update(op, actiondata); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_transform_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static void wm_xr_transform_grab_apply(bContext *C, + Scene *scene, + Object *obedit, + BMEditMesh *em, + const wmXrActionData *actiondata, + const XrGrabData *data, + bool bimanual, + bool apply_transform, + bool *r_selected) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + wmWindowManager *wm = CTX_wm_manager(C); + bScreen *screen_anim = ED_screen_animation_playing(wm); + float delta[4][4]; + + if (em) { /* TODO_XR: Non-mesh objects. */ + if (apply_transform) { + ToolSettings *ts = scene->toolsettings; + BMesh *bm = em->bm; + BMIter iter; + + if (bimanual) { + wm_xr_grab_compute_bimanual(actiondata, data, NULL, NULL, obedit->imat, false, delta); + } + else { + wm_xr_grab_compute(actiondata, data, NULL, NULL, obedit->imat, false, delta); + } + + if ((ts->selectmode & SCE_SELECT_VERTEX) != 0) { + BMVert *v; + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT) && + !BM_elem_flag_test(v, BM_ELEM_INTERNAL_TAG)) { + mul_m4_v3(delta, v->co); + BM_elem_flag_enable(v, BM_ELEM_INTERNAL_TAG); + } + } + } + if ((ts->selectmode & SCE_SELECT_EDGE) != 0) { + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + if (!BM_elem_flag_test(e->v1, BM_ELEM_INTERNAL_TAG)) { + mul_m4_v3(delta, e->v1->co); + BM_elem_flag_enable(e->v1, BM_ELEM_INTERNAL_TAG); + } + if (!BM_elem_flag_test(e->v2, BM_ELEM_INTERNAL_TAG)) { + mul_m4_v3(delta, e->v2->co); + BM_elem_flag_enable(e->v2, BM_ELEM_INTERNAL_TAG); + } + } + } + } + if ((ts->selectmode & SCE_SELECT_FACE) != 0) { + BMFace *f; + BMLoop *l; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + l = f->l_first; + for (int i = 0; i < f->len; ++i, l = l->next) { + if (!BM_elem_flag_test(l->v, BM_ELEM_INTERNAL_TAG)) { + mul_m4_v3(delta, l->v->co); + BM_elem_flag_enable(l->v, BM_ELEM_INTERNAL_TAG); + } + } + } + } + } + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_INTERNAL_TAG, false); + EDBM_mesh_normals_update(em); + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + } + + *r_selected = true; + } + else { + float out[4][4], m0[4][4], m1[4][4]; + + if (apply_transform) { + if (bimanual) { + wm_xr_grab_compute_bimanual(actiondata, data, NULL, NULL, NULL, false, delta); + } + else { + wm_xr_grab_compute(actiondata, data, NULL, NULL, NULL, false, delta); + } + } + + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (apply_transform) { + mul_m4_m4m4(out, delta, ob->obmat); + + if (ob->parent) { + invert_m4_m4(m0, ob->parentinv); + mul_m4_m4m4(m1, ob->parent->imat, out); + mul_m4_m4m4(out, m0, m1); + } + + if (!data->loc_lock) { + copy_v3_v3(ob->loc, out[3]); + } + if (!data->rot_lock) { + mat4_to_eul(ob->rot, out); + } + if (!data->scale_lock && bimanual) { + mat4_to_size(ob->scale, out); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + + if (screen_anim && autokeyframe_cfra_can_key(scene, &ob->id)) { + wm_xr_mocap_object_autokey(C, scene, view_layer, NULL, ob, true); + } + + *r_selected = true; + } + CTX_DATA_END; + } +} + +static int wm_xr_transform_grab_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + XrGrabData *data = op->customdata; + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = (obedit && (obedit->type == OB_MESH)) ? BKE_editmesh_from_object(obedit) : NULL; + bool apply_transform = false; + bool selected = false; + + const bool do_bimanual = wm_xr_grab_can_do_bimanual(actiondata, data); + + data->loc_lock = RNA_boolean_get(op->ptr, "location_lock"); + data->rot_lock = RNA_boolean_get(op->ptr, "rotation_lock"); + data->scale_lock = RNA_boolean_get(op->ptr, "scale_lock"); + + /* Check if navigation is locked. */ + if (!wm_xr_grab_is_locked(data, do_bimanual)) { + /* Prevent unwanted snapping (i.e. "jumpy" transform changes when transitioning from + * two-handed to one-handed interaction) at the end of a bimanual interaction. */ + if (!wm_xr_grab_is_bimanual_ending(actiondata, data)) { + apply_transform = true; + } + } + + wm_xr_transform_grab_apply( + C, scene, obedit, em, actiondata, data, do_bimanual, apply_transform, &selected); + + wm_xr_grab_update(op, actiondata); + + if (!selected || (event->val == KM_RELEASE)) { + wm_xr_grab_uninit(op); + + if (obedit && em) { + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + } + else { + WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, scene); + } + return OPERATOR_FINISHED; + } + else if (event->val == KM_PRESS) { + return OPERATOR_RUNNING_MODAL; + } + + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_grab_uninit(op); + return OPERATOR_CANCELLED; +} + +static void WM_OT_xr_transform_grab(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Transform Grab"; + ot->idname = "WM_OT_xr_transform_grab"; + ot->description = "Transform selected objects relative to a VR controller's pose"; + + /* callbacks */ + ot->invoke = wm_xr_transform_grab_invoke; + ot->exec = wm_xr_transform_grab_exec; + ot->modal = wm_xr_transform_grab_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + static const float default_offset[3] = {0}; + + RNA_def_boolean( + ot->srna, "location_lock", false, "Lock Location", "Preserve objects' original location"); + RNA_def_float(ot->srna, + "location_interpolation", + 0.0f, + 0.0f, + 1.0f, + "Location Interpolation", + "Interpolation factor between object and controller locations", + 0.0f, + 1.0f); + RNA_def_float_translation(ot->srna, + "location_offset", + 3, + default_offset, + -FLT_MAX, + FLT_MAX, + "Location Offset", + "Additional location offset in controller space", + -FLT_MAX, + FLT_MAX); + RNA_def_boolean( + ot->srna, "rotation_lock", false, "Lock Rotation", "Preserve objects' original rotation"); + RNA_def_float(ot->srna, + "rotation_interpolation", + 0.0f, + 0.0f, + 1.0f, + "Rotation Interpolation", + "Interpolation factor between object and controller rotations", + 0.0f, + 1.0f); + RNA_def_float_rotation(ot->srna, + "rotation_offset", + 3, + default_offset, + -2 * M_PI, + 2 * M_PI, + "Rotation Offset", + "Additional rotation offset in controller space", + -2 * M_PI, + 2 * M_PI); + RNA_def_boolean(ot->srna, "scale_lock", false, "Lock Scale", "Preserve objects' original scale"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Operator Registration * \{ */ @@ -1519,6 +2383,8 @@ void wm_xr_operatortypes_register(void) WM_operatortype_append(WM_OT_xr_navigation_fly); WM_operatortype_append(WM_OT_xr_navigation_teleport); WM_operatortype_append(WM_OT_xr_navigation_reset); + WM_operatortype_append(WM_OT_xr_select_raycast); + WM_operatortype_append(WM_OT_xr_transform_grab); } /** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index a4d2a65830f..9f511bc2fda 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -43,6 +43,9 @@ #include "wm_window.h" #include "wm_xr_intern.h" +/* OpenXR user path identifying the headset. Used for motion capture objects. */ +#define XR_HEADSET_PATH "/user/head" + static wmSurface *g_xr_surface = NULL; static CLG_LogRef LOG = {"wm.xr"}; @@ -67,24 +70,27 @@ static void wm_xr_session_create_cb(void) settings->base_scale = 1.0f; } state->prev_base_scale = settings->base_scale; + + /* Store motion capture object poses. */ + wm_xr_mocap_orig_poses_store(settings, state); } -static void wm_xr_session_controller_data_free(wmXrSessionState *state) +static void wm_xr_session_controller_data_free(ListBase *controllers) { - ListBase *lb = &state->controllers; wmXrController *c; - - while ((c = BLI_pophead(lb))) { + while ((c = BLI_pophead(controllers))) { if (c->model) { GPU_batch_discard(c->model); } - BLI_freelinkN(lb, c); + BLI_freelinkN(controllers, c); } } void wm_xr_session_data_free(wmXrSessionState *state) { - wm_xr_session_controller_data_free(state); + wm_xr_session_controller_data_free(&state->controllers); + wm_xr_session_controller_data_free(&state->trackers); + BLI_freelistN(&state->mocap_orig_poses); } static void wm_xr_session_exit_cb(void *customdata) @@ -96,6 +102,9 @@ static void wm_xr_session_exit_cb(void *customdata) xr_data->runtime->session_state.is_started = false; + /* Restore motion capture object poses. */ + wm_xr_mocap_orig_poses_restore(&xr_data->runtime->session_state, &xr_data->session_settings); + if (xr_data->runtime->exit_fn) { xr_data->runtime->exit_fn(xr_data); } @@ -499,6 +508,48 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, return true; } +bool WM_xr_session_state_tracker_location_get(const wmXrData *xr, + const char *subaction_path, + float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + zero_v3(r_location); + return false; + } + + const wmXrController *tracker = BLI_findstring(&xr->runtime->session_state.trackers, + subaction_path, + offsetof(wmXrController, subaction_path)); + if (!tracker) { + zero_v3(r_location); + return false; + } + + copy_v3_v3(r_location, tracker->grip_pose.position); + return true; +} + +bool WM_xr_session_state_tracker_rotation_get(const wmXrData *xr, + const char *subaction_path, + float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + unit_qt(r_rotation); + return false; + } + + const wmXrController *tracker = BLI_findstring(&xr->runtime->session_state.trackers, + subaction_path, + offsetof(wmXrController, subaction_path)); + if (!tracker) { + unit_qt(r_rotation); + return false; + } + + copy_qt_qt(r_rotation, tracker->grip_pose.orientation_quat); + return true; +} + bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { @@ -606,16 +657,80 @@ static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat); } -static void wm_xr_session_controller_data_update(const XrSessionSettings *settings, - const wmXrAction *grip_action, +static void wm_xr_session_controller_data_update(const wmXrAction *grip_action, const wmXrAction *aim_action, + unsigned int subaction_idx, + const float view_ofs[3], + const float base_mat[4][4], + const float nav_mat[4][4], + bContext *C, + XrSessionSettings *settings, GHOST_XrContextHandle xr_context, - wmXrSessionState *state) + Scene *scene, + ViewLayer *view_layer, + wmWindow *win, + bScreen *screen_anim, + bool *notify, + wmXrController *controller) +{ + wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx], + view_ofs, + base_mat, + nav_mat, + &controller->grip_pose, + controller->grip_mat, + controller->grip_mat_base); + wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], + view_ofs, + base_mat, + nav_mat, + &controller->aim_pose, + controller->aim_mat, + controller->aim_mat_base); + + /* Update motion capture objects. */ + wm_xr_mocap_objects_update(controller->subaction_path, + &controller->grip_pose, + C, + settings, + scene, + view_layer, + win, + screen_anim, + notify); + + /* Update controller model. */ + if (!controller->model) { + /* Notify GHOST to load/continue loading the controller model data. This can be called more + * than once since the model may not be available from the runtime yet. The batch itself will + * be created in wm_xr_draw_controllers(). */ + GHOST_XrLoadControllerModel(xr_context, controller->subaction_path); + } + else { + GHOST_XrUpdateControllerModelComponents(xr_context, controller->subaction_path); + } +} + +static void wm_xr_session_controller_and_tracker_data_update(const wmXrAction *grip_action, + const wmXrAction *aim_action, + const ListBase *tracker_actions, + bContext *C, + XrSessionSettings *settings, + GHOST_XrContextHandle xr_context, + wmXrSessionState *state, + wmWindow *win) { BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers)); + BLI_assert(BLI_listbase_count(tracker_actions) <= BLI_listbase_count(&state->trackers)); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + wmWindowManager *wm = CTX_wm_manager(C); + bScreen *screen_anim = ED_screen_animation_playing(wm); + bool notify = true; unsigned int subaction_idx = 0; + wmXrController *tracker = state->trackers.first; float view_ofs[3], base_mat[4][4], nav_mat[4][4]; if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { @@ -631,30 +746,47 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin wm_xr_pose_scale_to_mat(&state->prev_base_pose, state->prev_base_scale, base_mat); wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, nav_mat); + /* Update controllers. */ LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) { - wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx], - view_ofs, - base_mat, - nav_mat, - &controller->grip_pose, - controller->grip_mat, - controller->grip_mat_base); - wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], - view_ofs, - base_mat, - nav_mat, - &controller->aim_pose, - controller->aim_mat, - controller->aim_mat_base); - - if (!controller->model) { - /* Notify GHOST to load/continue loading the controller model data. This can be called more - * than once since the model may not be available from the runtime yet. The batch itself will - * be created in wm_xr_draw_controllers(). */ - GHOST_XrLoadControllerModel(xr_context, controller->subaction_path); - } - else { - GHOST_XrUpdateControllerModelComponents(xr_context, controller->subaction_path); + wm_xr_session_controller_data_update(grip_action, + aim_action, + subaction_idx, + view_ofs, + base_mat, + nav_mat, + C, + settings, + xr_context, + scene, + view_layer, + win, + screen_anim, + ¬ify, + controller); + } + + /* Update trackers. */ + LISTBASE_FOREACH (LinkData *, ld, tracker_actions) { + const wmXrAction *tracker_action = ld->data; + + for (subaction_idx = 0; subaction_idx < tracker_action->count_subaction_paths; + ++subaction_idx) { + wm_xr_session_controller_data_update(tracker_action, + tracker_action, + subaction_idx, + view_ofs, + base_mat, + nav_mat, + C, + settings, + xr_context, + scene, + view_layer, + win, + screen_anim, + ¬ify, + tracker); + tracker = tracker->next; } } } @@ -1165,8 +1297,9 @@ static void wm_xr_session_events_dispatch(wmXrData *xr, MEM_freeN(actions); } -void wm_xr_session_actions_update(wmWindowManager *wm) +void wm_xr_session_actions_update(const bContext *C) { + wmWindowManager *wm = CTX_wm_manager(C); wmXrData *xr = &wm->xr; if (!xr->runtime) { return; @@ -1175,6 +1308,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) XrSessionSettings *settings = &xr->session_settings; GHOST_XrContextHandle xr_context = xr->runtime->context; wmXrSessionState *state = &xr->runtime->session_state; + wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); if (state->is_navigation_dirty) { memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev)); @@ -1198,6 +1332,23 @@ void wm_xr_session_actions_update(wmWindowManager *wm) } wmXrActionSet *active_action_set = state->active_action_set; + /* Update headset motion capture objects. */ + { + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + bScreen *screen_anim = ED_screen_animation_playing(wm); + bool notify = true; + wm_xr_mocap_objects_update(XR_HEADSET_PATH, + &state->viewer_pose, + (bContext *)C, + settings, + scene, + view_layer, + win, + screen_anim, + ¬ify); + } + const bool synced = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); if (!synced) { @@ -1206,14 +1357,16 @@ void wm_xr_session_actions_update(wmWindowManager *wm) /* Only update controller data and dispatch events for active action set. */ if (active_action_set) { - wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); - - if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { - wm_xr_session_controller_data_update(settings, - active_action_set->controller_grip_action, - active_action_set->controller_aim_action, - xr_context, - state); + if ((active_action_set->controller_grip_action && active_action_set->controller_aim_action) || + !BLI_listbase_is_empty(&active_action_set->tracker_actions)) { + wm_xr_session_controller_and_tracker_data_update(active_action_set->controller_grip_action, + active_action_set->controller_aim_action, + &active_action_set->tracker_actions, + (bContext *)C, + settings, + xr_context, + state, + win); } if (win) { @@ -1240,15 +1393,13 @@ void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, wmXrSessionState *state = &xr->runtime->session_state; ListBase *controllers = &state->controllers; - BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); const unsigned int count = grip_action->count_subaction_paths; - wm_xr_session_controller_data_free(state); + wm_xr_session_controller_data_free(controllers); for (unsigned int i = 0; i < count; ++i) { wmXrController *controller = MEM_callocN(sizeof(*controller), __func__); - BLI_assert(STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])); strcpy(controller->subaction_path, grip_action->subaction_paths[i]); @@ -1269,7 +1420,7 @@ void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, void wm_xr_session_controller_data_clear(wmXrSessionState *state) { - wm_xr_session_controller_data_free(state); + wm_xr_session_controller_data_free(&state->controllers); /* Deactivate draw callback. */ if (g_xr_surface) { @@ -1283,6 +1434,53 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state) } } +void wm_xr_session_tracker_data_populate(const ListBase *tracker_actions, wmXrData *xr) +{ + wmXrSessionState *state = &xr->runtime->session_state; + ListBase *trackers = &state->trackers; + + wm_xr_session_controller_data_free(trackers); + + LISTBASE_FOREACH (const LinkData *, ld, tracker_actions) { + const wmXrAction *tracker_action = ld->data; + + for (unsigned int subaction_idx = 0; subaction_idx < tracker_action->count_subaction_paths; + ++subaction_idx) { + wmXrController *tracker = MEM_callocN(sizeof(*tracker), __func__); + strcpy(tracker->subaction_path, tracker_action->subaction_paths[subaction_idx]); + + BLI_addtail(trackers, tracker); + } + } + + /* Activate draw callback. */ + if (g_xr_surface) { + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + if (surface_data && !surface_data->tracker_draw_handle) { + if (surface_data->controller_art) { + surface_data->tracker_draw_handle = ED_region_draw_cb_activate( + surface_data->controller_art, wm_xr_draw_trackers, xr, REGION_DRAW_POST_VIEW); + } + } + } +} + +void wm_xr_session_tracker_data_clear(wmXrSessionState *state) +{ + wm_xr_session_controller_data_free(&state->trackers); + + /* Deactivate draw callback. */ + if (g_xr_surface) { + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + if (surface_data && surface_data->tracker_draw_handle) { + if (surface_data->controller_art) { + ED_region_draw_cb_exit(surface_data->controller_art, surface_data->tracker_draw_handle); + } + surface_data->tracker_draw_handle = NULL; + } + } +} + /** \} */ /* XR-Session Actions */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/xr/wm_xr.h b/source/blender/windowmanager/xr/wm_xr.h index 5eb17ceac4b..57adb4ff3c0 100644 --- a/source/blender/windowmanager/xr/wm_xr.h +++ b/source/blender/windowmanager/xr/wm_xr.h @@ -6,6 +6,7 @@ #pragma once +struct bContext; struct wmWindowManager; struct wmXrData; @@ -16,7 +17,7 @@ typedef void (*wmXrSessionExitFn)(const wmXrData *xr_data); bool wm_xr_init(wmWindowManager *wm); void wm_xr_exit(wmWindowManager *wm); void wm_xr_session_toggle(wmWindowManager *wm, wmWindow *win, wmXrSessionExitFn session_exit_fn); -bool wm_xr_events_handle(wmWindowManager *wm); +bool wm_xr_events_handle(const struct bContext *C); /* wm_xr_operators.c */ |