XR: Controller Data Improvements

Provides two key improvements to runtime controller data.

1. Separates controller poses into two components, "grip" and "aim",
which are both required to accurately represent the controllers
without manual offsets.

Following their OpenXR definitions, the grip pose represents the
user's hand when holding the controller, and the aim pose represents
the controller's aiming source.

2. Runtime controller data is now stored as a dynamic array instead
of a fixed array. This makes the API/functionality more adaptable to
different systems.

Does not bring about any changes for users since only internal
runtime functionality is currently affected.

Reviewed By: Julian Eisel

Differential Revision: http://developer.blender.org/D12073
This commit is contained in:
Peter Kim 2021-08-05 21:11:01 +09:00
parent f45860fba9
commit fb1822ddeb
5 changed files with 217 additions and 115 deletions

View File

@ -962,12 +962,18 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
float r_viewmat[4][4],
float *r_focal_len);
bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3]);
bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4]);
bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3]);
bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4]);
/* wm_xr_actions.c */
/* XR action functions to be called pre-XR session start.
@ -1002,9 +1008,10 @@ void WM_xr_action_binding_destroy(wmXrData *xr,
/* If action_set_name is NULL, then all action sets will be treated as active. */
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name);
bool WM_xr_controller_pose_action_set(wmXrData *xr,
const char *action_set_name,
const char *action_name);
bool WM_xr_controller_pose_actions_set(wmXrData *xr,
const char *action_set_name,
const char *grip_action_name,
const char *aim_action_name);
/* XR action functions to be called post-XR session start. */
bool WM_xr_action_state_get(const wmXrData *xr,

View File

@ -112,11 +112,11 @@ static wmXrAction *action_create(const char *action_name,
const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT);
if (is_float_action) {
action->float_thresholds = MEM_calloc_arrayN(
count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds");
count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds");
}
if (is_button_action) {
action->axis_flags = MEM_calloc_arrayN(
count, sizeof(*action->axis_flags), "XrAction_AxisFlags");
count, sizeof(*action->axis_flags), "XrAction_AxisFlags");
}
action->ot = ot;
@ -186,9 +186,9 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
wmXrSessionState *session_state = &xr->runtime->session_state;
if (action_set == session_state->active_action_set) {
if (action_set->controller_pose_action) {
if (action_set->controller_grip_action || action_set->controller_aim_action) {
wm_xr_session_controller_data_clear(session_state);
action_set->controller_pose_action = NULL;
action_set->controller_grip_action = action_set->controller_aim_action = NULL;
}
if (action_set->active_modal_action) {
action_set->active_modal_action = NULL;
@ -213,13 +213,8 @@ bool WM_xr_action_create(wmXrData *xr,
return false;
}
wmXrAction *action = action_create(action_name,
type,
count_subaction_paths,
subaction_paths,
ot,
op_properties,
op_flag);
wmXrAction *action = action_create(
action_name, type, count_subaction_paths, subaction_paths, ot, op_properties, op_flag);
GHOST_XrActionInfo info = {
.name = action_name,
@ -269,13 +264,16 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char
return;
}
if (action_set->controller_pose_action &&
STREQ(action_set->controller_pose_action->name, action_name)) {
if ((action_set->controller_grip_action &&
STREQ(action_set->controller_grip_action->name, action_name)) ||
(action_set->controller_aim_action &&
STREQ(action_set->controller_aim_action->name, action_name))) {
if (action_set == xr->runtime->session_state.active_action_set) {
wm_xr_session_controller_data_clear(&xr->runtime->session_state);
}
action_set->controller_pose_action = NULL;
action_set->controller_grip_action = action_set->controller_aim_action = NULL;
}
if (action_set->active_modal_action &&
STREQ(action_set->active_modal_action->name, action_name)) {
action_set->active_modal_action = NULL;
@ -284,7 +282,6 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char
GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name);
}
bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
const char *action_name,
@ -297,7 +294,7 @@ bool WM_xr_action_binding_create(wmXrData *xr,
const struct wmXrPose *poses)
{
GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN(
count_subaction_paths, sizeof(*binding_infos), __func__);
count_subaction_paths, sizeof(*binding_infos), __func__);
for (unsigned int i = 0; i < count_subaction_paths; ++i) {
GHOST_XrActionBindingInfo *binding_info = &binding_infos[i];
@ -334,7 +331,7 @@ void WM_xr_action_binding_destroy(wmXrData *xr,
const char *profile_path)
{
GHOST_XrDestroyActionBindings(
xr->runtime->context, action_set_name, 1, &action_name, &profile_path);
xr->runtime->context, action_set_name, 1, &action_name, &profile_path);
}
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
@ -360,31 +357,54 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
xr->runtime->session_state.active_action_set = action_set;
if (action_set->controller_pose_action) {
wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr);
if (action_set->controller_grip_action && action_set->controller_aim_action) {
wm_xr_session_controller_data_populate(
action_set->controller_grip_action, action_set->controller_aim_action, xr);
}
else {
wm_xr_session_controller_data_clear(&xr->runtime->session_state);
}
return true;
}
bool WM_xr_controller_pose_action_set(wmXrData *xr,
const char *action_set_name,
const char *action_name)
bool WM_xr_controller_pose_actions_set(wmXrData *xr,
const char *action_set_name,
const char *grip_action_name,
const char *aim_action_name)
{
wmXrActionSet *action_set = action_set_find(xr, action_set_name);
if (!action_set) {
return false;
}
wmXrAction *action = action_find(xr, action_set_name, action_name);
if (!action) {
wmXrAction *grip_action = action_find(xr, action_set_name, grip_action_name);
if (!grip_action) {
return false;
}
action_set->controller_pose_action = action;
wmXrAction *aim_action = action_find(xr, action_set_name, aim_action_name);
if (!aim_action) {
return false;
}
/* Ensure consistent subaction paths. */
const unsigned int count = grip_action->count_subaction_paths;
if (count != aim_action->count_subaction_paths) {
return false;
}
for (unsigned int i = 0; i < count; ++i) {
if (!STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])) {
return false;
}
}
action_set->controller_grip_action = grip_action;
action_set->controller_aim_action = aim_action;
if (action_set == xr->runtime->session_state.active_action_set) {
wm_xr_session_controller_data_populate(action, xr);
wm_xr_session_controller_data_populate(grip_action, aim_action, xr);
}
return true;

View File

@ -38,20 +38,20 @@
#include "wm_surface.h"
#include "wm_xr_intern.h"
void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4])
{
float iquat[4];
invert_qt_qt_normalized(iquat, pose->orientation_quat);
quat_to_mat4(r_viewmat, iquat);
translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
{
quat_to_mat4(r_mat, pose->orientation_quat);
copy_v3_v3(r_mat[3], pose->position);
}
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
{
float iquat[4];
invert_qt_qt_normalized(iquat, pose->orientation_quat);
quat_to_mat4(r_imat, iquat);
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,
@ -59,6 +59,7 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
float r_proj_mat[4][4])
{
GHOST_XrPose eye_pose;
float eye_inv[4][4], base_inv[4][4];
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
@ -69,6 +70,12 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
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);
perspective_m4_fov(r_proj_mat,
draw_view->fov.angle_left,
draw_view->fov.angle_right,
@ -76,15 +83,6 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
draw_view->fov.angle_down,
session_settings->clip_start,
session_settings->clip_end);
float eye_mat[4][4];
float base_mat[4][4];
wm_xr_pose_to_viewmat(&eye_pose, eye_mat);
/* Calculate the base pose matrix (in world space!). */
wm_xr_pose_to_viewmat(&draw_data->base_pose, base_mat);
mul_m4_m4m4(r_view_mat, eye_mat, base_mat);
}
static void wm_xr_draw_viewport_buffers_to_active_framebuffer(

View File

@ -26,19 +26,6 @@
struct wmXrActionSet;
typedef struct wmXrControllerData {
/** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
This subaction path will later be combined with a component path, and that combined path should
also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
/input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
*/
char subaction_path[64];
/** Last known controller pose (in world space) stored for queries. */
GHOST_XrPose pose;
/** The last known controller matrix, calculated from above's controller pose. */
float mat[4][4];
} wmXrControllerData;
typedef struct wmXrSessionState {
bool is_started;
@ -65,7 +52,7 @@ typedef struct wmXrSessionState {
bool is_view_data_set;
/** Last known controller data. */
wmXrControllerData controllers[2];
ListBase controllers; /* wmXrController */
/** The currently active action set that will be updated on calls to
* wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and
@ -135,14 +122,27 @@ typedef struct wmXrAction {
eXrOpFlag op_flag;
} wmXrAction;
typedef struct wmXrController {
struct wmXrController *next, *prev;
/** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
This subaction path will later be combined with a component path, and that combined path should
also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
/input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
*/
char subaction_path[64];
/* Pose (in world space) that represents the user's hand when holding the controller.*/
GHOST_XrPose grip_pose;
float grip_mat[4][4];
/* Pose (in world space) that represents the controller's aiming source. */
GHOST_XrPose aim_pose;
float aim_mat[4][4];
} wmXrController;
typedef struct wmXrActionSet {
char *name;
/** The XR pose action that determines the controller
* transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose",
* although it could differ depending on the specification and hardware. */
wmXrAction *controller_pose_action;
/** XR pose actions that determine the controller grip/aim transforms. */
wmXrAction *controller_grip_action;
wmXrAction *controller_aim_action;
/** The currently active modal action (if any). */
wmXrAction *active_modal_action;
} wmXrActionSet;
@ -165,10 +165,11 @@ 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_controller_data_populate(const wmXrAction *controller_pose_action,
void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
const wmXrAction *aim_action,
wmXrData *xr);
void wm_xr_session_controller_data_clear(wmXrSessionState *state);
void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]);
void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);

