From a9fad1e33b7b0729b89086f9b2faa62f0ea79ccf Mon Sep 17 00:00:00 2001 From: Christoph Haag Date: Sat, 6 Mar 2021 15:01:16 +0100 Subject: [PATCH] add proper eye rotation --- driver_openhmd.cpp | 186 ++++++++++++++++++++++++++++++++------------- 1 file changed, 132 insertions(+), 54 deletions(-) diff --git a/driver_openhmd.cpp b/driver_openhmd.cpp index 1a91a14..912e412 100644 --- a/driver_openhmd.cpp +++ b/driver_openhmd.cpp @@ -40,6 +40,15 @@ ohmd_context* ctx; class COpenHMDDeviceDriverController; +enum EyeRotation +{ + EYE_ROTATION_UNKNOWN, + EYE_ROTATION_NONE, + EYE_ROTATION_LEFT, + EYE_ROTATION_RIGHT, + EYE_ROTATION_180, +}; + // gets float values from the device and prints them void print_infof(ohmd_device* hmd, const char* name, int len, ohmd_float_value val) @@ -451,9 +460,9 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public { hmd = ohmd_list_open_device(ctx, hmddisplay_idx); if (hmdtracker_idx != -1 && hmdtracker_idx != hmddisplay_idx) - hmdtracker = ohmd_list_open_device(ctx, hmdtracker_idx); - else - hmdtracker = NULL; + hmdtracker = ohmd_list_open_device(ctx, hmdtracker_idx); + else + hmdtracker = NULL; if(!hmd){ DriverLog("failed to open device: %s\n", ohmd_ctx_get_error(ctx)); @@ -486,16 +495,16 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public { std::stringstream buf; - buf << ohmd_list_gets(ctx, 0, OHMD_PRODUCT); + buf << ohmd_list_gets(ctx, hmddisplay_idx, OHMD_PRODUCT); buf << ": "; - buf << ohmd_list_gets(ctx, 0, OHMD_PATH); + buf << ohmd_list_gets(ctx, hmddisplay_idx, OHMD_PATH); m_sSerialNumber = buf.str(); } { std::stringstream buf; buf << "OpenHMD: "; - buf << ohmd_list_gets(ctx, 0, OHMD_PRODUCT); + buf << ohmd_list_gets(ctx, hmddisplay_idx, OHMD_PRODUCT); m_sModelNumber = buf.str(); } @@ -504,7 +513,7 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public if (vendor_override) { m_sVendor = vendor_override; } else { - m_sVendor = ohmd_list_gets(ctx, 0, OHMD_VENDOR); + m_sVendor = ohmd_list_gets(ctx, hmddisplay_idx, OHMD_VENDOR); if (m_sVendor.find(' ') != std::string::npos) { m_sVendor = m_sVendor.substr(0, m_sVendor.find(' ')); } @@ -536,8 +545,48 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public ohmd_device_getf(hmd, OHMD_UNIVERSAL_DISTORTION_K, &(distortion_coeffs[0])); DriverLog("driver_openhmd: Distortion values a=%f b=%f c=%f d=%f\n", distortion_coeffs[0], distortion_coeffs[1], distortion_coeffs[2], distortion_coeffs[3]); - /* Sleep for 1 second while activating to let the display connect */ - std::this_thread::sleep_for( std::chrono::seconds(1) ); + + for (int i = 0; i < 2; i++) { + mat4x4f ohmdprojection; + if (i == Eye_Left) { + ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_PROJECTION_MATRIX, ohmdprojection.arr); + } else if (i == Eye_Right) { + ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, ohmdprojection.arr); + } else { + break; + } + + float yaw, pitch, roll; + float p[4][4]; + memcpy(p, ohmdprojection.arr, 16 * sizeof(float)); + columnMatrixToAngles(&yaw, &pitch, &roll, p); + + if (yaw > -5 && yaw < 5) { + eye_rotation[i] = EYE_ROTATION_NONE; + DriverLog( "eye_rotation %d: None\n", i); + } else if (yaw > 85 && yaw < 95) { + eye_rotation[i] = EYE_ROTATION_LEFT; + DriverLog( "eye_rotation %d: Left\n", i); + } else if (yaw> -95 && yaw < -85) { + eye_rotation[i] = EYE_ROTATION_RIGHT; + DriverLog( "eye_rotation %d: Right\n", i); + } else { + eye_rotation[i] = EYE_ROTATION_180; + DriverLog( "eye_rotation %d: 180\n", i); + } + } + + // quirk for device that doesn't have rotated display in openhmd + const char *prod = ohmd_list_gets(ctx, hmddisplay_idx, OHMD_PRODUCT); + if (strcmp(prod, "VR-Tek WVR") == 0 && m_nWindowWidth == 2560 && m_nWindowHeight == 1440) { + eye_rotation[0] = EYE_ROTATION_LEFT; + eye_rotation[1] = EYE_ROTATION_LEFT; + DriverLog( "Force eye_rotation: Left for %s\n", prod); + projection_matrix_rotated = false; + } + + /* Sleep for 1 second while activating to let the display connect */ + std::this_thread::sleep_for( std::chrono::seconds(1) ); } virtual ~COpenHMDDeviceDriver() @@ -598,8 +647,15 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public { *pnX = m_nWindowX; *pnY = m_nWindowY; - *pnWidth = m_nWindowWidth; - *pnHeight = m_nWindowHeight; + + // TODO: weird configurations like left eye unrotated, right eye 90°? + if (eye_rotation[Eye_Left] == EYE_ROTATION_NONE || eye_rotation[Eye_Left] == EYE_ROTATION_180) { + *pnWidth = m_nWindowWidth; + *pnHeight = m_nWindowHeight; + } else if (eye_rotation[Eye_Left] == EYE_ROTATION_LEFT || eye_rotation[Eye_Left] == EYE_ROTATION_RIGHT) { + *pnWidth = m_nWindowHeight; + *pnHeight = m_nWindowWidth; + } } bool IsDisplayOnDesktop() @@ -620,18 +676,50 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public void GetEyeOutputViewport( EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) { - *pnY = 0; - *pnWidth = m_nWindowWidth / 2; - *pnHeight = m_nWindowHeight; - - if ( eEye == Eye_Left ) - { + if (eye_rotation[eEye] == EYE_ROTATION_NONE) { + *pnY = 0; + *pnWidth = m_nWindowWidth / 2; + *pnHeight = m_nWindowHeight; + + if ( eEye == Eye_Left ) + { + *pnX = 0; + } + else + { + *pnX = m_nWindowWidth / 2; + } + } else if (eye_rotation[eEye] == EYE_ROTATION_LEFT) { + // assume other eye is also rotated left or right *pnX = 0; + *pnHeight = m_nWindowWidth / 2; + *pnWidth = m_nWindowHeight; + + if ( eEye == Eye_Left ) + { + *pnY = 0; + } + else + { + *pnY = m_nWindowWidth / 2; + } + } else if (eye_rotation[eEye] == EYE_ROTATION_RIGHT) { + // assume other eye is also rotated left or right + *pnY = 0; + *pnWidth = m_nWindowWidth / 2; + *pnHeight = m_nWindowHeight; + + if ( eEye == Eye_Right ) + { + *pnX = 0; + } + else + { + *pnX = m_nWindowWidth / 2; + } } - else - { - *pnX = m_nWindowWidth / 2; - } + + DriverLog("Viewport %dx%d at %d,%d\n", *pnWidth, *pnHeight, *pnX, *pnY); } // flatten 2D indices in a 4x4 matrix explicit so it's easy to see what's happening: @@ -726,25 +814,23 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public } } - void createUnRotation(float angle, mat4x4f *m) { + void createUnRotation(EyeRotation rotation, mat4x4f *m) { memset(m, 0, sizeof(*m)); m->m[0][0] = 1.0f; m->m[1][1] = 1.0f; m->m[2][2] = 1.0f; m->m[3][3] = 1.0f; - DriverLog("Unrotating for angle %f\n", angle); - - if (angle > -5 && angle < 5) { + if (rotation == EYE_ROTATION_NONE) { return; } - else if (angle > 85 && angle < 95) { + else if (rotation == EYE_ROTATION_LEFT) { m->m[0][0] = 0.0f; m->m[0][1] = -1.0f; m->m[1][0] = 1.0f; m->m[1][1] = 0.0f; m->m[0][1] = 1.0f; m->m[1][0] = -1.0f; } - else if (angle > -95 && angle < -85) { + else if (rotation == EYE_ROTATION_RIGHT) { m->m[0][0] = 0.0f; m->m[0][1] = -1.0f; m->m[1][0] = 1.0f; m->m[1][1] = 0.0f; } @@ -762,28 +848,19 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, ohmdprojection.arr); } - float yaw, pitch, roll; - float p[4][4]; - memcpy(p, ohmdprojection.arr, 16 * sizeof(float)); - columnMatrixToAngles(&yaw, &pitch, &roll, p); - - if (eEye == Eye_Left) { - rotation_left = yaw; - } else { - rotation_right = yaw; + if (projection_matrix_rotated) { + mat4x4f unrotation; + createUnRotation(eye_rotation[eEye], &unrotation); + + DriverLog("unrotation\n%f %f %f %f\n%f %f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", + unrotation.arr[0], unrotation.arr[1], unrotation.arr[2], unrotation.arr[3], + unrotation.arr[4], unrotation.arr[5], unrotation.arr[6], unrotation.arr[7], + unrotation.arr[8], unrotation.arr[9], unrotation.arr[10], unrotation.arr[11], + unrotation.arr[12], unrotation.arr[13], unrotation.arr[14], unrotation.arr[15]); + + omat4x4f_mult(&ohmdprojection, &unrotation, &ohmdprojection); } - mat4x4f unrotation; - createUnRotation(yaw, &unrotation); - - DriverLog("unrotation\n%f %f %f %f\n%f %f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", - unrotation.arr[0], unrotation.arr[1], unrotation.arr[2], unrotation.arr[3], - unrotation.arr[4], unrotation.arr[5], unrotation.arr[6], unrotation.arr[7], - unrotation.arr[8], unrotation.arr[9], unrotation.arr[10], unrotation.arr[11], - unrotation.arr[12], unrotation.arr[13], unrotation.arr[14], unrotation.arr[15]); - - omat4x4f_mult(&ohmdprojection, &unrotation, &ohmdprojection); - // http://stackoverflow.com/questions/10830293/ddg#12926655 // get projection matrix from openhmd, convert it into lrtb + near,far with SO formula // then divide by near plane distance to get the tangents of the angles from the center plane (tan = opposite side = these values divided by adjacent side = near plane distance) @@ -823,16 +900,14 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public DistortionCoordinates_t ComputeDistortion( EVREye eEye, float fU, float fV ) { - float angle = (eEye == Eye_Left ? rotation_left : rotation_right); - //DriverLog("Eye %d before: %f %f\n", eEye, fU, fV); - if (angle > -5 && angle < 5) { - } else if (angle > 85 && angle < 95) { + if (eye_rotation[eEye] == EYE_ROTATION_NONE) { + } else if (eye_rotation[eEye] == EYE_ROTATION_LEFT) { float tmp = fV; fV = 1. - fU; fU = tmp; - } else if (angle > -95 && angle < -85) { + } else if (eye_rotation[eEye] == EYE_ROTATION_RIGHT) { float tmp = fV; fV = fU; fU = 1.f - tmp; @@ -986,8 +1061,11 @@ class COpenHMDDeviceDriver final : public vr::ITrackedDeviceServerDriver, public float m_flDisplayFrequency; float m_flIPD; - float rotation_left = 0.0; - float rotation_right = 0.0; + EyeRotation eye_rotation[2] = { EYE_ROTATION_UNKNOWN, EYE_ROTATION_UNKNOWN }; + + // openhmd usually encodes the display rotation into the projection matrix. + // but there might also be devices where we support rotated display and openhmd doesn't have that yet. example: vrtek wvr2 + bool projection_matrix_rotated = true; }; //-----------------------------------------------------------------------------