// this is mostly copied / inspired by this: https://gist.github.com/cnlohr/6e452dc6cc2df7f48d5ade66059358d9 // made for this video: https://www.youtube.com/watch?v=r6kM3tR03g4 // thanks @cnlohr for rawdraw, alot of other functions, my motivation and the idea #include #include #include #define CNFG_IMPLEMENTATION #define CNFGOGL #include "os_generic.h" #include "CNFG.h" #undef EXTERN_C #include "openvr_capi.h" intptr_t VR_InitInternal( EVRInitError *peError, EVRApplicationType eType ); void VR_ShutdownInternal(); bool VR_IsHmdPresent(); intptr_t VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError ); bool VR_IsRuntimeInstalled(); const char * VR_GetVRInitErrorAsSymbol( EVRInitError error ); const char * VR_GetVRInitErrorAsEnglishDescription( EVRInitError error ); //rawdraw callbacks void HandleKey( int keycode, int bDown ) { } void HandleButton( int x, int y, int button, int bDown ) { } void HandleMotion( int x, int y, int mask ) { } void HandleDestroy() { exit(0); } // This function was copy-pasted from cnovr. void* CNOVRGetOpenVRFunctionTable( const char * interfacename ) { EVRInitError e; char fnTableName[128]; int result1 = snprintf( fnTableName, 128, "FnTable:%s", interfacename ); void* ret = (void *)VR_GetGenericInterface( fnTableName, &e ); printf( "Getting System FnTable: %s = %p (%d)\n", fnTableName, ret, e ); if( !ret ) { exit( 1 ); } return ret; } // These are interfaces into OpenVR, they are basically function call tables. struct VR_IVRSystem_FnTable* oSystem; struct VR_IVROverlay_FnTable* oOverlay; // The OpenVR Overlay handle. VROverlayHandle_t overlayID; const uint32_t WIDTH = 120, HEIGHT = 24; int initVR() { EVRInitError ierr; uint32_t token = VR_InitInternal( &ierr, EVRApplicationType_VRApplication_Overlay ); if( !token ) { printf( "Error!!!! Could not initialize OpenVR\n" ); return 1; } // Get the system and overlay interfaces. We pass in the version of these // interfaces that we wish to use, in case the runtime is newer, we can still // get the interfaces we expect. oSystem = CNOVRGetOpenVRFunctionTable( IVRSystem_Version ); oOverlay = CNOVRGetOpenVRFunctionTable( IVROverlay_Version ); oOverlay->CreateOverlay( "controlerhandmountedclock", "Controller Hand Mounted Clock", &overlayID ); oOverlay->SetOverlayWidthInMeters( overlayID, .2 ); oOverlay->SetOverlayColor( overlayID, 1., .8, .7 ); // Control texture bounds to control the way the texture is mapped to the overlay. VRTextureBounds_t bounds; bounds.uMin = 0; bounds.uMax = 1; bounds.vMin = 0; bounds.vMax = 1; oOverlay->SetOverlayTextureBounds( overlayID, &bounds ); return 0; } GLuint initOverlayTexture() { // Initialize the texture with junk data. uint8_t * myjunkdata = malloc( 128 * 128 * 4 ); int x, y; for( y = 0; y < 128; y++ ) for( x = 0; x < 128; x++ ) { myjunkdata[ ( x + y * 128 ) * 4 + 0 ] = x * 2; myjunkdata[ ( x + y * 128 ) * 4 + 1 ] = y * 2; myjunkdata[ ( x + y * 128 ) * 4 + 2 ] = 0; myjunkdata[ ( x + y * 128 ) * 4 + 3 ] = 255; } // We aren't doing it, but we could write directly into the overlay. //err = oOverlay->SetOverlayRaw( overlayID, myjunkdata, 128, 128, 4 ); // Generate the texture. GLuint textureid; glGenTextures( 1, &textureid ); glBindTexture( GL_TEXTURE_2D, textureid ); // It is required to setup the min and mag filter of the texture. glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); // Load the texture with our dummy data. Optionally we could pass 0 in where we are // passing in myjunkdata. That would allocate the RAM on the GPU but not do anything with it. glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, myjunkdata); free(myjunkdata); return textureid; } bool attach() { TrackedDeviceIndex_t index; index = oSystem->GetTrackedDeviceIndexForControllerRole( ETrackedControllerRole_TrackedControllerRole_LeftHand ); if( index == k_unTrackedDeviceIndexInvalid || index == k_unTrackedDeviceIndex_Hmd ) { printf( "Couldn't find your controller to attach our overlay to (%d)\n", index ); return false; } else { // We have a ETrackedControllerRole_TrackedControllerRole_LeftHand. Associate it. EVROverlayError err; // Transform that puts the text somewhere reasonable. HmdMatrix34_t transform = { 0 }; //rotation transform.m[0][0] = -1; transform.m[1][2] = -1; transform.m[2][1] = -1; //offset transform.m[0][3] = 0; transform.m[1][3] = -0.05; transform.m[2][3] = 0.15; // Apply the transform and attach the overlay to that tracked device object. err = oOverlay->SetOverlayTransformTrackedDeviceRelative( overlayID, index, &transform ); // Notify the terminal that this was associated. printf( "Successfully associated your clock to the tracked device (%d %d %08x).\n", err, index, overlayID ); return true; } } int main() { CNFGSetup( "CHAC", -WIDTH, -HEIGHT ); while(initVR()) { OGUSleep( (int)( 1000000 ) ); } oOverlay->ShowOverlay( overlayID ); GLuint texture = initOverlayTexture(); bool isAttached = false; CNFGBGColor = 0x00000000; //Background while(1) { if(!isAttached) { isAttached = attach(); OGUSleep( (int)( 1000000 ) ); //wait one sec continue; } CNFGClearFrame(); //Change color to white. CNFGColor( 0xffffffff ); CNFGPenX = 1; CNFGPenY = 1; //draw clock char clock[9]; time_t curr = time(NULL); struct tm * formtime = localtime(&curr); strftime(clock, 9, "%H:%M:%S", formtime); CNFGDrawText( clock, 5 ); // Finish rendering any pending draw operations. CNFGFlushRender(); // Bind the texture we will be sending to OpenVR. glBindTexture( GL_TEXTURE_2D, texture ); // Copy the current framebuffer into that texture. glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, WIDTH, HEIGHT, 0 ); { // Setup a Texture_t object to send in the texture. struct Texture_t tex; tex.eColorSpace = EColorSpace_ColorSpace_Auto; tex.eType = ETextureType_TextureType_OpenGL; tex.handle = (void*)(intptr_t)texture; // Send texture into OpenVR as the overlay. oOverlay->SetOverlayTexture( overlayID, &tex ); } // We have to process through texture events. { struct VREvent_t nEvent; oOverlay->PollNextOverlayEvent( overlayID, &nEvent, 0xffffff ); } // Display the image and wait for time to display next frame. CNFGSwapBuffers(); OGUSleep( (int)( 0.1 * 1000000 ) ); } }