View File

@ -63,6 +63,16 @@ static void wm_xr_session_create_cb(void)
wm_xr_session_actions_init(xr_data);
}
static void wm_xr_session_controller_data_free(wmXrSessionState *state)
{
BLI_freelistN(&state->controllers);
}
static void wm_xr_session_data_free(wmXrSessionState *state)
{
wm_xr_session_controller_data_free(state);
}
static void wm_xr_session_exit_cb(void *customdata)
{
wmXrData *xr_data = customdata;
@ -74,6 +84,7 @@ static void wm_xr_session_exit_cb(void *customdata)
}
/* Free the entire runtime data (including session state and context), to play safe. */
wm_xr_session_data_free(&xr_data->runtime->session_state);
wm_xr_runtime_data_free(&xr_data->runtime);
}
@ -338,7 +349,7 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
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_viewmat(&viewer_pose, state->viewer_viewmat);
wm_xr_pose_to_imat(&viewer_pose, 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,
@ -398,32 +409,71 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
return true;
}
bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3])
{
if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
(subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
zero_v3(r_location);
return false;
}
copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position);
const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
subaction_idx);
BLI_assert(controller);
copy_v3_v3(r_location, controller->grip_pose.position);
return true;
}
bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4])
{
if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
(subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
unit_qt(r_rotation);
return false;
}
copy_v4_v4(r_rotation,
xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat);
const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
subaction_idx);
BLI_assert(controller);
copy_qt_qt(r_rotation, controller->grip_pose.orientation_quat);
return true;
}
bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3])
{
if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
(subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
zero_v3(r_location);
return false;
}
const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
subaction_idx);
BLI_assert(controller);
copy_v3_v3(r_location, controller->aim_pose.position);
return true;
}
bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4])
{
if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
(subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
unit_qt(r_rotation);
return false;
}
const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
subaction_idx);
BLI_assert(controller);
copy_qt_qt(r_rotation, controller->aim_pose.orientation_quat);
return true;
}
@ -443,16 +493,34 @@ void wm_xr_session_actions_init(wmXrData *xr)
GHOST_XrAttachActionSets(xr->runtime->context);
}
static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings,
const wmXrAction *controller_pose_action,
static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose,
const float view_ofs[3],
const float base_mat[4][4],
GHOST_XrPose *r_pose,
float r_mat[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. */
sub_v3_v3(m[3], view_ofs);
mul_m4_m4m4(r_mat, base_mat, m);
/* 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,
const wmXrAction *aim_action,
wmXrSessionState *state)
{
const unsigned int count = (unsigned int)min_ii(
(int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers));
BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths);
BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers));
float view_ofs[3];
float base_inv[4][4];
float tmp[4][4];
unsigned int subaction_idx = 0;
float view_ofs[3], base_mat[4][4];
if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
copy_v3_v3(view_ofs, state->prev_local_pose.position);
@ -464,22 +532,19 @@ static void wm_xr_session_controller_mats_update(const XrSessionSettings *settin
add_v3_v3(view_ofs, state->prev_eye_position_ofs);
}
wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
invert_m4(base_inv);
wm_xr_pose_to_mat(&state->prev_base_pose, base_mat);
for (unsigned int i = 0; i < count; ++i) {
wmXrControllerData *controller = &state->controllers[i];
/* Calculate controller matrix in world space. */
wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp);
/* Apply eye position and base pose offsets. */
sub_v3_v3(tmp[3], view_ofs);
mul_m4_m4m4(controller->mat, base_inv, tmp);
/* Save final pose. */
mat4_to_loc_quat(
controller->pose.position, controller->pose.orientation_quat, controller->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,
&controller->grip_pose,
controller->grip_mat);
wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx],
view_ofs,
base_mat,
&controller->aim_pose,
controller->aim_mat);
}
}
@ -498,33 +563,44 @@ void wm_xr_session_actions_update(wmXrData *xr)
return;
}
/* Only update controller mats for active action set. */
/* Only update controller data for active action set. */
if (active_action_set) {
if (active_action_set->controller_pose_action) {
wm_xr_session_controller_mats_update(
&xr->session_settings, active_action_set->controller_pose_action, state);
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,
active_action_set->controller_aim_action,
state);
}
}
}
void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr)
void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
const wmXrAction *aim_action,
wmXrData *xr)
{
wmXrSessionState *state = &xr->runtime->session_state;
UNUSED_VARS(aim_action); /* Only used for asserts. */
const unsigned int count = (unsigned int)min_ii(
(int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths);
wmXrSessionState *state = &xr->runtime->session_state;
ListBase *controllers = &state->controllers;
BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths);
const unsigned int count = grip_action->count_subaction_paths;
wm_xr_session_controller_data_free(state);
for (unsigned int i = 0; i < count; ++i) {
wmXrControllerData *c = &state->controllers[i];
strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]);
memset(&c->pose, 0, sizeof(c->pose));
zero_m4(c->mat);
wmXrController *controller = MEM_callocN(sizeof(*controller), __func__);
BLI_assert(STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i]));
strcpy(controller->subaction_path, grip_action->subaction_paths[i]);
BLI_addtail(controllers, controller);
}
}
void wm_xr_session_controller_data_clear(wmXrSessionState *state)
{
memset(state->controllers, 0, sizeof(state->controllers));
wm_xr_session_controller_data_free(state);
}
/** \} */ /* XR-Session Actions */