nuclear@0: #ifdef WIN32 nuclear@0: #define OVR_OS_WIN32 nuclear@24: #elif defined(__APPLE__) nuclear@9: #define OVR_OS_MAC nuclear@24: #else nuclear@24: #define OVR_OS_LINUX nuclear@9: #endif nuclear@0: nuclear@0: #include "vr_impl.h" nuclear@0: nuclear@0: #ifdef USE_LIBOVR nuclear@0: #include nuclear@0: #include nuclear@9: #include nuclear@0: #include nuclear@30: #include "opengl.h" nuclear@0: #include "opt.h" nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@30: static PFNGLUSEPROGRAMPROC gl_use_program; nuclear@0: nuclear@0: static ovrHmd hmd; nuclear@0: static void *optdb; nuclear@0: static ovrEyeRenderDesc eye_render_desc[2]; nuclear@0: static ovrSizei eye_res[2]; nuclear@0: static ovrGLTexture eye_tex[2]; nuclear@0: static ovrFovPort eye_fov[2]; nuclear@19: static ovrPosef pose[2] = { nuclear@19: { {0, 0, 0, 1}, {0, 0, 0} }, nuclear@19: { {0, 0, 0, 1}, {0, 0, 0} } nuclear@19: }; nuclear@0: static int deferred_init_done; nuclear@0: nuclear@19: static int inside_begin_end; nuclear@19: nuclear@0: static int init(void) nuclear@0: { nuclear@0: int i, num_hmds; nuclear@9: int use_fake = 0; nuclear@14: ovrTrackingCaps tracking; nuclear@0: nuclear@28: if(!ovr_Initialize(0)) { nuclear@0: return -1; nuclear@0: } nuclear@0: printf("initialized LibOVR %s\n", ovr_GetVersionString()); nuclear@0: nuclear@0: if(!(num_hmds = ovrHmd_Detect())) { nuclear@9: if(getenv("VR_LIBOVR_FAKE")) { nuclear@9: use_fake = 1; nuclear@9: num_hmds = 1; nuclear@9: } else { nuclear@9: ovr_Shutdown(); nuclear@9: return -1; nuclear@9: } nuclear@0: } nuclear@0: printf("%d Oculus HMD(s) found\n", num_hmds); nuclear@0: nuclear@0: hmd = 0; nuclear@0: for(i=0; iManufacturer, h->ProductName); nuclear@0: nuclear@0: if(!hmd) { nuclear@0: hmd = h; nuclear@0: } else { nuclear@0: ovrHmd_Destroy(h); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if(!hmd) { nuclear@0: fprintf(stderr, "failed to initialize any Oculus HMDs\n"); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@14: tracking = ovrTrackingCap_Orientation | ovrTrackingCap_Position | nuclear@14: ovrTrackingCap_MagYawCorrection; nuclear@14: ovrHmd_ConfigureTracking(hmd, tracking, 0); nuclear@0: nuclear@0: eye_fov[0] = hmd->DefaultEyeFov[0]; nuclear@0: eye_fov[1] = hmd->DefaultEyeFov[1]; nuclear@0: nuclear@0: eye_res[0] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, eye_fov[0], 1.0); nuclear@0: eye_res[1] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Right, eye_fov[1], 1.0); nuclear@0: nuclear@0: /* create the options database */ nuclear@0: if((optdb = create_options())) { nuclear@11: set_option_int(optdb, VR_DISPLAY_WIDTH, hmd->Resolution.w); nuclear@11: set_option_int(optdb, VR_DISPLAY_HEIGHT, hmd->Resolution.h); nuclear@11: set_option_int(optdb, VR_LEYE_XRES, eye_res[0].w); nuclear@11: set_option_int(optdb, VR_LEYE_YRES, eye_res[0].h); nuclear@11: set_option_int(optdb, VR_REYE_XRES, eye_res[1].w); nuclear@11: set_option_int(optdb, VR_REYE_YRES, eye_res[1].h); nuclear@29: set_option_int(optdb, VR_RENDER_XRES, eye_res[0].w + eye_res[1].w); nuclear@29: set_option_int(optdb, VR_RENDER_YRES, eye_res[0].h > eye_res[1].h ? eye_res[0].h : eye_res[1].h); nuclear@16: set_option_float(optdb, VR_RENDER_RES_SCALE, 1.0); nuclear@11: set_option_float(optdb, VR_EYE_HEIGHT, ovrHmd_GetFloat(hmd, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT)); nuclear@11: set_option_float(optdb, VR_IPD, ovrHmd_GetFloat(hmd, OVR_KEY_IPD, OVR_DEFAULT_IPD)); nuclear@11: set_option_int(optdb, VR_WIN_XOFFS, hmd->WindowsPos.x); nuclear@11: set_option_int(optdb, VR_WIN_YOFFS, hmd->WindowsPos.y); nuclear@0: } nuclear@0: nuclear@30: if(!(gl_use_program = (PFNGLUSEPROGRAMPROC)vrimp_glfunc("glUseProgram"))) { nuclear@30: gl_use_program = (PFNGLUSEPROGRAMPROC)vrimp_glfunc("glUseProgramObjectARB"); nuclear@30: } nuclear@30: nuclear@0: deferred_init_done = 0; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static void deferred_init(void) nuclear@0: { nuclear@0: union ovrGLConfig glcfg; nuclear@0: unsigned int dcaps; nuclear@0: void *win = 0; nuclear@19: float leye_offs[3], reye_offs[3]; nuclear@0: nuclear@0: deferred_init_done = 1; nuclear@0: nuclear@0: memset(&glcfg, 0, sizeof glcfg); nuclear@0: glcfg.OGL.Header.API = ovrRenderAPI_OpenGL; nuclear@21: glcfg.OGL.Header.BackBufferSize = hmd->Resolution; nuclear@0: glcfg.OGL.Header.Multisample = 1; nuclear@0: #ifdef WIN32 nuclear@0: win = GetActiveWindow(); nuclear@24: glcfg.OGL.Window = win; nuclear@0: glcfg.OGL.DC = wglGetCurrentDC(); nuclear@24: #else nuclear@24: glcfg.OGL.Disp = glXGetCurrentDisplay(); nuclear@24: win = (void*)glXGetCurrentDrawable(); nuclear@24: nuclear@24: /* on linux the Oculus SDK docs are instructing users to leave the DK2 screen in nuclear@24: * portrait mode. So we'll have to flip width and height nuclear@24: */ nuclear@24: glcfg.OGL.Header.BackBufferSize.w = hmd->Resolution.h; nuclear@24: glcfg.OGL.Header.BackBufferSize.h = hmd->Resolution.w; nuclear@0: #endif nuclear@0: nuclear@0: if(!(hmd->HmdCaps & ovrHmdCap_ExtendDesktop)) { nuclear@0: ovrHmd_AttachToWindow(hmd, win, 0, 0); nuclear@0: printf("running in \"direct-to-rift\" mode\n"); nuclear@0: } else { nuclear@0: printf("running in \"extended desktop\" mode\n"); nuclear@0: } nuclear@0: ovrHmd_SetEnabledCaps(hmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); nuclear@0: nuclear@28: dcaps = ovrDistortionCap_TimeWarp | ovrDistortionCap_Overdrive; nuclear@24: #ifdef OVR_OS_LINUX nuclear@24: dcaps |= ovrDistortionCap_LinuxDevFullscreen; nuclear@24: #endif nuclear@0: nuclear@0: if(!ovrHmd_ConfigureRendering(hmd, &glcfg.Config, dcaps, eye_fov, eye_render_desc)) { nuclear@0: fprintf(stderr, "failed to configure LibOVR distortion renderer\n"); nuclear@0: } nuclear@0: nuclear@19: /* set the eye offset options */ nuclear@21: leye_offs[0] = eye_render_desc[ovrEye_Left].HmdToEyeViewOffset.x; nuclear@21: leye_offs[1] = eye_render_desc[ovrEye_Left].HmdToEyeViewOffset.y; nuclear@21: leye_offs[2] = eye_render_desc[ovrEye_Left].HmdToEyeViewOffset.z; nuclear@21: reye_offs[0] = eye_render_desc[ovrEye_Right].HmdToEyeViewOffset.x; nuclear@21: reye_offs[1] = eye_render_desc[ovrEye_Right].HmdToEyeViewOffset.y; nuclear@21: reye_offs[2] = eye_render_desc[ovrEye_Right].HmdToEyeViewOffset.z; nuclear@24: nuclear@24: /* sanity check ... on linux it seems I'm getting the eye offsets reversed for some reason */ nuclear@24: if(leye_offs[0] > reye_offs[0]) { nuclear@24: fprintf(stderr, "BUG %s:%d: eye offset reversed?! fixing but wtf...\n", __FILE__, __LINE__); nuclear@24: set_option_vec(optdb, VR_LEYE_OFFSET, reye_offs); nuclear@24: set_option_vec(optdb, VR_REYE_OFFSET, leye_offs); nuclear@24: } else { nuclear@24: set_option_vec(optdb, VR_LEYE_OFFSET, leye_offs); nuclear@24: set_option_vec(optdb, VR_REYE_OFFSET, reye_offs); nuclear@24: } nuclear@0: } nuclear@0: nuclear@0: static void cleanup(void) nuclear@0: { nuclear@0: if(hmd) { nuclear@0: ovrHmd_Destroy(hmd); nuclear@0: ovr_Shutdown(); nuclear@0: } nuclear@5: destroy_options(optdb); nuclear@0: } nuclear@0: nuclear@0: static int set_option(const char *opt, enum opt_type type, void *valp) nuclear@0: { nuclear@15: float fval; nuclear@15: nuclear@0: switch(type) { nuclear@0: case OTYPE_INT: nuclear@15: fval = (float)*(int*)valp; nuclear@0: set_option_int(optdb, opt, *(int*)valp); nuclear@0: break; nuclear@0: nuclear@0: case OTYPE_FLOAT: nuclear@15: fval = *(float*)valp; nuclear@15: set_option_float(optdb, opt, fval); nuclear@0: break; nuclear@24: nuclear@24: case OTYPE_VEC: nuclear@24: set_option_vec(optdb, opt, valp); nuclear@24: break; nuclear@0: } nuclear@15: nuclear@16: if(hmd && strcmp(opt, VR_RENDER_RES_SCALE) == 0) { nuclear@15: eye_res[0] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, eye_fov[0], fval); nuclear@15: eye_res[1] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Right, eye_fov[1], fval); nuclear@15: nuclear@15: set_option_int(optdb, VR_LEYE_XRES, eye_res[0].w); nuclear@15: set_option_int(optdb, VR_LEYE_YRES, eye_res[0].h); nuclear@15: set_option_int(optdb, VR_REYE_XRES, eye_res[1].w); nuclear@15: set_option_int(optdb, VR_REYE_YRES, eye_res[1].h); nuclear@15: } nuclear@15: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static int get_option(const char *opt, enum opt_type type, void *valp) nuclear@0: { nuclear@0: switch(type) { nuclear@0: case OTYPE_INT: nuclear@0: return get_option_int(optdb, opt, valp); nuclear@0: case OTYPE_FLOAT: nuclear@0: return get_option_float(optdb, opt, valp); nuclear@24: case OTYPE_VEC: nuclear@24: return get_option_vec(optdb, opt, valp); nuclear@0: } nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@7: static void translation(int eye, float *vec) nuclear@0: { nuclear@0: if(!hmd) { nuclear@0: vec[0] = vec[1] = vec[2] = 0; nuclear@9: return; nuclear@0: } nuclear@0: nuclear@19: /* if we're inside the begin-end block we can get a fresh pose, otherwise we'll just nuclear@19: * reuse the one we got last frame. nuclear@19: */ nuclear@19: if(inside_begin_end) { nuclear@21: pose[eye] = ovrHmd_GetHmdPosePerEye(hmd, eye == VR_EYE_LEFT ? ovrEye_Left : ovrEye_Right); nuclear@19: } nuclear@19: nuclear@19: vec[0] = pose[eye].Position.x; nuclear@19: vec[1] = pose[eye].Position.y; nuclear@19: vec[2] = pose[eye].Position.z; nuclear@0: } nuclear@0: nuclear@7: static void rotation(int eye, float *quat) nuclear@0: { nuclear@0: if(!hmd) { nuclear@0: quat[0] = quat[1] = quat[2] = 0.0f; nuclear@0: quat[3] = 1.0f; nuclear@9: return; nuclear@0: } nuclear@0: nuclear@19: /* same as above (translation) */ nuclear@19: if(inside_begin_end) { nuclear@21: pose[eye] = ovrHmd_GetHmdPosePerEye(hmd, eye == VR_EYE_LEFT ? ovrEye_Left : ovrEye_Right); nuclear@19: } nuclear@19: nuclear@0: quat[0] = pose[eye].Orientation.x; nuclear@0: quat[1] = pose[eye].Orientation.y; nuclear@0: quat[2] = pose[eye].Orientation.z; nuclear@0: quat[3] = pose[eye].Orientation.w; nuclear@0: } nuclear@0: nuclear@0: static const float idmat[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: nuclear@0: static void proj_matrix(int eye, float znear, float zfar, float *mat) nuclear@0: { nuclear@0: int i, j; nuclear@0: ovrMatrix4f vmat; nuclear@0: nuclear@0: if(!hmd) { nuclear@0: memcpy(mat, idmat, sizeof idmat); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: vmat = ovrMatrix4f_Projection(eye_render_desc[eye].Fov, znear, zfar, 1); nuclear@0: nuclear@0: for(i=0; i<4; i++) { nuclear@0: for(j=0; j<4; j++) { nuclear@0: *mat++ = vmat.M[j][i]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static void begin(int eye) nuclear@0: { nuclear@0: if(!hmd) return; nuclear@0: nuclear@0: if(!deferred_init_done) { nuclear@0: deferred_init(); nuclear@0: } nuclear@0: nuclear@19: if(!inside_begin_end) { nuclear@0: ovrHmd_BeginFrame(hmd, 0); nuclear@19: inside_begin_end = 1; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static int present(void) nuclear@0: { nuclear@30: int cur_prog = 0; nuclear@24: nuclear@0: if(!hmd) return 0; nuclear@0: nuclear@30: if(gl_use_program) { nuclear@30: glGetIntegerv(GL_CURRENT_PROGRAM, &cur_prog); nuclear@30: } nuclear@24: nuclear@0: ovrHmd_EndFrame(hmd, pose, &eye_tex[0].Texture); nuclear@19: inside_begin_end = 0; nuclear@0: nuclear@30: if(gl_use_program) { nuclear@30: gl_use_program(cur_prog); nuclear@24: } nuclear@24: nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: static void set_eye_texture(int eye, unsigned int tex, float umin, float vmin, float umax, float vmax) nuclear@0: { nuclear@0: ovrSizei texsz; nuclear@0: ovrRecti rect; nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, tex); nuclear@0: glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texsz.w); nuclear@0: glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texsz.h); nuclear@0: nuclear@0: rect.Pos.x = (int)(umin * texsz.w); nuclear@21: rect.Pos.y = (int)(vmin * texsz.h); nuclear@0: rect.Size.w = (int)((umax - umin) * texsz.w); nuclear@0: rect.Size.h = (int)((vmax - vmin) * texsz.h); nuclear@0: nuclear@0: eye_tex[eye].OGL.Header.API = ovrRenderAPI_OpenGL; nuclear@0: eye_tex[eye].OGL.Header.TextureSize = texsz; nuclear@0: eye_tex[eye].OGL.Header.RenderViewport = rect; nuclear@0: eye_tex[eye].OGL.TexId = tex; nuclear@0: } nuclear@0: nuclear@0: static void recenter(void) nuclear@0: { nuclear@0: if(hmd) { nuclear@0: ovrHmd_RecenterPose(hmd); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: struct vr_module *vr_module_libovr(void) nuclear@0: { nuclear@0: static struct vr_module m; nuclear@0: nuclear@0: if(!m.init) { nuclear@0: m.name = "libovr"; nuclear@0: m.init = init; nuclear@0: m.cleanup = cleanup; nuclear@0: m.set_option = set_option; nuclear@0: m.get_option = get_option; nuclear@0: m.translation = translation; nuclear@0: m.rotation = rotation; nuclear@0: m.proj_matrix = proj_matrix; nuclear@0: m.begin = begin; nuclear@0: m.present = present; nuclear@0: m.set_eye_texture = set_eye_texture; nuclear@0: m.recenter = recenter; nuclear@0: } nuclear@0: return &m; nuclear@0: } nuclear@0: nuclear@0: #else /* no libovr */ nuclear@0: nuclear@0: static int init(void) nuclear@0: { nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: struct vr_module *vr_module_libovr(void) nuclear@0: { nuclear@0: static struct vr_module m; nuclear@0: nuclear@0: if(!m.init) { nuclear@0: m.name = "libovr"; nuclear@0: m.init = init; nuclear@0: } nuclear@0: return &m; nuclear@0: } nuclear@0: nuclear@0: #endif /* USE_LIBOVR */