Skip to content

Commit

Permalink
add proper eye rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristophHaag committed Mar 6, 2021
1 parent 03f2129 commit a9fad1e
Showing 1 changed file with 132 additions and 54 deletions.
186 changes: 132 additions & 54 deletions driver_openhmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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();
}

Expand All @@ -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(' '));
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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;
}

Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};

//-----------------------------------------------------------------------------
Expand Down

0 comments on commit a9fad1e

Please sign in to comment.