The open source OpenXR runtime
at main 4.5 kB view raw
1// Copyright 2022, Collabora, Inc. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Test for change-of-basis transformations between left-handed and right-handed coordinate sytems for 6 * quaternions. 7 * @author Moshi Turner <moshiturner@protonmail.com> 8 */ 9#include "math/m_api.h" 10#include "xrt/xrt_defines.h" 11#include <util/u_worker.hpp> 12#include <util/u_logging.h> 13#include <math/m_space.h> 14#include <math/m_vec3.h> 15 16 17#include "catch_amalgamated.hpp" 18 19#include <random> 20 21 22#include "math/m_eigen_interop.hpp" 23#include <Eigen/Core> 24 25 26// https://stackoverflow.com/questions/28673777/convert-quaternion-from-right-handed-to-left-handed-coordinate-system 27 28 29// unity: x=0.3, w=0.95 . results in +40 rotation, which I think is wrong probably, should probably be -40 30// openxr, according to us: 31void 32log_quat(xrt_quat q) 33{ 34 U_LOG_E("xyzw: %f %f %f %f", q.x, q.y, q.z, q.w); 35} 36 37float 38quat_difference(xrt_quat q1, xrt_quat q2) 39{ 40 // https://math.stackexchange.com/a/90098 41 // d(q1,q2)=1−⟨q1,q2⟩2 42 43 float inner_product = (q1.w * q2.w) + (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z); 44 return 1.0 - (inner_product * inner_product); 45} 46 47xrt_quat 48random_quat() 49{ 50 std::random_device dev; 51 52 auto mt = std::mt19937(dev()); 53 auto rd = std::normal_distribution<float>(0, 1); 54 55 struct xrt_quat quat = {rd(mt), rd(mt), rd(mt), rd(mt)}; 56 57 math_quat_normalize(&quat); 58 return quat; 59} 60 61//! https://stackoverflow.com/questions/28673777/convert-quaternion-from-right-handed-to-left-handed-coordinate-system 62// Same as zldtt_ori_right in lm_main. 63void 64slow_change_of_basis_lh_to_rh(xrt_quat *in, xrt_quat *out) 65{ 66 67 xrt_vec3 x = XRT_VEC3_UNIT_X; 68 xrt_vec3 z = XRT_VEC3_UNIT_Z; 69 70 math_quat_rotate_vec3(in, &x, &x); 71 math_quat_rotate_vec3(in, &z, &z); 72 73 // This is a very squashed change-of-basis from left-handed coordinate systems to right-handed coordinate 74 // systems: you multiply everything by (-1 0 0) then negate the X axis. 75 76 x.y *= -1; 77 x.z *= -1; 78 79 z.x *= -1; 80 81 math_quat_from_plus_x_z(&x, &z, out); 82} 83 84void 85fast_change_of_basis_lh_to_rh(xrt_quat *in, xrt_quat *out) 86{ 87 out->x = -in->x; 88 out->y = in->y; 89 out->z = in->z; 90 out->w = -in->w; 91} 92 93/* 94OpenXR is +X right, +Y up, -Z forward 95Unity is X+ Right, Y+ Up, Z+ Forward 96 97So we're not swapping axes, we're just flipping them. It's the same change of basis as "left hand" to "right hand" 98(indeed we chould have implemented left vs right in our optical hand tracking that way), just the "flip" is on the XY 99plane not the YZ plane. 100 101Vaguely based on `make_joint_at_matrix_right_hand` from ccdik_main but rotated 102*/ 103 104void 105slow_change_of_basis_unity_to_oxr(xrt_quat *in, xrt_quat *out) 106{ 107 108 Eigen::Quaternionf q; 109 110 q.w() = in->w; 111 q.x() = in->x; 112 q.y() = in->y; 113 q.z() = in->z; 114 115 Eigen::Matrix3f unity_rotation(q); 116 117 Eigen::Matrix3f mirror_unity_to_openxr = Eigen::Matrix3f::Identity(); 118 mirror_unity_to_openxr(2, 2) = -1; 119 120 Eigen::Matrix3f intermediate = mirror_unity_to_openxr * unity_rotation; 121 122 intermediate(0, 2) *= -1; 123 intermediate(1, 2) *= -1; 124 intermediate(2, 2) *= -1; 125 126 Eigen::Quaternionf q_new; 127 128 q_new = intermediate; 129 130 131 out->w = q_new.w(); 132 out->x = q_new.x(); 133 out->y = q_new.y(); 134 out->z = q_new.z(); 135} 136 137void 138fast_change_of_basis_unity_to_oxr(xrt_quat *in, xrt_quat *out) 139{ 140 out->x = in->x; 141 out->y = in->y; 142 out->z = -in->z; 143 out->w = -in->w; 144} 145 146TEST_CASE("QuaternionChangeOfBasis") 147 148{ 149 U_LOG_E("LH to RH!"); 150 for (int i = 0; i < 3; i++) { 151 xrt_quat q = random_quat(); 152 xrt_quat q_prime; 153 xrt_quat q_prime_fast; 154 slow_change_of_basis_lh_to_rh(&q, &q_prime); 155 fast_change_of_basis_lh_to_rh(&q, &q_prime_fast); 156 log_quat(q); 157 log_quat(q_prime); 158 log_quat(q_prime_fast); 159 // xrt_quat factors; 160 // factors.w = q_prime.w / q.w; 161 // factors.x = q_prime.x / q.x; 162 // factors.y = q_prime.y / q.y; 163 // factors.z = q_prime.z / q.z; 164 // log_quat(factors); 165 166 CHECK(quat_difference(q_prime, q_prime_fast) < 0.01); 167 } 168 169 U_LOG_E("Unity to OpenXR!"); 170 for (int i = 0; i < 3; i++) { 171 xrt_quat q = random_quat(); 172 if (i == 0) { 173 q.x = 0.31; 174 q.y = 0; 175 q.z = 0; 176 q.w = 0.95; 177 math_quat_normalize(&q); 178 } 179 xrt_quat q_prime; 180 xrt_quat q_prime_fast; 181 slow_change_of_basis_unity_to_oxr(&q, &q_prime); 182 fast_change_of_basis_unity_to_oxr(&q, &q_prime_fast); 183 log_quat(q); 184 log_quat(q_prime); 185 log_quat(q_prime_fast); 186 // xrt_quat factors; 187 // factors.w = q_prime.w / q.w; 188 // factors.x = q_prime.x / q.x; 189 // factors.y = q_prime.y / q.y; 190 // factors.z = q_prime.z / q.z; 191 // log_quat(factors); 192 193 CHECK(quat_difference(q_prime, q_prime_fast) < 0.01); 194 } 195}