diff --git a/CMakeLists.txt b/CMakeLists.txt index 21a305e..1c18540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,3 +28,5 @@ if (ENABLE_XCB) endif(ENABLE_XCB) export(TARGETS lvrc FILE lvrc-config.cmake) + +add_subdirectory(examples) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..e6701ed --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(opengl) diff --git a/examples/opengl/CMakeLists.txt b/examples/opengl/CMakeLists.txt new file mode 100644 index 0000000..55db558 --- /dev/null +++ b/examples/opengl/CMakeLists.txt @@ -0,0 +1,13 @@ +project (openglexample C) + +find_package(OpenGL REQUIRED) +find_package(openhmd REQUIRED) + +add_executable(openglexample gl.c gl.h main.c) + +target_include_directories(openglexample PRIVATE ${OPENGL_INCLUDE_DIRS}) +target_link_libraries(openglexample PRIVATE ${OPENGL_LIBRARIES}) +target_link_libraries(openglexample PRIVATE m) +target_link_libraries(openglexample PRIVATE openhmd) +target_link_libraries(openglexample PRIVATE lvrc) + diff --git a/examples/opengl/gl.c b/examples/opengl/gl.c new file mode 100644 index 0000000..baaa8a3 --- /dev/null +++ b/examples/opengl/gl.c @@ -0,0 +1,261 @@ +/* + * OpenHMD - Free and Open Source API and drivers for immersive technology. + * Copyright (C) 2013 Fredrik Hultin. + * Copyright (C) 2013 Jakob Bornecrantz. + * Distributed under the Boost 1.0 licence, see LICENSE for full text. + */ + +/* OpenGL Test - GL Helper Functions Implementation */ + +#include "gl.h" +#include +#include +#include +#include + +#include + +#ifdef __unix +#include +#endif + +#ifndef M_PI +#define M_PI 3.14159265359 +#endif + + +void init_gl(gl_ctx* ctx, struct lvrcInstance * compositor) +{ + memset(ctx, 0, sizeof(gl_ctx)); + + bool bSwapChainCreated = lvrcInitSwapChain(compositor); + + if (!bSwapChainCreated) + { + fprintf(stderr, "swap chain not created\n"); + exit(-1); + } + + static const EGLint attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + //EGL_ALPHA_SIZE, 8, + //EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + // + // Get Native Display + EGLNativeDisplayType nativeDisplay = lvrcSwapChainGetNativeDisplay(compositor); + + if (!nativeDisplay) + { + fprintf(stderr, "invalid native display\n"); + exit(-1); + } + + // + // get an EGL display connection + ctx->display = eglGetDisplay(nativeDisplay); + + if (EGL_NO_DISPLAY == ctx->display) + { + fprintf(stderr, "eglGetDisplay() failed\n"); + exit(-1); + } + + // + // initialize the EGL display connection + EGLint major, minor; + + if (!eglInitialize(ctx->display, &major, &minor)) + { + fprintf(stderr, "eglInitialize() failed\n"); + exit(-1); + } + + // + // bind OpenGL API + eglBindAPI(EGL_OPENGL_API); + + // + // get an appropriate EGL frame buffer configuration + EGLint num_config; + EGLConfig configs[128]; + + if (!eglChooseConfig(ctx->display, attribs, configs, 128, &num_config)) + { + fprintf(stderr, "eglChooseConfig() failed\n"); + exit(-1); + } + + EGLConfig config = configs[0]; // FIXME + + // + // create context + ctx->context = eglCreateContext(ctx->display, config, EGL_NO_CONTEXT, context_attributes); + + if (EGL_NO_CONTEXT == ctx->context) + { + fprintf(stderr, "failed to create context\n"); + exit(-1); + } + + // + // Get Native Window + EGLNativeWindowType nativeWindow = lvrcSwapChainGetNativeWindow(compositor); + + if (!nativeWindow) + { + fprintf(stderr, "invalid native window\n"); + exit(-1); + } + + // + // Create Surface + ctx->surface = eglCreateWindowSurface(ctx->display, config, nativeWindow, NULL); + if (EGL_NO_SURFACE == ctx->surface) + { + fprintf(stderr, "eglCreateWindowSurface() failed\n"); + exit(-1); + } + + // + // Set current context + if (!eglMakeCurrent(ctx->display, ctx->surface, ctx->surface, ctx->context)) + { + fprintf(stderr, "failed to make context current\n"); + exit(-1); + } + + ctx->w = lvrcSwapChainGetWidth(compositor); + ctx->h = lvrcSwapChainGetHeight(compositor); + ctx->is_fullscreen = 1; + + // Disable ctrl-c catching on Linux (and OS X?) +#ifdef __unix + signal(SIGINT, SIG_DFL); +#endif + + printf("OpenGL Renderer: %s\n", glGetString(GL_RENDERER)); + printf("OpenGL Vendor: %s\n", glGetString(GL_VENDOR)); + printf("OpenGL Version: %s\n", glGetString(GL_VERSION)); + + // == Initialize OpenGL == + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glLoadIdentity(); + + glShadeModel(GL_SMOOTH); + glDisable(GL_DEPTH_TEST); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glEnable(GL_POLYGON_SMOOTH); + glLoadIdentity(); + + glViewport(0, 0, ctx->w, ctx->h); +} + +void ortho(gl_ctx* ctx) +{ + glMatrixMode(GL_PROJECTION); + //glPushMatrix(); + glLoadIdentity(); + + glOrtho(0.0f, ctx->w, ctx->h, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + //glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_DEPTH); + + glDisable(GL_MULTISAMPLE); +} + +void draw_cube() +{ + glBegin(GL_QUADS); + + glVertex3f( 0.5f, 0.5f, -0.5f); /* Top Right Of The Quad (Top) */ + glVertex3f( -0.5f, 0.5f, -0.5f); /* Top Left Of The Quad (Top) */ + glVertex3f( -0.5f, 0.5f, 0.5f); /* Bottom Left Of The Quad (Top) */ + glVertex3f( 0.5f, 0.5f, 0.5f); /* Bottom Right Of The Quad (Top) */ + + glVertex3f( 0.5f, -0.5f, 0.5f); /* Top Right Of The Quad (Botm) */ + glVertex3f( -0.5f, -0.5f, 0.5f); /* Top Left Of The Quad (Botm) */ + glVertex3f( -0.5f, -0.5f, -0.5f); /* Bottom Left Of The Quad (Botm) */ + glVertex3f( 0.5f, -0.5f, -0.5f); /* Bottom Right Of The Quad (Botm) */ + + glVertex3f( 0.5f, 0.5f, 0.5f); /* Top Right Of The Quad (Front) */ + glVertex3f( -0.5f, 0.5f, 0.5f); /* Top Left Of The Quad (Front) */ + glVertex3f( -0.5f, -0.5f, 0.5f); /* Bottom Left Of The Quad (Front) */ + glVertex3f( 0.5f, -0.5f, 0.5f); /* Bottom Right Of The Quad (Front) */ + + glVertex3f( 0.5f, -0.5f, -0.5f); /* Bottom Left Of The Quad (Back) */ + glVertex3f( -0.5f, -0.5f, -0.5f); /* Bottom Right Of The Quad (Back) */ + glVertex3f( -0.5f, 0.5f, -0.5f); /* Top Right Of The Quad (Back) */ + glVertex3f( 0.5f, 0.5f, -0.5f); /* Top Left Of The Quad (Back) */ + + glVertex3f( -0.5f, 0.5f, 0.5f); /* Top Right Of The Quad (Left) */ + glVertex3f( -0.5f, 0.5f, -0.5f); /* Top Left Of The Quad (Left) */ + glVertex3f( -0.5f, -0.5f, -0.5f); /* Bottom Left Of The Quad (Left) */ + glVertex3f( -0.5f, -0.5f, 0.5f); /* Bottom Right Of The Quad (Left) */ + + glVertex3f( 0.5f, 0.5f, -0.5f); /* Top Right Of The Quad (Right) */ + glVertex3f( 0.5f, 0.5f, 0.5f); /* Top Left Of The Quad (Right) */ + glVertex3f( 0.5f, -0.5f, 0.5f); /* Bottom Left Of The Quad (Right) */ + glVertex3f( 0.5f, -0.5f, -0.5f); /* Bottom Right Of The Quad (Right) */ + + glEnd(); + +} + +void create_fbo(int eye_width, int eye_height, GLuint* fbo, GLuint* color_tex, GLuint* depth_tex) +{ + glGenTextures(1, color_tex); + glGenTextures(1, depth_tex); + glGenFramebuffers(1, fbo); + + glBindTexture(GL_TEXTURE_2D, *color_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, eye_width, eye_height, 0, GL_RGBA, GL_UNSIGNED_INT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, *depth_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, eye_width, eye_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, *fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *color_tex, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, *depth_tex, 0); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + if(status != GL_FRAMEBUFFER_COMPLETE_EXT){ + printf("failed to create fbo %x\n", status); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +} diff --git a/examples/opengl/gl.h b/examples/opengl/gl.h new file mode 100644 index 0000000..cfa9f3a --- /dev/null +++ b/examples/opengl/gl.h @@ -0,0 +1,34 @@ +/* + * OpenHMD - Free and Open Source API and drivers for immersive technology. + * Copyright (C) 2013 Fredrik Hultin. + * Copyright (C) 2013 Jakob Bornecrantz. + * Distributed under the Boost 1.0 licence, see LICENSE for full text. + */ + +/* OpenGL Test - Interface For GL Helper Functions */ + +#ifndef GL_H +#define GL_H + +#define GL_GLEXT_PROTOTYPES +#include +#include + +#include + +typedef struct { + unsigned int w, h; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + int is_fullscreen; +} gl_ctx; + +void ortho(gl_ctx* ctx); +void perspective(gl_ctx* ctx); +void init_gl(gl_ctx* ctx, struct lvrcInstance * compositor); +void draw_cube(void); +void create_fbo(int eye_width, int eye_height, GLuint* fbo, GLuint* color_tex, GLuint* depth_tex); + + +#endif diff --git a/examples/opengl/main.c b/examples/opengl/main.c new file mode 100644 index 0000000..3828bc6 --- /dev/null +++ b/examples/opengl/main.c @@ -0,0 +1,355 @@ +/* + * OpenHMD - Free and Open Source API and drivers for immersive technology. + * Copyright (C) 2013 Fredrik Hultin. + * Copyright (C) 2013 Jakob Bornecrantz. + * Distributed under the Boost 1.0 licence, see LICENSE for full text. + */ + +/* OpenGL Test - Main Implementation */ + +#include +#include +#include +#include +#include +#include +#include "gl.h" + +#define OVERSAMPLE_SCALE 2.0 + +float randf() +{ + return (float)rand() / (float)RAND_MAX; +} + +GLuint gen_cubes() +{ + GLuint list = glGenLists(1); + + // Set the random seed. + srand(42); + + glNewList(list, GL_COMPILE); + + for(float a = 0.0f; a < 360.0f; a += 20.0f){ + glPushMatrix(); + + glRotatef(a, 0, 1, 0); + glTranslatef(0, 0, -1); + glScalef(0.2, 0.2, 0.2); + glRotatef(randf() * 360, randf(), randf(), randf()); + + glColor4f(randf(), randf(), randf(), randf() * .5f + .5f); + draw_cube(); + + glPopMatrix(); + } + + // draw floor + glColor4f(0, 1.0f, .25f, .25f); + glTranslatef(0, -2.5f, 0); + draw_cube(); + + glEndList(); + + return list; +} + +void draw_crosshairs(float len, float cx, float cy) +{ + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glBegin(GL_LINES); + float l = len/2.0f; + glVertex3f(cx - l, cy, 0.0); + glVertex3f(cx + l, cy, 0.0); + glVertex3f(cx, cy - l, 0.0); + glVertex3f(cx, cy + l, 0.0); + glEnd(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +void draw_scene(GLuint list) +{ + // draw cubes + glCallList(list); +} +static inline void +print_matrix(float m[]) +{ + printf("[[%0.4f, %0.4f, %0.4f, %0.4f],\n" + "[%0.4f, %0.4f, %0.4f, %0.4f],\n" + "[%0.4f, %0.4f, %0.4f, %0.4f],\n" + "[%0.4f, %0.4f, %0.4f, %0.4f]]\n", + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15]); +} + +int main(int argc, char** argv) +{ + int hmd_w, hmd_h; + + ohmd_context* ctx = ohmd_ctx_create(); + int num_devices = ohmd_ctx_probe(ctx); + if(num_devices < 0){ + printf("failed to probe devices: %s\n", ohmd_ctx_get_error(ctx)); + return 1; + } + + ohmd_device_settings* settings = ohmd_device_settings_create(ctx); + + // If OHMD_IDS_AUTOMATIC_UPDATE is set to 0, ohmd_ctx_update() must be called at least 10 times per second. + // It is enabled by default. + + int auto_update = 1; + ohmd_device_settings_seti(settings, OHMD_IDS_AUTOMATIC_UPDATE, &auto_update); + + ohmd_device* hmd = ohmd_list_open_device_s(ctx, 0, settings); + if(!hmd){ + printf("failed to open device: %s\n", ohmd_ctx_get_error(ctx)); + return 1; + } + ohmd_device_geti(hmd, OHMD_SCREEN_HORIZONTAL_RESOLUTION, &hmd_w); + ohmd_device_geti(hmd, OHMD_SCREEN_VERTICAL_RESOLUTION, &hmd_h); + float ipd; + ohmd_device_getf(hmd, OHMD_EYE_IPD, &ipd); + float viewport_scale[2]; + float distortion_coeffs[4]; + float aberr_scale[3]; + float sep; + float left_lens_center[2]; + float right_lens_center[2]; + //viewport is half the screen + ohmd_device_getf(hmd, OHMD_SCREEN_HORIZONTAL_SIZE, &(viewport_scale[0])); + viewport_scale[0] /= 2.0f; + ohmd_device_getf(hmd, OHMD_SCREEN_VERTICAL_SIZE, &(viewport_scale[1])); + //distortion coefficients + ohmd_device_getf(hmd, OHMD_UNIVERSAL_DISTORTION_K, &(distortion_coeffs[0])); + ohmd_device_getf(hmd, OHMD_UNIVERSAL_ABERRATION_K, &(aberr_scale[0])); + //calculate lens centers (assuming the eye separation is the distance between the lens centers) + ohmd_device_getf(hmd, OHMD_LENS_HORIZONTAL_SEPARATION, &sep); + ohmd_device_getf(hmd, OHMD_LENS_VERTICAL_POSITION, &(left_lens_center[1])); + ohmd_device_getf(hmd, OHMD_LENS_VERTICAL_POSITION, &(right_lens_center[1])); + left_lens_center[0] = viewport_scale[0] - sep/2.0f; + right_lens_center[0] = sep/2.0f; + //assume calibration was for lens view to which ever edge of screen is further away from lens center + float warp_scale = (left_lens_center[0] > right_lens_center[0]) ? left_lens_center[0] : right_lens_center[0]; + float warp_adj = 1.0f; + + ohmd_device_settings_destroy(settings); + + struct lvrcInstance * compositor = lvrcCreateInstance(hmd); + + gl_ctx gl; + init_gl(&gl, compositor); + + GLuint list = gen_cubes(); + + int eye_w = hmd_w/2*OVERSAMPLE_SCALE; + int eye_h = hmd_h*OVERSAMPLE_SCALE; + GLuint left_color_tex = 0, left_depth_tex = 0, left_fbo = 0; + create_fbo(eye_w, eye_h, &left_fbo, &left_color_tex, &left_depth_tex); + + GLuint right_color_tex = 0, right_depth_tex = 0, right_fbo = 0; + create_fbo(eye_w, eye_h, &right_fbo, &right_color_tex, &right_depth_tex); + + bool bCompositorInit = lvrcInitFrameResources(compositor); + + if (!bCompositorInit) + { + return(-1); + } + + bool done = false; + bool crosshair_overlay = false; + while(!done){ + ohmd_ctx_update(ctx); + + lvrcBeginFrame(compositor); + +#if 0 + SDL_Event event; + while(SDL_PollEvent(&event)){ + if(event.type == SDL_KEYDOWN){ + switch(event.key.keysym.sym){ + case SDLK_ESCAPE: + done = true; + break; + case SDLK_F1: + { + gl.is_fullscreen = !gl.is_fullscreen; + SDL_SetWindowFullscreen(gl.window, gl.is_fullscreen ? SDL_WINDOW_FULLSCREEN : 0); + } + break; + case SDLK_F2: + { + // reset rotation and position + float zero[] = {0, 0, 0, 1}; + ohmd_device_setf(hmd, OHMD_ROTATION_QUAT, zero); + ohmd_device_setf(hmd, OHMD_POSITION_VECTOR, zero); + } + break; + case SDLK_F3: + { + float mat[16]; + ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_PROJECTION_MATRIX, mat); + printf("Projection L: "); + print_matrix(mat); + printf("\n"); + ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, mat); + printf("Projection R: "); + print_matrix(mat); + printf("\n"); + ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX, mat); + printf("View: "); + print_matrix(mat); + printf("\n"); + printf("viewport_scale: [%0.4f, %0.4f]\n", viewport_scale[0], viewport_scale[1]); + printf("lens separation: %04f\n", sep); + printf("IPD: %0.4f\n", ipd); + printf("warp_scale: %0.4f\r\n", warp_scale); + printf("distortion coeffs: [%0.4f, %0.4f, %0.4f, %0.4f]\n", distortion_coeffs[0], distortion_coeffs[1], distortion_coeffs[2], distortion_coeffs[3]); + printf("aberration coeffs: [%0.4f, %0.4f, %0.4f]\n", aberr_scale[0], aberr_scale[1], aberr_scale[2]); + printf("left_lens_center: [%0.4f, %0.4f]\n", left_lens_center[0], left_lens_center[1]); + printf("right_lens_center: [%0.4f, %0.4f]\n", right_lens_center[0], right_lens_center[1]); + } + break; + case SDLK_w: + sep += 0.001; + left_lens_center[0] = viewport_scale[0] - sep/2.0f; + right_lens_center[0] = sep/2.0f; + break; + case SDLK_q: + sep -= 0.001; + left_lens_center[0] = viewport_scale[0] - sep/2.0f; + right_lens_center[0] = sep/2.0f; + break; + case SDLK_a: + warp_adj *= 1.0/0.9; + break; + case SDLK_z: + warp_adj *= 0.9; + break; + case SDLK_i: + ipd -= 0.001; + ohmd_device_setf(hmd, OHMD_EYE_IPD, &ipd); + break; + case SDLK_o: + ipd += 0.001; + ohmd_device_setf(hmd, OHMD_EYE_IPD, &ipd); + break; + case SDLK_d: + /* toggle between distorted and undistorted views */ + if ((distortion_coeffs[0] != 0.0) || + (distortion_coeffs[1] != 0.0) || + (distortion_coeffs[2] != 0.0) || + (distortion_coeffs[3] != 1.0)) { + distortion_coeffs[0] = 0.0; + distortion_coeffs[1] = 0.0; + distortion_coeffs[2] = 0.0; + distortion_coeffs[3] = 1.0; + } else { + ohmd_device_getf(hmd, OHMD_UNIVERSAL_DISTORTION_K, &(distortion_coeffs[0])); + } + break; + case SDLK_x: + crosshair_overlay = ! crosshair_overlay; + break; + default: + break; + } + } + } +#endif // 0 + + // Common scene state + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + float matrix[16]; + + // set hmd rotation, for left eye. + glMatrixMode(GL_PROJECTION); + ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_PROJECTION_MATRIX, matrix); + glLoadMatrixf(matrix); + + glMatrixMode(GL_MODELVIEW); + ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX, matrix); + glLoadMatrixf(matrix); + + // Draw scene into framebuffer. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, left_fbo); + glViewport(0, 0, eye_w, eye_h); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + draw_scene(list); + if (crosshair_overlay) { + glClear(GL_DEPTH_BUFFER_BIT); + glLineWidth(2.0*OVERSAMPLE_SCALE); + glColor4f(1.0, 0.5, 0.0, 1.0); + draw_crosshairs(0.1, 2*left_lens_center[0]/viewport_scale[0] - 1.0f, 2*left_lens_center[1]/viewport_scale[1] - 1.0f); + } + + // set hmd rotation, for right eye. + glMatrixMode(GL_PROJECTION); + ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, matrix); + glLoadMatrixf(matrix); + + glMatrixMode(GL_MODELVIEW); + ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX, matrix); + glLoadMatrixf(matrix); + + // Draw scene into framebuffer. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, right_fbo); + glViewport(0, 0, eye_w, eye_h); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + draw_scene(list); + if (crosshair_overlay) { + glClear(GL_DEPTH_BUFFER_BIT); + glLineWidth(5.0); + glColor4f(1.0, 0.5, 0.0, 1.0); + draw_crosshairs(0.1, 2*right_lens_center[0]/viewport_scale[0] - 1.0f, 2*right_lens_center[1]/viewport_scale[1] - 1.0f); + } + + // Clean up common draw state + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + + // Draw left eye + lvrcSubmitFrameLeft(compositor, left_color_tex); + + // Draw right eye + lvrcSubmitFrameRight(compositor, right_color_tex); + + // Clean up state. + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glUseProgram(0); + + // Da swap-dawup! + eglSwapBuffers(gl.display, gl.surface); + + lvrcEndFrame(compositor); + } + + lvrcReleaseFrameResources(compositor); + + // TODO : destroy OpenGL context here + + lvrcReleaseSwapChain(compositor); + + lvrcDestroyInstance(compositor); + + ohmd_ctx_destroy(ctx); + + return 0; +}