st/oxr: Implement dpad emulation

This commit is contained in:
Charlton Rodda 2022-05-27 19:01:37 +01:00 committed by Jakob Bornecrantz
parent 803d679e11
commit aa31ac3789
9 changed files with 634 additions and 11 deletions

View file

@ -84,11 +84,16 @@ process_dpad(struct oxr_logger *log,
for (size_t i = 0; i < ARRAY_SIZE(entry->dpads); i++) { for (size_t i = 0; i < ARRAY_SIZE(entry->dpads); i++) {
// Have we found a empty slot, add it. // Have we found a empty slot, add it.
if (entry->dpads[i].binding == XR_NULL_PATH) { if (entry->dpads[i].binding == XR_NULL_PATH) {
entry->dpads[i] = *dpad; struct oxr_dpad_binding_modification dpad_binding = {
.binding = dpad->binding,
// Don't leave potentially dangling pointers. .settings = {
entry->dpads[i].next = NULL; .forceThreshold = dpad->forceThreshold,
entry->dpads[i].actionSet = XR_NULL_HANDLE; .forceThresholdReleased = dpad->forceThresholdReleased,
.centerRegion = dpad->centerRegion,
.wedgeAngle = dpad->wedgeAngle,
.isSticky = dpad->isSticky,
}};
entry->dpads[i] = dpad_binding;
entry->dpad_count++; entry->dpad_count++;
added = true; added = true;

View file

@ -63,7 +63,7 @@ oxr_dpad_state_get_or_add(struct oxr_dpad_state *state, uint64_t key)
void void
oxr_dpad_state_deinit(struct oxr_dpad_state *state) oxr_dpad_state_deinit(struct oxr_dpad_state *state)
{ {
if (state->uhi != NULL) { if (state != NULL && state->uhi != NULL) {
u_hashmap_int_clear_and_call_for_each(state->uhi, destroy_callback, NULL); u_hashmap_int_clear_and_call_for_each(state->uhi, destroy_callback, NULL);
u_hashmap_int_destroy(&state->uhi); u_hashmap_int_destroy(&state->uhi);
} }

View file

@ -1258,6 +1258,115 @@ oxr_action_populate_input_transform(struct oxr_logger *log,
return oxr_input_transform_create_chain(log, slog, t, act->data->action_type, act->data->name, str, return oxr_input_transform_create_chain(log, slog, t, act->data->action_type, act->data->name, str,
&action_input->transforms, &action_input->transform_count); &action_input->transforms, &action_input->transform_count);
} }
/*!
* Find dpad settings in @p dpad_entry whose binding path
* is a prefix of @p bound_path_string.
*
* @returns true if settings were found and written to @p out_dpad_settings
*/
static bool
find_matching_dpad(struct oxr_logger *log,
struct oxr_instance *inst,
struct oxr_dpad_entry *dpad_entry,
const char *bound_path_string,
struct oxr_dpad_binding_modification **out_dpad_binding)
{
if (dpad_entry != NULL) {
for (uint32_t i = 0; i < dpad_entry->dpad_count; i++) {
const char *dpad_path_string;
size_t dpad_path_length;
oxr_path_get_string(log, inst, dpad_entry->dpads[i].binding, &dpad_path_string,
&dpad_path_length);
if (strncmp(bound_path_string, dpad_path_string, dpad_path_length) == 0) {
*out_dpad_binding = &dpad_entry->dpads[i];
return true;
}
}
}
return false;
}
/*!
* Try to produce a transform chain to create a dpad button from the selected input
* (potentially using other inputs like `/force` in the process)
*
* Populates @p action_input->transforms and @p action_input->transform_count on
* success.
*
* @returns false if it could not, true if it could
*/
static bool
oxr_action_populate_input_transform_dpad(struct oxr_logger *log,
struct oxr_sink_logger *slog,
struct oxr_session *sess,
struct oxr_action *act,
struct oxr_dpad_entry *dpad_entry,
enum oxr_dpad_region dpad_region,
struct oxr_interaction_profile *profile,
struct oxr_action_input *action_inputs,
uint32_t action_input_count,
uint32_t selected_input)
{
struct oxr_action_input *action_input = &(action_inputs[selected_input]);
assert(action_input->transforms == NULL);
assert(action_input->transform_count == 0);
const char *bound_path_string;
size_t bound_path_length;
oxr_path_get_string(log, sess->sys->inst, action_input->bound_path, &bound_path_string, &bound_path_length);
// find correct dpad entry
struct oxr_dpad_binding_modification *dpad_binding_modification = NULL;
find_matching_dpad(log, sess->sys->inst, dpad_entry, bound_path_string, &dpad_binding_modification);
enum xrt_input_type t = XRT_GET_INPUT_TYPE(action_input->input->name);
enum xrt_input_type activate_t = XRT_GET_INPUT_TYPE(action_input->dpad_activate_name);
return oxr_input_transform_create_chain_dpad(
log, slog, t, act->data->action_type, bound_path_string, dpad_binding_modification, dpad_region, activate_t,
action_input->dpad_activate, &action_input->transforms, &action_input->transform_count);
}
// based on get_subaction_path_from_path
static bool
get_dpad_region_from_path(struct oxr_logger *log,
struct oxr_instance *inst,
XrPath path,
enum oxr_dpad_region *out_dpad_region)
{
const char *str = NULL;
size_t length = 0;
XrResult ret;
ret = oxr_path_get_string(log, inst, path, &str, &length);
if (ret != XR_SUCCESS) {
return false;
}
// TODO: surely there's a better way to do this?
if (length >= 10 && strncmp("/dpad_left", str + (length - 10), 10) == 0) {
*out_dpad_region = OXR_DPAD_REGION_LEFT;
return true;
}
if (length >= 11 && strncmp("/dpad_right", str + (length - 11), 11) == 0) {
*out_dpad_region = OXR_DPAD_REGION_RIGHT;
return true;
}
if (length >= 8 && strncmp("/dpad_up", str + (length - 8), 8) == 0) {
*out_dpad_region = OXR_DPAD_REGION_UP;
return true;
}
if (length >= 10 && strncmp("/dpad_down", str + (length - 10), 10) == 0) {
*out_dpad_region = OXR_DPAD_REGION_DOWN;
return true;
}
if (length >= 12 && strncmp("/dpad_center", str + (length - 12), 12) == 0) {
*out_dpad_region = OXR_DPAD_REGION_CENTER;
return true;
}
return false;
}
static void static void
oxr_action_bind_io(struct oxr_logger *log, oxr_action_bind_io(struct oxr_logger *log,
@ -1282,9 +1391,18 @@ oxr_action_bind_io(struct oxr_logger *log,
cache->current.active = true; cache->current.active = true;
cache->inputs = U_TYPED_ARRAY_CALLOC(struct oxr_action_input, input_count); cache->inputs = U_TYPED_ARRAY_CALLOC(struct oxr_action_input, input_count);
for (uint32_t i = 0; i < input_count; i++) { for (uint32_t i = 0; i < input_count; i++) {
// Only add the input if we can find a transform. // Only add the input if we can find a transform.
if (oxr_action_populate_input_transform(log, slog, sess, act, &(inputs[i]))) {
enum oxr_dpad_region dpad_region;
if (get_dpad_region_from_path(log, sess->sys->inst, inputs[i].bound_path, &dpad_region)) {
struct oxr_dpad_entry *entry =
oxr_dpad_state_get(&profile->dpad_state, act->act_set->act_set_key);
if (oxr_action_populate_input_transform_dpad(log, slog, sess, act, entry, dpad_region,
profile, inputs, input_count, i)) {
cache->inputs[count++] = inputs[i];
continue;
}
} else if (oxr_action_populate_input_transform(log, slog, sess, act, &(inputs[i]))) {
cache->inputs[count++] = inputs[i]; cache->inputs[count++] = inputs[i];
continue; continue;
} }

View file

@ -8,6 +8,8 @@
* @ingroup oxr_input_transform * @ingroup oxr_input_transform
*/ */
#include "math/m_mathinclude.h"
#include "oxr_input_transform.h" #include "oxr_input_transform.h"
#include "oxr_logger.h" #include "oxr_logger.h"
#include "oxr_objects.h" #include "oxr_objects.h"
@ -105,6 +107,30 @@ oxr_input_transform_init_vec2_get_y(struct oxr_input_transform *transform, const
return true; return true;
} }
bool
oxr_input_transform_init_vec2_dpad(struct oxr_input_transform *transform,
const struct oxr_input_transform *parent,
struct oxr_dpad_settings dpad_settings,
enum oxr_dpad_region dpad_region,
enum xrt_input_type activation_input_type,
struct xrt_input *activation_input)
{
assert(transform != NULL);
assert(parent != NULL);
assert(parent->result_type == XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE);
U_ZERO(transform);
transform->type = INPUT_TRANSFORM_DPAD;
transform->result_type = XRT_INPUT_TYPE_BOOLEAN;
transform->data.dpad_state.settings = dpad_settings;
transform->data.dpad_state.bound_region = dpad_region;
transform->data.dpad_state.activation_input_type = activation_input_type;
transform->data.dpad_state.activation_input = activation_input;
transform->data.dpad_state.already_active = activation_input == NULL;
return true;
}
bool bool
oxr_input_transform_init_threshold(struct oxr_input_transform *transform, oxr_input_transform_init_threshold(struct oxr_input_transform *transform,
const struct oxr_input_transform *parent, const struct oxr_input_transform *parent,
@ -148,7 +174,7 @@ oxr_input_transform_init_bool_to_vec1(struct oxr_input_transform *transform,
} }
bool bool
oxr_input_transform_process(const struct oxr_input_transform *transform, oxr_input_transform_process(struct oxr_input_transform *transform,
size_t transform_count, size_t transform_count,
const struct oxr_input_value_tagged *input, const struct oxr_input_value_tagged *input,
struct oxr_input_value_tagged *out) struct oxr_input_value_tagged *out)
@ -158,7 +184,7 @@ oxr_input_transform_process(const struct oxr_input_transform *transform,
} }
struct oxr_input_value_tagged data = *input; struct oxr_input_value_tagged data = *input;
for (size_t i = 0; i < transform_count; ++i) { for (size_t i = 0; i < transform_count; ++i) {
const struct oxr_input_transform *xform = &(transform[i]); struct oxr_input_transform *xform = &(transform[i]);
switch (xform->type) { switch (xform->type) {
case INPUT_TRANSFORM_IDENTITY: case INPUT_TRANSFORM_IDENTITY:
// do nothing // do nothing
@ -178,6 +204,75 @@ oxr_input_transform_process(const struct oxr_input_transform *transform,
data.value.boolean ? xform->data.bool_to_vec1.true_val : xform->data.bool_to_vec1.false_val; data.value.boolean ? xform->data.bool_to_vec1.true_val : xform->data.bool_to_vec1.false_val;
break; break;
} }
case INPUT_TRANSFORM_DPAD: {
struct oxr_input_transform_dpad_data *dpad_state = &xform->data.dpad_state;
if (dpad_state->activation_input != NULL) {
bool active = true;
switch (dpad_state->activation_input_type) {
case XRT_INPUT_TYPE_BOOLEAN: {
active = dpad_state->activation_input->value.boolean;
break;
}
case XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE: {
float force = dpad_state->activation_input->value.vec1.x;
active = (force >= dpad_state->settings.forceThreshold) ||
(dpad_state->already_active &&
force >= dpad_state->settings.forceThresholdReleased);
break;
}
default: active = false;
}
dpad_state->already_active = active;
if (!active) {
dpad_state->active_regions = OXR_DPAD_REGION_CENTER;
data.value.boolean = false;
break;
}
}
enum oxr_dpad_region bound_region = dpad_state->bound_region;
enum oxr_dpad_region active_regions = OXR_DPAD_REGION_CENTER;
for (int i = 0; i < 4; i++) {
enum oxr_dpad_region query_region = 1 << i;
bool rot90 =
(query_region == OXR_DPAD_REGION_LEFT) || (query_region == OXR_DPAD_REGION_RIGHT);
bool rot180 =
(query_region == OXR_DPAD_REGION_DOWN) || (query_region == OXR_DPAD_REGION_RIGHT);
float localX = rot90 ? data.value.vec2.y : data.value.vec2.x;
float localY = rot90 ? -data.value.vec2.x : data.value.vec2.y;
if (rot180) {
localX = -localX;
localY = -localY;
}
float centerRadius = dpad_state->settings.centerRegion;
if (localX * localX + localY * localY <= centerRadius * centerRadius) {
continue;
}
float tanXY = atan2f(localX, localY);
float halfAngle = dpad_state->settings.wedgeAngle / 2.0f;
if (-halfAngle < tanXY && tanXY <= halfAngle) {
active_regions |= query_region;
}
}
if (!dpad_state->already_active || !dpad_state->settings.isSticky ||
(dpad_state->active_regions == OXR_DPAD_REGION_CENTER) ||
(active_regions == OXR_DPAD_REGION_CENTER)) {
dpad_state->active_regions = active_regions;
}
data.value.boolean = (dpad_state->active_regions == bound_region) ||
((dpad_state->active_regions & bound_region) != 0);
break;
}
case INPUT_TRANSFORM_INVALID: case INPUT_TRANSFORM_INVALID:
default: return false; default: return false;
} }
@ -346,3 +441,72 @@ oxr_input_transform_create_chain(struct oxr_logger *log,
return true; return true;
} }
bool
oxr_input_transform_create_chain_dpad(struct oxr_logger *log,
struct oxr_sink_logger *slog,
enum xrt_input_type input_type,
XrActionType result_type,
const char *bound_path_string,
struct oxr_dpad_binding_modification *dpad_binding_modification,
enum oxr_dpad_region dpad_region,
enum xrt_input_type activation_input_type,
struct xrt_input *activation_input,
struct oxr_input_transform **out_transforms,
size_t *out_transform_count)
{
struct oxr_input_transform chain[OXR_MAX_INPUT_TRANSFORMS] = {0};
// these default settings are specified by OpenXR and thus must not be changed
struct oxr_dpad_settings dpad_settings = {
.forceThreshold = 0.5f,
.forceThresholdReleased = 0.4f,
.centerRegion = 0.5f,
.wedgeAngle = M_PI_2,
.isSticky = false,
};
if (dpad_binding_modification != NULL) {
dpad_settings = dpad_binding_modification->settings;
}
oxr_slog(slog, "\t\tAdding dpad transform from '%s' to '%s'\n", xr_action_type_to_str(result_type),
xrt_input_type_to_str(input_type));
struct oxr_input_transform *current_xform = &(chain[0]);
if (!oxr_input_transform_init_root(current_xform, input_type)) {
*out_transform_count = 0;
*out_transforms = NULL;
return false;
}
// We start over here.
size_t transform_count = 0;
input_type = current_xform->result_type;
if (input_type != XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE) {
oxr_slog(slog, "\t\t\tUnexpected input type for dpad binding %s\n", bound_path_string);
return false;
}
if (result_type != XR_ACTION_TYPE_BOOLEAN_INPUT) {
oxr_slog(slog, "\t\t\tUnexpected output type for dpad binding %s\n", bound_path_string);
return false;
}
struct oxr_input_transform *new_xform = &(chain[transform_count]);
if (!oxr_input_transform_init_vec2_dpad(new_xform, current_xform, dpad_settings, dpad_region,
activation_input_type, activation_input)) {
// Error has already been logged.
*out_transform_count = 0;
*out_transforms = NULL;
return false;
}
current_xform = new_xform;
transform_count++;
*out_transform_count = transform_count;
*out_transforms = oxr_input_transform_clone_chain(chain, transform_count);
return true;
}

View file

@ -11,6 +11,7 @@
#pragma once #pragma once
#include "xrt/xrt_device.h" #include "xrt/xrt_device.h"
#include "oxr_objects.h"
// we need no platform-specific defines from OpenXR. // we need no platform-specific defines from OpenXR.
#include "openxr/openxr.h" #include "openxr/openxr.h"
@ -80,6 +81,15 @@ enum oxr_input_transform_type
* @see oxr_input_transform_bool_to_vec1_data * @see oxr_input_transform_bool_to_vec1_data
*/ */
INPUT_TRANSFORM_BOOL_TO_VEC1, INPUT_TRANSFORM_BOOL_TO_VEC1,
/*!
* Interpret a 2D joystick or trackpad as a dpad.
*
* This transform type has data:
*
* @see oxr_input_transform_dpad_data
*/
INPUT_TRANSFORM_DPAD,
}; };
struct oxr_input_transform; struct oxr_input_transform;
@ -110,6 +120,21 @@ struct oxr_input_transform_bool_to_vec1_data
float false_val; float false_val;
}; };
/*!
* Data required for INPUT_TRANSFORM_DPAD
* @see oxr_input_transform
* @see INPUT_TRANSFORM_DPAD
*/
struct oxr_input_transform_dpad_data
{
enum oxr_dpad_region bound_region;
enum oxr_dpad_region active_regions;
struct oxr_dpad_settings settings;
enum xrt_input_type activation_input_type;
struct xrt_input *activation_input;
bool already_active;
};
/*! /*!
* Variant type for input transforms. * Variant type for input transforms.
* *
@ -139,6 +164,11 @@ struct oxr_input_transform
* INPUT_TRANSFORM_BOOL_TO_VEC1 * INPUT_TRANSFORM_BOOL_TO_VEC1
*/ */
struct oxr_input_transform_bool_to_vec1_data bool_to_vec1; struct oxr_input_transform_bool_to_vec1_data bool_to_vec1;
/*!
* Populated when oxr_input_transform::type is
* INPUT_TRANSFORM_DPAD
*/
struct oxr_input_transform_dpad_data dpad_state;
} data; } data;
}; };
@ -173,7 +203,7 @@ oxr_input_transform_destroy(struct oxr_input_transform **transform_ptr);
* @public @memberof oxr_input_transform * @public @memberof oxr_input_transform
*/ */
bool bool
oxr_input_transform_process(const struct oxr_input_transform *transforms, oxr_input_transform_process(struct oxr_input_transform *transforms,
size_t transform_count, size_t transform_count,
const struct oxr_input_value_tagged *input, const struct oxr_input_value_tagged *input,
struct oxr_input_value_tagged *out); struct oxr_input_value_tagged *out);
@ -300,6 +330,40 @@ oxr_input_transform_create_chain(struct oxr_logger *log,
struct oxr_input_transform **out_transforms, struct oxr_input_transform **out_transforms,
size_t *out_transform_count); size_t *out_transform_count);
/*!
* Create a transform array to process a 2D input plus activation input to a dpad.
*
* @param[in] log The logger
* @param[in] slog The sink logger
* @param[in] input_type The type of input received from the hardware
* @param[in] result_type The type of input the application requested
* @param[in] bound_path_string The path name string that has been bound.
* @param[in] dpad_settings The dpad settings provided by the application
* as a binding modification. NULL means use the defaults as per the spec.
* @param[in] dpad_region The dpad region associated with this binding
* @param[in] activation_input_type The type of the activation input
* @param[in] activation_input The activation input, i.e. the input used
* to determine when the emulated dpad buttons should activate
* @param[out] out_transforms A pointer that will be populated with the output
* array's address, or NULL.
* @param[out] out_transform_count Where to populate the array size
* @return false if not possible
*
* @relates oxr_input_transform
*/
bool
oxr_input_transform_create_chain_dpad(struct oxr_logger *log,
struct oxr_sink_logger *slog,
enum xrt_input_type input_type,
XrActionType result_type,
const char *bound_path_string,
struct oxr_dpad_binding_modification *dpad_binding_modification,
enum oxr_dpad_region dpad_region,
enum xrt_input_type activation_input_type,
struct xrt_input *activation_input,
struct oxr_input_transform **out_transforms,
size_t *out_transform_count);
/*! /*!
* @} * @}
*/ */

