nuclear@1: #include nuclear@12: #include nuclear@1: #include "vr.h" nuclear@1: #include "vr_impl.h" nuclear@15: #include "vr_sdr.h" nuclear@12: nuclear@19: #ifndef WIN32 nuclear@19: #include nuclear@19: #else nuclear@19: #include nuclear@19: #endif nuclear@19: nuclear@21: #define USUB 28 nuclear@21: #define VSUB 40 nuclear@21: nuclear@21: /* these are just used for the shaderless precomputed distortion method */ nuclear@21: struct Mesh { nuclear@21: int prim; nuclear@21: int num_verts, num_faces; nuclear@21: unsigned int vbo; nuclear@21: unsigned int ibo; nuclear@21: }; nuclear@21: struct Vertex { nuclear@21: float x, y, z; nuclear@21: float tx, ty; nuclear@21: }; nuclear@21: nuclear@21: nuclear@18: static void init_ctx(); nuclear@1: static bool init_ovr(); nuclear@12: static bool init_sdr(); nuclear@1: nuclear@21: static Mesh gen_view_mesh(int usub, int vsub, float aspect, float lens_center_offset, nuclear@21: float scale, const float *dist_factors, float tex_scale_x, float tex_scale_y); nuclear@21: static void distort_texcoords(float *tc, float aspect, float lens_center_offset, float scale, const float *dist_factors); nuclear@21: static float barrel_scale(float rad, const float *k); nuclear@21: nuclear@3: VRContext vr_ctx; nuclear@12: static unsigned int sdrprog; nuclear@1: nuclear@21: static Mesh wrapmesh[2]; nuclear@21: static bool mesh_valid; nuclear@21: nuclear@21: bool dbg_enable; nuclear@21: nuclear@21: nuclear@1: extern "C" int vr_init(enum vr_init_mode mode) nuclear@1: { nuclear@12: glewInit(); nuclear@12: nuclear@18: init_ctx(); nuclear@18: nuclear@1: if(!init_ovr()) { nuclear@1: return -1; nuclear@1: } nuclear@12: nuclear@12: if(!init_sdr()) { nuclear@12: return -1; nuclear@12: } nuclear@12: nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: extern "C" void vr_shutdown(void) nuclear@1: { nuclear@15: delete [] vr_ctx.info.display; nuclear@3: //System::Destroy(); nuclear@1: } nuclear@1: nuclear@18: static void init_ctx() nuclear@18: { nuclear@18: vr_ctx.info.width = 1280; nuclear@18: vr_ctx.info.height = 800; nuclear@18: vr_ctx.info.fov = M_PI / 2.0; nuclear@18: vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height; nuclear@18: vr_ctx.info.ipd = 0.035; nuclear@18: vr_ctx.info.scale = 1.0; nuclear@18: } nuclear@18: nuclear@1: static bool init_ovr() nuclear@1: { nuclear@8: LogMaskConstants log_level = LogMask_All; nuclear@1: // initialize Oculus SDK nuclear@5: const char *logenv = getenv("VR_LOGLEVEL"); nuclear@5: if(logenv) { nuclear@5: switch(atoi(logenv)) { nuclear@5: case 0: nuclear@5: log_level = LogMask_None; nuclear@5: break; nuclear@5: case 1: nuclear@5: log_level = LogMask_Regular; nuclear@5: break; nuclear@5: case 2: nuclear@5: default: nuclear@5: log_level = LogMask_All; nuclear@5: break; nuclear@5: } nuclear@5: } nuclear@5: nuclear@5: System::Init(Log::ConfigureDefaultLog(log_level)); nuclear@3: if(!(vr_ctx.ovr_devman = DeviceManager::Create())) { nuclear@1: fprintf(stderr, "failed to create OVR device manager\n"); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // create the display device nuclear@4: HMDInfo info; nuclear@3: if(!(vr_ctx.ovr_hmd_dev = vr_ctx.ovr_devman->EnumerateDevices().CreateDevice())) { nuclear@1: fprintf(stderr, "no oculus rift devices found\n"); nuclear@4: } else { nuclear@4: if(vr_ctx.ovr_hmd_dev->GetDeviceInfo(&info)) { nuclear@4: printf("oculus device info:\n"); nuclear@4: printf(" name: %s\n", info.DisplayDeviceName); nuclear@4: printf(" ipd: %f\n", info.InterpupillaryDistance); nuclear@4: printf(" distortion: %f %f %f %f\n", info.DistortionK[0], nuclear@4: info.DistortionK[1], info.DistortionK[2], info.DistortionK[3]); nuclear@4: } nuclear@1: nuclear@4: // calculate and store viewing parameters nuclear@4: vr_ctx.info.width = info.HResolution; nuclear@4: vr_ctx.info.height = info.VResolution; nuclear@4: vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height; nuclear@4: nuclear@4: vr_ctx.info.ipd = info.InterpupillaryDistance; nuclear@4: for(int i=0; i<4; i++) { nuclear@4: vr_ctx.info.distort[i] = info.DistortionK[i]; nuclear@4: } nuclear@12: nuclear@14: Util::Render::StereoConfig stereohelp; nuclear@14: stereohelp.SetFullViewport(Util::Render::Viewport(0, 0, vr_ctx.info.width, vr_ctx.info.height)); nuclear@14: stereohelp.SetStereoMode(Util::Render::Stereo_LeftRight_Multipass); nuclear@14: stereohelp.SetHMDInfo(info); nuclear@14: stereohelp.SetDistortionFitPointVP(-1.0, 0.0); nuclear@14: nuclear@14: vr_ctx.info.scale = stereohelp.GetDistortionScale(); nuclear@13: nuclear@13: float vhalfsz = vr_ctx.info.scale * info.VScreenSize * 0.5; nuclear@13: vr_ctx.info.fov = 2.0 * atan(vhalfsz / info.EyeToScreenDistance); nuclear@13: nuclear@13: vr_ctx.info.lens_center_offset = 0.5 - info.LensSeparationDistance / info.HScreenSize; nuclear@13: nuclear@13: // calculate center of projection shift to match the lens positions nuclear@13: float center_dist_meters = info.HScreenSize * 0.25; nuclear@13: float proj_shift = center_dist_meters - info.LensSeparationDistance * 0.5; nuclear@13: vr_ctx.info.proj_center_offset = 4.0 * proj_shift / info.HScreenSize; nuclear@15: nuclear@16: // grab the display info nuclear@15: vr_ctx.info.display = new char[strlen(info.DisplayDeviceName) + 1]; nuclear@15: strcpy(vr_ctx.info.display, info.DisplayDeviceName); nuclear@16: nuclear@16: vr_ctx.info.display_xoffs = info.DesktopX; nuclear@16: vr_ctx.info.display_yoffs = info.DesktopY; nuclear@16: nuclear@16: printf("display: \"%s\" offset: %+d %+d\n", vr_ctx.info.display, nuclear@16: vr_ctx.info.display_xoffs, vr_ctx.info.display_yoffs); nuclear@1: } nuclear@1: nuclear@1: // get the sensor device nuclear@4: if(vr_ctx.ovr_hmd_dev) { nuclear@4: if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_hmd_dev->GetSensor())) { nuclear@4: fprintf(stderr, "failed to get oculus sensor device\n"); nuclear@4: } nuclear@4: } else { nuclear@4: if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_devman->EnumerateDevices().CreateDevice())) { nuclear@4: fprintf(stderr, "failed to get oculus sensor device\n"); nuclear@4: } nuclear@1: } nuclear@1: nuclear@4: if(vr_ctx.ovr_sensor_dev) { nuclear@4: SensorInfo sinfo; nuclear@4: if(vr_ctx.ovr_sensor_dev->GetDeviceInfo(&sinfo)) { nuclear@4: printf("oculus sensor device info:\n"); nuclear@4: printf(" name: %s\n", sinfo.ProductName); nuclear@4: } nuclear@4: nuclear@4: vr_ctx.ovr_sfusion.AttachToSensor(vr_ctx.ovr_sensor_dev); nuclear@1: } nuclear@1: return true; nuclear@1: } nuclear@3: nuclear@13: #define EXTERNAL_SHADER nuclear@13: nuclear@12: static bool init_sdr() nuclear@12: { nuclear@12: int status; nuclear@12: nuclear@13: #ifdef EXTERNAL_SHADER nuclear@17: FILE *fp = fopen("sdr/sdr.glsl", "rb"); nuclear@13: if(!fp) { nuclear@13: perror("failed to load sdr.glsl"); nuclear@13: return false; nuclear@13: } nuclear@13: fseek(fp, 0, SEEK_END); nuclear@13: long sz = ftell(fp); nuclear@13: rewind(fp); nuclear@13: nuclear@13: char *buf = (char*)alloca(sz + 1); nuclear@13: fread(buf, 1, sz, fp); nuclear@13: buf[sz] = 0; nuclear@13: sdr_src = buf; nuclear@13: nuclear@13: fclose(fp); nuclear@13: #endif nuclear@13: nuclear@12: unsigned int sdr = glCreateShader(GL_FRAGMENT_SHADER); nuclear@12: glShaderSource(sdr, 1, &sdr_src, 0); nuclear@12: glCompileShader(sdr); nuclear@12: glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); nuclear@12: if(!status) { nuclear@12: fprintf(stderr, "failed to compile distortion shader\n"); nuclear@14: nuclear@14: int loglen; nuclear@14: glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &loglen); nuclear@14: nuclear@14: if(loglen > 0) { nuclear@14: char *log = (char*)alloca(loglen); nuclear@14: glGetShaderInfoLog(sdr, loglen, &loglen, log); nuclear@14: fprintf(stderr, "%s\n", log); nuclear@14: } nuclear@14: nuclear@12: return false; nuclear@12: } nuclear@12: nuclear@12: sdrprog = glCreateProgram(); nuclear@12: glAttachShader(sdrprog, sdr); nuclear@12: glLinkProgram(sdrprog); nuclear@12: if(!status) { nuclear@12: fprintf(stderr, "failed to link distortion shader program\n"); nuclear@12: glDeleteShader(sdr); nuclear@12: return false; nuclear@12: } nuclear@12: nuclear@12: int loc; nuclear@12: nuclear@12: glUseProgram(sdrprog); nuclear@12: nuclear@12: if((loc = glGetUniformLocation(sdrprog, "tex")) != -1) { nuclear@12: glUniform1i(loc, 0); nuclear@12: } nuclear@13: if((loc = glGetUniformLocation(sdrprog, "lens_center_offset")) != -1) { nuclear@13: glUniform1f(loc, 0); nuclear@12: } nuclear@12: if((loc = glGetUniformLocation(sdrprog, "scr_center")) != -1) { nuclear@12: glUniform2f(loc, 0, 0); nuclear@12: } nuclear@12: if((loc = glGetUniformLocation(sdrprog, "scale")) != -1) { nuclear@13: glUniform1f(loc, vr_ctx.info.scale); nuclear@13: } nuclear@13: if((loc = glGetUniformLocation(sdrprog, "aspect")) != -1) { nuclear@13: printf("setting aspect: %f\n", vr_ctx.info.aspect / 2.0); nuclear@13: glUniform1f(loc, vr_ctx.info.aspect / 2.0); nuclear@12: } nuclear@12: if((loc = glGetUniformLocation(sdrprog, "scale_in")) != -1) { nuclear@12: glUniform2f(loc, 1, 1); nuclear@12: } nuclear@13: if((loc = glGetUniformLocation(sdrprog, "dist_factors")) != -1) { nuclear@12: glUniform4f(loc, vr_ctx.info.distort[0], vr_ctx.info.distort[1], nuclear@12: vr_ctx.info.distort[2], vr_ctx.info.distort[3]); nuclear@12: } nuclear@12: nuclear@12: return true; nuclear@12: } nuclear@12: nuclear@16: extern "C" const char *vr_get_display_name(void) nuclear@16: { nuclear@16: return vr_ctx.info.display; nuclear@16: } nuclear@16: nuclear@16: extern "C" void vr_get_display_pos(int *xptr, int *yptr) nuclear@16: { nuclear@16: *xptr = vr_ctx.info.display_xoffs; nuclear@16: *yptr = vr_ctx.info.display_yoffs; nuclear@16: } nuclear@16: nuclear@3: extern "C" int vr_get_width(void) nuclear@3: { nuclear@3: return vr_ctx.info.width; nuclear@3: } nuclear@3: nuclear@3: extern "C" int vr_get_height(void) nuclear@3: { nuclear@3: return vr_ctx.info.height; nuclear@3: } nuclear@3: nuclear@3: extern "C" float vr_get_fov(void) nuclear@3: { nuclear@3: return vr_ctx.info.fov; nuclear@3: } nuclear@3: nuclear@3: extern "C" float vr_get_aspect(void) nuclear@3: { nuclear@3: return vr_ctx.info.aspect; nuclear@3: } nuclear@3: nuclear@3: extern "C" void vr_set_eyedist(float ipd) nuclear@3: { nuclear@3: vr_ctx.info.ipd = ipd; nuclear@3: } nuclear@3: nuclear@3: extern "C" float vr_get_eyedist(void) nuclear@3: { nuclear@3: return vr_ctx.info.ipd; nuclear@3: } nuclear@3: nuclear@3: extern "C" void vr_set_distort(const float *coef) nuclear@3: { nuclear@3: memcpy(vr_ctx.info.distort, coef, sizeof vr_ctx.info.distort); nuclear@3: } nuclear@3: nuclear@3: extern "C" void vr_get_distort(float *coef) nuclear@3: { nuclear@3: memcpy(coef, vr_ctx.info.distort, sizeof vr_ctx.info.distort); nuclear@3: } nuclear@3: nuclear@3: extern "C" void vr_set_prediction_sec(float dt) nuclear@3: { nuclear@3: vr_ctx.ovr_sfusion.SetPrediction(dt); nuclear@3: } nuclear@3: nuclear@3: extern "C" float vr_get_prediction_sec(void) nuclear@3: { nuclear@3: return vr_ctx.ovr_sfusion.GetPredictionDelta(); nuclear@3: } nuclear@3: nuclear@13: extern "C" void vr_get_view_matrix(float *res, int eye) nuclear@13: { nuclear@13: // TODO nuclear@13: } nuclear@13: nuclear@13: extern "C" void vr_get_proj_matrix(float *res, int eye) nuclear@13: { nuclear@13: static float eye_scale[] = {0.0, 1.0, -1.0}; nuclear@13: nuclear@13: Matrix4f proj = Matrix4f::PerspectiveRH(vr_ctx.info.fov, vr_ctx.info.aspect / 2.0, 0.3, 1000.0); nuclear@13: proj = Matrix4f::Translation(vr_ctx.info.proj_center_offset * eye_scale[eye], 0, 0) * proj; nuclear@13: nuclear@13: memcpy(res, proj.M[0], 16 * sizeof(float)); nuclear@13: } nuclear@13: nuclear@3: extern "C" void vr_get_translation(float *offs) nuclear@3: { nuclear@3: // current oculus devkit doesn't do translation nuclear@3: offs[0] = offs[1] = offs[2] = 0.0f; nuclear@3: } nuclear@3: nuclear@3: extern "C" void vr_get_rotation(float *quat) nuclear@3: { nuclear@3: Quatf oq = vr_ctx.ovr_sfusion.GetPredictedOrientation(); nuclear@3: quat[0] = oq.x; nuclear@3: quat[1] = oq.y; nuclear@3: quat[2] = oq.z; nuclear@3: quat[3] = oq.w; nuclear@3: } nuclear@3: nuclear@3: extern "C" void vr_get_rotation_euler(float *euler) nuclear@3: { nuclear@3: Quatf oq = vr_ctx.ovr_sfusion.GetPredictedOrientation(); nuclear@3: oq.GetEulerAngles(euler + 1, euler, euler + 2); nuclear@3: } nuclear@12: nuclear@18: extern "C" void vr_draw_eye(int eye, unsigned int tex, float tex_scale_x, float tex_scale_y) nuclear@12: { nuclear@12: static const float rects[3][4] = { nuclear@12: {-1, -1, 1, 1}, nuclear@12: {-1, -1, 0, 1}, nuclear@12: {0, -1, 1, 1} nuclear@12: }; nuclear@21: static const float quad_trans[3] = {0, -1, 1}; nuclear@21: static const float quad_scale[3] = {1, 0.5, 0.5}; nuclear@12: static const float offs_scale[3] = {0.0, -1.0, 1.0}; nuclear@21: static int prev_tex_scale_x, prev_tex_scale_y; nuclear@12: nuclear@12: glPushAttrib(GL_ENABLE_BIT); nuclear@12: glDisable(GL_DEPTH_TEST); nuclear@12: glDisable(GL_LIGHTING); nuclear@21: glDisable(GL_CULL_FACE); nuclear@21: nuclear@21: glMatrixMode(GL_PROJECTION); nuclear@21: glPushMatrix(); nuclear@21: glLoadIdentity(); nuclear@12: nuclear@12: glMatrixMode(GL_MODELVIEW); nuclear@12: glPushMatrix(); nuclear@12: glLoadIdentity(); nuclear@12: nuclear@21: if(!dbg_enable) { nuclear@21: glUseProgram(sdrprog); nuclear@12: nuclear@21: if(sdrprog) { nuclear@21: int loc; nuclear@21: if((loc = glGetUniformLocation(sdrprog, "lens_center_offset")) != -1) { nuclear@21: float offset = vr_ctx.info.lens_center_offset * offs_scale[eye]; nuclear@21: glUniform1f(loc, offset); nuclear@21: } nuclear@12: nuclear@21: if((loc = glGetUniformLocation(sdrprog, "tex_scale")) != -1) { nuclear@21: glUniform2f(loc, tex_scale_x, tex_scale_y); nuclear@21: } nuclear@13: } nuclear@18: nuclear@21: glBindTexture(GL_TEXTURE_2D, tex); nuclear@21: glBegin(GL_QUADS); nuclear@21: glColor4f(1, 1, 1, 1); nuclear@21: glTexCoord2f(0, 0); glVertex2f(rects[eye][0], rects[eye][1]); nuclear@21: glTexCoord2f(1, 0); glVertex2f(rects[eye][2], rects[eye][1]); nuclear@21: glTexCoord2f(1, 1); glVertex2f(rects[eye][2], rects[eye][3]); nuclear@21: glTexCoord2f(0, 1); glVertex2f(rects[eye][0], rects[eye][3]); nuclear@21: glEnd(); nuclear@21: nuclear@21: glUseProgram(0); nuclear@21: nuclear@21: } else { nuclear@21: nuclear@21: if(!mesh_valid || tex_scale_x != prev_tex_scale_x || tex_scale_y != prev_tex_scale_y) { nuclear@21: for(int i=0; i<2; i++) { nuclear@21: int eye = i + VR_EYE_LEFT; nuclear@21: nuclear@21: if(wrapmesh[i].vbo) { nuclear@21: glDeleteBuffers(1, &wrapmesh[i].vbo); nuclear@21: } nuclear@21: if(wrapmesh[i].ibo) { nuclear@21: glDeleteBuffers(1, &wrapmesh[i].ibo); nuclear@21: } nuclear@21: nuclear@21: float aspect = vr_ctx.info.aspect / 2.0; nuclear@21: float offset = vr_ctx.info.lens_center_offset * offs_scale[eye]; nuclear@21: wrapmesh[i] = gen_view_mesh(USUB, VSUB, aspect, offset, vr_ctx.info.scale, vr_ctx.info.distort, nuclear@21: tex_scale_x, tex_scale_y); nuclear@21: } nuclear@21: mesh_valid = true; nuclear@21: prev_tex_scale_x = tex_scale_x; nuclear@21: prev_tex_scale_y = tex_scale_y; nuclear@18: } nuclear@21: nuclear@21: glScalef(quad_scale[eye], 1.0, 1.0); nuclear@21: glTranslatef(quad_trans[eye], 0, 0); nuclear@21: nuclear@21: glUseProgram(0); nuclear@21: glBindTexture(GL_TEXTURE_2D, tex); nuclear@21: glEnable(GL_TEXTURE_2D); nuclear@21: nuclear@21: glColor3f(1, 1, 1); nuclear@21: nuclear@21: glEnableClientState(GL_VERTEX_ARRAY); nuclear@21: glEnableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@21: nuclear@21: int meshidx = eye - VR_EYE_LEFT; nuclear@21: glBindBuffer(GL_ARRAY_BUFFER, wrapmesh[meshidx].vbo); nuclear@21: glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0); nuclear@21: glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx)); nuclear@21: nuclear@21: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wrapmesh[meshidx].ibo); nuclear@21: glDrawElements(GL_TRIANGLES, wrapmesh[meshidx].num_faces * 3, GL_UNSIGNED_INT, 0); nuclear@21: nuclear@21: glBindBuffer(GL_ARRAY_BUFFER, 0); nuclear@21: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); nuclear@21: nuclear@21: glDisableClientState(GL_VERTEX_ARRAY); nuclear@21: glDisableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@12: } nuclear@12: nuclear@21: glMatrixMode(GL_PROJECTION); nuclear@12: glPopMatrix(); nuclear@12: glMatrixMode(GL_MODELVIEW); nuclear@12: glPopMatrix(); nuclear@12: nuclear@12: glPopAttrib(); nuclear@12: } nuclear@21: nuclear@21: static Mesh gen_view_mesh(int usub, int vsub, float aspect, float lens_center_offset, nuclear@21: float scale, const float *dist_factors, float tex_scale_x, float tex_scale_y) nuclear@21: { nuclear@21: int uverts = usub + 1; nuclear@21: int vverts = vsub + 1; nuclear@21: nuclear@21: int num_verts = uverts * vverts; nuclear@21: int num_quads = usub * vsub; nuclear@21: int num_tris = num_quads * 2; nuclear@21: nuclear@21: Vertex *varr = new Vertex[num_verts]; nuclear@21: unsigned int *iarr = new unsigned int[num_tris * 3]; nuclear@21: nuclear@21: float du = 1.0 / (float)usub; nuclear@21: float dv = 1.0 / (float)vsub; nuclear@21: nuclear@21: Vertex *vptr = varr; nuclear@21: for(int i=0; ix = x; nuclear@21: vptr->y = y; nuclear@21: vptr->z = 0; nuclear@21: vptr->tx = tc[0] * tex_scale_x; nuclear@21: vptr->ty = tc[1] * tex_scale_y; nuclear@21: vptr++; nuclear@21: } nuclear@21: } nuclear@21: nuclear@21: unsigned int *iptr = iarr; nuclear@21: for(int i=0; i [-1, 1] nuclear@21: float ptx = tc[0] * 2.0 - 1.0; nuclear@21: float pty = tc[1] * 2.0 - 1.0; nuclear@21: nuclear@21: ptx += lens_center_offset * 2.0; nuclear@21: pty /= aspect; // correct for aspect ratio nuclear@21: nuclear@21: float rad = barrel_scale(ptx * ptx + pty * pty, dist_factors); nuclear@21: ptx *= rad; // scale the point by the computer distortion radius nuclear@21: pty *= rad; nuclear@21: nuclear@21: ptx /= scale; nuclear@21: pty /= scale; nuclear@21: nuclear@21: pty *= aspect; nuclear@21: ptx -= lens_center_offset * 2.0; nuclear@21: nuclear@21: // map back to range [0, 1] nuclear@21: tc[0] = ptx * 0.5 + 0.5; nuclear@21: tc[1] = pty * 0.5 + 0.5; nuclear@21: } nuclear@21: nuclear@21: static float barrel_scale(float rad, const float *k) nuclear@21: { nuclear@21: float radsq = rad * rad; nuclear@21: float radquad = radsq * radsq; nuclear@21: return k[0] + k[1] * radsq + k[2] * radquad + k[3] * radquad * radsq; nuclear@21: }