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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenlib/BLI_math_geom.h6
-rw-r--r--source/blender/blenlib/intern/math_geom.c17
-rw-r--r--source/blender/blenloader/intern/readfile.c4
-rw-r--r--source/blender/blenloader/intern/versioning_300.c8
-rw-r--r--source/blender/draw/engines/overlay/overlay_grid.c9
-rw-r--r--source/blender/draw/intern/draw_manager.c62
-rw-r--r--source/blender/draw/intern/draw_manager_data.c8
-rw-r--r--source/blender/editors/include/ED_space_api.h3
-rw-r--r--source/blender/editors/include/ED_transform.h15
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h2
-rw-r--r--source/blender/editors/include/ED_view3d.h18
-rw-r--r--source/blender/editors/include/ED_view3d_offscreen.h3
-rw-r--r--source/blender/editors/include/UI_interface.h1
-rw-r--r--source/blender/editors/interface/CMakeLists.txt3
-rw-r--r--source/blender/editors/interface/interface_templates.c38
-rw-r--r--source/blender/editors/space_api/spacetypes.c14
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c74
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c56
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c42
-rw-r--r--source/blender/editors/transform/CMakeLists.txt3
-rw-r--r--source/blender/editors/transform/transform_convert.c4
-rw-r--r--source/blender/editors/transform/transform_convert.h1
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c2
-rw-r--r--source/blender/editors/transform/transform_convert_object.c14
-rw-r--r--source/blender/editors/transform/transform_ops.c120
-rw-r--r--source/blender/editors/transform/transform_snap_object.c10
-rw-r--r--source/blender/makesdna/DNA_screen_types.h5
-rw-r--r--source/blender/makesdna/DNA_view3d_enums.h3
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h3
-rw-r--r--source/blender/makesdna/DNA_xr_types.h114
-rw-r--r--source/blender/makesrna/intern/rna_screen.c1
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_wm.c332
-rw-r--r--source/blender/makesrna/intern/rna_xr.c694
-rw-r--r--source/blender/windowmanager/CMakeLists.txt4
-rw-r--r--source/blender/windowmanager/WM_api.h87
-rw-r--r--source/blender/windowmanager/WM_types.h54
-rw-r--r--source/blender/windowmanager/intern/wm.c122
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c69
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c120
-rw-r--r--source/blender/windowmanager/intern/wm_files.c12
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c138
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c88
-rw-r--r--source/blender/windowmanager/intern/wm_window.c2
-rw-r--r--source/blender/windowmanager/wm_event_system.h7
-rw-r--r--source/blender/windowmanager/wm_event_types.h5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c11
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actionmap.c348
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c255
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h109
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_mocap.c267
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_operators.c2492
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c894
-rw-r--r--source/blender/windowmanager/xr/wm_xr.h6
-rw-r--r--source/creator/creator.c4
56 files changed, 6339 insertions, 454 deletions
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index bcda25ca533..d2a19813f5a 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -717,6 +717,12 @@ void map_to_plane_axis_angle_v2_v3v3fl(float r_co[2],
const float co[3],
const float axis[3],
const float angle);
+void map_to_pixel(int r_co[2],
+ const float co[3],
+ const float viewmat[4][4],
+ const float winmat[4][4],
+ int winx,
+ int winy);
/********************************** Normals **********************************/
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 8afb6b5a2be..64bf9e92270 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -5277,6 +5277,23 @@ void map_to_plane_axis_angle_v2_v3v3fl(float r_co[2],
copy_v2_v2(r_co, tmp);
}
+void map_to_pixel(int r_co[2],
+ const float co[3],
+ const float viewmat[4][4],
+ const float winmat[4][4],
+ int winx,
+ int winy)
+{
+ float persmat[4][4];
+ float vec[3];
+
+ mul_m4_m4m4(persmat, winmat, viewmat);
+ mul_v3_project_m4_v3(vec, persmat, co);
+
+ r_co[0] = (int)(((float)winx / 2.0f) * (1.0f + vec[0]));
+ r_co[1] = (int)(((float)winy / 2.0f) * (1.0f + vec[1]));
+}
+
/********************************* Normals **********************************/
void accumulate_vertex_normals_tri_v3(float n1[3],
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 3ee5e41bf1f..cb3e81ba6d5 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2489,6 +2489,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/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 95fa3058931..323391d9715 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -1189,6 +1189,14 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 7)) {
+ LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
+ wm->xr.session_settings.draw_flags |= (V3D_OFSDRAW_SHOW_SELECTION |
+ V3D_OFSDRAW_XR_SHOW_CONTROLLERS |
+ V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS);
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(bmain, 300, 8)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->master_collection != NULL) {
diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c
index 31c8ed9d664..cc8ab00baef 100644
--- a/source/blender/draw/engines/overlay/overlay_grid.c
+++ b/source/blender/draw/engines/overlay/overlay_grid.c
@@ -200,6 +200,15 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
shd->grid_distance = dist / 2.0f;
ED_view3d_grid_steps(scene, v3d, rv3d, shd->grid_steps);
+
+ if (((v3d->flag & V3D_XR_SESSION_SURFACE) != 0) || ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0)) {
+ /* The calculations for the grid parameters assume that the view matrix has no scale component,
+ * which may not be correct if the user is "shrunk" or "enlarged" by zooming in or out.
+ * Therefore, we need to compensate the values here. */
+ float viewinvscale = len_v3(
+ viewinv[0]); /* Assumption is uniform scaling (all column vectors are of same length). */
+ shd->grid_distance *= viewinvscale;
+ }
}
void OVERLAY_grid_cache_init(OVERLAY_Data *vedata)
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index e65fdce5f2e..1d63db80a0e 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -51,6 +51,7 @@
#include "BKE_pbvh.h"
#include "BKE_pointcache.h"
#include "BKE_pointcloud.h"
+#include "BKE_screen.h"
#include "BKE_volume.h"
#include "DNA_camera_types.h"
@@ -1402,6 +1403,27 @@ void DRW_draw_callbacks_post_scene(void)
ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW);
+#ifdef WITH_XR_OPENXR
+ /* XR callbacks (controllers, custom draw functions). */
+ if ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0) {
+ if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) {
+ ARegionType *art = WM_xr_surface_controller_region_type_get();
+ if (art) {
+ ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
+ }
+ }
+ if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
+ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ if (st) {
+ ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
+ if (art) {
+ ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
+ }
+ }
+ }
+ }
+#endif
+
/* Callback can be nasty and do whatever they want with the state.
* Don't trust them! */
DRW_state_reset();
@@ -1448,6 +1470,46 @@ void DRW_draw_callbacks_post_scene(void)
ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
+
+#ifdef WITH_XR_OPENXR
+ if ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0) {
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+
+ DRW_state_reset();
+
+ GPU_framebuffer_bind(dfbl->overlay_fb);
+
+ GPU_matrix_projection_set(rv3d->winmat);
+ GPU_matrix_set(rv3d->viewmat);
+
+ /* Callbacks (controllers, custom draw functions). */
+ if (((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) ||
+ ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0)) {
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_apply_state();
+
+ if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) {
+ ARegionType *art = WM_xr_surface_controller_region_type_get();
+ if (art) {
+ ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
+ }
+ }
+ if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
+ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ if (st) {
+ ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
+ if (art) {
+ ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
+ }
+ }
+ }
+
+ DRW_state_reset();
+ }
+
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ }
+#endif
}
}
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index af331c86a8b..171c1e8154e 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -1682,10 +1682,12 @@ static void draw_frustum_bound_sphere_calc(const BoundBox *bbox,
bsphere->center[0] = farcenter[0] * z / e;
bsphere->center[1] = farcenter[1] * z / e;
bsphere->center[2] = z;
- bsphere->radius = len_v3v3(bsphere->center, farpoint);
- /* Transform to world space. */
- mul_m4_v3(viewinv, bsphere->center);
+ /* For XR, the view matrix may contain a scale factor. Then, transforming only the center
+ * into world space after calculating the radius will result in incorrect behavior. */
+ mul_m4_v3(viewinv, bsphere->center); /* Transform to world space. */
+ mul_m4_v3(viewinv, farpoint);
+ bsphere->radius = len_v3v3(bsphere->center, farpoint);
}
}
diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h
index 1a3aa7e5496..b43829c77a3 100644
--- a/source/blender/editors/include/ED_space_api.h
+++ b/source/blender/editors/include/ED_space_api.h
@@ -72,7 +72,8 @@ void *ED_region_draw_cb_activate(struct ARegionType *art,
void (*draw)(const struct bContext *, struct ARegion *, void *),
void *customdata,
int type);
-void ED_region_draw_cb_draw(const struct bContext *, struct ARegion *, int);
+void ED_region_draw_cb_draw(const struct bContext *C, struct ARegion *region, int type);
+void ED_region_surface_draw_cb_draw(struct ARegionType *art, int type);
void ED_region_draw_cb_exit(struct ARegionType *, void *);
void ED_region_draw_cb_remove_by_type(struct ARegionType *art,
void *draw_fn,
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index bedd0e2fa35..c29d477f71e 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -31,8 +31,10 @@ extern "C" {
struct Object;
struct bContext;
+struct ViewLayer;
struct wmKeyConfig;
struct wmOperatorType;
+struct wmTimer;
void ED_keymap_transform(struct wmKeyConfig *keyconf);
void transform_operatortypes(void);
@@ -193,6 +195,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 Object *ob);
+
+/* 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);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index 42e73bbf744..8938e1114d9 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -44,6 +44,8 @@ typedef enum {
SNAP_NOT_SELECTED = 1,
SNAP_NOT_ACTIVE = 2,
SNAP_ONLY_ACTIVE = 3,
+ SNAP_SELECTED = 4,
+ SNAP_SELECTABLE = 5,
} eSnapSelect;
typedef enum {
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index cf8dcbd7995..9076781bce5 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -595,6 +595,7 @@ void ED_view3d_viewcontext_init(struct bContext *C,
void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact);
void view3d_operator_needs_opengl(const struct bContext *C);
void view3d_region_operator_needs_opengl(struct wmWindow *win, struct ARegion *region);
+bool object_deselect_all_except(struct ViewLayer *view_layer, struct Base *b);
/* XXX should move to BLI_math */
bool edge_inside_circle(const float cent[2],
@@ -776,12 +777,27 @@ void ED_view3d_buttons_region_layout_ex(const struct bContext *C,
bool ED_view3d_local_collections_set(struct Main *bmain, struct View3D *v3d);
void ED_view3d_local_collections_reset(struct bContext *C, const bool reset_all);
+void ED_view3d_view_params_get(const struct View3D *v3d,
+ const struct RegionView3D *rv3d,
+ float *r_lens,
+ float *r_clip_start,
+ float *r_clip_end,
+ float r_viewmat[4][4]);
+void ED_view3d_view_params_set(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct View3D *v3d,
+ struct ARegion *region,
+ const float lens,
+ const float clip_start,
+ const float clip_end,
+ const float viewmat[4][4]);
+
#ifdef WITH_XR_OPENXR
void ED_view3d_xr_mirror_update(const struct ScrArea *area,
const struct View3D *v3d,
const bool enable);
void ED_view3d_xr_shading_update(struct wmWindowManager *wm,
- const View3D *v3d,
+ const struct View3D *v3d,
const struct Scene *scene);
bool ED_view3d_is_region_xr_mirror_active(const struct wmWindowManager *wm,
const struct View3D *v3d,
diff --git a/source/blender/editors/include/ED_view3d_offscreen.h b/source/blender/editors/include/ED_view3d_offscreen.h
index c490e96031f..8b695e61a35 100644
--- a/source/blender/editors/include/ED_view3d_offscreen.h
+++ b/source/blender/editors/include/ED_view3d_offscreen.h
@@ -60,7 +60,7 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph,
void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph,
struct Scene *scene,
struct View3DShading *shading_override,
- int drawtype,
+ eDrawType drawtype,
int winx,
int winy,
unsigned int draw_flags,
@@ -68,6 +68,7 @@ void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph,
const float winmat[4][4],
float clip_start,
float clip_end,
+ bool is_xr_surface,
bool is_image_render,
bool draw_background,
const char *viewname,
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index f642895f64e..59d688ad2d0 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2192,6 +2192,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 8fcc704a301..f27ff9694c2 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -113,5 +113,8 @@ 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 320371ad9ea..acfac544ed3 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -6236,6 +6236,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 *ami_p, void *UNUSED(unused))
+{
+ XrActionMapItem *ami = (XrActionMapItem *)ami_p;
+ WM_xr_actionconfig_update_tag(NULL, ami);
+}
+#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_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 3da283089c5..149067a94fe 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -264,9 +264,9 @@ void ED_region_draw_cb_exit(ARegionType *art, void *handle)
}
}
-void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type)
+static void ed_region_draw_cb_draw(const bContext *C, ARegion *region, ARegionType *art, int type)
{
- LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &region->type->drawcalls) {
+ LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) {
if (rdc->type == type) {
rdc->draw(C, region, rdc->customdata);
@@ -276,6 +276,16 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type)
}
}
+void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type)
+{
+ ed_region_draw_cb_draw(C, region, region->type, type);
+}
+
+void ED_region_surface_draw_cb_draw(ARegionType *art, int type)
+{
+ ed_region_draw_cb_draw(NULL, NULL, art, type);
+}
+
void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*free)(void *))
{
LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) {
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index f68a4d78a00..bedc24a6287 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1798,5 +1798,10 @@ void ED_spacetype_view3d(void)
art = ED_area_type_hud(st->spaceid);
BLI_addhead(&st->regiontypes, art);
+ /* regions: xr */
+ art = MEM_callocN(sizeof(ARegionType), "spacetype view3d xr region");
+ art->regionid = RGN_TYPE_XR;
+ BLI_addhead(&st->regiontypes, art);
+
BKE_spacetype_register(st);
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 86f79718a68..e7be9903b89 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -328,19 +328,44 @@ static void view3d_xr_mirror_setup(const wmWindowManager *wm,
ARegion *region,
const rcti *rect)
{
- RegionView3D *rv3d = region->regiondata;
float viewmat[4][4];
const float lens_old = v3d->lens;
-
- if (!WM_xr_session_state_viewer_pose_matrix_info_get(&wm->xr, viewmat, &v3d->lens)) {
+ const float clip_start_old = v3d->clip_start;
+ const float clip_end_old = v3d->clip_end;
+ /* Here we need to use the viewmat from the selection eye instead of the eye centroid because
+ * this function may be called from a GPU select operation. In that case we need to match the
+ * selection eye's view, which was used to project 3D to 2D, for a correct result. */
+ const bool from_selection_eye = true;
+
+ if (!WM_xr_session_state_viewer_pose_matrix_info_get(
+ &wm->xr, from_selection_eye, viewmat, &v3d->lens, &v3d->clip_start, &v3d->clip_end)) {
/* Can't get info from XR session, use fallback values. */
- copy_m4_m4(viewmat, rv3d->viewmat);
+ copy_m4_m4(viewmat, ((RegionView3D *)region->regiondata)->viewmat);
v3d->lens = lens_old;
+ v3d->clip_start = clip_start_old;
+ v3d->clip_end = clip_end_old;
}
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, NULL, rect);
- /* Reset overridden View3D data */
+ if ((wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) != 0) {
+ v3d->flag2 |= V3D_XR_SHOW_CONTROLLERS;
+ }
+ else {
+ v3d->flag2 &= ~V3D_XR_SHOW_CONTROLLERS;
+ }
+ if ((wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
+ v3d->flag2 |= V3D_XR_SHOW_CUSTOM_OVERLAYS;
+ }
+ else {
+ v3d->flag2 &= ~V3D_XR_SHOW_CUSTOM_OVERLAYS;
+ }
+ /* Hide navigation gizmo. */
+ v3d->gizmo_flag |= V3D_GIZMO_HIDE_NAVIGATE;
+
+ /* Reset overridden View3D data. */
v3d->lens = lens_old;
+ v3d->clip_start = clip_start_old;
+ v3d->clip_end = clip_end_old;
}
#endif /* WITH_XR_OPENXR */
@@ -1750,7 +1775,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
Scene *scene,
View3DShading *shading_override,
- int drawtype,
+ eDrawType drawtype,
int winx,
int winy,
uint draw_flags,
@@ -1758,6 +1783,7 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
const float winmat[4][4],
float clip_start,
float clip_end,
+ bool is_xr_surface,
bool is_image_render,
bool draw_background,
const char *viewname,
@@ -1787,23 +1813,37 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
}
- if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
- v3d.flag2 |= V3D_SHOW_ANNOTATION;
+ if ((draw_flags & ~V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS) == V3D_OFSDRAW_NONE) {
+ v3d.flag2 = V3D_HIDE_OVERLAYS;
}
- if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
- v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
- v3d.grid = 1.0f;
- v3d.gridlines = 16;
- v3d.gridsubdiv = 10;
-
- /* Show grid, disable other overlays (set all available _HIDE_ flags). */
+ else {
+ if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
+ v3d.flag2 |= V3D_SHOW_ANNOTATION;
+ }
+ if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
+ v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
+ v3d.grid = 1.0f;
+ v3d.gridlines = 16;
+ v3d.gridsubdiv = 10;
+ }
+ if (draw_flags & V3D_OFSDRAW_SHOW_SELECTION) {
+ v3d.flag |= V3D_SELECT_OUTLINE;
+ }
+ if (draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) {
+ v3d.flag2 |= V3D_XR_SHOW_CONTROLLERS;
+ }
+ if (draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) {
+ v3d.flag2 |= V3D_XR_SHOW_CUSTOM_OVERLAYS;
+ }
+ /* Disable other overlays (set all available _HIDE_ flags). */
v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES |
V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
v3d.flag |= V3D_HIDE_HELPLINES;
}
- else {
- v3d.flag2 = V3D_HIDE_OVERLAYS;
+
+ if (is_xr_surface) {
+ v3d.flag |= V3D_XR_SESSION_SURFACE;
}
rv3d.persp = RV3D_PERSP;
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 07f1f8a753c..ac5a06d22bb 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -169,7 +169,7 @@ static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d)
}
/* deselect all except b */
-static bool object_deselect_all_except(ViewLayer *view_layer, Base *b)
+bool object_deselect_all_except(ViewLayer *view_layer, Base *b)
{
bool changed = false;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
@@ -2818,6 +2818,57 @@ static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *even
return WM_operator_flag_only_pass_through_on_press(retval, event);
}
+static int view3d_select_invoke_3d(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BLI_assert(event->type == EVT_XR_ACTION);
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ const wmXrActionData *actiondata = event->customdata;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ float lens_prev;
+ float clip_start_prev, clip_end_prev;
+ float viewmat_prev[4][4];
+ int mval[2];
+ int retval;
+
+ /* Since this function is called in a window context, we need to replace the
+ * window view parameters with the XR surface counterparts to get a correct
+ * result for GPU select. */
+ ED_view3d_view_params_get(v3d, rv3d, &lens_prev, &clip_start_prev, &clip_end_prev, viewmat_prev);
+ ED_view3d_view_params_set(depsgraph,
+ scene,
+ v3d,
+ region,
+ actiondata->eye_lens,
+ xr->session_settings.clip_start,
+ xr->session_settings.clip_end,
+ actiondata->eye_viewmat);
+
+ map_to_pixel(mval,
+ actiondata->controller_loc,
+ actiondata->eye_viewmat,
+ rv3d->winmat,
+ region->winx,
+ region->winy);
+
+ RNA_int_set_array(op->ptr, "location", mval);
+
+ retval = view3d_select_exec(C, op);
+
+ /* Restore window view. */
+ ED_view3d_view_params_set(
+ depsgraph, scene, v3d, region, lens_prev, clip_start_prev, clip_end_prev, viewmat_prev);
+
+ return retval;
+}
+
void VIEW3D_OT_select(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -2829,6 +2880,7 @@ void VIEW3D_OT_select(wmOperatorType *ot)
/* api callbacks */
ot->invoke = view3d_select_invoke;
+ ot->invoke_3d = view3d_select_invoke_3d;
ot->exec = view3d_select_exec;
ot->poll = ED_operator_view3d_active;
@@ -3698,8 +3750,10 @@ void VIEW3D_OT_select_box(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_gesture_box_invoke;
+ ot->invoke_3d = WM_gesture_box_invoke_3d;
ot->exec = view3d_box_select_exec;
ot->modal = WM_gesture_box_modal;
+ ot->modal_3d = WM_gesture_box_modal_3d;
ot->poll = view3d_selectable_data;
ot->cancel = WM_gesture_box_cancel;
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index f5da7c14a88..df085faab7d 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -1692,6 +1692,44 @@ void ED_view3d_local_collections_reset(struct bContext *C, const bool reset_all)
/** \name XR Functionality
* \{ */
+/* Used for invoke_3d/modal_3d (XR) operators. */
+void ED_view3d_view_params_get(const struct View3D *v3d,
+ const struct RegionView3D *rv3d,
+ float *r_lens,
+ float *r_clip_start,
+ float *r_clip_end,
+ float r_viewmat[4][4])
+{
+ *r_lens = v3d->lens;
+ *r_clip_start = v3d->clip_start;
+ *r_clip_end = v3d->clip_end;
+
+ if (r_viewmat) {
+ copy_m4_m4(r_viewmat, rv3d->viewmat);
+ }
+}
+
+void ED_view3d_view_params_set(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct View3D *v3d,
+ struct ARegion *region,
+ const float lens,
+ const float clip_start,
+ const float clip_end,
+ const float viewmat[4][4])
+{
+ v3d->lens = lens;
+ v3d->clip_start = clip_start;
+ v3d->clip_end = clip_end;
+
+ if (viewmat) {
+ ED_view3d_update_viewmat(depsgraph, scene, v3d, region, viewmat, NULL, NULL, false);
+ }
+ else {
+ view3d_winmatrix_set(depsgraph, region, v3d, NULL);
+ }
+}
+
#ifdef WITH_XR_OPENXR
static void view3d_xr_mirror_begin(RegionView3D *rv3d)
@@ -1729,8 +1767,6 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S
{
if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) {
View3DShading *xr_shading = &wm->xr.session_settings.shading;
- /* Flags that shouldn't be overridden by the 3D View shading. */
- const int flag_copy = V3D_SHADING_WORLD_ORIENTATION;
BLI_assert(WM_xr_session_exists(&wm->xr));
@@ -1748,9 +1784,7 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S
}
/* Copy shading from View3D to VR view. */
- const int old_xr_shading_flag = xr_shading->flag;
*xr_shading = v3d->shading;
- xr_shading->flag = (xr_shading->flag & ~flag_copy) | (old_xr_shading_flag & flag_copy);
if (v3d->shading.prop) {
xr_shading->prop = IDP_CopyProperty(xr_shading->prop);
}
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 64a720322c1..ccf9f9adf55 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -129,5 +129,8 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_XR_OPENXR)
+ add_definitions(-DWITH_XR_OPENXR)
+endif()
blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 557fa79e7ac..e11e36cc712 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -1597,11 +1597,9 @@ 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 Object *ob)
+void ED_transform_animrecord_check_state(Scene *scene, wmTimer *animtimer, struct Object *ob)
{
- Scene *scene = t->scene;
ID *id = &ob->id;
- 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 66d84bca2d2..308356ceed9 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -65,7 +65,6 @@ struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struc
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);
-void animrecord_check_state(TransInfo *t, struct Object *ob);
/* transform_convert_action.c */
void createTransActionData(bContext *C, TransInfo *t);
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 1bbbbc6294e..d19ff123037 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -1449,7 +1449,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);
+ ED_transform_animrecord_check_state(t->scene, t->animtimer, ob);
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 1acd8787f51..09aa4314b32 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -728,7 +728,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);
@@ -850,7 +850,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),
@@ -895,11 +895,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);
- autokeyframe_object(t->context, t->scene, t->view_layer, ob, t->mode);
+ ED_transform_animrecord_check_state(t->scene, t->animtimer, ob);
+ 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
@@ -971,10 +971,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_ops.c b/source/blender/editors/transform/transform_ops.c
index 3a4a9342e18..7989ea3f8ab 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -470,6 +470,68 @@ static int transform_modal(bContext *C, wmOperator *op, const wmEvent *event)
return exit_code;
}
+static int transform_modal_3d(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BLI_assert(event->type == EVT_XR_ACTION);
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ const wmXrActionData *actiondata = event->customdata;
+ TransInfo *t = op->customdata;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ float lens_prev;
+ float clip_start_prev, clip_end_prev;
+ float viewmat_prev[4][4];
+ int retval;
+
+ wmEvent event_mut;
+ memcpy(&event_mut, event, sizeof(wmEvent));
+
+ /* Replace window view parameters with XR surface counterparts. */
+ ED_view3d_view_params_get(v3d, rv3d, &lens_prev, &clip_start_prev, &clip_end_prev, viewmat_prev);
+ ED_view3d_view_params_set(
+ depsgraph,
+ scene,
+ v3d,
+ region,
+ actiondata->eye_lens,
+ xr->session_settings.clip_start,
+ xr->session_settings.clip_end,
+ t->viewmat); /* Use viewmat from when transform was invoked instead of latest XR viewmat. */
+
+ map_to_pixel(event_mut.mval,
+ actiondata->controller_loc,
+ t->viewmat,
+ rv3d->winmat,
+ region->winx,
+ region->winy);
+
+ if (event->val == KM_PRESS) {
+ event_mut.type = MOUSEMOVE;
+ }
+ else if (event->val == KM_RELEASE) {
+ event_mut.type = LEFTMOUSE;
+ }
+ else {
+ /* XR events currently only support press and release. */
+ BLI_assert(false);
+ }
+
+ retval = transform_modal(C, op, &event_mut);
+
+ /* Restore window view. */
+ ED_view3d_view_params_set(
+ depsgraph, scene, v3d, region, lens_prev, clip_start_prev, clip_end_prev, viewmat_prev);
+
+ return retval;
+}
+
static void transform_cancel(bContext *C, wmOperator *op)
{
TransInfo *t = op->customdata;
@@ -530,6 +592,56 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+static int transform_invoke_3d(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BLI_assert(event->type == EVT_XR_ACTION);
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ const wmXrActionData *actiondata = event->customdata;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ float lens_prev;
+ float clip_start_prev, clip_end_prev;
+ float viewmat_prev[4][4];
+ int retval;
+
+ wmEvent event_mut;
+ memcpy(&event_mut, event, sizeof(wmEvent));
+ event_mut.type = LEFTMOUSE;
+
+ /* Replace window view parameters with XR surface counterparts. */
+ ED_view3d_view_params_get(v3d, rv3d, &lens_prev, &clip_start_prev, &clip_end_prev, viewmat_prev);
+ ED_view3d_view_params_set(depsgraph,
+ scene,
+ v3d,
+ region,
+ actiondata->eye_lens,
+ xr->session_settings.clip_start,
+ xr->session_settings.clip_end,
+ actiondata->eye_viewmat);
+
+ map_to_pixel(event_mut.mval,
+ actiondata->controller_loc,
+ actiondata->eye_viewmat,
+ rv3d->winmat,
+ region->winx,
+ region->winy);
+
+ retval = transform_invoke(C, op, &event_mut);
+
+ /* Restore window view. */
+ ED_view3d_view_params_set(
+ depsgraph, scene, v3d, region, lens_prev, clip_start_prev, clip_end_prev, viewmat_prev);
+
+ return retval;
+}
+
static bool transform_poll_property(const bContext *UNUSED(C),
wmOperator *op,
const PropertyRNA *prop)
@@ -747,8 +859,10 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = transform_invoke;
+ ot->invoke_3d = transform_invoke_3d;
ot->exec = transform_exec;
ot->modal = transform_modal;
+ ot->modal_3d = transform_modal_3d;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->poll_property = transform_poll_property;
@@ -774,8 +888,10 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = transform_invoke;
+ ot->invoke_3d = transform_invoke_3d;
ot->exec = transform_exec;
ot->modal = transform_modal;
+ ot->modal_3d = transform_modal_3d;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->poll_property = transform_poll_property;
@@ -862,8 +978,10 @@ static void TRANSFORM_OT_rotate(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = transform_invoke;
+ ot->invoke_3d = transform_invoke_3d;
ot->exec = transform_exec;
ot->modal = transform_modal;
+ ot->modal_3d = transform_modal_3d;
ot->cancel = transform_cancel;
ot->poll = transform_rotate_poll;
ot->poll_property = transform_poll_property;
@@ -1264,8 +1382,10 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = transform_invoke;
+ ot->invoke_3d = transform_invoke_3d;
ot->exec = transform_exec;
ot->modal = transform_modal;
+ ot->modal_3d = transform_modal_3d;
ot->cancel = transform_cancel;
ot->poll = ED_operator_screenactive;
ot->poll_property = transform_poll_property;
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 883a89b8467..70297fad4ff 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -499,6 +499,16 @@ static void iter_snap_objects(SnapObjectContext *sctx,
continue;
}
}
+ else if (snap_select == SNAP_SELECTED) {
+ if (!(base->flag & BASE_SELECTED) && !(base->flag_legacy & BA_WAS_SEL)) {
+ continue;
+ }
+ }
+ else if (snap_select == SNAP_SELECTABLE) {
+ if (!(base->flag & BASE_SELECTABLE)) {
+ continue;
+ }
+ }
Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object);
if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) {
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index d5b7458ae7b..42dd6d2181b 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -666,8 +666,11 @@ typedef enum eRegionType {
RGN_TYPE_EXECUTE = 10,
RGN_TYPE_FOOTER = 11,
RGN_TYPE_TOOL_HEADER = 12,
+ /* Region type used exclusively by internal code and add-ons to register draw callbacks to the XR
+ context (surface, mirror view). Does not represent any real region. */
+ RGN_TYPE_XR = 13,
-#define RGN_TYPE_LEN (RGN_TYPE_TOOL_HEADER + 1)
+#define RGN_TYPE_LEN (RGN_TYPE_XR + 1)
} eRegionType;
/* use for function args */
diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h
index aec52da1bf9..59012a0cd8d 100644
--- a/source/blender/makesdna/DNA_view3d_enums.h
+++ b/source/blender/makesdna/DNA_view3d_enums.h
@@ -30,6 +30,9 @@ typedef enum eV3DOffscreenDrawFlag {
V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0),
V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1),
V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2),
+ V3D_OFSDRAW_SHOW_SELECTION = (1 << 3),
+ V3D_OFSDRAW_XR_SHOW_CONTROLLERS = (1 << 4),
+ V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS = (1 << 5),
} eV3DOffscreenDrawFlag;
/** #View3DShading.light */
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 4d88f6f0c15..fafc470b95a 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -373,6 +373,7 @@ typedef struct View3D {
#define V3D_HIDE_HELPLINES (1 << 2)
#define V3D_FLAG_UNUSED_2 (1 << 3) /* cleared */
#define V3D_XR_SESSION_MIRROR (1 << 4)
+#define V3D_XR_SESSION_SURFACE (1 << 5)
#define V3D_FLAG_UNUSED_10 (1 << 10) /* cleared */
#define V3D_SELECT_OUTLINE (1 << 11)
@@ -465,6 +466,8 @@ enum {
#define V3D_FLAG2_UNUSED_13 (1 << 13) /* cleared */
#define V3D_FLAG2_UNUSED_14 (1 << 14) /* cleared */
#define V3D_FLAG2_UNUSED_15 (1 << 15) /* cleared */
+#define V3D_XR_SHOW_CONTROLLERS (1 << 16)
+#define V3D_XR_SHOW_CUSTOM_OVERLAYS (1 << 17)
/** #View3D.gp_flag (short) */
#define V3D_GP_FADE_OBJECTS (1 << 0) /* Fade all non GP objects */
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index a9d427777f7..57347c3d710 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -26,15 +26,17 @@
extern "C" {
#endif
+struct XrActionConfig;
+
/* -------------------------------------------------------------------- */
typedef struct XrSessionSettings {
/** Shading settings, struct shared with 3D-View so settings are the same. */
struct View3DShading shading;
- char _pad[7];
-
- char base_pose_type; /* #eXRSessionBasePoseType */
+ float base_scale;
+ char _pad[3];
+ char base_pose_type; /* eXrSessionBasePoseType */
/** Object to take the location and rotation as base position from. */
Object *base_pose_object;
float base_pose_location[3];
@@ -42,12 +44,30 @@ typedef struct XrSessionSettings {
/** View3D draw flags (V3D_OFSDRAW_NONE, V3D_OFSDRAW_SHOW_ANNOTATION, ...). */
char draw_flags;
- char _pad2[3];
+ /** Draw style for controller visualization. */
+ char controller_draw_style;
+ /** The eye (view) used when projecting 3D to 2D (e.g. when performing GPU select). */
+ char selection_eye;
+ char _pad2;
/** Clipping distance. */
float clip_start, clip_end;
- int flag;
+ int flag; /* eXrSessionFlag */
+
+ /** Known action configurations. */
+ ListBase actionconfigs; /* XrActionConfig */
+ /** Default configuration. */
+ struct XrActionConfig *defaultconf;
+ /** Addon configuration. */
+ struct XrActionConfig *addonconf;
+ /** User configuration. */
+ struct XrActionConfig *userconf;
+
+ /** Objects to bind to headset/controller poses. */
+ ListBase mocap_objects; /* XrMotionCaptureObject */
+ short sel_mocap_object;
+ char _pad3[6];
} XrSessionSettings;
typedef enum eXrSessionFlag {
@@ -55,11 +75,23 @@ typedef enum eXrSessionFlag {
XR_SESSION_USE_ABSOLUTE_TRACKING = (1 << 1),
} eXrSessionFlag;
-typedef enum eXRSessionBasePoseType {
+typedef enum eXrSessionBasePoseType {
XR_BASE_POSE_SCENE_CAMERA = 0,
XR_BASE_POSE_OBJECT = 1,
XR_BASE_POSE_CUSTOM = 2,
-} eXRSessionBasePoseType;
+} eXrSessionBasePoseType;
+
+typedef enum eXrSessionControllerDrawStyle {
+ XR_CONTROLLER_DRAW_DARK = 0,
+ XR_CONTROLLER_DRAW_LIGHT = 1,
+ XR_CONTROLLER_DRAW_DARK_RAY = 2,
+ XR_CONTROLLER_DRAW_LIGHT_RAY = 3,
+} eXrSessionControllerDrawStyle;
+
+typedef enum eXrSessionEye {
+ XR_EYE_LEFT = 0,
+ XR_EYE_RIGHT = 1,
+} eXrSessionEye;
/** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */
typedef enum eXrActionType {
@@ -94,11 +126,11 @@ typedef enum eXrHapticFlag {
XR_HAPTIC_REPEAT = (1 << 3),
} eXrHapticFlag;
-/**
- * For axis-based inputs (thumb-stick/track-pad/etc).
- * Determines the region for action execution (mutually exclusive per axis).
- */
typedef enum eXrAxisFlag {
+ /**
+ * For axis-based inputs (thumb-stick/track-pad/etc).
+ * Determines the region for action execution (mutually exclusive per axis).
+ */
XR_AXIS0_POS = (1 << 0),
XR_AXIS0_NEG = (1 << 1),
XR_AXIS1_POS = (1 << 2),
@@ -111,6 +143,11 @@ typedef enum eXrPoseFlag {
XR_POSE_AIM = (1 << 1),
} eXrPoseFlag;
+typedef enum eXrMotionCaptureFlag {
+ XR_MOCAP_OBJECT_ENABLE = (1 << 0),
+ XR_MOCAP_OBJECT_AUTOKEY = (1 << 1),
+} eXrMotionCaptureFlag;
+
/* -------------------------------------------------------------------- */
typedef struct XrActionMapBinding {
@@ -151,7 +188,7 @@ typedef struct XrActionMapItem {
char user_path1[64];
/** Operator to be called on XR events. */
- char op[64]; /* OP_MAX_TYPENAME */
+ char op[64];
/** Operator properties, assigned to ptr->data and can be written to a file. */
IDProperty *op_properties;
/** RNA pointer to access properties. */
@@ -171,10 +208,15 @@ typedef struct XrActionMapItem {
float haptic_amplitude;
short selbinding;
- char _pad3[2];
+ short flag;
ListBase bindings; /* XrActionMapBinding */
} XrActionMapItem;
+/** #XrActionMapItem.flag */
+enum {
+ XR_ACTIONMAP_ITEM_UPDATE = (1 << 0),
+};
+
/* -------------------------------------------------------------------- */
typedef struct XrActionMap {
@@ -185,9 +227,53 @@ typedef struct XrActionMap {
ListBase items; /* XrActionMapItem */
short selitem;
- char _pad[6];
+ short flag;
+ char _pad[4];
} XrActionMap;
+/** #XrActionMap.flag */
+enum {
+ XR_ACTIONMAP_UPDATE = (1 << 0),
+};
+
+/* -------------------------------------------------------------------- */
+
+typedef struct XrActionConfig {
+ struct XrActionConfig *next, *prev;
+
+ /** Unique name. */
+ char name[64]; /* MAX_NAME */
+
+ ListBase actionmaps; /* XrActionMap */
+ short actactionmap;
+ short selactionmap;
+ short flag;
+ char _pad[2];
+} XrActionConfig;
+
+/** #XrActionConfig.flag */
+enum {
+ XR_ACTIONCONF_USER = (1 << 0), /* User action config. */
+};
+
+/* -------------------------------------------------------------------- */
+
+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];
+
+ /** Location/rotation offsets. */
+ float location_offset[3];
+ float rotation_offset[3];
+
+ short flag; /* eXrMotionCaptureFlag */
+ char _pad[6];
+} XrMotionCaptureObject;
+
/* -------------------------------------------------------------------- */
#ifdef __cplusplus
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index 912f47ba9aa..a8f28f6091c 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -46,6 +46,7 @@ const EnumPropertyItem rna_enum_region_type_items[] = {
{RGN_TYPE_EXECUTE, "EXECUTE", 0, "Execute Buttons", ""},
{RGN_TYPE_FOOTER, "FOOTER", 0, "Footer", ""},
{RGN_TYPE_TOOL_HEADER, "TOOL_HEADER", 0, "Tool Header", ""},
+ {RGN_TYPE_XR, "XR", 0, "XR", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index f96b3fc5eee..eea9fe89008 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -1755,6 +1755,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_wm.c b/source/blender/makesrna/intern/rna_wm.c
index c2d1ac67675..107616c363f 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -362,6 +362,8 @@ const EnumPropertyItem rna_enum_event_type_items[] = {
0,
"ActionZone Fullscreen",
"AZone FullScr"},
+ /* xr */
+ {EVT_XR_ACTION, "XR_ACTION", 0, "XR Action", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -700,6 +702,238 @@ static void rna_Event_tilt_get(PointerRNA *ptr, float *values)
WM_event_tablet_data(event, NULL, values);
}
+static bool rna_Event_is_xr_get(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ return WM_event_is_xr(event);
+}
+
+static void rna_Event_xr_action_set_get(PointerRNA *ptr, char *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+ else {
+ value[0] = '\0';
+ }
+}
+
+static int rna_Event_xr_action_set_length(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ wmXrActionData *data = event->customdata;
+ return strlen(data->action_set) + 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static void rna_Event_xr_action_get(PointerRNA *ptr, char *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, &value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+ else {
+ value[0] = '\0';
+ }
+}
+
+static int rna_Event_xr_action_length(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ wmXrActionData *data = event->customdata;
+ return strlen(data->action) + 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int rna_Event_xr_type_get(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ int type;
+ WM_event_xr_data(event,
+ NULL,
+ NULL,
+ (char *)&type,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ return type;
+ }
+ else {
+ return 0;
+ }
+}
+
+static void rna_Event_xr_state_get(PointerRNA *ptr, float *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+ else {
+ memset(value, 0, sizeof(float[2]));
+ }
+}
+
+static void rna_Event_xr_state_other_get(PointerRNA *ptr, float *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, value, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+ else {
+ memset(value, 0, sizeof(float[2]));
+ }
+}
+
+static float rna_Event_xr_float_threshold_get(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ float float_threshold;
+ WM_event_xr_data(event,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &float_threshold,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ return float_threshold;
+ }
+ else {
+ return 0.0f;
+ }
+}
+
+static void rna_Event_xr_controller_location_get(PointerRNA *ptr, float *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, NULL, NULL, value, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+ else {
+ memset(value, 0, sizeof(float[3]));
+ }
+}
+
+static void rna_Event_xr_controller_rotation_get(PointerRNA *ptr, float *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, value, NULL, NULL, NULL, NULL, NULL);
+ }
+ else {
+ value[0] = 1.0f;
+ value[1] = value[2] = value[3] = 0.0f;
+ }
+}
+
+static void rna_Event_xr_controller_location_other_get(PointerRNA *ptr, float *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, value, NULL, NULL, NULL, NULL);
+ }
+ else {
+ memset(value, 0, sizeof(float[3]));
+ }
+}
+
+static void rna_Event_xr_controller_rotation_other_get(PointerRNA *ptr, float *value)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, value, NULL, NULL, NULL);
+ }
+ else {
+ value[0] = 1.0f;
+ value[1] = value[2] = value[3] = 0.0f;
+ }
+}
+
+static float rna_Event_xr_focal_length_get(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ float focal_len;
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &focal_len, NULL, NULL);
+ return focal_len;
+ }
+ else {
+ return 0.0f;
+ }
+}
+
+static void rna_Event_xr_view_matrix_get(PointerRNA *ptr, float values[16])
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ WM_event_xr_data(event,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (float(*)[4])values,
+ NULL);
+ }
+ else {
+ memset(values, 0, sizeof(float[16]));
+ }
+}
+
+static bool rna_Event_xr_bimanual_get(PointerRNA *ptr)
+{
+ const wmEvent *event = ptr->data;
+ if (WM_event_is_xr(event)) {
+ bool bimanual;
+ WM_event_xr_data(
+ event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &bimanual);
+ return bimanual;
+ }
+ else {
+ return false;
+ }
+}
+
static PointerRNA rna_PopupMenu_layout_get(PointerRNA *ptr)
{
struct uiPopupMenu *pup = ptr->data;
@@ -2130,6 +2364,13 @@ static void rna_def_event(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ static const EnumPropertyItem xr_action_types[] = {
+ {XR_BOOLEAN_INPUT, "BOOLEAN", 0, "Boolean", "Boolean value"},
+ {XR_FLOAT_INPUT, "FLOAT", 0, "Float", "Float value"},
+ {XR_VECTOR2F_INPUT, "VECTOR2F", 0, "Vector2f", "2D float vector value"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "Event", NULL);
RNA_def_struct_ui_text(srna, "Event", "Window Manager Event");
RNA_def_struct_sdna(srna, "wmEvent");
@@ -2250,6 +2491,97 @@ static void rna_def_event(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "OS Key", "True when the Cmd key is held");
+ /* xr */
+ prop = RNA_def_property(srna, "is_xr", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_Event_is_xr_get", NULL);
+ RNA_def_property_ui_text(prop, "Is XR", "The event has XR data");
+
+ prop = RNA_def_property(srna, "xr_action_set", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_Event_xr_action_set_get", "rna_Event_xr_action_set_length", NULL);
+ RNA_def_property_ui_text(prop, "XR Action Set", "XR action set name");
+
+ prop = RNA_def_property(srna, "xr_action", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_Event_xr_action_get", "rna_Event_xr_action_length", NULL);
+ RNA_def_property_ui_text(prop, "XR Action", "XR action name");
+
+ prop = RNA_def_property(srna, "xr_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_enum_items(prop, xr_action_types);
+ RNA_def_property_enum_funcs(prop, "rna_Event_xr_type_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "XR Type", "XR action type");
+
+ prop = RNA_def_property(srna, "xr_state", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_state_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "XR State", "XR action values corresponding to type");
+
+ prop = RNA_def_property(srna, "xr_state_other", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_state_other_get", NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "XR State Other", "State of the other user path for bimanual actions");
+
+ prop = RNA_def_property(srna, "xr_float_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_float_threshold_get", NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "XR Float Threshold", "Input threshold for Float/Vector2f actions");
+
+ prop = RNA_def_property(srna, "xr_controller_location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_controller_location_get", NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "XR Controller Location",
+ "Location of the action's corresponding controller aim in world space");
+
+ prop = RNA_def_property(srna, "xr_controller_rotation", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_controller_rotation_get", NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "XR Controller Rotation",
+ "Rotation of the action's corresponding controller aim in world space");
+
+ prop = RNA_def_property(srna, "xr_controller_location_other", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_controller_location_other_get", NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "XR Controller Location Other",
+ "Controller aim location of the other user path for bimanual actions");
+
+ prop = RNA_def_property(srna, "xr_controller_rotation_other", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_controller_rotation_other_get", NULL, NULL);
+ RNA_def_property_ui_text(prop,
+ "XR Controller Rotation Other",
+ "Controller aim rotation of the other user path for bimanual actions");
+
+ prop = RNA_def_property(srna, "xr_focal_length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_focal_length_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "XR Focal Length", "Focal length of the XR selection eye");
+
+ prop = RNA_def_property(srna, "xr_view_matrix", PROP_FLOAT, PROP_MATRIX);
+ RNA_def_property_array(prop, 16);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_funcs(prop, "rna_Event_xr_view_matrix_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "XR View Matrix", "View matrix of the XR selection eye");
+
+ prop = RNA_def_property(srna, "xr_bimanual", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_Event_xr_bimanual_get", NULL);
+ RNA_def_property_ui_text(prop, "XR Bimanual", "Bimanual interaction is occurring");
+
RNA_define_verify_sdna(1); /* not in sdna */
}
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
index f24d28d1209..f64b6c5daca 100644
--- a/source/blender/makesrna/intern/rna_xr.c
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -40,24 +40,7 @@
# include "WM_api.h"
/* -------------------------------------------------------------------- */
-
-# ifdef WITH_XR_OPENXR
-static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr)
-{
- /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just
- * consistently pass wmXrData pointers to the WM_xr_xxx() API. */
-
- BLI_assert((ptr->type == &RNA_XrSessionSettings) || (ptr->type == &RNA_XrSessionState));
-
- wmWindowManager *wm = (wmWindowManager *)ptr->owner_id;
- BLI_assert(wm && (GS(wm->id.name) == ID_WM));
-
- return &wm->xr;
-}
-# endif
-
-/* -------------------------------------------------------------------- */
-/** \name XR Action Map
+/** \name XR Action Configuration
* \{ */
static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami,
@@ -171,15 +154,17 @@ 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 (actionmap) {
- XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem);
- if (ami) {
- XrActionMapBinding *amb = ptr->data;
- WM_xr_actionmap_binding_ensure_unique(ami, amb);
+ if (wm) {
+ XrSessionSettings *settings = &wm->xr.session_settings;
+ XrActionConfig *actionconf = WM_xr_actionconfig_active_get(settings);
+ if (actionconf) {
+ XrActionMap *actionmap = BLI_findlink(&actionconf->actionmaps, actionconf->selactionmap);
+ if (actionmap) {
+ XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem);
+ if (ami) {
+ XrActionMapBinding *amb = ptr->data;
+ WM_xr_actionmap_binding_ensure_unique(ami, amb);
+ }
}
}
}
@@ -413,13 +398,15 @@ 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 (actionmap) {
- XrActionMapItem *ami = ptr->data;
- WM_xr_actionmap_item_ensure_unique(actionmap, ami);
+ if (wm) {
+ XrSessionSettings *settings = &wm->xr.session_settings;
+ XrActionConfig *actionconf = WM_xr_actionconfig_active_get(settings);
+ if (actionconf) {
+ XrActionMap *actionmap = BLI_findlink(&actionconf->actionmaps, actionconf->selactionmap);
+ if (actionmap) {
+ XrActionMapItem *ami = ptr->data;
+ WM_xr_actionmap_item_ensure_unique(actionmap, ami);
+ }
}
}
# else
@@ -437,50 +424,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(XrActionConfig *actionconf,
+ 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(actionconf, name, replace_existing);
# else
- UNUSED_VARS(ptr, name, replace_existing);
+ UNUSED_VARS(actionconf, 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(XrActionConfig *actionconf,
+ 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(actionconf, am_src);
# else
- UNUSED_VARS(ptr, am_src);
+ UNUSED_VARS(actionconf, am_src);
return NULL;
# endif
}
-static void rna_XrActionMap_remove(ReportList *reports, PointerRNA *ptr, PointerRNA *actionmap_ptr)
+static void rna_XrActionMap_remove(XrActionConfig *actionconf,
+ 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(actionconf, 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(actionconf, reports, actionmap_ptr);
# endif
}
-static XrActionMap *rna_XrActionMap_find(PointerRNA *ptr, const char *name)
+static XrActionMap *rna_XrActionMap_find(XrActionConfig *actionconf, 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(actionconf, name);
# else
- UNUSED_VARS(ptr, name);
+ UNUSED_VARS(actionconf, name);
return NULL;
# endif
}
@@ -489,18 +477,224 @@ 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) {
- XrActionMap *actionmap = ptr->data;
- WM_xr_actionmap_ensure_unique(wm->xr.runtime, actionmap);
+ if (wm) {
+ XrSessionSettings *settings = &wm->xr.session_settings;
+ XrActionConfig *actionconf = WM_xr_actionconfig_active_get(settings);
+ if (actionconf) {
+ XrActionMap *actionmap = ptr->data;
+ WM_xr_actionmap_ensure_unique(actionconf, actionmap);
+ }
}
# else
UNUSED_VARS(bmain, ptr);
# endif
}
+static XrActionConfig *rna_XrActionConfig_new(XrSessionSettings *settings, const char *name)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionconfig_new(settings, name, true);
+# else
+ UNUSED_VARS(settings, name);
+ return NULL;
+# endif
+}
+
+static void rna_XrActionConfig_remove(XrSessionSettings *settings,
+ ReportList *reports,
+ PointerRNA *actionconf_ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionConfig *actionconf = actionconf_ptr->data;
+ if (WM_xr_actionconfig_remove(settings, actionconf) == false) {
+ BKE_reportf(reports, RPT_ERROR, "ActionConfig '%s' cannot be removed", actionconf->name);
+ return;
+ }
+ RNA_POINTER_INVALIDATE(actionconf_ptr);
+# else
+ UNUSED_VARS(settings, reports, actionconf_ptr);
+# endif
+}
+
+static void rna_XrActionConfig_update(XrSessionSettings *settings)
+{
+# ifdef WITH_XR_OPENXR
+ WM_xr_actionconfig_update(settings);
+# else
+ UNUSED_VARS(settings);
+# endif
+}
+
+static PointerRNA rna_XrSessionSettings_actionconfig_active_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrSessionSettings *settings = ptr->data;
+ XrActionConfig *ac = WM_xr_actionconfig_active_get(settings);
+ if (!ac) {
+ ac = settings->defaultconf;
+ }
+ return rna_pointer_inherit_refine(ptr, &RNA_XrActionConfig, ac);
+# else
+ UNUSED_VARS(ptr);
+ return PointerRNA_NULL;
+# endif
+}
+
+static void rna_XrSessionSettings_actionconfig_active_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *UNUSED(reports))
+{
+# ifdef WITH_XR_OPENXR
+ XrSessionSettings *settings = ptr->data;
+ XrActionConfig *ac = value.data;
+ if (ac) {
+ WM_xr_actionconfig_active_set(settings, ac->name);
+ }
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
/** \} */
/* -------------------------------------------------------------------- */
+/** \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
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+# ifdef WITH_XR_OPENXR
+static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr)
+{
+ /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just
+ * consistently pass wmXrData pointers to the WM_xr_xxx() API. */
+
+ BLI_assert((ptr->type == &RNA_XrSessionSettings) || (ptr->type == &RNA_XrSessionState));
+
+ wmWindowManager *wm = (wmWindowManager *)ptr->owner_id;
+ BLI_assert(wm && (GS(wm->id.name) == ID_WM));
+
+ return &wm->xr;
+}
+# endif
+
+/* -------------------------------------------------------------------- */
/** \name XR Session Settings
* \{ */
@@ -892,54 +1086,66 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *
# endif
}
-static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+static void rna_XrSessionState_nav_location_get(PointerRNA *ptr, float *r_values)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_location_get(xr, r_values);
+# else
+ UNUSED_VARS(ptr);
+ zero_v3(r_values);
+# endif
+}
+
+static void rna_XrSessionState_nav_location_set(PointerRNA *ptr, const float *values)
{
# 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);
+ WM_xr_session_state_nav_location_set(xr, values);
# else
- UNUSED_VARS(iter, ptr);
+ UNUSED_VARS(ptr, values);
# endif
}
-static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr)
+static void rna_XrSessionState_nav_rotation_get(PointerRNA *ptr, float *r_values)
{
# ifdef WITH_XR_OPENXR
const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
- return WM_xr_actionmap_active_index_get(xr->runtime);
+ WM_xr_session_state_nav_rotation_get(xr, r_values);
# else
UNUSED_VARS(ptr);
- return -1;
+ unit_qt(r_values);
# endif
}
-static void rna_XrSessionState_active_actionmap_set(PointerRNA *ptr, int value)
+static void rna_XrSessionState_nav_rotation_set(PointerRNA *ptr, const float *values)
{
# ifdef WITH_XR_OPENXR
wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
- WM_xr_actionmap_active_index_set(xr->runtime, (short)value);
+ WM_xr_session_state_nav_rotation_set(xr, values);
# else
- UNUSED_VARS(ptr, value);
+ UNUSED_VARS(ptr, values);
# endif
}
-static int rna_XrSessionState_selected_actionmap_get(PointerRNA *ptr)
+static float rna_XrSessionState_nav_scale_get(PointerRNA *ptr)
{
+ float value;
# ifdef WITH_XR_OPENXR
const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
- return WM_xr_actionmap_selected_index_get(xr->runtime);
+ WM_xr_session_state_nav_scale_get(xr, &value);
# else
UNUSED_VARS(ptr);
- return -1;
+ value = 1.0f;
# endif
+ return value;
}
-static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value)
+static void rna_XrSessionState_nav_scale_set(PointerRNA *ptr, float value)
{
# ifdef WITH_XR_OPENXR
wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
- WM_xr_actionmap_selected_index_set(xr->runtime, (short)value);
+ WM_xr_session_state_nav_scale_set(xr, value);
# else
UNUSED_VARS(ptr, value);
# endif
@@ -1037,7 +1243,7 @@ static const EnumPropertyItem rna_enum_xr_axis1_flags[] = {
};
/* -------------------------------------------------------------------- */
-/** \name XR Action Map
+/** \name XR Action Configuration
* \{ */
static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop)
@@ -1058,7 +1264,7 @@ static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop)
"replace_existing",
true,
"Replace Existing",
- "Replace any existing binding with the same name");
+ "Replace any existing binding with same name");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(
func, "binding", "XrActionMapBinding", "Binding", "Added action map binding");
@@ -1107,7 +1313,7 @@ static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop)
"replace_existing",
true,
"Replace Existing",
- "Replace any existing item with the same name");
+ "Replace any existing item with same name");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Added action map item");
RNA_def_function_return(func, parm);
@@ -1140,27 +1346,22 @@ 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, "XrActionConfig");
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,
"replace_existing",
true,
"Replace Existing",
- "Replace any existing actionmap with the same name");
+ "Replace any existing actionmap with same name");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Added action map");
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);
@@ -1168,17 +1369,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(
@@ -1186,11 +1382,106 @@ static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
}
-static void rna_def_xr_actionmap(BlenderRNA *brna)
+static void rna_def_xr_actionconfigs(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *prop, *parm;
+
+ RNA_def_property_srna(cprop, "XrActionConfigurations");
+ srna = RNA_def_struct(brna, "XrActionConfigurations", NULL);
+ RNA_def_struct_sdna(srna, "XrSessionSettings");
+ RNA_def_struct_ui_text(
+ srna, "XR Action Configurations", "Collection of XR action configurations");
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrActionConfig");
+ RNA_def_property_pointer_funcs(prop,
+ "rna_XrSessionSettings_actionconfig_active_get",
+ "rna_XrSessionSettings_actionconfig_active_set",
+ NULL,
+ NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Active XR Action Configuration", "Active XR action configuration (preset)");
+
+ prop = RNA_def_property(srna, "default", PROP_POINTER, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "defaultconf");
+ RNA_def_property_struct_type(prop, "XrActionConfig");
+ RNA_def_property_ui_text(
+ prop, "Default XR Action Configuration", "Default builtin XR action configuration");
+
+ prop = RNA_def_property(srna, "addon", PROP_POINTER, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "addonconf");
+ RNA_def_property_struct_type(prop, "XrActionConfig");
+ RNA_def_property_ui_text(prop,
+ "Add-on XR Action Configuration",
+ "XR action configuration that can be extended by add-ons");
+
+ prop = RNA_def_property(srna, "user", PROP_POINTER, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "userconf");
+ RNA_def_property_struct_type(prop, "XrActionConfig");
+ RNA_def_property_ui_text(prop,
+ "User XR Action Configuration",
+ "XR action configuration that can be extended by users");
+
+ func = RNA_def_function(srna, "new", "rna_XrActionConfig_new");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "actionconfig",
+ "XrActionConfig",
+ "Action Configuration",
+ "Added action configuration");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_XrActionConfig_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func,
+ "actionconfig",
+ "XrActionConfig",
+ "Action Configuration",
+ "Removed action configuration");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ RNA_def_function(srna, "update", "rna_XrActionConfig_update");
+}
+
+static void rna_def_xr_actionconfig(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
+ /* XrActionConfig */
+ srna = RNA_def_struct(brna, "XrActionConfig", NULL);
+ RNA_def_struct_sdna(srna, "XrActionConfig");
+ RNA_def_struct_ui_text(srna, "XR Action Configuration", "");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the action configuration");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrActionMap");
+ RNA_def_property_ui_text(
+ prop, "Action Maps", "Action maps configured as part of this configuration");
+ rna_def_xr_actionmaps(brna, prop);
+
+ prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "actactionmap");
+ 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, "selactionmap");
+ RNA_def_property_ui_text(prop, "Selected Action Map", "");
+
+ prop = RNA_def_property(srna, "is_user_defined", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", KEYCONF_USER);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "User Defined", "Indicates that an action configuration was defined by the user");
+
/* XrActionMap */
srna = RNA_def_struct(brna, "XrActionMap", NULL);
RNA_def_struct_sdna(srna, "XrActionMap");
@@ -1321,7 +1612,7 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "XrActionMapBinding");
RNA_def_property_ui_text(
- prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input");
+ prop, "Bindings", "Bindings in the actionmap, linking an action to an XR input");
rna_def_xr_actionmap_bindings(brna, prop);
prop = RNA_def_property(srna, "selected_binding", PROP_INT, PROP_NONE);
@@ -1331,7 +1622,9 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
/* XrActionMapBinding */
srna = RNA_def_struct(brna, "XrActionMapBinding", NULL);
RNA_def_struct_sdna(srna, "XrActionMapBinding");
- RNA_def_struct_ui_text(srna, "XR Action Map Binding", "Binding in an XR action map item");
+ RNA_def_struct_ui_text(srna,
+ "XR Action Map Binding",
+ "Bindings for the action map item, mapping the action to an XR input");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Name", "Name of the action map binding");
@@ -1353,7 +1646,7 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
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);
- RNA_def_property_ui_text(prop, "Threshold", "Input threshold for button/axis actions");
+ RNA_def_property_ui_text(prop, "Input Threshold", "Input threshold for button/axis actions");
prop = RNA_def_property(srna, "axis0_region", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_xr_axis0_flags);
@@ -1374,10 +1667,101 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
prop, "Axis 1 Region", "Action execution region for the second input axis");
prop = RNA_def_property(srna, "pose_location", PROP_FLOAT, PROP_TRANSLATION);
- RNA_def_property_ui_text(prop, "Pose Location Offset", "");
+ RNA_def_property_ui_text(prop, "Pose Location Offset", "Pose location offset");
prop = RNA_def_property(srna, "pose_rotation", PROP_FLOAT, PROP_EULER);
- RNA_def_property_ui_text(prop, "Pose Rotation Offset", "");
+ RNA_def_property_ui_text(prop, "Pose Rotation Offset", "Pose rotation offset");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \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");
}
/** \} */
@@ -1410,6 +1794,32 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem controller_draw_styles[] = {
+ {XR_CONTROLLER_DRAW_DARK, "DARK", 0, "Dark", "Draw dark controller"},
+ {XR_CONTROLLER_DRAW_LIGHT, "LIGHT", 0, "Light", "Draw light controller"},
+ {XR_CONTROLLER_DRAW_DARK_RAY,
+ "DARK_RAY",
+ 0,
+ "Dark + Ray",
+ "Draw dark controller with aiming axis ray"},
+ {XR_CONTROLLER_DRAW_LIGHT_RAY,
+ "LIGHT_RAY",
+ 0,
+ "Light + Ray",
+ "Draw light controller with aiming axis ray"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem selection_eyes[] = {
+ {XR_EYE_LEFT, "EYE_LEFT", 0, "Left Eye", "Use the left eye's perspective for VR selection"},
+ {XR_EYE_RIGHT,
+ "EYE_RIGHT",
+ 0,
+ "Right Eye",
+ "Use the right eye's perspective for VR selection"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "XrSessionSettings", NULL);
RNA_def_struct_ui_text(srna, "XR Session Settings", "");
@@ -1450,6 +1860,13 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
"Rotation angle around the Z-Axis to apply the rotation deltas from the VR headset to");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+ prop = RNA_def_property(srna, "base_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Base Scale", "Uniform scale to apply to VR view");
+ RNA_def_property_range(prop, 0.001f, 1000.0f);
+ RNA_def_property_ui_range(prop, 0.001f, 1000.0f, 1, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR);
RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid");
@@ -1460,6 +1877,36 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+ prop = RNA_def_property(srna, "show_selection", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_SELECTION);
+ RNA_def_property_ui_text(prop, "Show Selection", "Show selection outlines");
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ prop = RNA_def_property(srna, "show_controllers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_XR_SHOW_CONTROLLERS);
+ RNA_def_property_ui_text(
+ prop, "Show Controllers", "Show VR controllers (requires VR actions for controller poses)");
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ prop = RNA_def_property(srna, "show_custom_overlays", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS);
+ RNA_def_property_ui_text(prop, "Show Custom Overlays", "Show custom VR overlays");
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ prop = RNA_def_property(srna, "controller_draw_style", PROP_ENUM, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, controller_draw_styles);
+ RNA_def_property_ui_text(
+ prop, "Controller Draw Style", "Style to use when drawing VR controllers");
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ prop = RNA_def_property(srna, "selection_eye", PROP_ENUM, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, selection_eyes);
+ RNA_def_property_ui_text(
+ prop, "Selection Eye", "Which eye's perspective to use when selecting in VR (GPU select)");
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
prop = RNA_def_property(srna, "clip_start", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_range(prop, 1e-6f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3);
@@ -1489,6 +1936,23 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ prop = RNA_def_property(srna, "actionconfigs", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrActionConfig");
+ RNA_def_property_ui_text(
+ prop, "XR Action Configurations", "Registered XR action configurations");
+ rna_def_xr_actionconfigs(brna, prop);
+
+ prop = RNA_def_property(srna, "mocap_objects", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrMotionCaptureObject");
+ 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");
}
/** \} */
@@ -1793,33 +2257,30 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
"Viewer Pose Rotation",
"Last known rotation of the viewer pose (center between the eyes) in world space");
- prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_funcs(prop,
- "rna_XrSessionState_actionmaps_begin",
- "rna_iterator_listbase_next",
- "rna_iterator_listbase_end",
- "rna_iterator_listbase_get",
- NULL,
- 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, "navigation_location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(
+ prop, "rna_XrSessionState_nav_location_get", "rna_XrSessionState_nav_location_set", NULL);
+ RNA_def_property_ui_text(
+ prop,
+ "Navigation Location",
+ "Location offset to apply to base pose when determining viewer location");
- 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, "navigation_rotation", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_float_funcs(
+ prop, "rna_XrSessionState_nav_rotation_get", "rna_XrSessionState_nav_rotation_set", NULL);
+ RNA_def_property_ui_text(
+ prop,
+ "Navigation Rotation",
+ "Rotation offset to apply to base pose when determining viewer rotation");
- 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", "");
+ prop = RNA_def_property(srna, "navigation_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_funcs(
+ prop, "rna_XrSessionState_nav_scale_get", "rna_XrSessionState_nav_scale_set", NULL);
+ RNA_def_property_ui_text(prop,
+ "Navigation Scale",
+ "Scale multiplier to apply to base pose when determining viewer scale");
}
/** \} */
@@ -1828,7 +2289,8 @@ void RNA_def_xr(BlenderRNA *brna)
{
RNA_define_animate_sdna(false);
- rna_def_xr_actionmap(brna);
+ rna_def_xr_actionconfig(brna);
+ rna_def_xr_motioncapture_object(brna);
rna_def_xr_session_settings(brna);
rna_def_xr_session_state(brna);
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 4d65726fe2b..0a13eeec6db 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -195,6 +195,8 @@ if(WITH_XR_OPENXR)
list(APPEND INC
xr
+ # for wm_xr_operators.c: BKE_editmesh.h
+ ../bmesh
)
list(APPEND SRC
@@ -202,6 +204,8 @@ 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
xr/wm_xr.h
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 577561017c4..cf90179f129 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -73,10 +73,6 @@ struct wmTabletData;
struct wmNDOFMotionData;
#endif
-#ifdef WITH_XR_OPENXR
-struct wmXrRuntimeData;
-#endif
-
typedef struct wmGizmo wmGizmo;
typedef struct wmGizmoMap wmGizmoMap;
typedef struct wmGizmoMapType wmGizmoMapType;
@@ -664,7 +660,13 @@ void WM_paneltype_remove(struct PanelType *pt);
/* wm_gesture_ops.c */
int WM_gesture_box_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+int WM_gesture_box_invoke_3d(struct bContext *C,
+ struct wmOperator *op,
+ const struct wmEvent *event);
int WM_gesture_box_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+int WM_gesture_box_modal_3d(struct bContext *C,
+ struct wmOperator *op,
+ const struct wmEvent *event);
void WM_gesture_box_cancel(struct bContext *C, struct wmOperator *op);
int WM_gesture_circle_invoke(struct bContext *C,
struct wmOperator *op,
@@ -932,6 +934,22 @@ bool WM_event_is_tablet(const struct wmEvent *event);
int WM_event_absolute_delta_x(const struct wmEvent *event);
int WM_event_absolute_delta_y(const struct wmEvent *event);
+void WM_event_xr_data(const struct wmEvent *event,
+ char **action_set,
+ char **action,
+ char *type,
+ float state[2],
+ float state_other[2],
+ float *float_threshold,
+ float controller_loc[3],
+ float controller_rot[4],
+ float controller_loc_other[3],
+ float controller_rot_other[4],
+ float *eye_lens,
+ float eye_viewmat[4][4],
+ bool *bimanual);
+bool WM_event_is_xr(const struct wmEvent *event);
+
#ifdef WITH_INPUT_IME
bool WM_event_is_ime_switch(const struct wmEvent *event);
#endif
@@ -982,8 +1000,11 @@ void WM_xr_session_base_pose_reset(wmXrData *xr);
bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]);
bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_rotation[4]);
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
+ bool from_selection_eye,
float r_viewmat[4][4],
- float *r_focal_len);
+ float *r_focal_len,
+ float *r_clip_start,
+ float *r_clip_end);
bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3]);
@@ -996,6 +1017,15 @@ 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_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]);
+void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]);
+bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale);
+void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale);
+void WM_xr_session_state_navigation_reset(struct wmXrSessionState *state);
+
+struct ARegionType *WM_xr_surface_controller_region_type_get(void);
/* wm_xr_actions.c */
/* XR action functions to be called pre-XR session start.
@@ -1060,20 +1090,24 @@ void WM_xr_haptic_action_stop(wmXrData *xr,
const char *subaction_path);
/* wm_xr_actionmap.c */
-XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime,
+void WM_xr_actionconfig_init(struct bContext *C);
+void WM_xr_actionconfig_update_tag(XrActionMap *actionmap, XrActionMapItem *ami);
+void WM_xr_actionconfig_update(XrSessionSettings *settings);
+
+XrActionConfig *WM_xr_actionconfig_new(XrSessionSettings *settings,
+ const char *name,
+ bool user_defined);
+bool WM_xr_actionconfig_remove(XrSessionSettings *settings, XrActionConfig *actionconf);
+XrActionConfig *WM_xr_actionconfig_active_get(XrSessionSettings *settings);
+void WM_xr_actionconfig_active_set(XrSessionSettings *settings, const char *name);
+
+XrActionMap *WM_xr_actionmap_new(XrActionConfig *actionconf,
const char *name,
bool replace_existing);
-void WM_xr_actionmap_ensure_unique(struct wmXrRuntimeData *runtime, XrActionMap *actionmap);
-XrActionMap *WM_xr_actionmap_add_copy(struct wmXrRuntimeData *runtime, XrActionMap *am_src);
-bool WM_xr_actionmap_remove(struct wmXrRuntimeData *runtime, XrActionMap *actionmap);
-XrActionMap *WM_xr_actionmap_find(struct wmXrRuntimeData *runtime, const char *name);
-void WM_xr_actionmap_clear(XrActionMap *actionmap);
-void WM_xr_actionmaps_clear(struct wmXrRuntimeData *runtime);
-ListBase *WM_xr_actionmaps_get(struct wmXrRuntimeData *runtime);
-short WM_xr_actionmap_active_index_get(const struct wmXrRuntimeData *runtime);
-void WM_xr_actionmap_active_index_set(struct wmXrRuntimeData *runtime, short idx);
-short WM_xr_actionmap_selected_index_get(const struct wmXrRuntimeData *runtime);
-void WM_xr_actionmap_selected_index_set(struct wmXrRuntimeData *runtime, short idx);
+void WM_xr_actionmap_ensure_unique(XrActionConfig *actionconf, XrActionMap *actionmap);
+XrActionMap *WM_xr_actionmap_add_copy(XrActionConfig *actionconf, XrActionMap *am_src);
+bool WM_xr_actionmap_remove(XrActionConfig *actionconf, XrActionMap *actionmap);
+XrActionMap *WM_xr_actionmap_find(XrActionConfig *actionconf, const char *name);
XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
const char *name,
@@ -1094,6 +1128,25 @@ bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *am
XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name);
#endif /* WITH_XR_OPENXR */
+/* wm.c */
+/* These need to be accessible even when WITH_XR_OPENXR is not defined since actionmaps can be
+ * stored in files. */
+void WM_xr_actionconfig_free(XrActionConfig *actionconf);
+void WM_xr_actionconfig_clear(XrActionConfig *actionconf);
+void WM_xr_actionmap_clear(XrActionMap *actionmap);
+void WM_xr_actionmap_item_properties_free(XrActionMapItem *ami);
+void WM_xr_actionmap_item_bindings_clear(XrActionMapItem *ami);
+
+/* 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);
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 588c895c742..2f98834703c 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -109,17 +109,20 @@
#pragma once
struct ID;
+struct IDProperty;
struct ImBuf;
struct bContext;
struct wmDrag;
struct wmDropBox;
struct wmEvent;
struct wmOperator;
+struct wmOperatorType;
struct wmWindowManager;
#include "BLI_compiler_attrs.h"
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
+#include "DNA_xr_types.h"
#include "RNA_types.h"
/* exported types for WM */
@@ -723,6 +726,41 @@ typedef struct wmXrActionState {
} wmXrActionState;
#endif
+typedef struct wmXrActionData {
+ /** Action set name. */
+ char action_set[64];
+ /** Action name. */
+ char action[64];
+ /** Type. */
+ eXrActionType type;
+ /** State. Set appropriately based on type. */
+ float state[2];
+ /** State of the other subaction path for bimanual actions. */
+ float state_other[2];
+
+ /** Input threshold for float/vector2f actions. */
+ float float_threshold;
+
+ /** Controller aim pose corresponding to the action's subaction path. */
+ float controller_loc[3];
+ float controller_rot[4];
+ /** Controller aim pose of the other subaction path for bimanual actions. */
+ float controller_loc_other[3];
+ float controller_rot_other[4];
+
+ /** Lens (focal length) of the selection eye. */
+ float eye_lens;
+ /** Viewmat of the selection eye. */
+ float eye_viewmat[4][4];
+
+ /** Operator. */
+ struct wmOperatorType *ot;
+ struct IDProperty *op_properties;
+
+ /** Whether bimanual interaction is occuring. */
+ bool bimanual;
+} wmXrActionData;
+
/** Timer flags. */
typedef enum {
/** Do not attempt to free customdata pointer even if non-NULL. */
@@ -797,6 +835,14 @@ typedef struct wmOperatorType {
const struct wmEvent *) ATTR_WARN_UNUSED_RESULT;
/**
+ * Same as invoke() but intended to be called from an XR session.
+ * The event type should be EVT_XR_ACTION and custom data type EVT_DATA_XR.
+ */
+ int (*invoke_3d)(struct bContext *,
+ struct wmOperator *,
+ const struct wmEvent *) ATTR_WARN_UNUSED_RESULT;
+
+ /**
* Called when a modal operator is canceled (not used often).
* Internal cleanup can be done here if needed.
*/
@@ -813,6 +859,14 @@ typedef struct wmOperatorType {
const struct wmEvent *) ATTR_WARN_UNUSED_RESULT;
/**
+ * Same as modal() but intended to be called from an XR session.
+ * The event type should be EVT_XR_ACTION and custom data type EVT_DATA_XR.
+ */
+ int (*modal_3d)(struct bContext *,
+ struct wmOperator *,
+ const struct wmEvent *) ATTR_WARN_UNUSED_RESULT;
+
+ /**
* Verify if the operator can be executed in the current context, note
* that the operator might still fail to execute even if this return true.
*/
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 0b7d5e5f1f4..27fe0d1c7b5 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -106,6 +106,32 @@ 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 (XrActionConfig *, ac, &xr_data->session_settings.actionconfigs) {
+ BLO_write_struct(writer, XrActionConfig, ac);
+
+ /* Only write actionmaps for user configs. */
+ if ((ac->flag & XR_ACTIONCONF_USER) != 0) {
+ LISTBASE_FOREACH (XrActionMap *, am, &ac->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 (XrActionMapBinding *, amb, &ami->bindings) {
+ BLO_write_struct(writer, XrActionMapBinding, amb);
+ }
+ }
+ }
+ }
+ }
+
+ 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)
@@ -134,6 +160,39 @@ 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.actionconfigs);
+
+ LISTBASE_FOREACH (XrActionConfig *, ac, &xr_data->session_settings.actionconfigs) {
+ BLO_read_list(reader, &ac->actionmaps);
+
+ LISTBASE_FOREACH (XrActionMap *, am, &ac->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->bindings);
+ }
+ }
+ }
+
+ BLO_read_data_address(reader, &xr_data->session_settings.defaultconf);
+ BLO_read_data_address(reader, &xr_data->session_settings.addonconf);
+ BLO_read_data_address(reader, &xr_data->session_settings.userconf);
+
+ BLO_read_list(reader, &xr_data->session_settings.mocap_objects);
}
static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
@@ -227,6 +286,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,
@@ -433,6 +496,55 @@ void WM_operator_handlers_clear(wmWindowManager *wm, wmOperatorType *ot)
}
}
+void WM_xr_actionmap_item_bindings_clear(XrActionMapItem *ami)
+{
+ BLI_freelistN(&ami->bindings);
+
+ ami->selbinding = 0;
+}
+
+void WM_xr_actionmap_item_properties_free(XrActionMapItem *ami)
+{
+ if (ami->op_properties_ptr) {
+ WM_operator_properties_free(ami->op_properties_ptr);
+ MEM_freeN(ami->op_properties_ptr);
+ ami->op_properties_ptr = NULL;
+ ami->op_properties = NULL;
+ }
+ else {
+ BLI_assert(ami->op_properties == NULL);
+ }
+}
+
+void WM_xr_actionmap_clear(XrActionMap *actionmap)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ WM_xr_actionmap_item_bindings_clear(ami);
+ WM_xr_actionmap_item_properties_free(ami);
+ }
+
+ BLI_freelistN(&actionmap->items);
+
+ actionmap->selitem = 0;
+}
+
+void WM_xr_actionconfig_clear(XrActionConfig *actionconf)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &actionconf->actionmaps) {
+ WM_xr_actionmap_clear(am);
+ }
+
+ BLI_freelistN(&actionconf->actionmaps);
+
+ actionconf->selactionmap = actionconf->actactionmap = 0;
+}
+
+void WM_xr_actionconfig_free(XrActionConfig *actionconf)
+{
+ WM_xr_actionconfig_clear(actionconf);
+ MEM_freeN(actionconf);
+}
+
/* ****************************************** */
void WM_keyconfig_reload(bContext *C)
@@ -507,6 +619,9 @@ void WM_check(bContext *C)
/* Case: fileread. */
if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) {
WM_keyconfig_init(C);
+#ifdef WITH_XR_OPENXR
+ WM_xr_actionconfig_init(C);
+#endif
WM_autosave_init(wm);
}
@@ -595,6 +710,13 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
WM_keyconfig_free(keyconf);
}
+ XrActionConfig *actionconf;
+ while ((actionconf = BLI_pophead(&wm->xr.session_settings.actionconfigs))) {
+ WM_xr_actionconfig_free(actionconf);
+ }
+
+ BLI_freelistN(&wm->xr.session_settings.mocap_objects);
+
BLI_freelistN(&wm->notifier_queue);
if (wm->message_bus != NULL) {
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 7b5691b99a0..364aab9482a 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -486,6 +486,75 @@ int WM_event_absolute_delta_y(const struct wmEvent *event)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name XR Input Access
+ * \{ */
+
+void WM_event_xr_data(const wmEvent *event,
+ char **action_set,
+ char **action,
+ char *type,
+ float state[2],
+ float state_other[2],
+ float *float_threshold,
+ float controller_loc[3],
+ float controller_rot[4],
+ float controller_loc_other[3],
+ float controller_rot_other[4],
+ float *eye_lens,
+ float eye_viewmat[4][4],
+ bool *bimanual)
+{
+ const wmXrActionData *data = event->customdata;
+
+ if (action_set) {
+ strcpy(*action_set, data->action_set);
+ }
+ if (action) {
+ strcpy(*action, data->action);
+ }
+ if (type) {
+ *type = data->type;
+ }
+ if (state) {
+ copy_v2_v2(state, data->state);
+ }
+ if (state_other) {
+ copy_v2_v2(state_other, data->state_other);
+ }
+ if (float_threshold) {
+ *float_threshold = data->float_threshold;
+ }
+ if (controller_loc) {
+ copy_v3_v3(controller_loc, data->controller_loc);
+ }
+ if (controller_rot) {
+ copy_v4_v4(controller_rot, data->controller_rot);
+ }
+ if (controller_loc_other) {
+ copy_v3_v3(controller_loc_other, data->controller_loc_other);
+ }
+ if (controller_rot_other) {
+ copy_v4_v4(controller_rot_other, data->controller_rot_other);
+ }
+ if (eye_lens) {
+ *eye_lens = data->eye_lens;
+ }
+ if (eye_viewmat) {
+ copy_m4_m4(eye_viewmat, data->eye_viewmat);
+ }
+ if (bimanual) {
+ *bimanual = data->bimanual;
+ }
+}
+
+bool WM_event_is_xr(const struct wmEvent *event)
+{
+ return (event->custom == EVT_DATA_XR);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Event IME Input Access
* \{ */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 537d5264ba9..f025fd43b49 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -83,6 +83,7 @@
#include "wm.h"
#include "wm_event_system.h"
#include "wm_event_types.h"
+#include "wm_surface.h"
#include "wm_window.h"
#include "DEG_depsgraph.h"
@@ -1341,7 +1342,19 @@ static int wm_operator_invoke(bContext *C,
ot->idname);
}
- if (op->type->invoke && event) {
+ if (op->type->invoke_3d && event && (event->type == EVT_XR_ACTION)) {
+ if (op->type->flag & OPTYPE_UNDO) {
+ wm->op_undo_depth++;
+ }
+
+ retval = op->type->invoke_3d(C, op, event);
+ OPERATOR_RETVAL_CHECK(retval);
+
+ if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
+ wm->op_undo_depth--;
+ }
+ }
+ else if (op->type->invoke && event) {
wm_region_mouse_co(C, event);
if (op->type->flag & OPTYPE_UNDO) {
@@ -2227,7 +2240,7 @@ static int wm_handler_operator_call(bContext *C,
* nothing to do in this case.
*/
}
- else if (ot->modal) {
+ else if (ot->modal || ot->modal_3d) {
/* We set context to where modal handler came from. */
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
@@ -2245,7 +2258,15 @@ static int wm_handler_operator_call(bContext *C,
}
/* Warning, after this call all context data and 'event' may be freed. see check below. */
- retval = ot->modal(C, op, event);
+ if (ot->modal_3d && event->type == EVT_XR_ACTION) {
+ retval = ot->modal_3d(C, op, event);
+ }
+ else if (ot->modal) {
+ retval = ot->modal(C, op, event);
+ }
+ else {
+ /* Pass through. An "XR operator" (only modal_3d) received a non-XR event.*/
+ }
OPERATOR_RETVAL_CHECK(retval);
if (ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
@@ -3434,6 +3455,71 @@ static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event)
* Handle events for all windows, run from the #WM_main event loop.
* \{ */
+#ifdef WITH_XR_OPENXR
+static void wm_event_handle_xrevent(
+ bContext *C, wmWindowManager *wm, wmWindow *win, bScreen *screen, wmEvent *event)
+{
+ int action = 0;
+
+ /* Find a valid region for XR operator execution and modal handling. */
+ ED_screen_areas_iter (win, screen, area) {
+ CTX_wm_area_set(C, area);
+ if (!CTX_wm_view3d(C)) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ if (!WM_region_use_viewport(area, region)) {
+ continue;
+ }
+ CTX_wm_region_set(C, region);
+
+ action |= wm_handlers_do(C, event, &win->modalhandlers);
+
+ if ((action & WM_HANDLER_BREAK) == 0) {
+ wmXrActionData *actiondata = event->customdata;
+ if ((actiondata->ot->modal || actiondata->ot->modal_3d) && event->val == KM_RELEASE) {
+ /* Don't execute modal operators on release. */
+ }
+ else {
+ PointerRNA properties = {.type = actiondata->ot->srna,
+ .data = actiondata->op_properties};
+ if (actiondata->ot->invoke || actiondata->ot->invoke_3d) {
+ /* Invoke operator, either executing operator or transferring responsibility to window
+ * modal handlers. */
+ wm_operator_invoke(C,
+ actiondata->ot,
+ event,
+ actiondata->op_properties ? &properties : NULL,
+ NULL,
+ false,
+ false);
+ }
+ else {
+ /* Execute operator. */
+ wmOperator *op = wm_operator_create(
+ wm, actiondata->ot, actiondata->op_properties ? &properties : NULL, NULL);
+ if ((WM_operator_call(C, op) & OPERATOR_HANDLED) == 0) {
+ WM_operator_free(op);
+ }
+ }
+ }
+ action |= WM_HANDLER_BREAK;
+ }
+
+ CTX_wm_region_set(C, NULL);
+ break;
+ }
+
+ if (action & WM_HANDLER_BREAK) {
+ break;
+ }
+ }
+
+ CTX_wm_area_set(C, NULL);
+}
+#endif /* WITH_XR_OPENXR */
+
/* Called in main loop. */
/* Goes over entire hierarchy: events -> window -> screen -> area -> region. */
void wm_event_do_handlers(bContext *C)
@@ -3531,6 +3617,16 @@ void wm_event_do_handlers(bContext *C)
CTX_wm_window_set(C, win);
+#ifdef WITH_XR_OPENXR
+ if (event->type == EVT_XR_ACTION) {
+ wm_event_handle_xrevent(C, wm, win, screen, event);
+ BLI_remlink(&win->event_queue, event);
+ wm_event_free(event);
+ /* Skip mouse event handling below, which is unnecessary for XR events. */
+ continue;
+ }
+#endif
+
/* Clear tool-tip on mouse move. */
if (screen->tool_tip && screen->tool_tip->exit_on_event) {
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
@@ -5083,6 +5179,24 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
#endif
}
+#ifdef WITH_XR_OPENXR
+void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val)
+{
+ BLI_assert(val == KM_PRESS || val == KM_RELEASE);
+
+ wmEvent event = {
+ .type = EVT_XR_ACTION,
+ .val = val,
+ .is_repeat = false,
+ .custom = EVT_DATA_XR,
+ .customdata = actiondata,
+ .customdatafree = 1,
+ };
+
+ wm_event_add(win, &event);
+}
+#endif /* WITH_XR_OPENXR */
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index a5ebf988edd..24a3b58fc26 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -204,6 +204,18 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist)
WM_event_remove_handlers(C, &win->modalhandlers);
ED_screen_exit(C, win, WM_window_get_active_screen(win));
}
+
+ /* Free XR actionconfigs and motion capture objects. */
+ XrSessionSettings *xrsettings = &wm->xr.session_settings;
+ XrActionConfig *actionconf;
+ while ((actionconf = BLI_pophead(&xrsettings->actionconfigs))) {
+ WM_xr_actionconfig_free(actionconf);
+ }
+ xrsettings->defaultconf = NULL;
+ xrsettings->addonconf = NULL;
+ xrsettings->userconf = NULL;
+
+ BLI_freelistN(&xrsettings->mocap_objects);
}
/* reset active window */
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 578699dda60..b087bd9d668 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -36,6 +36,8 @@
#include "BKE_context.h"
+#include "DEG_depsgraph.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -45,6 +47,9 @@
#include "ED_screen.h"
#include "ED_select_utils.h"
+#include "ED_view3d.h"
+
+#include "../editors/space_view3d/view3d_intern.h"
#include "UI_interface.h"
@@ -202,6 +207,60 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+int WM_gesture_box_invoke_3d(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BLI_assert(event->type == EVT_XR_ACTION);
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ const wmXrActionData *actiondata = event->customdata;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ float lens_prev;
+ float clip_start_prev, clip_end_prev;
+ int mval[2];
+ int retval;
+
+ wmEvent event_mut;
+ memcpy(&event_mut, event, sizeof(wmEvent));
+
+ /* Replace window view parameters with XR surface counterparts. */
+ ED_view3d_view_params_get(v3d, rv3d, &lens_prev, &clip_start_prev, &clip_end_prev, NULL);
+ ED_view3d_view_params_set(depsgraph,
+ scene,
+ v3d,
+ region,
+ actiondata->eye_lens,
+ xr->session_settings.clip_start,
+ xr->session_settings.clip_end,
+ NULL);
+
+ map_to_pixel(mval,
+ actiondata->controller_loc,
+ actiondata->eye_viewmat,
+ rv3d->winmat,
+ region->winx,
+ region->winy);
+
+ event_mut.x = region->winrct.xmin + mval[0];
+ event_mut.y = region->winrct.ymin + mval[1];
+
+ RNA_boolean_set(op->ptr, "wait_for_input", false);
+
+ retval = WM_gesture_box_invoke(C, op, &event_mut);
+
+ /* Restore window view. */
+ ED_view3d_view_params_set(
+ depsgraph, scene, v3d, region, lens_prev, clip_start_prev, clip_end_prev, NULL);
+
+ return retval;
+}
+
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
@@ -280,6 +339,85 @@ int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+int WM_gesture_box_modal_3d(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BLI_assert(event->type == EVT_XR_ACTION);
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ const bool release = (event->val == KM_RELEASE);
+
+ const wmXrActionData *actiondata = event->customdata;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ float lens_prev;
+ float clip_start_prev, clip_end_prev;
+ float viewmat_prev[4][4];
+ int mval[2];
+ int retval;
+
+ wmEvent event_mut;
+ memcpy(&event_mut, event, sizeof(wmEvent));
+
+ /* Since this function is called in a window context, we need to replace the
+ * window view parameters with the XR surface counterparts to get a correct
+ * result for some operators (e.g. GPU select). */
+ ED_view3d_view_params_get(
+ v3d, rv3d, &lens_prev, &clip_start_prev, &clip_end_prev, release ? viewmat_prev : NULL);
+ ED_view3d_view_params_set(depsgraph,
+ scene,
+ v3d,
+ region,
+ actiondata->eye_lens,
+ xr->session_settings.clip_start,
+ xr->session_settings.clip_end,
+ release ? actiondata->eye_viewmat : NULL);
+
+ map_to_pixel(mval,
+ actiondata->controller_loc,
+ actiondata->eye_viewmat,
+ rv3d->winmat,
+ region->winx,
+ region->winy);
+
+ if (event->val == KM_PRESS) {
+ event_mut.type = MOUSEMOVE;
+ {
+ wmGesture *gesture = op->customdata;
+ gesture->is_active = true;
+ }
+ event_mut.x = region->winrct.xmin + mval[0];
+ event_mut.y = region->winrct.ymin + mval[1];
+ }
+ else if (event->val == KM_RELEASE) {
+ event_mut.type = EVT_MODAL_MAP;
+ event_mut.val = GESTURE_MODAL_SELECT;
+ }
+ else {
+ /* XR events currently only support press and release. */
+ BLI_assert(false);
+ }
+
+ retval = WM_gesture_box_modal(C, op, &event_mut);
+
+ /* Restore window view. */
+ ED_view3d_view_params_set(depsgraph,
+ scene,
+ v3d,
+ region,
+ lens_prev,
+ clip_start_prev,
+ clip_end_prev,
+ release ? viewmat_prev : NULL);
+
+ return retval;
+}
+
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
{
gesture_modal_end(C, op);
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 81dcc5ccea0..79368fa9eef 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -3760,87 +3760,6 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot)
/** \} */
-#ifdef WITH_XR_OPENXR
-
-static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data)
-{
- const bool session_exists = WM_xr_session_exists(xr_data);
-
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) {
- if (slink->spacetype == SPACE_VIEW3D) {
- View3D *v3d = (View3D *)slink;
-
- if (v3d->flag & V3D_XR_SESSION_MIRROR) {
- ED_view3d_xr_mirror_update(area, v3d, session_exists);
- }
-
- if (session_exists) {
- wmWindowManager *wm = bmain->wm.first;
- const Scene *scene = WM_windows_scene_get_from_screen(wm, screen);
-
- ED_view3d_xr_shading_update(wm, v3d, scene);
- }
- /* Ensure no 3D View is tagged as session root. */
- else {
- v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT;
- }
- }
- }
- }
- }
-
- WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL);
-}
-
-static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data)
-{
- /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */
- wm_xr_session_update_screen(G_MAIN, xr_data);
-}
-
-static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win = CTX_wm_window(C);
- View3D *v3d = CTX_wm_view3d(C);
-
- /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */
- if (wm_xr_init(wm) == false) {
- return OPERATOR_CANCELLED;
- }
-
- v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT;
- wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb);
- wm_xr_session_update_screen(bmain, &wm->xr);
-
- WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-static void WM_OT_xr_session_toggle(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Toggle VR Session";
- ot->idname = "WM_OT_xr_session_toggle";
- ot->description =
- "Open a view for use with virtual reality headsets, or close it if already "
- "opened";
-
- /* callbacks */
- ot->exec = wm_xr_session_toggle_exec;
- ot->poll = ED_operator_view3d_active;
-
- /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the
- * UI instead. Not meant as a permanent solution. */
- ot->flag = OPTYPE_INTERNAL;
-}
-
-#endif /* WITH_XR_OPENXR */
-
/* -------------------------------------------------------------------- */
/** \name Operator Registration & Keymaps
* \{ */
@@ -3882,9 +3801,6 @@ void wm_operatortypes_register(void)
WM_operatortype_append(WM_OT_call_panel);
WM_operatortype_append(WM_OT_radial_control);
WM_operatortype_append(WM_OT_stereo3d_set);
-#ifdef WITH_XR_OPENXR
- WM_operatortype_append(WM_OT_xr_session_toggle);
-#endif
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
#endif
@@ -3895,6 +3811,10 @@ void wm_operatortypes_register(void)
/* gizmos */
WM_operatortype_append(GIZMOGROUP_OT_gizmo_select);
WM_operatortype_append(GIZMOGROUP_OT_gizmo_tweak);
+
+#ifdef WITH_XR_OPENXR
+ wm_xr_operatortypes_register();
+#endif
}
/* circleselect-like modal operators */
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 8baf4a0e013..55b0d267cec 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1561,7 +1561,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/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index 787c840de8a..f6d24fbe9b1 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -33,6 +33,10 @@ struct ARegion;
struct GHOST_TabletData;
struct ScrArea;
+#ifdef WITH_XR_OPENXR
+struct wmXrActionData;
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -147,6 +151,9 @@ void wm_event_free_handler(wmEventHandler *handler);
void wm_event_do_handlers(bContext *C);
void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata);
+#ifdef WITH_XR_OPENXR
+void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val);
+#endif
void wm_event_do_depsgraph(bContext *C, bool is_after_open_file);
void wm_event_do_refresh_wm_and_depsgraph(bContext *C);
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 905c57d901a..fabd77847c4 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -34,6 +34,7 @@ enum {
EVT_DATA_TIMER = 2,
EVT_DATA_DRAGDROP = 3,
EVT_DATA_NDOF_MOTION = 4,
+ EVT_DATA_XR = 5,
};
/* tablet active, matches GHOST_TTabletMode */
@@ -341,6 +342,10 @@ enum {
/* could become gizmo callback */
EVT_GIZMO_UPDATE = 0x5025, /* 20517 */
+
+ /* XR events: 0x503x */
+ EVT_XR_ACTION = 0x5030, /* 20528 */
+
/* ********** End of Blender internal events. ********** */
};
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 8891840cb75..2b6a2abd64b 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -22,6 +22,7 @@
* representation of the OpenXR runtime connection within the application.
*/
+#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_report.h"
@@ -54,6 +55,7 @@ static void wm_xr_error_handler(const GHOST_XrError *error)
wmWindowManager *wm = handler_data->wm;
BKE_reports_clear(&wm->reports);
+
WM_report(RPT_ERROR, error->user_message);
WM_report_banner_show();
@@ -130,14 +132,16 @@ void wm_xr_exit(wmWindowManager *wm)
}
}
-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. */
+ /* Process OpenXR action events and dispatch to XR surface / window queues. */
if (WM_xr_session_is_ready(&wm->xr)) {
- wm_xr_session_actions_update(&wm->xr);
+ wm_xr_session_actions_update(C);
}
/* wm_window_process_events() uses the return value to determine if it can put the main thread
@@ -173,7 +177,6 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime)
(*runtime)->context = 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_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
index 8903305adb4..42149d5552d 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
@@ -26,24 +26,67 @@
#include <string.h>
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
-#include "GHOST_Types.h"
-
#include "MEM_guardedalloc.h"
#include "WM_api.h"
#include "WM_types.h"
-#include "wm_xr_intern.h"
-
+#define WM_XR_ACTIONCONF_STR_DEFAULT "blender"
#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap"
#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action"
#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding"
+/* Replacement for U.keyconfigstr for actionconfigs. */
+static char g_xr_actionconfigstr[64];
+
+/* Actionconfig update flag. */
+enum {
+ WM_XR_ACTIONCONF_UPDATE_ACTIVE = (1 << 0), /* Update active actionconfig. */
+ WM_XR_ACTIONCONF_UPDATE_ALL = (1 << 1), /* Update all actionconfigs. */
+ WM_XR_ACTIONCONF_UPDATE_ENSURE =
+ (1 << 2), /* Skip actionmap/item flag checks when updating actionconfigs. */
+};
+
+static char g_xr_actionconfig_update_flag = 0;
+
+/* -------------------------------------------------------------------- */
+
+void WM_xr_actionconfig_update_tag(XrActionMap *actionmap, XrActionMapItem *ami)
+{
+ g_xr_actionconfig_update_flag |= WM_XR_ACTIONCONF_UPDATE_ACTIVE;
+
+ if (actionmap) {
+ actionmap->flag |= XR_ACTIONMAP_UPDATE;
+ }
+ if (ami) {
+ ami->flag |= XR_ACTIONMAP_ITEM_UPDATE;
+ }
+}
+
+#if 0 /* Currently unused. */
+static void wm_xr_actionconfig_update_active(XrActionMap *actionmap, XrActionMapItem *ami, bool ensure)
+{
+ WM_xr_actionconfig_update_tag(actionmap, ami);
+ if (ensure) {
+ g_xr_actionconfig_update_flag |= WM_XR_ACTIONCONF_UPDATE_ENSURE;
+ }
+}
+
+static void wm_xr_actionconfig_update_all(bool ensure)
+{
+ g_xr_actionconfig_update_flag |= WM_XR_ACTIONCONF_UPDATE_ALL;
+ if (ensure) {
+ g_xr_actionconfig_update_flag |= WM_XR_ACTIONCONF_UPDATE_ENSURE;
+ }
+}
+#endif
+
/* -------------------------------------------------------------------- */
/** \name Action Map Binding
*
@@ -51,16 +94,16 @@
* \{ */
XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami,
- const char *name,
+ const char *idname,
bool replace_existing)
{
- XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name);
+ XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, idname);
if (amb_prev && replace_existing) {
return amb_prev;
}
XrActionMapBinding *amb = MEM_callocN(sizeof(XrActionMapBinding), __func__);
- BLI_strncpy(amb->name, name, MAX_NAME);
+ BLI_strncpy(amb->name, idname, MAX_NAME);
if (amb_prev) {
WM_xr_actionmap_binding_ensure_unique(ami, amb);
}
@@ -173,31 +216,12 @@ XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const cha
* Item in an XR action map, that maps an XR event to an operator, pose, or haptic output.
* \{ */
-static void wm_xr_actionmap_item_bindings_clear(XrActionMapItem *ami)
-{
- BLI_freelistN(&ami->bindings);
- ami->selbinding = 0;
-}
-
static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami)
{
WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op);
WM_operator_properties_sanitize(ami->op_properties_ptr, 1);
}
-static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami)
-{
- if (ami->op_properties_ptr) {
- WM_operator_properties_free(ami->op_properties_ptr);
- MEM_freeN(ami->op_properties_ptr);
- ami->op_properties_ptr = NULL;
- ami->op_properties = NULL;
- }
- else {
- BLI_assert(ami->op_properties == NULL);
- }
-}
-
/**
* Similar to #wm_xr_actionmap_item_properties_set()
* but checks for the #eXrActionType and #wmOperatorType having changed.
@@ -211,13 +235,13 @@ void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami)
break;
case XR_POSE_INPUT:
case XR_VIBRATION_OUTPUT:
- wm_xr_actionmap_item_properties_free(ami);
+ WM_xr_actionmap_item_properties_free(ami);
memset(ami->op, 0, sizeof(ami->op));
return;
}
if (ami->op[0] == 0) {
- wm_xr_actionmap_item_properties_free(ami);
+ WM_xr_actionmap_item_properties_free(ami);
return;
}
@@ -237,7 +261,33 @@ void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami)
}
}
else {
- wm_xr_actionmap_item_properties_free(ami);
+ WM_xr_actionmap_item_properties_free(ami);
+ }
+ }
+}
+
+static void wm_xr_actionmap_item_properties_update_ot_from_list(ListBase *am_lb, bool ensure)
+{
+ if (ensure) {
+ LISTBASE_FOREACH (XrActionMap *, am, am_lb) {
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &am->items) {
+ WM_xr_actionmap_item_properties_update_ot(ami);
+ ami->flag &= ~XR_ACTIONMAP_ITEM_UPDATE;
+ }
+ am->flag &= ~XR_ACTIONMAP_UPDATE;
+ }
+ }
+ else {
+ LISTBASE_FOREACH (XrActionMap *, am, am_lb) {
+ if ((am->flag & XR_ACTIONMAP_UPDATE) != 0) {
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &am->items) {
+ if ((ami->flag & XR_ACTIONMAP_ITEM_UPDATE) != 0) {
+ WM_xr_actionmap_item_properties_update_ot(ami);
+ ami->flag &= ~XR_ACTIONMAP_ITEM_UPDATE;
+ }
+ }
+ am->flag &= ~XR_ACTIONMAP_UPDATE;
+ }
}
}
}
@@ -248,7 +298,7 @@ XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
{
XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name);
if (ami_prev && replace_existing) {
- wm_xr_actionmap_item_properties_free(ami_prev);
+ WM_xr_actionmap_item_properties_free(ami_prev);
return ami_prev;
}
@@ -263,12 +313,14 @@ XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
/* Set type to float (button) input by default. */
ami->type = XR_FLOAT_INPUT;
+ WM_xr_actionconfig_update_tag(actionmap, ami);
+
return ami;
}
static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap,
const char *name,
- const XrActionMapItem *amiexcept)
+ XrActionMapItem *amiexcept)
{
LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
if (STREQLEN(name, ami->name, MAX_NAME) && (ami != amiexcept)) {
@@ -308,25 +360,32 @@ void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem
BLI_strncpy(ami->name, name, MAX_NAME);
}
-static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami)
+static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src)
{
- XrActionMapItem *amin = MEM_dupallocN(ami);
+ XrActionMapItem *ami_dst = MEM_dupallocN(ami_src);
+
+ ami_dst->prev = ami_dst->next = NULL;
+ BLI_listbase_clear(&ami_dst->bindings);
+ ami_dst->flag &= ~XR_ACTIONMAP_ITEM_UPDATE;
- amin->prev = amin->next = NULL;
+ LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami_src->bindings) {
+ XrActionMapBinding *amb_new = wm_xr_actionmap_binding_copy(amb);
+ BLI_addtail(&ami_dst->bindings, amb_new);
+ }
- if (amin->op_properties) {
- amin->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
- WM_operator_properties_create(amin->op_properties_ptr, amin->op);
+ 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);
- amin->op_properties = IDP_CopyProperty(amin->op_properties);
- amin->op_properties_ptr->data = amin->op_properties;
+ ami_dst->op_properties = IDP_CopyProperty(ami_dst->op_properties);
+ ami_dst->op_properties_ptr->data = ami_dst->op_properties;
}
else {
- amin->op_properties = NULL;
- amin->op_properties_ptr = NULL;
+ ami_dst->op_properties = NULL;
+ ami_dst->op_properties_ptr = NULL;
}
- return amin;
+ return ami_dst;
}
XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src)
@@ -337,6 +396,8 @@ XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionM
BLI_addtail(&actionmap->items, ami_dst);
+ WM_xr_actionconfig_update_tag(actionmap, ami_dst);
+
return ami_dst;
}
@@ -345,8 +406,8 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
int idx = BLI_findindex(&actionmap->items, ami);
if (idx != -1) {
- wm_xr_actionmap_item_bindings_clear(ami);
- wm_xr_actionmap_item_properties_free(ami);
+ WM_xr_actionmap_item_bindings_clear(ami);
+ WM_xr_actionmap_item_properties_free(ami);
BLI_freelinkN(&actionmap->items, ami);
if (idx <= actionmap->selitem) {
@@ -355,6 +416,8 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
}
}
+ WM_xr_actionconfig_update_tag(actionmap, NULL);
+
return true;
}
@@ -379,9 +442,11 @@ 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)
+XrActionMap *WM_xr_actionmap_new(XrActionConfig *actionconf,
+ const char *name,
+ bool replace_existing)
{
- XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name);
+ XrActionMap *am_prev = WM_xr_actionmap_find(actionconf, name);
if (am_prev && replace_existing) {
WM_xr_actionmap_clear(am_prev);
return am_prev;
@@ -390,31 +455,32 @@ XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, boo
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(actionconf, am);
}
- BLI_addtail(&runtime->actionmaps, am);
+ BLI_addtail(&actionconf->actionmaps, am);
+
+ WM_xr_actionconfig_update_tag(am, NULL);
return am;
}
-static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime,
+static XrActionMap *wm_xr_actionmap_find_except(XrActionConfig *actionconf,
const char *name,
- const XrActionMap *am_except)
+ XrActionMap *am_except)
{
- LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ LISTBASE_FOREACH (XrActionMap *, am, &actionconf->actionmaps) {
if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) {
return am;
}
}
-
return NULL;
}
/**
* Ensure unique name among all action maps.
*/
-void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap)
+void WM_xr_actionmap_ensure_unique(XrActionConfig *actionconf, XrActionMap *actionmap)
{
char name[MAX_NAME];
char *suffix;
@@ -425,7 +491,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(actionconf, name, actionmap)) {
if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
/* Use default base name. */
BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME);
@@ -447,6 +513,7 @@ static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src)
am_dst->prev = am_dst->next = NULL;
BLI_listbase_clear(&am_dst->items);
+ am_dst->flag &= ~(XR_ACTIONMAP_UPDATE);
LISTBASE_FOREACH (XrActionMapItem *, ami, &am_src->items) {
XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(ami);
@@ -456,33 +523,35 @@ 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(XrActionConfig *actionconf, 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(actionconf, am_dst);
- BLI_addtail(&runtime->actionmaps, am_dst);
+ BLI_addtail(&actionconf->actionmaps, am_dst);
+
+ WM_xr_actionconfig_update_tag(am_dst, NULL);
return am_dst;
}
-bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap)
+bool WM_xr_actionmap_remove(XrActionConfig *actionconf, XrActionMap *actionmap)
{
- int idx = BLI_findindex(&runtime->actionmaps, actionmap);
+ int idx = BLI_findindex(&actionconf->actionmaps, actionmap);
if (idx != -1) {
WM_xr_actionmap_clear(actionmap);
- BLI_freelinkN(&runtime->actionmaps, actionmap);
+ BLI_freelinkN(&actionconf->actionmaps, actionmap);
- if (idx <= runtime->actactionmap) {
- if (--runtime->actactionmap < 0) {
- runtime->actactionmap = 0;
+ if (idx <= actionconf->actactionmap) {
+ if (--actionconf->actactionmap < 0) {
+ actionconf->actactionmap = 0;
}
}
- if (idx <= runtime->selactionmap) {
- if (--runtime->selactionmap < 0) {
- runtime->selactionmap = 0;
+ if (idx <= actionconf->selactionmap) {
+ if (--actionconf->selactionmap < 0) {
+ actionconf->selactionmap = 0;
}
}
@@ -492,9 +561,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(XrActionConfig *actionconf, const char *name)
{
- LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ LISTBASE_FOREACH (XrActionMap *, am, &actionconf->actionmaps) {
if (STREQLEN(name, am->name, MAX_NAME)) {
return am;
}
@@ -502,52 +571,147 @@ XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name)
return NULL;
}
-void WM_xr_actionmap_clear(XrActionMap *actionmap)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Action Configuration
+ *
+ * List of XR action maps.
+ * \{ */
+
+XrActionConfig *WM_xr_actionconfig_new(XrSessionSettings *settings,
+ const char *name,
+ bool user_defined)
{
- LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
- wm_xr_actionmap_item_bindings_clear(ami);
- wm_xr_actionmap_item_properties_free(ami);
+ XrActionConfig *actionconf = BLI_findstring(
+ &settings->actionconfigs, name, offsetof(XrActionConfig, name));
+ if (actionconf) {
+ WM_xr_actionconfig_clear(actionconf);
+ return actionconf;
}
- BLI_freelistN(&actionmap->items);
+ actionconf = MEM_callocN(sizeof(XrActionConfig), __func__);
+ BLI_strncpy(actionconf->name, name, sizeof(actionconf->name));
+ BLI_addtail(&settings->actionconfigs, actionconf);
+
+ if (user_defined) {
+ actionconf->flag |= XR_ACTIONCONF_USER;
+ }
- actionmap->selitem = 0;
+ return actionconf;
}
-void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
+bool WM_xr_actionconfig_remove(XrSessionSettings *settings, XrActionConfig *actionconf)
{
- LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
- WM_xr_actionmap_clear(am);
- }
+ if (BLI_findindex(&settings->actionconfigs, actionconf) != -1) {
+ if (STREQLEN(g_xr_actionconfigstr, actionconf->name, sizeof(g_xr_actionconfigstr))) {
+ BLI_strncpy(g_xr_actionconfigstr, settings->defaultconf->name, sizeof(g_xr_actionconfigstr));
+ WM_xr_actionconfig_update_tag(NULL, NULL);
+ }
- BLI_freelistN(&runtime->actionmaps);
+ BLI_remlink(&settings->actionconfigs, actionconf);
+ WM_xr_actionconfig_free(actionconf);
- runtime->actactionmap = runtime->selactionmap = 0;
-}
+ return true;
+ }
-ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime)
-{
- return &runtime->actionmaps;
+ return false;
}
-short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime)
+XrActionConfig *WM_xr_actionconfig_active_get(XrSessionSettings *settings)
{
- return runtime->actactionmap;
+ XrActionConfig *actionconf;
+
+ /* First try from preset. */
+ actionconf = BLI_findstring(
+ &settings->actionconfigs, g_xr_actionconfigstr, offsetof(XrActionConfig, name));
+ if (actionconf) {
+ return actionconf;
+ }
+
+ /* Otherwise use default. */
+ return settings->defaultconf;
}
-void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx)
+void WM_xr_actionconfig_active_set(XrSessionSettings *settings, const char *idname)
{
- runtime->actactionmap = idx;
+ /* Setting a different action configuration as active: we ensure all is
+ * updated properly before and after making the change. */
+
+ WM_xr_actionconfig_update(settings);
+
+ BLI_strncpy(g_xr_actionconfigstr, idname, sizeof(g_xr_actionconfigstr));
+
+ WM_xr_actionconfig_update_tag(NULL, NULL);
+ WM_xr_actionconfig_update(settings);
}
-short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Action Configuration API
+ *
+ * API functions for managing XR action configurations.
+ *
+ * \{ */
+
+void WM_xr_actionconfig_init(bContext *C)
{
- return runtime->selactionmap;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ XrSessionSettings *settings = &wm->xr.session_settings;
+
+ /* Create standard action configs. */
+ if (settings->defaultconf == NULL) {
+ settings->defaultconf = WM_xr_actionconfig_new(settings, WM_XR_ACTIONCONF_STR_DEFAULT, false);
+ }
+ if (settings->addonconf == NULL) {
+ settings->addonconf = WM_xr_actionconfig_new(
+ settings, WM_XR_ACTIONCONF_STR_DEFAULT " addon", false);
+ }
+ if (settings->userconf == NULL) {
+ /* Treat user config as user-defined so its actionmaps can be saved to files. */
+ settings->userconf = WM_xr_actionconfig_new(
+ settings, WM_XR_ACTIONCONF_STR_DEFAULT " user", true);
+ }
}
-void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx)
+void WM_xr_actionconfig_update(XrSessionSettings *settings)
{
- runtime->selactionmap = idx;
+ if (G.background) {
+ return;
+ }
+
+ if (g_xr_actionconfig_update_flag == 0) {
+ return;
+ }
+
+ const bool ensure = ((g_xr_actionconfig_update_flag & WM_XR_ACTIONCONF_UPDATE_ENSURE) != 0);
+
+ if ((g_xr_actionconfig_update_flag & WM_XR_ACTIONCONF_UPDATE_ALL) != 0) {
+ /* Update properties for all actionconfigs. */
+ LISTBASE_FOREACH (XrActionConfig *, ac, &settings->actionconfigs) {
+ wm_xr_actionmap_item_properties_update_ot_from_list(&ac->actionmaps, ensure);
+ }
+
+ g_xr_actionconfig_update_flag &= ~(WM_XR_ACTIONCONF_UPDATE_ALL |
+ WM_XR_ACTIONCONF_UPDATE_ACTIVE);
+ }
+
+ if ((g_xr_actionconfig_update_flag & WM_XR_ACTIONCONF_UPDATE_ACTIVE) != 0) {
+ /* Update properties for active actionconfig. */
+ XrActionConfig *ac = WM_xr_actionconfig_active_get(settings);
+ if (ac) {
+ wm_xr_actionmap_item_properties_update_ot_from_list(&ac->actionmaps, ensure);
+ }
+
+ g_xr_actionconfig_update_flag &= ~WM_XR_ACTIONCONF_UPDATE_ACTIVE;
+ }
+
+ if (ensure) {
+ g_xr_actionconfig_update_flag &= ~WM_XR_ACTIONCONF_UPDATE_ENSURE;
+ }
+
+ BLI_assert(g_xr_actionconfig_update_flag == 0);
}
/** \} */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index bbb73fc2007..3287e29cdcf 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -24,6 +24,8 @@
#include <string.h>
+#include "BKE_context.h"
+
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -31,6 +33,9 @@
#include "GHOST_C-api.h"
+#include "GPU_batch_presets.h"
+#include "GPU_immediate.h"
+#include "GPU_matrix.h"
#include "GPU_viewport.h"
#include "WM_api.h"
@@ -44,6 +49,16 @@ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
copy_v3_v3(r_mat[3], pose->position);
}
+void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4])
+{
+ wm_xr_pose_to_mat(pose, r_mat);
+
+ BLI_assert(scale > 0.0f);
+ mul_v3_fl(r_mat[0], scale);
+ mul_v3_fl(r_mat[1], scale);
+ mul_v3_fl(r_mat[2], scale);
+}
+
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
{
float iquat[4];
@@ -52,15 +67,33 @@ void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
+void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4])
+{
+ float iquat[4];
+ invert_qt_qt_normalized(iquat, pose->orientation_quat);
+ quat_to_mat4(r_imat, iquat);
+
+ BLI_assert(scale > 0.0f);
+ scale = 1.0f / scale;
+ mul_v3_fl(r_imat[0], scale);
+ mul_v3_fl(r_imat[1], scale);
+ mul_v3_fl(r_imat[2], scale);
+
+ translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
+}
+
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
- float r_view_mat[4][4],
- float r_proj_mat[4][4])
+ const wmXrSessionState *session_state,
+ float r_viewmat[4][4],
+ float r_viewmat_base[4][4],
+ float r_projmat[4][4])
{
GHOST_XrPose eye_pose;
- float eye_inv[4][4], base_inv[4][4];
+ float eye_inv[4][4], base_inv[4][4], nav_inv[4][4];
+ /* Calculate inverse eye matrix. */
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
@@ -69,14 +102,15 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
if ((session_settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
}
-
wm_xr_pose_to_imat(&eye_pose, eye_inv);
- /* Calculate the base pose matrix (in world space!). */
- wm_xr_pose_to_imat(&draw_data->base_pose, base_inv);
- mul_m4_m4m4(r_view_mat, eye_inv, base_inv);
+ /* Apply base pose and navigation. */
+ wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv);
+ wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv);
+ mul_m4_m4m4(r_viewmat_base, eye_inv, base_inv);
+ mul_m4_m4m4(r_viewmat, r_viewmat_base, nav_inv);
- perspective_m4_fov(r_proj_mat,
+ perspective_m4_fov(r_projmat,
draw_view->fov.angle_left,
draw_view->fov.angle_right,
draw_view->fov.angle_up,
@@ -121,13 +155,14 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
const int display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags;
- float viewmat[4][4], winmat[4][4];
+ float viewmat[4][4], viewmat_base[4][4], winmat[4][4];
BLI_assert(WM_xr_session_is_ready(xr_data));
wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data);
- wm_xr_draw_matrices_create(draw_data, draw_view, settings, viewmat, winmat);
- wm_xr_session_state_update(settings, draw_data, draw_view, session_state);
+ wm_xr_draw_matrices_create(
+ draw_data, draw_view, settings, session_state, viewmat, viewmat_base, winmat);
+ wm_xr_session_state_update(settings, draw_data, draw_view, viewmat, viewmat_base, session_state);
if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) {
return;
@@ -153,6 +188,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
winmat,
settings->clip_start,
settings->clip_end,
+ true,
false,
true,
NULL,
@@ -172,3 +208,200 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view);
}
+
+static GPUBatch *wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_context,
+ const char *subaction_path)
+{
+ GHOST_XrControllerModelData model_data;
+
+ if (!GHOST_XrGetControllerModelData(xr_context, subaction_path, &model_data) ||
+ model_data.count_vertices < 1) {
+ return NULL;
+ }
+
+ GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, model_data.count_vertices);
+ void *vbo_data = GPU_vertbuf_get_data(vbo);
+ memcpy(
+ vbo_data, model_data.vertices, model_data.count_vertices * sizeof(model_data.vertices[0]));
+
+ GPUIndexBuf *ibo = NULL;
+ if (model_data.count_indices > 0 && ((model_data.count_indices % 3) == 0)) {
+ GPUIndexBufBuilder ibo_builder;
+ const unsigned int prim_len = model_data.count_indices / 3;
+ GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, prim_len, model_data.count_vertices);
+ for (unsigned int i = 0; i < prim_len; ++i) {
+ const uint32_t *idx = &model_data.indices[i * 3];
+ GPU_indexbuf_add_tri_verts(&ibo_builder, idx[0], idx[1], idx[2]);
+ }
+ ibo = GPU_indexbuf_build(&ibo_builder);
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
+}
+
+static void wm_xr_controller_model_draw(const XrSessionSettings *settings,
+ GHOST_XrContextHandle xr_context,
+ wmXrSessionState *state)
+{
+ GHOST_XrControllerModelData model_data;
+
+ float color[4];
+ switch (settings->controller_draw_style) {
+ case XR_CONTROLLER_DRAW_DARK:
+ case XR_CONTROLLER_DRAW_DARK_RAY:
+ color[0] = color[1] = color[2] = 0.0f, color[3] = 0.4f;
+ break;
+ case XR_CONTROLLER_DRAW_LIGHT:
+ case XR_CONTROLLER_DRAW_LIGHT_RAY:
+ color[0] = 0.422f, color[1] = 0.438f, color[2] = 0.446f, color[3] = 0.4f;
+ break;
+ }
+
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
+ GPUBatch *model = controller->model;
+ if (!model) {
+ model = controller->model = wm_xr_controller_model_batch_create(xr_context,
+ controller->subaction_path);
+ }
+
+ if (model &&
+ GHOST_XrGetControllerModelData(xr_context, controller->subaction_path, &model_data) &&
+ model_data.count_components > 0) {
+ GPU_batch_program_set_builtin(model, GPU_SHADER_3D_UNIFORM_COLOR);
+ GPU_batch_uniform_4fv(model, "color", color);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(controller->grip_mat);
+ for (unsigned int component_idx = 0; component_idx < model_data.count_components;
+ ++component_idx) {
+ const GHOST_XrControllerModelComponent *component = &model_data.components[component_idx];
+ GPU_matrix_push();
+ GPU_matrix_mul(component->transform);
+ GPU_batch_draw_range(model,
+ model->elem ? component->index_offset : component->vertex_offset,
+ model->elem ? component->index_count : component->vertex_count);
+ GPU_matrix_pop();
+ }
+ GPU_matrix_pop();
+ }
+ else {
+ /* Fallback. */
+ const float scale = 0.05f;
+ GPUBatch *sphere = GPU_batch_preset_sphere(2);
+ GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
+ GPU_batch_uniform_4fv(sphere, "color", color);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(controller->grip_mat);
+ GPU_matrix_scale_1f(scale);
+ GPU_batch_draw(sphere);
+ GPU_matrix_pop();
+ }
+ }
+}
+
+static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state)
+{
+ bool draw_ray;
+ switch (settings->controller_draw_style) {
+ case XR_CONTROLLER_DRAW_DARK:
+ case XR_CONTROLLER_DRAW_LIGHT:
+ draw_ray = false;
+ break;
+ case XR_CONTROLLER_DRAW_DARK_RAY:
+ case XR_CONTROLLER_DRAW_LIGHT_RAY:
+ draw_ray = true;
+ break;
+ }
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ immUniform1f("lineWidth", 3.0f * U.pixelsize);
+
+ if (draw_ray) {
+ const uchar color[4] = {89, 89, 255, 127};
+ const float scale = settings->clip_end;
+ float ray[3];
+
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 2);
+
+ LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
+ const float(*mat)[4] = controller->aim_mat;
+ madd_v3_v3v3fl(ray, mat[3], mat[2], -scale);
+
+ immAttrSkip(col);
+ immVertex3fv(pos, mat[3]);
+ immAttr4ubv(col, color);
+ immVertex3fv(pos, ray);
+ }
+
+ immEnd();
+ }
+ else {
+ const uchar r[4] = {255, 51, 82, 255};
+ const uchar g[4] = {139, 220, 0, 255};
+ const uchar b[4] = {40, 144, 255, 255};
+ const float scale = 0.01f;
+ float x_axis[3], y_axis[3], z_axis[3];
+
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_blend(GPU_BLEND_NONE);
+
+ immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 6);
+
+ LISTBASE_FOREACH (wmXrController *, controller, &state->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);
+ madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale);
+
+ immAttrSkip(col);
+ immVertex3fv(pos, mat[3]);
+ immAttr4ubv(col, r);
+ immVertex3fv(pos, x_axis);
+
+ immAttrSkip(col);
+ immVertex3fv(pos, mat[3]);
+ immAttr4ubv(col, g);
+ immVertex3fv(pos, y_axis);
+
+ immAttrSkip(col);
+ immVertex3fv(pos, mat[3]);
+ immAttr4ubv(col, b);
+ immVertex3fv(pos, z_axis);
+ }
+
+ immEnd();
+ }
+
+ immUnbindProgram();
+}
+
+void wm_xr_draw_controllers(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;
+ wmXrSessionState *state = &xr->runtime->session_state;
+
+ wm_xr_controller_model_draw(settings, xr_context, state);
+ wm_xr_controller_aim_draw(settings, state);
+}
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index fc54e261f79..6fce270643f 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -20,10 +20,23 @@
#pragma once
+#include "BLI_sys_types.h"
+
#include "CLG_log.h"
#include "wm_xr.h"
+struct ARegion;
+struct ARegionType;
+struct bContext;
+struct bScreen;
+struct Depsgraph;
+struct GPUBatch;
+struct GPUOffScreen;
+struct GPUViewport;
+struct Scene;
+struct ViewLayer;
+struct wmOperatorType;
struct wmXrActionSet;
typedef struct wmXrSessionState {
@@ -31,18 +44,25 @@ typedef struct wmXrSessionState {
/** Last known viewer pose (centroid of eyes, in world space) stored for queries. */
GHOST_XrPose viewer_pose;
- /** The last known view matrix, calculated from above's viewer pose. */
+ /** The last known view matrix (inverse viewer matrix), calculated from above's viewer pose. */
float viewer_viewmat[4][4];
- float focal_len;
+ /** The last known viewer matrix, without navigation applied. */
+ float viewer_mat_base[4][4];
+ /** Last known eye data. */
+ ListBase eyes; /* wmXrEye */
+ /** Last known controller data. */
+ ListBase controllers; /* wmXrController */
/** Copy of XrSessionSettings.base_pose_ data to detect changes that need
* resetting to base pose. */
- char prev_base_pose_type; /* eXRSessionBasePoseType */
+ char prev_base_pose_type; /* eXrSessionBasePoseType */
Object *prev_base_pose_object;
/** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */
int prev_settings_flag;
/** Copy of wmXrDrawData.base_pose. */
GHOST_XrPose prev_base_pose;
+ /** Copy of wmXrDrawData.base_scale. */
+ float prev_base_scale;
/** Copy of GHOST_XrDrawViewInfo.local_pose. */
GHOST_XrPose prev_local_pose;
/** Copy of wmXrDrawData.eye_position_ofs. */
@@ -51,13 +71,23 @@ typedef struct wmXrSessionState {
bool force_reset_to_base_pose;
bool is_view_data_set;
- /** Last known controller data. */
- ListBase controllers; /* wmXrController */
+ /** Current navigation transforms. */
+ GHOST_XrPose nav_pose;
+ float nav_scale;
+ /** Navigation transforms from the last actions sync, used to calculate the viewer/controller
+ * poses. */
+ GHOST_XrPose nav_pose_prev;
+ float nav_scale_prev;
+ bool is_navigation_dirty;
/** 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
* updated. */
struct wmXrActionSet *active_action_set;
+
+ /* Original poses for motion capture objects. Used to properly restore object transforms on
+ * session end. */
+ ListBase mocap_orig_poses; /* wmXrMotionCapturePose */
} wmXrSessionState;
typedef struct wmXrRuntimeData {
@@ -70,10 +100,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 {
@@ -85,6 +111,11 @@ typedef struct wmXrViewportPair {
typedef struct {
/** Off-screen buffers/viewports for each view. */
ListBase viewports; /* wmXrViewportPair */
+
+ /** Dummy region type for controller draw callback. */
+ struct ARegionType *controller_art;
+ /** Controller draw callback handle. */
+ void *controller_draw_handle;
} wmXrSurfaceData;
typedef struct wmXrDrawData {
@@ -98,11 +129,21 @@ typedef struct wmXrDrawData {
* space). With positional tracking enabled, it should be the same as the base pose, when
* disabled it also contains a location delta from the moment the option was toggled. */
GHOST_XrPose base_pose;
+ /** Base scale (uniform, world space). */
+ float base_scale;
/** Offset to _substract_ from the OpenXR eye and viewer pose to get the wanted effective pose
* (e.g. a pose exactly at the landmark position). */
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef struct wmXrEye {
+ struct wmXrEye *next, *prev;
+ float focal_len;
+ float viewmat[4][4];
+ /** Viewmat without navigation applied. */
+ float viewmat_base[4][4];
+} wmXrEye;
+
typedef struct wmXrController {
struct wmXrController *next, *prev;
/** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
@@ -111,12 +152,18 @@ typedef struct wmXrController {
/input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
*/
char subaction_path[64];
+
/* Pose (in world space) that represents the user's hand when holding the controller.*/
GHOST_XrPose grip_pose;
float grip_mat[4][4];
+ float grip_mat_base[4][4];
/* Pose (in world space) that represents the controller's aiming source. */
GHOST_XrPose aim_pose;
float aim_mat[4][4];
+ float aim_mat_base[4][4];
+
+ /** Controller model. */
+ struct GPUBatch *model;
} wmXrController;
typedef struct wmXrAction {
@@ -167,22 +214,32 @@ typedef struct wmXrActionSet {
wmXrAction *controller_aim_action;
/** Currently active modal actions. */
- ListBase active_modal_actions;
+ ListBase active_modal_actions; /* wmXrAction */
/** 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);
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
-void wm_xr_session_data_free(wmXrSessionState *state);
-void wm_xr_session_draw_data_update(const wmXrSessionState *state,
+/* wm_xr_session.c */
+void wm_xr_session_data_free(wmXrSessionState *state);
+void wm_xr_session_draw_data_update(wmXrSessionState *state,
const XrSessionSettings *settings,
const GHOST_XrDrawViewInfo *draw_view,
wmXrDrawData *draw_data);
void wm_xr_session_state_update(const XrSessionSettings *settings,
const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
+ const float viewmat[4][4],
+ const float viewmat_base[4][4],
wmXrSessionState *state);
bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
const GHOST_XrDrawViewInfo *draw_view);
@@ -190,12 +247,36 @@ 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(wmXrData *xr);
+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);
+/* 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]);
+void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]);
+
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);
+
+/* 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..e7dde563225
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_mocap.c
@@ -0,0 +1,267 @@
+/*
+ * 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);
+ }
+
+ 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)) {
+ ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME);
+ }
+
+ 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
new file mode 100644
index 00000000000..4f7499f3aa0
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c
@@ -0,0 +1,2492 @@
+/*
+ * 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 Operators
+ *
+ * Collection of XR-related operators.
+ */
+
+#include "BLI_kdopbvh.h"
+#include "BLI_listbase.h"
+#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_object.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"
+
+#include "GHOST_Types.h"
+
+#include "GPU_immediate.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "PIL_time.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Conditions
+ * \{ */
+
+/* op->poll */
+static bool wm_xr_operator_sessionactive(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return WM_xr_session_is_ready(&wm->xr);
+}
+
+static bool wm_xr_operator_test_event(const wmOperator *op, const wmEvent *event)
+{
+ if (event->type != EVT_XR_ACTION) {
+ return false;
+ }
+
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ wmXrActionData *actiondata = event->customdata;
+ return (actiondata->ot == op->type &&
+ IDP_EqualsProperties(actiondata->op_properties, op->properties));
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Session Toggle
+ *
+ * Toggles an XR session, creating an XR context if necessary.
+ * \{ */
+
+static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data)
+{
+ const bool session_exists = WM_xr_session_exists(xr_data);
+
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) {
+ if (slink->spacetype == SPACE_VIEW3D) {
+ View3D *v3d = (View3D *)slink;
+
+ if (v3d->flag & V3D_XR_SESSION_MIRROR) {
+ ED_view3d_xr_mirror_update(area, v3d, session_exists);
+ }
+
+ if (session_exists) {
+ wmWindowManager *wm = bmain->wm.first;
+ const Scene *scene = WM_windows_scene_get_from_screen(wm, screen);
+
+ ED_view3d_xr_shading_update(wm, v3d, scene);
+ }
+ /* Ensure no 3D View is tagged as session root. */
+ else {
+ v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT;
+ }
+ }
+ }
+ }
+ }
+
+ WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL);
+}
+
+static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data)
+{
+ /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */
+ wm_xr_session_update_screen(G_MAIN, xr_data);
+}
+
+static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */
+ if (wm_xr_init(wm) == false) {
+ return OPERATOR_CANCELLED;
+ }
+
+ v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT;
+ wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb);
+ wm_xr_session_update_screen(bmain, &wm->xr);
+
+ WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void WM_OT_xr_session_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Toggle VR Session";
+ ot->idname = "WM_OT_xr_session_toggle";
+ ot->description =
+ "Open a view for use with virtual reality headsets, or close it if already "
+ "opened";
+
+ /* callbacks */
+ ot->exec = wm_xr_session_toggle_exec;
+ ot->poll = ED_operator_view3d_active;
+
+ /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the
+ * UI instead. Not meant as a permanent solution. */
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Grab Utilities
+ * \{ */
+
+typedef struct XrGrabData {
+ float mat_prev[4][4];
+ float mat_other_prev[4][4];
+ bool bimanual_prev;
+} XrGrabData;
+
+static void wm_xr_grab_init(wmOperator *op)
+{
+ BLI_assert(op->customdata == NULL);
+
+ op->customdata = MEM_callocN(sizeof(XrGrabData), __func__);
+}
+
+static void wm_xr_grab_uninit(wmOperator *op)
+{
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static void wm_xr_grab_update(wmOperator *op, const wmXrActionData *actiondata)
+{
+ XrGrabData *data = op->customdata;
+
+ quat_to_mat4(data->mat_prev, actiondata->controller_rot);
+ copy_v3_v3(data->mat_prev[3], actiondata->controller_loc);
+
+ if (actiondata->bimanual) {
+ quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other);
+ copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other);
+ data->bimanual_prev = true;
+ }
+ else {
+ data->bimanual_prev = false;
+ }
+}
+
+static void orient_mat_z_normalized(float R[4][4], const float z_axis[3])
+{
+ const float scale = len_v3(R[0]);
+ float x_axis[3], y_axis[3];
+
+ cross_v3_v3v3(y_axis, z_axis, R[0]);
+ normalize_v3(y_axis);
+ mul_v3_v3fl(R[1], y_axis, scale);
+
+ cross_v3_v3v3(x_axis, R[1], z_axis);
+ normalize_v3(x_axis);
+ mul_v3_v3fl(R[0], x_axis, scale);
+
+ mul_v3_v3fl(R[2], z_axis, scale);
+}
+
+static void wm_xr_navlocks_apply(const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ bool loc_lock,
+ bool locz_lock,
+ bool rotz_lock,
+ float r_prev[4][4],
+ float r_curr[4][4])
+{
+ /* Locked in base pose coordinates. */
+ float prev_base[4][4], curr_base[4][4];
+
+ mul_m4_m4m4(prev_base, nav_inv, r_prev);
+ mul_m4_m4m4(curr_base, nav_inv, r_curr);
+
+ if (rotz_lock) {
+ const float z_axis[3] = {0.0f, 0.0f, 1.0f};
+ orient_mat_z_normalized(prev_base, z_axis);
+ orient_mat_z_normalized(curr_base, z_axis);
+ }
+
+ if (loc_lock) {
+ copy_v3_v3(curr_base[3], prev_base[3]);
+ }
+ else if (locz_lock) {
+ curr_base[3][2] = prev_base[3][2];
+ }
+
+ mul_m4_m4m4(r_prev, nav_mat, prev_base);
+ mul_m4_m4m4(r_curr, nav_mat, curr_base);
+}
+
+static void wm_xr_grab_compute(const wmXrActionData *actiondata,
+ const XrGrabData *data,
+ const Object *obedit,
+ const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ bool reverse,
+ bool loc_lock,
+ bool locz_lock,
+ bool rot_lock,
+ bool rotz_lock,
+ float r_delta[4][4])
+{
+ const bool nav_lock = (nav_mat && nav_inv);
+ float prev[4][4], curr[4][4];
+
+ if (!rot_lock) {
+ copy_m4_m4(prev, data->mat_prev);
+ zero_v3(prev[3]);
+ quat_to_mat4(curr, actiondata->controller_rot);
+ }
+ else {
+ unit_m4(prev);
+ unit_m4(curr);
+ }
+
+ if (!loc_lock || nav_lock) {
+ copy_v3_v3(prev[3], data->mat_prev[3]);
+ copy_v3_v3(curr[3], actiondata->controller_loc);
+ }
+
+ if (obedit) {
+ mul_m4_m4m4(prev, obedit->imat, prev);
+ mul_m4_m4m4(curr, obedit->imat, curr);
+ }
+
+ if (nav_lock) {
+ wm_xr_navlocks_apply(nav_mat, nav_inv, loc_lock, locz_lock, rotz_lock, prev, curr);
+ }
+
+ if (reverse) {
+ invert_m4(curr);
+ mul_m4_m4m4(r_delta, prev, curr);
+ }
+ else {
+ invert_m4(prev);
+ mul_m4_m4m4(r_delta, curr, prev);
+ }
+}
+
+static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata,
+ const XrGrabData *data,
+ const Object *obedit,
+ const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ bool reverse,
+ bool loc_lock,
+ bool locz_lock,
+ bool rot_lock,
+ bool rotz_lock,
+ bool scale_lock,
+ float r_delta[4][4])
+{
+ const bool nav_lock = (nav_mat && nav_inv);
+ float prev[4][4], curr[4][4];
+ unit_m4(prev);
+ unit_m4(curr);
+
+ if (!rot_lock) {
+ /* Rotation. */
+ float x_axis_prev[3], x_axis_curr[3], y_axis_prev[3], y_axis_curr[3], z_axis_prev[3],
+ z_axis_curr[3];
+ float m0[3][3], m1[3][3];
+ quat_to_mat3(m0, actiondata->controller_rot);
+ quat_to_mat3(m1, actiondata->controller_rot_other);
+
+ /* x-axis is the base line between the two controllers. */
+ sub_v3_v3v3(x_axis_prev, data->mat_prev[3], data->mat_other_prev[3]);
+ sub_v3_v3v3(x_axis_curr, actiondata->controller_loc, actiondata->controller_loc_other);
+ /* y-axis is the average of the controllers' y-axes. */
+ add_v3_v3v3(y_axis_prev, data->mat_prev[1], data->mat_other_prev[1]);
+ mul_v3_fl(y_axis_prev, 0.5f);
+ add_v3_v3v3(y_axis_curr, m0[1], m1[1]);
+ mul_v3_fl(y_axis_curr, 0.5f);
+ /* z-axis is the cross product of the two. */
+ cross_v3_v3v3(z_axis_prev, x_axis_prev, y_axis_prev);
+ cross_v3_v3v3(z_axis_curr, x_axis_curr, y_axis_curr);
+ /* Fix the y-axis to be orthogonal. */
+ cross_v3_v3v3(y_axis_prev, z_axis_prev, x_axis_prev);
+ cross_v3_v3v3(y_axis_curr, z_axis_curr, x_axis_curr);
+ /* Normalize. */
+ normalize_v3_v3(prev[0], x_axis_prev);
+ normalize_v3_v3(prev[1], y_axis_prev);
+ normalize_v3_v3(prev[2], z_axis_prev);
+ normalize_v3_v3(curr[0], x_axis_curr);
+ normalize_v3_v3(curr[1], y_axis_curr);
+ normalize_v3_v3(curr[2], z_axis_curr);
+ }
+
+ if (!loc_lock || nav_lock) {
+ /* Translation: translation of the averaged controller locations. */
+ add_v3_v3v3(prev[3], data->mat_prev[3], data->mat_other_prev[3]);
+ mul_v3_fl(prev[3], 0.5f);
+ add_v3_v3v3(curr[3], actiondata->controller_loc, actiondata->controller_loc_other);
+ mul_v3_fl(curr[3], 0.5f);
+ }
+
+ if (!scale_lock) {
+ /* Scaling: distance between controllers. */
+ float scale, v[3];
+
+ sub_v3_v3v3(v, data->mat_prev[3], data->mat_other_prev[3]);
+ scale = len_v3(v);
+ mul_v3_fl(prev[0], scale);
+ mul_v3_fl(prev[1], scale);
+ mul_v3_fl(prev[2], scale);
+
+ sub_v3_v3v3(v, actiondata->controller_loc, actiondata->controller_loc_other);
+ scale = len_v3(v);
+ mul_v3_fl(curr[0], scale);
+ mul_v3_fl(curr[1], scale);
+ mul_v3_fl(curr[2], scale);
+ }
+
+ if (obedit) {
+ mul_m4_m4m4(prev, obedit->imat, prev);
+ mul_m4_m4m4(curr, obedit->imat, curr);
+ }
+
+ if (nav_lock) {
+ wm_xr_navlocks_apply(nav_mat, nav_inv, loc_lock, locz_lock, rotz_lock, prev, curr);
+ }
+
+ if (reverse) {
+ invert_m4(curr);
+ mul_m4_m4m4(r_delta, prev, curr);
+ }
+ else {
+ invert_m4(prev);
+ mul_m4_m4m4(r_delta, curr, prev);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Grab
+ *
+ * Navigates the scene by grabbing with XR controllers.
+ * \{ */
+
+static int wm_xr_navigation_grab_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ const wmXrActionData *actiondata = event->customdata;
+
+ 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_navigation_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return OPERATOR_CANCELLED;
+}
+
+static int wm_xr_navigation_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;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ bool loc_lock, locz_lock, rot_lock, rotz_lock, scale_lock;
+ GHOST_XrPose nav_pose;
+ float nav_scale, nav_mat[4][4], nav_inv[4][4], delta[4][4], out[4][4];
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "lock_location");
+ loc_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ prop = RNA_struct_find_property(op->ptr, "lock_location_z");
+ locz_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ prop = RNA_struct_find_property(op->ptr, "lock_rotation");
+ rot_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ prop = RNA_struct_find_property(op->ptr, "lock_rotation_z");
+ rotz_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ prop = RNA_struct_find_property(op->ptr, "lock_scale");
+ scale_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ const bool do_bimanual = (actiondata->bimanual && data->bimanual_prev);
+ const bool apply_navigation = (do_bimanual ? !(loc_lock && rot_lock && scale_lock) :
+ !(loc_lock && rot_lock)) &&
+ (actiondata->bimanual || !data->bimanual_prev);
+
+ if (apply_navigation) {
+ const bool nav_lock = (loc_lock || locz_lock || rotz_lock);
+
+ WM_xr_session_state_nav_location_get(xr, nav_pose.position);
+ WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat);
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+
+ wm_xr_pose_scale_to_mat(&nav_pose, nav_scale, nav_mat);
+ if (nav_lock) {
+ wm_xr_pose_scale_to_imat(&nav_pose, nav_scale, nav_inv);
+ }
+
+ if (do_bimanual) {
+ wm_xr_grab_compute_bimanual(actiondata,
+ data,
+ NULL,
+ nav_lock ? nav_mat : NULL,
+ nav_lock ? nav_inv : NULL,
+ true,
+ loc_lock,
+ locz_lock,
+ rot_lock,
+ rotz_lock,
+ scale_lock,
+ delta);
+ }
+ else {
+ wm_xr_grab_compute(actiondata,
+ data,
+ NULL,
+ nav_lock ? nav_mat : NULL,
+ nav_lock ? nav_inv : NULL,
+ true,
+ loc_lock,
+ locz_lock,
+ rot_lock,
+ rotz_lock,
+ delta);
+ }
+
+ mul_m4_m4m4(out, delta, nav_mat);
+
+ /* Limit scale to reasonable values. */
+ nav_scale = len_v3(out[0]);
+
+ if (!(nav_scale < 0.001f || nav_scale > 1000.0f)) {
+ WM_xr_session_state_nav_location_set(xr, out[3]);
+ if (!rot_lock) {
+ mat4_to_quat(nav_pose.orientation_quat, out);
+ normalize_qt(nav_pose.orientation_quat);
+ WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat);
+ }
+ if (!scale_lock && do_bimanual) {
+ WM_xr_session_state_nav_scale_set(xr, nav_scale);
+ }
+ }
+ }
+
+ if (actiondata->bimanual) {
+ if (!data->bimanual_prev) {
+ quat_to_mat4(data->mat_prev, actiondata->controller_rot);
+ copy_v3_v3(data->mat_prev[3], actiondata->controller_loc);
+ quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other);
+ copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other);
+ }
+ data->bimanual_prev = true;
+ }
+ else {
+ if (data->bimanual_prev) {
+ quat_to_mat4(data->mat_prev, actiondata->controller_rot);
+ copy_v3_v3(data->mat_prev[3], actiondata->controller_loc);
+ }
+ data->bimanual_prev = false;
+ }
+
+ if (event->val == KM_PRESS) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else if (event->val == KM_RELEASE) {
+ wm_xr_grab_uninit(op);
+ return OPERATOR_FINISHED;
+ }
+
+ /* XR events currently only support press and release. */
+ BLI_assert(false);
+ wm_xr_grab_uninit(op);
+ return OPERATOR_CANCELLED;
+}
+
+static void WM_OT_xr_navigation_grab(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Grab";
+ ot->idname = "WM_OT_xr_navigation_grab";
+ ot->description = "Navigate the VR scene by grabbing with controllers";
+
+ /* callbacks */
+ ot->invoke = wm_xr_navigation_grab_invoke;
+ ot->exec = wm_xr_navigation_grab_exec;
+ ot->modal = wm_xr_navigation_grab_modal;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ RNA_def_boolean(
+ ot->srna, "lock_location", false, "Lock Location", "Prevent changes to viewer location");
+ RNA_def_boolean(
+ ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation");
+ RNA_def_boolean(
+ ot->srna, "lock_rotation", false, "Lock Rotation", "Prevent changes to viewer rotation");
+ RNA_def_boolean(ot->srna,
+ "lock_rotation_z",
+ false,
+ "Lock Up Orientation",
+ "Prevent changes to viewer up orientation");
+ RNA_def_boolean(ot->srna, "lock_scale", false, "Lock Scale", "Prevent changes to viewer scale");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Raycast Utilities
+ * \{ */
+
+static const float g_xr_default_raycast_axis[3] = {0.0f, 0.0f, -1.0f};
+static const float g_xr_default_raycast_color[4] = {0.35f, 0.35f, 1.0f, 1.0f};
+
+typedef struct XrRaycastData {
+ bool from_viewer;
+ float origin[3];
+ float direction[3];
+ float end[3];
+ float color[4];
+ void *draw_handle;
+} XrRaycastData;
+
+static void wm_xr_raycast_draw(const bContext *UNUSED(C),
+ ARegion *UNUSED(region),
+ void *customdata)
+{
+ const XrRaycastData *data = customdata;
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ if (data->from_viewer) {
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformColor4fv(data->color);
+
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_point_size(7.0f);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, data->end);
+ immEnd();
+ }
+ else {
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ immUniform1f("lineWidth", 3.0f * U.pixelsize);
+
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immAttrSkip(col);
+ immVertex3fv(pos, data->origin);
+ immAttr4fv(col, data->color);
+ immVertex3fv(pos, data->end);
+ immEnd();
+ }
+
+ immUnbindProgram();
+}
+
+static void wm_xr_raycast_init(wmOperator *op)
+{
+ BLI_assert(op->customdata == NULL);
+
+ op->customdata = MEM_callocN(sizeof(XrRaycastData), __func__);
+
+ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ if (st) {
+ ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
+ if (art) {
+ ((XrRaycastData *)op->customdata)->draw_handle = ED_region_draw_cb_activate(
+ art, wm_xr_raycast_draw, op->customdata, REGION_DRAW_POST_VIEW);
+ }
+ }
+}
+
+static void wm_xr_raycast_uninit(wmOperator *op)
+{
+ if (op->customdata) {
+ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ if (st) {
+ ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
+ if (art) {
+ ED_region_draw_cb_exit(art, ((XrRaycastData *)op->customdata)->draw_handle);
+ }
+ }
+
+ MEM_freeN(op->customdata);
+ }
+}
+
+static void wm_xr_raycast_update(wmOperator *op,
+ const wmXrData *xr,
+ const wmXrActionData *actiondata)
+{
+ XrRaycastData *data = op->customdata;
+ float ray_length, axis[3];
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "from_viewer");
+ data->from_viewer = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ prop = RNA_struct_find_property(op->ptr, "axis");
+ if (prop) {
+ RNA_property_float_get_array(op->ptr, prop, axis);
+ normalize_v3(axis);
+ }
+ else {
+ copy_v3_v3(axis, g_xr_default_raycast_axis);
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "color");
+ if (prop) {
+ RNA_property_float_get_array(op->ptr, prop, data->color);
+ }
+ else {
+ copy_v4_v4(data->color, g_xr_default_raycast_color);
+ }
+
+ if (data->from_viewer) {
+ float viewer_rot[4];
+ WM_xr_session_state_viewer_pose_location_get(xr, data->origin);
+ WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_rot);
+ mul_qt_v3(viewer_rot, axis);
+ ray_length = (xr->session_settings.clip_start + xr->session_settings.clip_end) / 2.0f;
+ }
+ else {
+ copy_v3_v3(data->origin, actiondata->controller_loc);
+ mul_qt_v3(actiondata->controller_rot, axis);
+ ray_length = xr->session_settings.clip_end;
+ }
+
+ copy_v3_v3(data->direction, axis);
+ madd_v3_v3v3fl(data->end, data->origin, data->direction, ray_length);
+}
+
+static void wm_xr_raycast(Depsgraph *depsgraph,
+ ViewContext *vc,
+ 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,
+ Object **r_ob,
+ float r_obmat[4][4])
+{
+ /* Uses same raycast method as Scene.ray_cast(). */
+ SnapObjectContext *sctx = ED_transform_snap_object_context_create(vc->scene, 0);
+
+ ED_transform_snap_object_project_ray_ex(
+ sctx,
+ depsgraph,
+ &(const struct SnapObjectParams){
+ .snap_select = vc->em ? SNAP_SELECTED : (selectable_only ? SNAP_SELECTABLE : SNAP_ALL)},
+ origin,
+ direction,
+ ray_dist,
+ r_location,
+ r_normal,
+ r_index,
+ r_ob,
+ r_obmat);
+
+ ED_transform_snap_object_context_destroy(sctx);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Fly
+ *
+ * Navigates the scene by moving/turning relative to navigation space or the XR viewer or
+ * controller.
+ * \{ */
+
+#define XR_DEFAULT_FLY_SPEED_MOVE 0.054f
+#define XR_DEFAULT_FLY_SPEED_TURN 0.03f
+
+typedef enum eXrFlyMode {
+ XR_FLY_FORWARD = 0,
+ XR_FLY_BACK = 1,
+ XR_FLY_LEFT = 2,
+ XR_FLY_RIGHT = 3,
+ XR_FLY_UP = 4,
+ XR_FLY_DOWN = 5,
+ XR_FLY_TURNLEFT = 6,
+ XR_FLY_TURNRIGHT = 7,
+ XR_FLY_VIEWER_FORWARD = 8,
+ XR_FLY_VIEWER_BACK = 9,
+ XR_FLY_VIEWER_LEFT = 10,
+ XR_FLY_VIEWER_RIGHT = 11,
+ XR_FLY_CONTROLLER_FORWARD = 12,
+} eXrFlyMode;
+
+typedef struct XrFlyData {
+ float viewer_rot[4];
+ double time_prev;
+} XrFlyData;
+
+static void wm_xr_fly_init(wmOperator *op, const wmXrData *xr)
+{
+ BLI_assert(op->customdata == NULL);
+
+ XrFlyData *data = op->customdata = MEM_callocN(sizeof(XrFlyData), __func__);
+
+ WM_xr_session_state_viewer_pose_rotation_get(xr, data->viewer_rot);
+ data->time_prev = PIL_check_seconds_timer();
+}
+
+static void wm_xr_fly_uninit(wmOperator *op)
+{
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static void wm_xr_fly_compute_move(eXrFlyMode mode,
+ float speed,
+ const float ref_quat[4],
+ const float nav_mat[4][4],
+ bool locz_lock,
+ float r_delta[4][4])
+{
+ float ref_axes[3][3];
+ quat_to_mat3(ref_axes, ref_quat);
+
+ unit_m4(r_delta);
+
+ switch (mode) {
+ /* Navigation space reference. */
+ case XR_FLY_FORWARD:
+ madd_v3_v3fl(r_delta[3], ref_axes[1], speed);
+ return;
+ case XR_FLY_BACK:
+ madd_v3_v3fl(r_delta[3], ref_axes[1], -speed);
+ return;
+ case XR_FLY_LEFT:
+ madd_v3_v3fl(r_delta[3], ref_axes[0], -speed);
+ return;
+ case XR_FLY_RIGHT:
+ madd_v3_v3fl(r_delta[3], ref_axes[0], speed);
+ return;
+ case XR_FLY_UP:
+ case XR_FLY_DOWN:
+ if (!locz_lock) {
+ madd_v3_v3fl(r_delta[3], ref_axes[2], (mode == XR_FLY_UP) ? speed : -speed);
+ }
+ return;
+ /* Viewer/controller space reference. */
+ case XR_FLY_VIEWER_FORWARD:
+ case XR_FLY_CONTROLLER_FORWARD:
+ negate_v3_v3(r_delta[3], ref_axes[2]);
+ break;
+ case XR_FLY_VIEWER_BACK:
+ copy_v3_v3(r_delta[3], ref_axes[2]);
+ break;
+ case XR_FLY_VIEWER_LEFT:
+ negate_v3_v3(r_delta[3], ref_axes[0]);
+ break;
+ case XR_FLY_VIEWER_RIGHT:
+ copy_v3_v3(r_delta[3], ref_axes[0]);
+ break;
+ /* Unused. */
+ case XR_FLY_TURNLEFT:
+ case XR_FLY_TURNRIGHT:
+ BLI_assert_unreachable();
+ return;
+ }
+
+ if (locz_lock) {
+ /* Lock elevation in navigation space. */
+ float z_axis[3], projected[3];
+
+ normalize_v3_v3(z_axis, nav_mat[2]);
+ project_v3_v3v3_normalized(projected, r_delta[3], z_axis);
+ sub_v3_v3(r_delta[3], projected);
+
+ normalize_v3(r_delta[3]);
+ }
+
+ mul_v3_fl(r_delta[3], speed);
+}
+
+static void wm_xr_fly_compute_turn(eXrFlyMode mode,
+ float speed,
+ const float viewer_mat[4][4],
+ const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ float r_delta[4][4])
+{
+ BLI_assert(mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT);
+
+ float z_axis[3], m[3][3], prev[4][4], curr[4][4];
+
+ /* Turn around Z-axis in navigation space. */
+ normalize_v3_v3(z_axis, nav_mat[2]);
+ axis_angle_normalized_to_mat3(m, z_axis, (mode == XR_FLY_TURNLEFT) ? speed : -speed);
+ copy_m4_m3(r_delta, m);
+
+ copy_m4_m4(prev, viewer_mat);
+ mul_m4_m4m4(curr, r_delta, viewer_mat);
+
+ /* Lock location in base pose space. */
+ wm_xr_navlocks_apply(nav_mat, nav_inv, true, false, false, prev, curr);
+
+ invert_m4(prev);
+ mul_m4_m4m4(r_delta, curr, prev);
+}
+
+static void wm_xr_basenav_rotation_calc(const wmXrData *xr,
+ const float nav_rotation[4],
+ float r_rotation[4])
+{
+ /* Apply nav rotation to base pose Z-rotation. */
+ float base_eul[3], base_quatz[4];
+ quat_to_eul(base_eul, xr->runtime->session_state.prev_base_pose.orientation_quat);
+ axis_angle_to_quat_single(base_quatz, 'Z', base_eul[2]);
+ mul_qt_qtqt(r_rotation, nav_rotation, base_quatz);
+}
+
+static int wm_xr_navigation_fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ wm_xr_fly_init(op, &wm->xr);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_xr_navigation_fly_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return OPERATOR_CANCELLED;
+}
+
+static int wm_xr_navigation_fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ if (event->val == KM_RELEASE) {
+ wm_xr_fly_uninit(op);
+ return OPERATOR_FINISHED;
+ }
+
+ const wmXrActionData *actiondata = event->customdata;
+ XrFlyData *data = op->customdata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ eXrFlyMode mode;
+ bool turn, locz_lock, dir_lock, speed_frame_based;
+ bool speed_interp_cubic = false;
+ float speed, speed_max, speed_p0[2], speed_p1[2];
+ GHOST_XrPose nav_pose;
+ float nav_mat[4][4], delta[4][4], out[4][4];
+
+ const double time_now = PIL_check_seconds_timer();
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "mode");
+ mode = prop ? RNA_property_enum_get(op->ptr, prop) : XR_FLY_VIEWER_FORWARD;
+
+ turn = (mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT);
+
+ prop = RNA_struct_find_property(op->ptr, "lock_location_z");
+ locz_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ prop = RNA_struct_find_property(op->ptr, "lock_direction");
+ dir_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ prop = RNA_struct_find_property(op->ptr, "speed_frame_based");
+ speed_frame_based = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ prop = RNA_struct_find_property(op->ptr, "speed_min");
+ speed = prop ? RNA_property_float_get(op->ptr, prop) :
+ (turn ? XR_DEFAULT_FLY_SPEED_TURN : XR_DEFAULT_FLY_SPEED_MOVE) /
+ (speed_frame_based ? 3.0f : 0.03f);
+
+ prop = RNA_struct_find_property(op->ptr, "speed_max");
+ speed_max = prop ? RNA_property_float_get(op->ptr, prop) :
+ (turn ? XR_DEFAULT_FLY_SPEED_TURN : XR_DEFAULT_FLY_SPEED_MOVE) /
+ (speed_frame_based ? 1.0f : 0.01f);
+
+ prop = RNA_struct_find_property(op->ptr, "speed_interpolation0");
+ if (prop && RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_float_get_array(op->ptr, prop, speed_p0);
+ speed_interp_cubic = true;
+ }
+ else {
+ speed_p0[0] = speed_p0[1] = 0.0f;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "speed_interpolation1");
+ if (prop && RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_float_get_array(op->ptr, prop, speed_p1);
+ speed_interp_cubic = true;
+ }
+ else {
+ speed_p1[0] = speed_p1[1] = 1.0f;
+ }
+
+ /* Ensure valid interpolation. */
+ if (speed_max < speed) {
+ speed_max = speed;
+ }
+
+ /* Interpolate between min/max speeds based on button state. */
+ switch (actiondata->type) {
+ case XR_BOOLEAN_INPUT:
+ speed = speed_max;
+ break;
+ case XR_FLOAT_INPUT:
+ case XR_VECTOR2F_INPUT: {
+ float state = (actiondata->type == XR_FLOAT_INPUT) ? fabsf(actiondata->state[0]) :
+ len_v2(actiondata->state);
+ float speed_t = (actiondata->float_threshold < 1.0f) ?
+ (state - actiondata->float_threshold) /
+ (1.0f - actiondata->float_threshold) :
+ 1.0f;
+ if (speed_interp_cubic) {
+ float start[2], end[2], p[2];
+
+ start[0] = 0.0f;
+ start[1] = speed;
+ speed_p0[1] = speed + speed_p0[1] * (speed_max - speed);
+ speed_p1[1] = speed + speed_p1[1] * (speed_max - speed);
+ end[0] = 1.0f;
+ end[1] = speed_max;
+
+ interp_v2_v2v2v2v2_cubic(p, start, speed_p0, speed_p1, end, speed_t);
+ speed = p[1];
+ }
+ else {
+ speed += speed_t * (speed_max - speed);
+ }
+ break;
+ }
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ if (!speed_frame_based) {
+ /* Adjust speed based on last update time. */
+ speed *= time_now - data->time_prev;
+ }
+ data->time_prev = time_now;
+
+ WM_xr_session_state_nav_location_get(xr, nav_pose.position);
+ WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat);
+ wm_xr_pose_to_mat(&nav_pose, nav_mat);
+
+ if (turn) {
+ if (dir_lock) {
+ unit_m4(delta);
+ }
+ else {
+ GHOST_XrPose viewer_pose;
+ float viewer_mat[4][4], nav_inv[4][4];
+
+ WM_xr_session_state_viewer_pose_location_get(xr, viewer_pose.position);
+ WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_pose.orientation_quat);
+ wm_xr_pose_to_mat(&viewer_pose, viewer_mat);
+ wm_xr_pose_to_imat(&nav_pose, nav_inv);
+
+ wm_xr_fly_compute_turn(mode, speed, viewer_mat, nav_mat, nav_inv, delta);
+ }
+ }
+ else {
+ float nav_scale, ref_quat[4];
+
+ /* Adjust speed for navigation scale. */
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+ speed *= nav_scale;
+
+ switch (mode) {
+ /* Move relative to navigation space. */
+ case XR_FLY_FORWARD:
+ case XR_FLY_BACK:
+ case XR_FLY_LEFT:
+ case XR_FLY_RIGHT:
+ case XR_FLY_UP:
+ case XR_FLY_DOWN:
+ wm_xr_basenav_rotation_calc(xr, nav_pose.orientation_quat, ref_quat);
+ break;
+ /* Move relative to viewer. */
+ case XR_FLY_VIEWER_FORWARD:
+ case XR_FLY_VIEWER_BACK:
+ case XR_FLY_VIEWER_LEFT:
+ case XR_FLY_VIEWER_RIGHT:
+ if (dir_lock) {
+ copy_qt_qt(ref_quat, data->viewer_rot);
+ }
+ else {
+ WM_xr_session_state_viewer_pose_rotation_get(xr, ref_quat);
+ }
+ break;
+ /* Move relative to controller. */
+ case XR_FLY_CONTROLLER_FORWARD:
+ copy_qt_qt(ref_quat, actiondata->controller_rot);
+ break;
+ /* Unused. */
+ case XR_FLY_TURNLEFT:
+ case XR_FLY_TURNRIGHT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ wm_xr_fly_compute_move(mode, speed, ref_quat, nav_mat, locz_lock, delta);
+ }
+
+ mul_m4_m4m4(out, delta, nav_mat);
+
+ WM_xr_session_state_nav_location_set(xr, out[3]);
+ if (turn) {
+ mat4_to_quat(nav_pose.orientation_quat, out);
+ WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat);
+ }
+
+ if (event->val == KM_PRESS) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ /* XR events currently only support press and release. */
+ BLI_assert(false);
+ wm_xr_fly_uninit(op);
+ return OPERATOR_CANCELLED;
+}
+
+static void WM_OT_xr_navigation_fly(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Fly";
+ ot->idname = "WM_OT_xr_navigation_fly";
+ ot->description = "Move/turn relative to the VR viewer or controller";
+
+ /* callbacks */
+ ot->invoke = wm_xr_navigation_fly_invoke;
+ ot->exec = wm_xr_navigation_fly_exec;
+ ot->modal = wm_xr_navigation_fly_modal;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ static const EnumPropertyItem fly_modes[] = {
+ {XR_FLY_FORWARD, "FORWARD", 0, "Forward", "Move along navigation forward axis"},
+ {XR_FLY_BACK, "BACK", 0, "Back", "Move along navigation back axis"},
+ {XR_FLY_LEFT, "LEFT", 0, "Left", "Move along navigation left axis"},
+ {XR_FLY_RIGHT, "RIGHT", 0, "Right", "Move along navigation right axis"},
+ {XR_FLY_UP, "UP", 0, "Up", "Move along navigation up axis"},
+ {XR_FLY_DOWN, "DOWN", 0, "Down", "Move along navigation down axis"},
+ {XR_FLY_TURNLEFT,
+ "TURNLEFT",
+ 0,
+ "Turn Left",
+ "Turn counter-clockwise around navigation up axis"},
+ {XR_FLY_TURNRIGHT, "TURNRIGHT", 0, "Turn Right", "Turn clockwise around navigation up axis"},
+ {XR_FLY_VIEWER_FORWARD,
+ "VIEWER_FORWARD",
+ 0,
+ "Viewer Forward",
+ "Move along viewer's forward axis"},
+ {XR_FLY_VIEWER_BACK, "VIEWER_BACK", 0, "Viewer Back", "Move along viewer's back axis"},
+ {XR_FLY_VIEWER_LEFT, "VIEWER_LEFT", 0, "Viewer Left", "Move along viewer's left axis"},
+ {XR_FLY_VIEWER_RIGHT, "VIEWER_RIGHT", 0, "Viewer Right", "Move along viewer's right axis"},
+ {XR_FLY_CONTROLLER_FORWARD,
+ "CONTROLLER_FORWARD",
+ 0,
+ "Controller Forward",
+ "Move along controller's forward axis"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const float default_speed_p0[2] = {0.0f, 0.0f};
+ static const float default_speed_p1[2] = {1.0f, 1.0f};
+
+ RNA_def_enum(ot->srna, "mode", fly_modes, XR_FLY_VIEWER_FORWARD, "Mode", "Fly mode");
+ RNA_def_boolean(
+ ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation");
+ RNA_def_boolean(ot->srna,
+ "lock_direction",
+ false,
+ "Lock Direction",
+ "Limit movement to viewer's intial direction");
+ RNA_def_boolean(ot->srna,
+ "speed_frame_based",
+ true,
+ "Frame Based Speed",
+ "Apply fixed movement deltas every update");
+ RNA_def_float(ot->srna,
+ "speed_min",
+ XR_DEFAULT_FLY_SPEED_MOVE / 3.0f,
+ 0.0f,
+ 1000.0f,
+ "Minimum Speed",
+ "Minimum move (turn) speed in meters (radians) per second or frame",
+ 0.0f,
+ 1000.0f);
+ RNA_def_float(ot->srna,
+ "speed_max",
+ XR_DEFAULT_FLY_SPEED_MOVE,
+ 0.0f,
+ 1000.0f,
+ "Maximum Speed",
+ "Maximum move (turn) speed in meters (radians) per second or frame",
+ 0.0f,
+ 1000.0f);
+ RNA_def_float_vector(ot->srna,
+ "speed_interpolation0",
+ 2,
+ default_speed_p0,
+ 0.0f,
+ 1.0f,
+ "Speed Interpolation 0",
+ "First cubic spline control point between min/max speeds",
+ 0.0f,
+ 1.0f);
+ RNA_def_float_vector(ot->srna,
+ "speed_interpolation1",
+ 2,
+ default_speed_p1,
+ 0.0f,
+ 1.0f,
+ "Speed Interpolation 1",
+ "Second cubic spline control point between min/max speeds",
+ 0.0f,
+ 1.0f);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Teleport
+ *
+ * Casts a ray from an XR controller's pose and teleports to any hit geometry.
+ * \{ */
+
+static void wm_xr_navigation_teleport(bContext *C,
+ wmXrData *xr,
+ const float origin[3],
+ const float direction[3],
+ float *ray_dist,
+ bool selectable_only,
+ const bool teleport_axes[3],
+ float teleport_t,
+ float teleport_ofs)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+ vc.em = NULL; /* Set to NULL to always enable teleport to non-edited objects. */
+
+ float location[3];
+ float normal[3];
+ int index;
+ Object *ob = NULL;
+ float obmat[4][4];
+
+ wm_xr_raycast(depsgraph,
+ &vc,
+ origin,
+ direction,
+ ray_dist,
+ selectable_only,
+ location,
+ normal,
+ &index,
+ &ob,
+ obmat);
+
+ /* Teleport. */
+ if (ob) {
+ float nav_location[3], nav_rotation[4], viewer_location[3];
+ float nav_axes[3][3], projected[3], v0[3], v1[3];
+ float out[3] = {0.0f, 0.0f, 0.0f};
+
+ WM_xr_session_state_nav_location_get(xr, nav_location);
+ WM_xr_session_state_nav_rotation_get(xr, nav_rotation);
+ WM_xr_session_state_viewer_pose_location_get(xr, viewer_location);
+
+ wm_xr_basenav_rotation_calc(xr, nav_rotation, nav_rotation);
+ quat_to_mat3(nav_axes, nav_rotation);
+
+ /* Project locations onto navigation axes. */
+ for (int a = 0; a < 3; ++a) {
+ project_v3_v3v3_normalized(projected, nav_location, nav_axes[a]);
+ if (teleport_axes[a]) {
+ /* Interpolate between projected locations. */
+ project_v3_v3v3_normalized(v0, location, nav_axes[a]);
+ project_v3_v3v3_normalized(v1, viewer_location, nav_axes[a]);
+ sub_v3_v3(v0, v1);
+ madd_v3_v3fl(projected, v0, teleport_t);
+ /* Subtract offset. */
+ project_v3_v3v3_normalized(v0, normal, nav_axes[a]);
+ madd_v3_v3fl(projected, v0, teleport_ofs);
+ }
+ /* Add to final location. */
+ add_v3_v3(out, projected);
+ }
+
+ WM_xr_session_state_nav_location_set(xr, out);
+ }
+}
+
+static int wm_xr_navigation_teleport_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);
+
+ 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_navigation_teleport_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return OPERATOR_CANCELLED;
+}
+
+static int wm_xr_navigation_teleport_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);
+
+ if (event->val == KM_PRESS) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else if (event->val == KM_RELEASE) {
+ XrRaycastData *data = op->customdata;
+ bool selectable_only, teleport_axes[3];
+ float teleport_t, teleport_ofs, ray_dist;
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "teleport_axes");
+ if (prop) {
+ RNA_property_boolean_get_array(op->ptr, prop, teleport_axes);
+ }
+ else {
+ teleport_axes[0] = teleport_axes[1] = teleport_axes[2] = true;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "interpolation");
+ teleport_t = prop ? RNA_property_float_get(op->ptr, prop) : 1.0f;
+
+ prop = RNA_struct_find_property(op->ptr, "offset");
+ teleport_ofs = prop ? RNA_property_float_get(op->ptr, prop) : 0.0f;
+
+ prop = RNA_struct_find_property(op->ptr, "selectable_only");
+ selectable_only = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ prop = RNA_struct_find_property(op->ptr, "distance");
+ ray_dist = prop ? RNA_property_float_get(op->ptr, prop) : BVH_RAYCAST_DIST_MAX;
+
+ wm_xr_navigation_teleport(C,
+ xr,
+ data->origin,
+ data->direction,
+ &ray_dist,
+ selectable_only,
+ teleport_axes,
+ teleport_t,
+ teleport_ofs);
+
+ wm_xr_raycast_uninit(op);
+
+ return OPERATOR_FINISHED;
+ }
+
+ /* XR events currently only support press and release. */
+ BLI_assert(false);
+ wm_xr_raycast_uninit(op);
+ return OPERATOR_CANCELLED;
+}
+
+static void WM_OT_xr_navigation_teleport(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Teleport";
+ ot->idname = "WM_OT_xr_navigation_teleport";
+ ot->description = "Set VR viewer location to controller raycast hit location";
+
+ /* callbacks */
+ ot->invoke = wm_xr_navigation_teleport_invoke;
+ ot->exec = wm_xr_navigation_teleport_exec;
+ ot->modal = wm_xr_navigation_teleport_modal;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ static bool default_teleport_axes[3] = {true, true, true};
+
+ RNA_def_boolean_vector(ot->srna,
+ "teleport_axes",
+ 3,
+ default_teleport_axes,
+ "Teleport Axes",
+ "Enabled teleport axes in navigation space");
+ RNA_def_float(ot->srna,
+ "interpolation",
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ "Interpolation",
+ "Interpolation factor between viewer and hit locations",
+ 0.0f,
+ 1.0f);
+ RNA_def_float(ot->srna,
+ "offset",
+ 0.0f,
+ 0.0f,
+ FLT_MAX,
+ "Offset",
+ "Offset along hit normal to subtract from final location",
+ 0.0f,
+ FLT_MAX);
+ 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 Navigation Reset
+ *
+ * Resets XR navigation deltas relative to session base pose.
+ * \{ */
+
+static int wm_xr_navigation_reset_exec(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ bool reset_loc, reset_rot, reset_scale;
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "location");
+ reset_loc = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ prop = RNA_struct_find_property(op->ptr, "rotation");
+ reset_rot = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ prop = RNA_struct_find_property(op->ptr, "scale");
+ reset_scale = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ if (reset_loc) {
+ float loc[3];
+ if (!reset_scale) {
+ float nav_rotation[4], nav_scale;
+
+ WM_xr_session_state_nav_rotation_get(xr, nav_rotation);
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+
+ /* Adjust location based on scale. */
+ mul_v3_v3fl(loc, xr->runtime->session_state.prev_base_pose.position, nav_scale);
+ sub_v3_v3(loc, xr->runtime->session_state.prev_base_pose.position);
+ mul_qt_v3(nav_rotation, loc);
+ negate_v3(loc);
+ }
+ else {
+ zero_v3(loc);
+ }
+ WM_xr_session_state_nav_location_set(xr, loc);
+ }
+
+ if (reset_rot) {
+ float rot[4];
+ unit_qt(rot);
+ WM_xr_session_state_nav_rotation_set(xr, rot);
+ }
+
+ if (reset_scale) {
+ if (!reset_loc) {
+ float nav_location[3], nav_rotation[4], nav_scale;
+ float nav_axes[3][3], v[3];
+
+ WM_xr_session_state_nav_location_get(xr, nav_location);
+ WM_xr_session_state_nav_rotation_get(xr, nav_rotation);
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+
+ /* Offset any location changes when changing scale. */
+ mul_v3_v3fl(v, xr->runtime->session_state.prev_base_pose.position, nav_scale);
+ sub_v3_v3(v, xr->runtime->session_state.prev_base_pose.position);
+ mul_qt_v3(nav_rotation, v);
+ add_v3_v3(nav_location, v);
+
+ /* Reset elevation to base pose value. */
+ quat_to_mat3(nav_axes, nav_rotation);
+ project_v3_v3v3_normalized(v, nav_location, nav_axes[2]);
+ sub_v3_v3(nav_location, v);
+
+ WM_xr_session_state_nav_location_set(xr, nav_location);
+ }
+ WM_xr_session_state_nav_scale_set(xr, 1.0f);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void WM_OT_xr_navigation_reset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Reset";
+ ot->idname = "WM_OT_xr_navigation_reset";
+ ot->description = "Reset VR navigation deltas relative to session base pose";
+
+ /* callbacks */
+ ot->exec = wm_xr_navigation_reset_exec;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "location", true, "Location", "Reset location deltas");
+ RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "Reset rotation deltas");
+ RNA_def_boolean(ot->srna, "scale", true, "Scale", "Reset scale deltas");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \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(depsgraph,
+ &vc,
+ origin,
+ direction,
+ ray_dist,
+ selectable_only,
+ 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 = 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) {
+ 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);
+
+ 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);
+
+ if (event->val == KM_PRESS) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else if (event->val == KM_RELEASE) {
+ XrRaycastData *data = op->customdata;
+ eSelectOp select_op = SEL_OP_SET;
+ bool deselect_all, selectable_only;
+ float ray_dist;
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "toggle");
+ if (prop && RNA_property_boolean_get(op->ptr, prop)) {
+ select_op = SEL_OP_XOR;
+ }
+ prop = RNA_struct_find_property(op->ptr, "deselect");
+ if (prop && RNA_property_boolean_get(op->ptr, prop)) {
+ select_op = SEL_OP_SUB;
+ }
+ prop = RNA_struct_find_property(op->ptr, "extend");
+ if (prop && RNA_property_boolean_get(op->ptr, prop)) {
+ select_op = SEL_OP_ADD;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "deselect_all");
+ deselect_all = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ prop = RNA_struct_find_property(op->ptr, "selectable_only");
+ selectable_only = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ prop = RNA_struct_find_property(op->ptr, "distance");
+ ray_dist = prop ? RNA_property_float_get(op->ptr, prop) : BVH_RAYCAST_DIST_MAX;
+
+ 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;
+ }
+
+ /* XR events currently only support press and release. */
+ BLI_assert(false);
+ 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;
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "location_lock");
+ loc_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ if (!loc_lock) {
+ prop = RNA_struct_find_property(op->ptr, "location_interpolation");
+ loc_t = prop ? RNA_property_float_get(op->ptr, prop) : 0.0f;
+ 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;
+ }
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "rotation_lock");
+ rot_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ if (!rot_lock) {
+ prop = RNA_struct_find_property(op->ptr, "rotation_interpolation");
+ rot_t = prop ? RNA_property_float_get(op->ptr, prop) : 0.0f;
+ 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;
+ }
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "scale_lock");
+ scale_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ 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 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);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = (obedit && (obedit->type == OB_MESH)) ? BKE_editmesh_from_object(obedit) : NULL;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ bScreen *screen_anim = ED_screen_animation_playing(wm);
+ bool loc_lock, rot_lock, scale_lock;
+ bool selected = false;
+ float delta[4][4];
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "location_lock");
+ loc_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ prop = RNA_struct_find_property(op->ptr, "rotation_lock");
+ rot_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+ prop = RNA_struct_find_property(op->ptr, "scale_lock");
+ scale_lock = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ const bool do_bimanual = (actiondata->bimanual && data->bimanual_prev);
+ const bool apply_transform = do_bimanual ? !(loc_lock && rot_lock && scale_lock) :
+ !(loc_lock && rot_lock);
+
+ if (em) { /* TODO_XR: Non-mesh objects. */
+ if (apply_transform) {
+ ToolSettings *ts = scene->toolsettings;
+ BMesh *bm = em->bm;
+ BMIter iter;
+
+ if (do_bimanual) {
+ wm_xr_grab_compute_bimanual(actiondata,
+ data,
+ obedit,
+ NULL,
+ NULL,
+ false,
+ loc_lock,
+ false,
+ rot_lock,
+ false,
+ scale_lock,
+ delta);
+ }
+ else {
+ wm_xr_grab_compute(
+ actiondata, data, obedit, NULL, NULL, false, loc_lock, false, rot_lock, 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);
+ }
+
+ selected = true;
+ }
+ else {
+ float out[4][4], m0[4][4], m1[4][4];
+
+ if (apply_transform) {
+ if (do_bimanual) {
+ wm_xr_grab_compute_bimanual(actiondata,
+ data,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ loc_lock,
+ false,
+ rot_lock,
+ false,
+ scale_lock,
+ delta);
+ }
+ else {
+ wm_xr_grab_compute(
+ actiondata, data, NULL, NULL, NULL, false, loc_lock, false, rot_lock, 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 (!loc_lock) {
+ copy_v3_v3(ob->loc, out[3]);
+ }
+ if (!rot_lock) {
+ mat4_to_eul(ob->rot, out);
+ }
+ if (!scale_lock && do_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);
+ }
+
+ selected = true;
+ }
+ CTX_DATA_END;
+ }
+
+ 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(false);
+ 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 XR Motion Capture Objects Toggle
+ *
+ * Toggles enabled/auto key behavior for XR motion capture objects.
+ * \{ */
+
+static void wm_xr_mocap_object_toggle(wmXrData *xr,
+ XrMotionCaptureObject *mocap_ob,
+ bool enable,
+ bool autokey)
+{
+ eXrMotionCaptureFlag *flag = (eXrMotionCaptureFlag *)&mocap_ob->flag;
+
+ if (enable) {
+ const bool disabled_prev = (*flag & XR_MOCAP_OBJECT_ENABLE) == 0;
+ SET_FLAG_FROM_TEST(*flag, disabled_prev, XR_MOCAP_OBJECT_ENABLE);
+
+ if (disabled_prev) {
+ /* Now enabled -> store object's original pose. */
+ WM_xr_session_state_mocap_pose_set(xr, mocap_ob);
+ }
+ else {
+ /* Now disabled -> restore object's original pose. */
+ WM_xr_session_state_mocap_pose_get(xr, mocap_ob);
+ }
+ }
+
+ if (autokey) {
+ SET_FLAG_FROM_TEST(*flag, (*flag & XR_MOCAP_OBJECT_AUTOKEY) == 0, XR_MOCAP_OBJECT_AUTOKEY);
+ }
+}
+
+static int wm_xr_mocap_objects_toggle_exec(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ bool enable, autokey;
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enable");
+ enable = prop ? RNA_property_boolean_get(op->ptr, prop) : true;
+
+ prop = RNA_struct_find_property(op->ptr, "autokey");
+ autokey = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
+
+ LISTBASE_FOREACH (XrMotionCaptureObject *, mocap_ob, &xr->session_settings.mocap_objects) {
+ wm_xr_mocap_object_toggle(xr, mocap_ob, enable, autokey);
+ }
+
+ WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void WM_OT_xr_mocap_objects_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Motion Capture Objects Toggle";
+ ot->idname = "WM_OT_xr_mocap_objects_toggle";
+ ot->description = "Toggle bindings/auto keying for VR motion capture objects";
+
+ /* callbacks */
+ ot->exec = wm_xr_mocap_objects_toggle_exec;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "enable", true, "Enable", "Toggle object bindings");
+ RNA_def_boolean(ot->srna, "autokey", false, "Auto Key", "Toggle auto keying");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Registration
+ * \{ */
+
+void wm_xr_operatortypes_register(void)
+{
+ WM_operatortype_append(WM_OT_xr_session_toggle);
+ WM_operatortype_append(WM_OT_xr_navigation_grab);
+ 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);
+ WM_operatortype_append(WM_OT_xr_mocap_objects_toggle);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index 88bf3ff453c..845c0fa20b4 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -21,8 +21,10 @@
#include "BKE_callbacks.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_idprop.h"
#include "BKE_main.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -30,18 +32,26 @@
#include "DEG_depsgraph.h"
#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
#include "DRW_engine.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+
#include "GHOST_C-api.h"
+#include "GPU_batch.h"
#include "GPU_viewport.h"
#include "MEM_guardedalloc.h"
+#include "PIL_time.h"
+
#include "WM_api.h"
#include "WM_types.h"
+#include "wm_event_system.h"
#include "wm_surface.h"
#include "wm_window.h"
#include "wm_xr_intern.h"
@@ -56,21 +66,45 @@ static void wm_xr_session_create_cb(void)
Main *bmain = G_MAIN;
wmWindowManager *wm = bmain->wm.first;
wmXrData *xr_data = &wm->xr;
+ XrSessionSettings *settings = &xr_data->session_settings;
+ wmXrSessionState *state = &xr_data->runtime->session_state;
/* Get action set data from Python. */
BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE);
wm_xr_session_actions_init(xr_data);
+
+ /* Store motion capture object poses. */
+ wm_xr_mocap_orig_poses_store(settings, state);
+
+ /* Initialize navigation. */
+ WM_xr_session_state_navigation_reset(state);
+ if (settings->base_scale < 0.001f) {
+ settings->base_scale = 1.0f;
+ }
+ state->prev_base_scale = settings->base_scale;
}
static void wm_xr_session_controller_data_free(wmXrSessionState *state)
{
- BLI_freelistN(&state->controllers);
+ ListBase *lb = &state->controllers;
+ wmXrController *c;
+
+ while ((c = BLI_pophead(lb))) {
+ if (c->model) {
+ GPU_batch_discard(c->model);
+ }
+ BLI_freelinkN(lb, c);
+ }
}
void wm_xr_session_data_free(wmXrSessionState *state)
{
+ BLI_freelistN(&state->eyes);
+
wm_xr_session_controller_data_free(state);
+
+ BLI_freelistN(&state->mocap_orig_poses);
}
static void wm_xr_session_exit_cb(void *customdata)
@@ -80,7 +114,13 @@ static void wm_xr_session_exit_cb(void *customdata)
return;
}
- xr_data->runtime->session_state.is_started = false;
+ XrSessionSettings *settings = &xr_data->session_settings;
+ wmXrSessionState *state = &xr_data->runtime->session_state;
+
+ state->is_started = false;
+
+ /* Restore motion capture object poses. */
+ wm_xr_mocap_orig_poses_restore(state, settings);
if (xr_data->runtime->exit_fn) {
xr_data->runtime->exit_fn(xr_data);
@@ -111,6 +151,7 @@ void wm_xr_session_toggle(wmWindowManager *wm,
if (WM_xr_session_exists(xr_data)) {
GHOST_XrSessionEnd(xr_data->runtime->context);
+ xr_data->runtime->session_state.is_started = false;
}
else {
GHOST_XrSessionBeginInfo begin_info;
@@ -148,7 +189,8 @@ bool WM_xr_session_is_ready(const wmXrData *xr)
static void wm_xr_session_base_pose_calc(const Scene *scene,
const XrSessionSettings *settings,
- GHOST_XrPose *r_base_pose)
+ GHOST_XrPose *r_base_pose,
+ float *r_base_scale)
{
const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) &&
settings->base_pose_object) ?
@@ -179,6 +221,8 @@ static void wm_xr_session_base_pose_calc(const Scene *scene,
copy_v3_fl(r_base_pose->position, 0.0f);
axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2);
}
+
+ *r_base_scale = settings->base_scale;
}
static void wm_xr_session_draw_data_populate(wmXrData *xr_data,
@@ -194,7 +238,8 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data,
r_draw_data->xr_data = xr_data;
r_draw_data->surface_data = g_xr_surface->customdata;
- wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose);
+ wm_xr_session_base_pose_calc(
+ r_draw_data->scene, settings, &r_draw_data->base_pose, &r_draw_data->base_scale);
}
static wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm,
@@ -272,7 +317,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState
return SESSION_STATE_EVENT_NONE;
}
-void wm_xr_session_draw_data_update(const wmXrSessionState *state,
+void wm_xr_session_draw_data_update(wmXrSessionState *state,
const XrSessionSettings *settings,
const GHOST_XrDrawViewInfo *draw_view,
wmXrDrawData *draw_data)
@@ -300,6 +345,8 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
else {
copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
}
+ /* Reset navigation. */
+ WM_xr_session_state_navigation_reset(state);
break;
case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE:
if (use_position_tracking) {
@@ -326,40 +373,59 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
void wm_xr_session_state_update(const XrSessionSettings *settings,
const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
+ const float viewmat[4][4],
+ const float viewmat_base[4][4],
wmXrSessionState *state)
{
GHOST_XrPose viewer_pose;
- const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING;
- const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING;
-
- mul_qt_qtqt(viewer_pose.orientation_quat,
- draw_data->base_pose.orientation_quat,
- draw_view->local_pose.orientation_quat);
- copy_v3_v3(viewer_pose.position, draw_data->base_pose.position);
- /* The local pose and the eye pose (which is copied from an earlier local pose) both are view
- * space, so Y-up. In this case we need them in regular Z-up. */
- if (use_position_tracking) {
- viewer_pose.position[0] += draw_view->local_pose.position[0];
- viewer_pose.position[1] -= draw_view->local_pose.position[2];
- viewer_pose.position[2] += draw_view->local_pose.position[1];
- }
- if (!use_absolute_tracking) {
- viewer_pose.position[0] -= draw_data->eye_position_ofs[0];
- viewer_pose.position[1] += draw_data->eye_position_ofs[2];
- viewer_pose.position[2] -= draw_data->eye_position_ofs[1];
- }
-
- copy_v3_v3(state->viewer_pose.position, viewer_pose.position);
- copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat);
- wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat);
+ float viewer_mat[4][4], base_mat[4][4], nav_mat[4][4];
+
+ wmXrEye *eye = NULL;
+ if (draw_view->view_idx >= BLI_listbase_count(&state->eyes)) {
+ eye = MEM_callocN(sizeof(*eye), __func__);
+ BLI_addtail(&state->eyes, eye);
+ }
+ else {
+ eye = BLI_findlink(&state->eyes, draw_view->view_idx);
+ }
+ BLI_assert(eye);
+
+ /* Calculate viewer matrix. */
+ copy_qt_qt(viewer_pose.orientation_quat, draw_view->local_pose.orientation_quat);
+ if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
+ zero_v3(viewer_pose.position);
+ }
+ else {
+ copy_v3_v3(viewer_pose.position, draw_view->local_pose.position);
+ }
+ if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
+ sub_v3_v3(viewer_pose.position, draw_data->eye_position_ofs);
+ }
+ wm_xr_pose_to_mat(&viewer_pose, viewer_mat);
+
+ /* Apply base pose and navigation. */
+ wm_xr_pose_scale_to_mat(&draw_data->base_pose, draw_data->base_scale, base_mat);
+ wm_xr_pose_scale_to_mat(&state->nav_pose_prev, state->nav_scale_prev, nav_mat);
+ mul_m4_m4m4(state->viewer_mat_base, base_mat, viewer_mat);
+ mul_m4_m4m4(viewer_mat, nav_mat, state->viewer_mat_base);
+
+ /* Save final viewer pose and viewmat. */
+ mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat);
+ wm_xr_pose_scale_to_imat(
+ &state->viewer_pose, draw_data->base_scale * state->nav_scale_prev, state->viewer_viewmat);
+
/* No idea why, but multiplying by two seems to make it match the VR view more. */
- state->focal_len = 2.0f *
- fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left,
- DEFAULT_SENSOR_WIDTH);
+ eye->focal_len = 2.0f *
+ fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left,
+ DEFAULT_SENSOR_WIDTH);
+ copy_m4_m4(eye->viewmat, viewmat);
+ copy_m4_m4(eye->viewmat_base, viewmat_base);
- copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose));
+ state->prev_base_scale = draw_data->base_scale;
memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose));
+ copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
+
state->prev_settings_flag = settings->flag;
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
@@ -391,22 +457,38 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
return false;
}
- copy_v4_v4(r_rotation, xr->runtime->session_state.viewer_pose.orientation_quat);
+ copy_qt_qt(r_rotation, xr->runtime->session_state.viewer_pose.orientation_quat);
return true;
}
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
+ bool from_selection_eye,
float r_viewmat[4][4],
- float *r_focal_len)
+ float *r_focal_len,
+ float *r_clip_start,
+ float *r_clip_end)
{
- if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) {
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ (xr->session_settings.selection_eye >=
+ BLI_listbase_count(&xr->runtime->session_state.eyes))) {
unit_m4(r_viewmat);
- *r_focal_len = 0.0f;
+ *r_focal_len = *r_clip_start = *r_clip_end = 0.0f;
return false;
}
- copy_m4_m4(r_viewmat, xr->runtime->session_state.viewer_viewmat);
- *r_focal_len = xr->runtime->session_state.focal_len;
+ const wmXrEye *eye = BLI_findlink(&xr->runtime->session_state.eyes,
+ xr->session_settings.selection_eye);
+ BLI_assert(eye);
+ if (from_selection_eye) {
+ copy_m4_m4(r_viewmat, eye->viewmat);
+ }
+ else {
+ copy_m4_m4(r_viewmat, xr->runtime->session_state.viewer_viewmat);
+ }
+ /* Since eye centroid does not have a focal length, just take it from selection eye. */
+ *r_focal_len = eye->focal_len;
+ *r_clip_start = xr->session_settings.clip_start;
+ *r_clip_end = xr->session_settings.clip_end;
return true;
}
@@ -479,6 +561,74 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
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) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ copy_v3_v3(r_location, xr->runtime->session_state.nav_pose.position);
+ return true;
+}
+
+void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3])
+{
+ if (WM_xr_session_exists(xr)) {
+ copy_v3_v3(xr->runtime->session_state.nav_pose.position, location);
+ xr->runtime->session_state.is_navigation_dirty = true;
+ }
+}
+
+bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, 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;
+ }
+
+ copy_qt_qt(r_rotation, xr->runtime->session_state.nav_pose.orientation_quat);
+ return true;
+}
+
+void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4])
+{
+ if (WM_xr_session_exists(xr)) {
+ BLI_ASSERT_UNIT_QUAT(rotation);
+ copy_qt_qt(xr->runtime->session_state.nav_pose.orientation_quat, rotation);
+ xr->runtime->session_state.is_navigation_dirty = true;
+ }
+}
+
+bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale)
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) {
+ *r_scale = 1.0f;
+ return false;
+ }
+
+ *r_scale = xr->runtime->session_state.nav_scale;
+ return true;
+}
+
+void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale)
+{
+ if (WM_xr_session_exists(xr)) {
+ /* Clamp to reasonable values. */
+ CLAMP(scale, 0.001f, 1000.0f);
+ xr->runtime->session_state.nav_scale = scale;
+ xr->runtime->session_state.is_navigation_dirty = true;
+ }
+}
+
+void WM_xr_session_state_navigation_reset(wmXrSessionState *state)
+{
+ zero_v3(state->nav_pose.position);
+ unit_qt(state->nav_pose.orientation_quat);
+ state->nav_scale = 1.0f;
+ state->is_navigation_dirty = true;
+}
+
/* -------------------------------------------------------------------- */
/** \name XR-Session Actions
*
@@ -498,31 +648,43 @@ void wm_xr_session_actions_init(wmXrData *xr)
static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose,
const float view_ofs[3],
const float base_mat[4][4],
+ const float nav_mat[4][4],
GHOST_XrPose *r_pose,
- float r_mat[4][4])
+ float r_mat[4][4],
+ float r_mat_base[4][4])
{
float m[4][4];
/* Calculate controller matrix in world space. */
wm_xr_pose_to_mat(raw_pose, m);
- /* Apply eye position and base pose offsets. */
+ /* Apply eye position offset. */
sub_v3_v3(m[3], view_ofs);
- mul_m4_m4m4(r_mat, base_mat, m);
+
+ /* Apply base pose and navigation. */
+ mul_m4_m4m4(r_mat_base, base_mat, m);
+ mul_m4_m4m4(r_mat, nav_mat, r_mat_base);
/* Save final 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,
- wmXrSessionState *state)
+ 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));
+ 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);
unsigned int subaction_idx = 0;
- float view_ofs[3], base_mat[4][4];
+ float view_ofs[3], base_mat[4][4], nav_mat[4][4];
if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
copy_v3_v3(view_ofs, state->prev_local_pose.position);
@@ -534,44 +696,634 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin
add_v3_v3(view_ofs, state->prev_eye_position_ofs);
}
- wm_xr_pose_to_mat(&state->prev_base_pose, base_mat);
+ 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);
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,
+ 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,
+ 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,
+ (subaction_idx == 0) ? true : false);
+
+ /* 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 const GHOST_XrPose *wm_xr_session_controller_aim_pose_find(const wmXrSessionState *state,
+ const char *subaction_path)
+{
+ const wmXrController *controller = BLI_findstring(
+ &state->controllers, subaction_path, offsetof(wmXrController, subaction_path));
+ return controller ? &controller->aim_pose : NULL;
+}
+
+BLI_INLINE bool test_float_state(const float *state, float threshold, eXrAxisFlag flag)
+{
+ if ((flag & XR_AXIS0_POS) != 0) {
+ if (*state > threshold) {
+ return true;
+ }
+ }
+ else if ((flag & XR_AXIS0_NEG) != 0) {
+ if (*state < -threshold) {
+ return true;
+ }
+ }
+ else {
+ if (fabsf(*state) > threshold) {
+ return true;
+ }
+ }
+ return false;
+}
+
+BLI_INLINE bool test_vec2f_state(const float state[2], float threshold, eXrAxisFlag flag)
+{
+ if ((flag & XR_AXIS0_POS) != 0) {
+ if (state[0] < 0.0f) {
+ return false;
+ }
+ }
+ else if ((flag & XR_AXIS0_NEG) != 0) {
+ if (state[0] > 0.0f) {
+ return false;
+ }
+ }
+ if ((flag & XR_AXIS1_POS) != 0) {
+ if (state[1] < 0.0f) {
+ return false;
+ }
+ }
+ else if ((flag & XR_AXIS1_NEG) != 0) {
+ if (state[1] > 0.0f) {
+ return false;
+ }
+ }
+ return (len_v2(state) > threshold);
+}
+
+static bool wm_xr_session_modal_action_test(const ListBase *active_modal_actions,
+ const wmXrAction *action,
+ bool *r_found)
+{
+ if (r_found) {
+ *r_found = false;
+ }
+
+ LISTBASE_FOREACH (LinkData *, ld, active_modal_actions) {
+ wmXrAction *active_modal_action = ld->data;
+ if (action == active_modal_action) {
+ if (r_found) {
+ *r_found = true;
+ }
+ return true;
+ }
+ if (action->ot == active_modal_action->ot &&
+ IDP_EqualsProperties(action->op_properties, active_modal_action->op_properties)) {
+ /* Don't allow duplicate modal operators since this can lead to unwanted modal handler
+ * behavior. */
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void wm_xr_session_modal_action_test_add(ListBase *active_modal_actions,
+ const wmXrAction *action)
+{
+ bool found;
+ if (wm_xr_session_modal_action_test(active_modal_actions, action, &found) && !found) {
+ LinkData *ld = MEM_callocN(sizeof(LinkData), __func__);
+ ld->data = (void *)action;
+ BLI_addtail(active_modal_actions, ld);
+ }
+}
+
+static void wm_xr_session_modal_action_remove(ListBase *active_modal_actions,
+ const wmXrAction *action)
+{
+ LISTBASE_FOREACH (LinkData *, ld, active_modal_actions) {
+ if (action == ld->data) {
+ BLI_freelinkN(active_modal_actions, ld);
+ return;
+ }
+ }
+}
+
+static wmXrHapticAction *wm_xr_session_haptic_action_find(ListBase *active_haptic_actions,
+ const wmXrAction *action,
+ const char *subaction_path)
+{
+ LISTBASE_FOREACH (wmXrHapticAction *, ha, active_haptic_actions) {
+ if ((action == ha->action) && (subaction_path == ha->subaction_path)) {
+ return ha;
+ }
+ }
+
+ return NULL;
+}
+
+static void wm_xr_session_haptic_action_add(ListBase *active_haptic_actions,
+ const wmXrAction *action,
+ const char *subaction_path,
+ int64_t time_now)
+{
+ wmXrHapticAction *ha = wm_xr_session_haptic_action_find(
+ active_haptic_actions, action, subaction_path);
+ if (ha) {
+ /* Reset start time since OpenXR restarts haptics if they are already active. */
+ ha->time_start = time_now;
+ }
+ else {
+ ha = MEM_callocN(sizeof(wmXrHapticAction), __func__);
+ ha->action = (wmXrAction *)action;
+ ha->subaction_path = subaction_path;
+ ha->time_start = time_now;
+ BLI_addtail(active_haptic_actions, ha);
+ }
+}
+
+static void wm_xr_session_haptic_action_remove(ListBase *active_haptic_actions,
+ const wmXrAction *action)
+{
+ LISTBASE_FOREACH (wmXrHapticAction *, ha, active_haptic_actions) {
+ if (action == ha->action) {
+ BLI_freelinkN(active_haptic_actions, ha);
+ return;
+ }
+ }
+}
+
+static void wm_xr_session_haptic_timers_check(ListBase *active_haptic_actions, int64_t time_now)
+{
+ wmXrHapticAction *ha = active_haptic_actions->first;
+ while (ha) {
+ if (time_now - ha->time_start >= ha->action->haptic_duration) {
+ wmXrHapticAction *_ha = ha;
+ ha = ha->next;
+ BLI_freelinkN(active_haptic_actions, _ha);
+ }
+ else {
+ ha = ha->next;
+ }
+ }
+}
+
+static void wm_xr_session_action_states_interpret(wmXrData *xr,
+ const char *action_set_name,
+ wmXrAction *action,
+ unsigned int subaction_idx,
+ ListBase *active_modal_actions,
+ ListBase *active_haptic_actions,
+ int64_t time_now,
+ bool modal,
+ bool haptic,
+ short *r_val)
+{
+ const char *haptic_subaction_path = ((action->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) ?
+ action->subaction_paths[subaction_idx] :
+ NULL;
+ bool curr = false;
+ bool prev = false;
+
+ switch (action->type) {
+ case XR_BOOLEAN_INPUT: {
+ const bool *state = &((bool *)action->states)[subaction_idx];
+ bool *state_prev = &((bool *)action->states_prev)[subaction_idx];
+ if (*state) {
+ curr = true;
+ }
+ if (*state_prev) {
+ prev = true;
+ }
+ *state_prev = *state;
+ break;
+ }
+ case XR_FLOAT_INPUT: {
+ const float *state = &((float *)action->states)[subaction_idx];
+ float *state_prev = &((float *)action->states_prev)[subaction_idx];
+ if (test_float_state(
+ state, action->float_thresholds[subaction_idx], action->axis_flags[subaction_idx])) {
+ curr = true;
+ }
+ if (test_float_state(state_prev,
+ action->float_thresholds[subaction_idx],
+ action->axis_flags[subaction_idx])) {
+ prev = true;
+ }
+ *state_prev = *state;
+ break;
+ }
+ case XR_VECTOR2F_INPUT: {
+ const float(*state)[2] = &((float(*)[2])action->states)[subaction_idx];
+ float(*state_prev)[2] = &((float(*)[2])action->states_prev)[subaction_idx];
+ if (test_vec2f_state(*state,
+ action->float_thresholds[subaction_idx],
+ action->axis_flags[subaction_idx])) {
+ curr = true;
+ }
+ if (test_vec2f_state(*state_prev,
+ action->float_thresholds[subaction_idx],
+ action->axis_flags[subaction_idx])) {
+ prev = true;
+ }
+ copy_v2_v2(*state_prev, *state);
+ break;
+ }
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ if (curr) {
+ if (!prev) {
+ if (modal || (action->op_flag == XR_OP_PRESS)) {
+ *r_val = KM_PRESS;
+ }
+ if (haptic && (action->haptic_flag & (XR_HAPTIC_PRESS | XR_HAPTIC_REPEAT)) != 0) {
+ /* Apply haptics. */
+ if (WM_xr_haptic_action_apply(xr,
+ action_set_name,
+ action->haptic_name,
+ haptic_subaction_path,
+ &action->haptic_duration,
+ &action->haptic_frequency,
+ &action->haptic_amplitude)) {
+ wm_xr_session_haptic_action_add(
+ active_haptic_actions, action, haptic_subaction_path, time_now);
+ }
+ }
+ }
+ else if (modal) {
+ *r_val = KM_PRESS;
+ }
+ if (modal && !action->active_modal_path) {
+ /* Set active modal path. */
+ action->active_modal_path = action->subaction_paths[subaction_idx];
+ /* Add to active modal actions. */
+ wm_xr_session_modal_action_test_add(active_modal_actions, action);
+ }
+ if (haptic && ((action->haptic_flag & XR_HAPTIC_REPEAT) != 0)) {
+ if (!wm_xr_session_haptic_action_find(
+ active_haptic_actions, action, haptic_subaction_path)) {
+ /* Apply haptics. */
+ if (WM_xr_haptic_action_apply(xr,
+ action_set_name,
+ action->haptic_name,
+ haptic_subaction_path,
+ &action->haptic_duration,
+ &action->haptic_frequency,
+ &action->haptic_amplitude)) {
+ wm_xr_session_haptic_action_add(
+ active_haptic_actions, action, haptic_subaction_path, time_now);
+ }
+ }
+ }
+ }
+ else if (prev) {
+ if (modal || (action->op_flag == XR_OP_RELEASE)) {
+ *r_val = KM_RELEASE;
+ if (modal && (action->subaction_paths[subaction_idx] == action->active_modal_path)) {
+ /* Unset active modal path. */
+ action->active_modal_path = NULL;
+ /* Remove from active modal actions. */
+ wm_xr_session_modal_action_remove(active_modal_actions, action);
+ }
+ }
+ if (haptic) {
+ if ((action->haptic_flag & XR_HAPTIC_RELEASE) != 0) {
+ /* Apply haptics. */
+ if (WM_xr_haptic_action_apply(xr,
+ action_set_name,
+ action->haptic_name,
+ haptic_subaction_path,
+ &action->haptic_duration,
+ &action->haptic_frequency,
+ &action->haptic_amplitude)) {
+ wm_xr_session_haptic_action_add(
+ active_haptic_actions, action, haptic_subaction_path, time_now);
+ }
+ }
+ else if ((action->haptic_flag & XR_HAPTIC_REPEAT) != 0) {
+ /* Stop any active haptics. */
+ WM_xr_haptic_action_stop(xr, action_set_name, action->haptic_name, haptic_subaction_path);
+ wm_xr_session_haptic_action_remove(active_haptic_actions, action);
+ }
+ }
}
}
-void wm_xr_session_actions_update(wmXrData *xr)
+static bool wm_xr_session_action_test_bimanual(const wmXrSessionState *session_state,
+ wmXrAction *action,
+ unsigned int subaction_idx,
+ unsigned int *r_subaction_idx_other,
+ const GHOST_XrPose **r_aim_pose_other)
{
+ if ((action->action_flag & XR_ACTION_BIMANUAL) == 0) {
+ return false;
+ }
+
+ bool bimanual = false;
+
+ *r_subaction_idx_other = (subaction_idx == 0) ?
+ (unsigned int)min_ii(1, action->count_subaction_paths - 1) :
+ 0;
+
+ switch (action->type) {
+ case XR_BOOLEAN_INPUT: {
+ const bool *state = &((bool *)action->states)[*r_subaction_idx_other];
+ if (*state) {
+ bimanual = true;
+ }
+ break;
+ }
+ case XR_FLOAT_INPUT: {
+ const float *state = &((float *)action->states)[*r_subaction_idx_other];
+ if (test_float_state(state,
+ action->float_thresholds[*r_subaction_idx_other],
+ action->axis_flags[*r_subaction_idx_other])) {
+ bimanual = true;
+ }
+ break;
+ }
+ case XR_VECTOR2F_INPUT: {
+ const float(*state)[2] = &((float(*)[2])action->states)[*r_subaction_idx_other];
+ if (test_vec2f_state(*state,
+ action->float_thresholds[*r_subaction_idx_other],
+ action->axis_flags[*r_subaction_idx_other])) {
+ bimanual = true;
+ }
+ break;
+ }
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ if (bimanual) {
+ *r_aim_pose_other = wm_xr_session_controller_aim_pose_find(
+ session_state, action->subaction_paths[*r_subaction_idx_other]);
+ }
+
+ return bimanual;
+}
+
+static wmXrActionData *wm_xr_session_event_create(const char *action_set_name,
+ const wmXrAction *action,
+ const wmXrEye *selection_eye,
+ const GHOST_XrPose *controller_aim_pose,
+ const GHOST_XrPose *controller_aim_pose_other,
+ unsigned int subaction_idx,
+ unsigned int subaction_idx_other,
+ bool bimanual)
+{
+ wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__);
+ strcpy(data->action_set, action_set_name);
+ strcpy(data->action, action->name);
+ data->type = action->type;
+
+ switch (action->type) {
+ case XR_BOOLEAN_INPUT:
+ data->state[0] = ((bool *)action->states)[subaction_idx] ? 1.0f : 0.0f;
+ if (bimanual) {
+ data->state_other[0] = ((bool *)action->states)[subaction_idx_other] ? 1.0f : 0.0f;
+ }
+ break;
+ case XR_FLOAT_INPUT:
+ data->state[0] = ((float *)action->states)[subaction_idx];
+ if (bimanual) {
+ data->state_other[0] = ((float *)action->states)[subaction_idx_other];
+ }
+ data->float_threshold = action->float_thresholds[subaction_idx];
+ break;
+ case XR_VECTOR2F_INPUT:
+ copy_v2_v2(data->state, ((float(*)[2])action->states)[subaction_idx]);
+ if (bimanual) {
+ copy_v2_v2(data->state_other, ((float(*)[2])action->states)[subaction_idx_other]);
+ }
+ data->float_threshold = action->float_thresholds[subaction_idx];
+ break;
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ if (controller_aim_pose) {
+ copy_v3_v3(data->controller_loc, controller_aim_pose->position);
+ copy_qt_qt(data->controller_rot, controller_aim_pose->orientation_quat);
+
+ if (bimanual && controller_aim_pose_other) {
+ copy_v3_v3(data->controller_loc_other, controller_aim_pose_other->position);
+ copy_qt_qt(data->controller_rot_other, controller_aim_pose_other->orientation_quat);
+ }
+ else {
+ data->controller_rot_other[0] = 1.0f;
+ }
+ }
+ else {
+ data->controller_rot[0] = 1.0f;
+ data->controller_rot_other[0] = 1.0f;
+ }
+
+ if (selection_eye) {
+ copy_m4_m4(data->eye_viewmat, selection_eye->viewmat);
+ data->eye_lens = selection_eye->focal_len;
+ }
+
+ data->ot = action->ot;
+ data->op_properties = action->op_properties;
+
+ data->bimanual = bimanual;
+
+ return data;
+}
+
+/* Dispatch events to XR surface / window queues. */
+static void wm_xr_session_events_dispatch(wmXrData *xr,
+ const XrSessionSettings *settings,
+ GHOST_XrContextHandle xr_context,
+ wmXrActionSet *action_set,
+ wmXrSessionState *session_state,
+ wmWindow *win)
+{
+ const char *action_set_name = action_set->name;
+
+ const unsigned int count = GHOST_XrGetActionCount(xr_context, action_set_name);
+ if (count < 1 || (settings->selection_eye >= BLI_listbase_count(&session_state->eyes))) {
+ return;
+ }
+
+ const int64_t time_now = (int64_t)(PIL_check_seconds_timer() * 1000);
+
+ const wmXrEye *selection_eye = BLI_findlink(&session_state->eyes, settings->selection_eye);
+ ListBase *active_modal_actions = &action_set->active_modal_actions;
+ ListBase *active_haptic_actions = &action_set->active_haptic_actions;
+
+ wmXrAction **actions = MEM_calloc_arrayN(count, sizeof(*actions), __func__);
+
+ GHOST_XrGetActionCustomdataArray(xr_context, action_set_name, (void **)actions);
+
+ /* Check haptic action timers. */
+ wm_xr_session_haptic_timers_check(active_haptic_actions, time_now);
+
+ for (unsigned int action_idx = 0; action_idx < count; ++action_idx) {
+ wmXrAction *action = actions[action_idx];
+ if (action && action->ot) {
+ const bool modal = (action->ot->modal || action->ot->modal_3d);
+ const bool haptic = (GHOST_XrGetActionCustomdata(
+ xr_context, action_set_name, action->haptic_name) != NULL);
+
+ for (unsigned int subaction_idx = 0; subaction_idx < action->count_subaction_paths;
+ ++subaction_idx) {
+ short val = KM_NOTHING;
+
+ /* Interpret action states (update modal/haptic action lists, apply haptics, etc). */
+ wm_xr_session_action_states_interpret(xr,
+ action_set_name,
+ action,
+ subaction_idx,
+ active_modal_actions,
+ active_haptic_actions,
+ time_now,
+ modal,
+ haptic,
+ &val);
+
+ if ((val != KM_NOTHING) &&
+ (!modal || (wm_xr_session_modal_action_test(active_modal_actions, action, NULL) &&
+ (!action->active_modal_path || (action->subaction_paths[subaction_idx] ==
+ action->active_modal_path))))) {
+ const GHOST_XrPose *aim_pose = wm_xr_session_controller_aim_pose_find(
+ session_state, action->subaction_paths[subaction_idx]);
+ const GHOST_XrPose *aim_pose_other = NULL;
+ unsigned int subaction_idx_other = 0;
+
+ /* Test for bimanual interaction. */
+ const bool bimanual = wm_xr_session_action_test_bimanual(
+ session_state, action, subaction_idx, &subaction_idx_other, &aim_pose_other);
+
+ wmXrActionData *actiondata = wm_xr_session_event_create(action_set_name,
+ action,
+ selection_eye,
+ aim_pose,
+ aim_pose_other,
+ subaction_idx,
+ subaction_idx_other,
+ bimanual);
+ wm_event_add_xrevent(win, actiondata, val);
+ }
+ }
+ }
+ }
+
+ MEM_freeN(actions);
+}
+
+void wm_xr_session_actions_update(const bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
if (!xr->runtime) {
return;
}
+ XrSessionSettings *settings = &xr->session_settings;
+ wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime);
GHOST_XrContextHandle xr_context = xr->runtime->context;
wmXrSessionState *state = &xr->runtime->session_state;
wmXrActionSet *active_action_set = state->active_action_set;
+ if (state->is_navigation_dirty) {
+ memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev));
+ state->nav_scale_prev = state->nav_scale;
+ state->is_navigation_dirty = false;
+
+ /* Update viewer/eye poses with any navigation changes since the last actions sync so that data
+ * is correct for queries. */
+ float m[4][4], viewer_mat[4][4];
+ wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, m);
+ mul_m4_m4m4(viewer_mat, m, state->viewer_mat_base);
+ mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat);
+ wm_xr_pose_scale_to_imat(
+ &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat);
+
+ wm_xr_pose_scale_to_imat(&state->nav_pose, state->nav_scale, m);
+ LISTBASE_FOREACH (wmXrEye *, eye, &state->eyes) {
+ mul_m4_m4m4(eye->viewmat, eye->viewmat_base, m);
+ }
+ }
+
+ /* Update headset motion capture objects. */
+ {
+ const char *user_path = "/user/head";
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ bScreen *screen_anim = ED_screen_animation_playing(wm);
+
+ wm_xr_mocap_objects_update(user_path,
+ &state->viewer_pose,
+ (bContext *)C,
+ settings,
+ scene,
+ view_layer,
+ win,
+ screen_anim,
+ true);
+ }
+
int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL);
if (!ret) {
return;
}
- /* Only update controller data for active action set. */
+ /* Only update controller data and dispatch events for active action set. */
if (active_action_set) {
if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) {
- wm_xr_session_controller_data_update(&xr->session_settings,
- active_action_set->controller_grip_action,
+ wm_xr_session_controller_data_update(active_action_set->controller_grip_action,
active_action_set->controller_aim_action,
- state);
+ (bContext *)C,
+ &xr->session_settings,
+ xr_context,
+ state,
+ win);
+ }
+
+ if (win) {
+ wm_xr_session_events_dispatch(xr, settings, xr_context, active_action_set, state, win);
}
}
}
@@ -598,11 +1350,29 @@ void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
BLI_addtail(controllers, controller);
}
+
+ /* Activate draw callback. */
+ if (g_xr_surface) {
+ wmXrSurfaceData *surface_data = g_xr_surface->customdata;
+ if (surface_data->controller_art && !surface_data->controller_draw_handle) {
+ surface_data->controller_draw_handle = ED_region_draw_cb_activate(
+ surface_data->controller_art, wm_xr_draw_controllers, xr, REGION_DRAW_POST_VIEW);
+ }
+ }
}
void wm_xr_session_controller_data_clear(wmXrSessionState *state)
{
wm_xr_session_controller_data_free(state);
+
+ /* Deactivate draw callback. */
+ if (g_xr_surface) {
+ wmXrSurfaceData *surface_data = g_xr_surface->customdata;
+ if (surface_data->controller_art && surface_data->controller_draw_handle) {
+ ED_region_draw_cb_exit(surface_data->controller_art, surface_data->controller_draw_handle);
+ surface_data->controller_draw_handle = NULL;
+ }
+ }
}
/** \} */ /* XR-Session Actions */
@@ -628,7 +1398,7 @@ static void wm_xr_session_surface_draw(bContext *C)
Main *bmain = CTX_data_main(C);
wmXrDrawData draw_data;
- if (!GHOST_XrSessionIsRunning(wm->xr.runtime->context)) {
+ if (!WM_xr_session_is_ready(&wm->xr)) {
return;
}
@@ -728,6 +1498,11 @@ static void wm_xr_session_surface_free_data(wmSurface *surface)
BLI_freelinkN(lb, vp);
}
+ if (data->controller_art) {
+ BLI_freelistN(&data->controller_art->drawcalls);
+ MEM_freeN(data->controller_art);
+ }
+
MEM_freeN(surface->customdata);
g_xr_surface = NULL;
@@ -742,6 +1517,7 @@ static wmSurface *wm_xr_session_surface_create(void)
wmSurface *surface = MEM_callocN(sizeof(*surface), __func__);
wmXrSurfaceData *data = MEM_callocN(sizeof(*data), "XrSurfaceData");
+ data->controller_art = MEM_callocN(sizeof(*(data->controller_art)), "XrControllerRegionType");
surface->draw = wm_xr_session_surface_draw;
surface->free_data = wm_xr_session_surface_free_data;
@@ -751,6 +1527,7 @@ static wmSurface *wm_xr_session_surface_create(void)
surface->ghost_ctx = DRW_xr_opengl_context_get();
surface->gpu_ctx = DRW_xr_gpu_context_get();
+ data->controller_art->regionid = RGN_TYPE_XR;
surface->customdata = data;
g_xr_surface = surface;
@@ -784,4 +1561,13 @@ void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle UNUSED(contex
WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL);
}
+ARegionType *WM_xr_surface_controller_region_type_get(void)
+{
+ if (g_xr_surface) {
+ wmXrSurfaceData *data = g_xr_surface->customdata;
+ return data->controller_art;
+ }
+ return NULL;
+}
+
/** \} */ /* XR-Session Surface */
diff --git a/source/blender/windowmanager/xr/wm_xr.h b/source/blender/windowmanager/xr/wm_xr.h
index 0f0fbe8bc00..cfe20177d87 100644
--- a/source/blender/windowmanager/xr/wm_xr.h
+++ b/source/blender/windowmanager/xr/wm_xr.h
@@ -22,6 +22,7 @@
struct wmWindowManager;
struct wmXrData;
+struct bContext;
typedef void (*wmXrSessionExitFn)(const wmXrData *xr_data);
@@ -29,4 +30,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 */
+void wm_xr_operatortypes_register(void);
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 2ec4a2aa616..be096db6135 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -513,6 +513,10 @@ int main(int argc,
CTX_py_init_set(C, true);
WM_keyconfig_init(C);
+#ifdef WITH_XR_OPENXR
+ WM_xr_actionconfig_init(C);
+#endif
+
#ifdef WITH_FREESTYLE
/* Initialize Freestyle. */
FRS_init();