Something went wrong. Try again.
The open source OpenXR runtime
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554// Copyright 2018-2020, Collabora, Ltd.// Copyright 2026, NVIDIA CORPORATION.// SPDX-License-Identifier: BSL-1.0/*! * @file * @brief Input transform tests. * @author Rylie Pavlik <rylie.pavlik@collabora.com> */
#include "math/m_mathinclude.h"
#include "catch_amalgamated.hpp"
#include <xrt/xrt_defines.h>
#include <oxr/actions/oxr_input_transform.h>#include <oxr/oxr_logger.h>#include <oxr/oxr_objects.h>
using Catch::Generators::values;
TEST_CASE("input_transform"){ 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 = {};
SECTION("Float action") { XrActionType action_type = XR_ACTION_TYPE_FLOAT_INPUT;
SECTION("From Vec1 -1 to 1 identity") { input.type = XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE;
CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_float", &transforms, &transform_count));
// Just identity CHECK(transform_count == 1); CHECK(transforms != nullptr);
SECTION("Roundtrip") { auto value = GENERATE(values({-1.f, -0.5f, 0.f, -0.f, 0.5f, 1.f})); input.value.vec1.x = value;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(input.value.vec1.x == output.value.vec1.x); } }
SECTION("From Vec1 0 to 1 identity") { input.type = XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE;
CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_float", &transforms, &transform_count));
// Just identity CHECK(transform_count == 1); CHECK(transforms != nullptr);
SECTION("Roundtrip") { auto value = GENERATE(values({0.f, -0.f, 0.5f, 1.f})); input.value.vec1.x = value;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(input.value.vec1.x == output.value.vec1.x); } }
SECTION("From Vec2 input") { input.type = XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE; input.value.vec2.x = -1; input.value.vec2.y = 1;
SECTION("path component x") { CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_vec2/x", &transforms, &transform_count));
// A get-x CHECK(transform_count == 1); CHECK(transforms != nullptr);
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(input.value.vec2.x == output.value.vec1.x); }
SECTION("path component y") { CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_vec2/y", &transforms, &transform_count));
// A get-y CHECK(transform_count == 1); CHECK(transforms != nullptr);
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(input.value.vec2.y == output.value.vec1.x); }
SECTION("no component") { CHECK_FALSE(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_vec2", &transforms, &transform_count));
// Shouldn't make a transform, not possible CHECK(transform_count == 0); CHECK(transforms == nullptr);
// shouldn't do anything, but shouldn't explode. CHECK_FALSE(oxr_input_transform_process(transforms, transform_count, &input, &output)); } }
SECTION("From bool input") { input.type = XRT_INPUT_TYPE_BOOLEAN; CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_bool", &transforms, &transform_count));
// A bool-to-float CHECK(transform_count == 1); CHECK(transforms != nullptr);
SECTION("False") { input.value.boolean = false;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(0.0f == output.value.vec1.x); }
SECTION("True") { input.value.boolean = true;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(1.0f == output.value.vec1.x); } } }
SECTION("Bool action") { XrActionType action_type = XR_ACTION_TYPE_BOOLEAN_INPUT; SECTION("From Bool identity") { input.type = XRT_INPUT_TYPE_BOOLEAN;
CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "bool_action", "/mock_bool", &transforms, &transform_count)); CHECK(transform_count == 1); CHECK(transforms != nullptr);
SECTION("Roundtrip") { auto value = GENERATE(values({0, 1})); input.value.boolean = bool(value); CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(input.value.boolean == output.value.boolean); } }
SECTION("From Vec1 -1 to 1") { input.type = XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE;
CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "bool_action", "/mock_float", &transforms, &transform_count)); CHECK(transform_count == 1); CHECK(transforms != nullptr);
SECTION("True") { auto value = GENERATE(values({0.5f, 1.f})); input.value.vec1.x = value;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(output.value.boolean == true); }
SECTION("False") { auto value = GENERATE(values({0.0f, -1.f})); input.value.vec1.x = value;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(output.value.boolean == false); } }
SECTION("From Vec1 0 to 1") { input.type = XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE;
CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "bool_action", "/mock_float", &transforms, &transform_count)); // A bool to float CHECK(transform_count == 1); CHECK(transforms != nullptr);
SECTION("True") { auto value = GENERATE(values({0.95f, 1.f})); input.value.vec1.x = value;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(output.value.boolean == true); }
SECTION("False") { auto value = GENERATE(values({0.0f, 0.5f})); input.value.vec1.x = value;
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(output.value.boolean == false); } }
SECTION("From Vec2") { input.type = XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE; input.value.vec2.x = -1; input.value.vec2.y = 1;
SECTION("x") { CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_vec2/x", &transforms, &transform_count)); CHECK(transform_count == 2); CHECK(transforms != nullptr);
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(false == output.value.boolean); }
SECTION("y") { CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock_vec2/y", &transforms, &transform_count)); CHECK(transform_count == 2); CHECK(transforms != nullptr);
CHECK(oxr_input_transform_process(transforms, transform_count, &input, &output)); CHECK(true == output.value.boolean); }
SECTION("no component") { CHECK_FALSE(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "float_action", "/mock", &transforms, &transform_count));
// Shouldn't make a transform, not possible CHECK(transform_count == 0); CHECK(transforms == nullptr);
// shouldn't do anything, but shouldn't explode. CHECK_FALSE(oxr_input_transform_process(transforms, transform_count, &input, &output)); } } }
SECTION("Pose action") { XrActionType action_type = XR_ACTION_TYPE_POSE_INPUT;
SECTION("From Pose identity") { input.type = XRT_INPUT_TYPE_POSE; CHECK(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "pose_action", "/mock_pose", &transforms, &transform_count)); // Identity, just so this binding doesn't get culled. CHECK(transform_count == 1); }
SECTION("From other input") { auto input_type = GENERATE(values({ XRT_INPUT_TYPE_BOOLEAN, XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE, XRT_INPUT_TYPE_VEC3_MINUS_ONE_TO_ONE, }));
CAPTURE(input_type); input.type = input_type;
CHECK_FALSE(oxr_input_transform_create_chain(&log, &slog, input.type, action_type, "pose_action", "/mock", &transforms, &transform_count));
// not possible CHECK(transform_count == 0); CHECK(transforms == nullptr); } }
oxr_log_slog(&log, &slog); oxr_input_transform_destroy(&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, "/mock_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, "/mock_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, "/mock_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 (float)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, "/mock_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);}