View file

@ -173,6 +173,18 @@ enum oxr_subaction_path
OXR_SUB_ACTION_PATH_GAMEPAD, OXR_SUB_ACTION_PATH_GAMEPAD,
}; };
/*!
* Region of a dpad binding that an input is mapped to
*/
enum oxr_dpad_region
{
OXR_DPAD_REGION_CENTER = 0,
OXR_DPAD_REGION_UP = (1 << 0),
OXR_DPAD_REGION_DOWN = (1 << 1),
OXR_DPAD_REGION_LEFT = (1 << 2),
OXR_DPAD_REGION_RIGHT = (1 << 3),
};
/*! /*!
* Tracks the state of a image that belongs to a @ref oxr_swapchain. * Tracks the state of a image that belongs to a @ref oxr_swapchain.
*/ */
@ -1596,6 +1608,29 @@ oxr_session_success_focused_result(struct oxr_session *session)
} }
} }
/*!
* dpad settings we need extracted from XrInteractionProfileDpadBindingEXT
*
* @ingroup oxr_input
*/
struct oxr_dpad_settings
{
float forceThreshold;
float forceThresholdReleased;
float centerRegion;
float wedgeAngle;
bool isSticky;
};
/*!
* dpad binding extracted from XrInteractionProfileDpadBindingEXT
*/
struct oxr_dpad_binding_modification
{
XrPath binding;
struct oxr_dpad_settings settings;
};
/*! /*!
* A entry in the dpad state for one action set. * A entry in the dpad state for one action set.
* *

View file

@ -9,6 +9,7 @@
#include "oxr_objects.h" // For now needs to come before oxr_logger.h #include "oxr_objects.h" // For now needs to come before oxr_logger.h
#include "oxr_logger.h" #include "oxr_logger.h"
#include "oxr_input_transform.h"
#include "oxr_pretty_print.h" #include "oxr_pretty_print.h"

View file

@ -20,6 +20,7 @@
#include "oxr_objects.h" #include "oxr_objects.h"
#include "oxr_logger.h" #include "oxr_logger.h"
#include "oxr_handle.h" #include "oxr_handle.h"
#include "oxr_input_transform.h"
#include "oxr_chain.h" #include "oxr_chain.h"
#include "oxr_pretty_print.h" #include "oxr_pretty_print.h"

View file

@ -6,6 +6,8 @@
* @author Ryan Pavlik <ryan.pavlik@collabora.com> * @author Ryan Pavlik <ryan.pavlik@collabora.com>
*/ */
#include "math/m_mathinclude.h"
#include "catch/catch.hpp" #include "catch/catch.hpp"
#include <xrt/xrt_defines.h> #include <xrt/xrt_defines.h>
@ -315,3 +317,236 @@ TEST_CASE("input_transform")
oxr_input_transform_destroy(&transforms); oxr_input_transform_destroy(&transforms);
CHECK(NULL == transforms); CHECK(NULL == transforms);
} }
struct dpad_test_case
{
float x;
float y;
enum oxr_dpad_region active_regions;
};
TEST_CASE("input_transform_dpad")
{
struct oxr_logger log;
oxr_log_init(&log, "test");
struct oxr_sink_logger slog = {};
struct oxr_input_transform *transforms = NULL;
size_t transform_count = 0;
oxr_input_value_tagged input = {};
oxr_input_value_tagged output = {};
struct oxr_dpad_binding_modification *dpad_binding_modification = NULL;
enum xrt_input_type activation_input_type = XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE;
struct xrt_input activation_input = {};
enum oxr_dpad_region dpad_region = OXR_DPAD_REGION_UP;
SECTION("Default settings")
{
XrActionType action_type = XR_ACTION_TYPE_BOOLEAN_INPUT;
SECTION("without an activation input")
{
input.type = XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE;
CHECK(oxr_input_transform_create_chain_dpad(
&log, &slog, input.type, action_type, "/dummy_vec2/dpad_up", dpad_binding_modification,
dpad_region, activation_input_type, NULL, &transforms, &transform_count));
CHECK(transform_count == 1);
CHECK(transforms != nullptr);
CHECK(transforms[0].type == INPUT_TRANSFORM_DPAD);
SECTION("up region is off in center")
{
input.value.vec2.x = 0.0f;
input.value.vec2.y = 0.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
SECTION("up region is on when pointing up")
{
input.value.vec2.x = 0.0f;
input.value.vec2.y = 1.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
}
struct dpad_test_case cases[9] = {
// obvious
{0.0f, 0.0f, OXR_DPAD_REGION_CENTER},
{0.0f, 1.0f, OXR_DPAD_REGION_UP},
{0.0f, -1.0f, OXR_DPAD_REGION_DOWN},
{-1.0f, 0.0f, OXR_DPAD_REGION_LEFT},
{1.0f, 0.0f, OXR_DPAD_REGION_RIGHT},
// boundary cases
{1.0f, 1.0f, OXR_DPAD_REGION_UP},
{-1.0f, -1.0f, OXR_DPAD_REGION_DOWN},
{-1.0f, 1.0f, OXR_DPAD_REGION_LEFT},
{1.0f, -1.0f, OXR_DPAD_REGION_RIGHT},
};
for (uint32_t i = 0; i < ARRAY_SIZE(cases); i++) {
DYNAMIC_SECTION("with (x, y) of (" << cases[i].x << ", " << cases[i].y << ")")
{
input.value.vec2.x = cases[i].x;
input.value.vec2.y = cases[i].y;
CHECK(
oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(cases[i].active_regions == transforms[0].data.dpad_state.active_regions);
}
}
}
SECTION("with a boolean activation input")
{
input.type = XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE;
input.value.vec2.x = 0.0f;
input.value.vec2.y = 1.0f;
activation_input_type = XRT_INPUT_TYPE_BOOLEAN;
CHECK(oxr_input_transform_create_chain_dpad(
&log, &slog, input.type, action_type, "/dummy_vec2/dpad_up", dpad_binding_modification,
dpad_region, activation_input_type, &activation_input, &transforms, &transform_count));
CHECK(transform_count == 1);
CHECK(transforms != nullptr);
CHECK(transforms[0].type == INPUT_TRANSFORM_DPAD);
SECTION("when activation input is set to true")
{
activation_input.value.boolean = true;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
}
SECTION("when activation input is set to false")
{
activation_input.value.boolean = false;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
}
SECTION("with a float activation input")
{
input.type = XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE;
input.value.vec2.x = 0.0f;
input.value.vec2.y = 1.0f;
activation_input_type = XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE;
CHECK(oxr_input_transform_create_chain_dpad(
&log, &slog, input.type, action_type, "/dummy_vec2/dpad_up", dpad_binding_modification,
dpad_region, activation_input_type, &activation_input, &transforms, &transform_count));
CHECK(transform_count == 1);
CHECK(transforms != nullptr);
CHECK(transforms[0].type == INPUT_TRANSFORM_DPAD);
SECTION("when activation input is set to 1.0")
{
activation_input.value.vec1.x = 1.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
}
SECTION("when activation input is set to 0.0")
{
activation_input.value.vec1.x = 0.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
SECTION("when activation input varies")
{
activation_input.value.vec1.x = 0.45f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
activation_input.value.vec1.x = 0.6f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
activation_input.value.vec1.x = 0.45f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
activation_input.value.vec1.x = 0.35f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
}
}
SECTION("Sticky enabled")
{
XrActionType action_type = XR_ACTION_TYPE_BOOLEAN_INPUT;
struct oxr_dpad_binding_modification dpad_binding_modification_val = {
XR_NULL_PATH, // XrPath binding, unused at this stage
{
0.5f, // float forceThreshold
0.4f, // float forceThresholdReleased
0.5f, // float centerRegion
M_PI_2, // float wedgeAngle
true, // bool isSticky
}};
dpad_binding_modification = &dpad_binding_modification_val;
SECTION("without an activation input")
{
input.type = XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE;
input.value.vec2.x = 0.0f;
input.value.vec2.y = 1.0f;
CHECK(oxr_input_transform_create_chain_dpad(
&log, &slog, input.type, action_type, "/dummy_vec2/dpad_up", dpad_binding_modification,
dpad_region, activation_input_type, NULL, &transforms, &transform_count));
CHECK(transform_count == 1);
CHECK(transforms != nullptr);
CHECK(transforms[0].type == INPUT_TRANSFORM_DPAD);
SECTION("up region is off in center")
{
input.value.vec2.x = 0.0f;
input.value.vec2.y = 0.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
SECTION("up region is on when pointing up")
{
input.value.vec2.x = 0.0f;
input.value.vec2.y = 1.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
}
SECTION("up region is off when pointing down")
{
input.value.vec2.x = 0.0f;
input.value.vec2.y = -1.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
SECTION("up region stays on when stick moves clockwise to down")
{
input.value.vec2.x = 0.0f;
input.value.vec2.y = 1.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
input.value.vec2.x = 1.0f;
input.value.vec2.y = 0.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
input.value.vec2.x = 0.0f;
input.value.vec2.y = -1.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(true == output.value.boolean);
input.value.vec2.x = 0.0f;
input.value.vec2.y = 0.0f;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output));
CHECK(false == output.value.boolean);
}
}
}
oxr_log_slog(&log, &slog);
oxr_input_transform_destroy(&transforms);
CHECK(NULL == transforms);
}