/************************************************************************************ Filename : VrApi_Helpers.h Content : Pure, stateless, inlined helper functions, used to initialize parameters to the VrApi. Created : March 2, 2015 Authors : J.M.P. van Waveren Copyright : Copyright 2015 Oculus VR, LLC. All Rights reserved. *************************************************************************************/ #ifndef OVR_VrApi_Helpers_h #define OVR_VrApi_Helpers_h #include "math.h" // for cosf(), sinf(), tanf() #include "string.h" // for memset() #include "VrApi_Config.h" #include "VrApi_Version.h" #include "VrApi_Types.h" #define VRAPI_PI 3.14159265358979323846f #define VRAPI_ZNEAR 0.1f #if defined( __GNUC__ ) # define VRAPI_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0) #else # define VRAPI_UNUSED(a) (a) #endif //----------------------------------------------------------------- // Matrix helper functions. //----------------------------------------------------------------- // Use left-multiplication to accumulate transformations. static inline ovrMatrix4f ovrMatrix4f_Multiply( const ovrMatrix4f * a, const ovrMatrix4f * b ) { ovrMatrix4f out; out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] + a->M[0][3] * b->M[3][0]; out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] + a->M[1][3] * b->M[3][0]; out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] + a->M[2][3] * b->M[3][0]; out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] + a->M[3][3] * b->M[3][0]; out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] + a->M[0][3] * b->M[3][1]; out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] + a->M[1][3] * b->M[3][1]; out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] + a->M[2][3] * b->M[3][1]; out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] + a->M[3][3] * b->M[3][1]; out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] + a->M[0][3] * b->M[3][2]; out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] + a->M[1][3] * b->M[3][2]; out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] + a->M[2][3] * b->M[3][2]; out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] + a->M[3][3] * b->M[3][2]; out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] + a->M[0][3] * b->M[3][3]; out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] + a->M[1][3] * b->M[3][3]; out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] + a->M[2][3] * b->M[3][3]; out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] + a->M[3][3] * b->M[3][3]; return out; } // Returns the transpose of a 4x4 matrix. static inline ovrMatrix4f ovrMatrix4f_Transpose( const ovrMatrix4f * a ) { ovrMatrix4f out; out.M[0][0] = a->M[0][0]; out.M[0][1] = a->M[1][0]; out.M[0][2] = a->M[2][0]; out.M[0][3] = a->M[3][0]; out.M[1][0] = a->M[0][1]; out.M[1][1] = a->M[1][1]; out.M[1][2] = a->M[2][1]; out.M[1][3] = a->M[3][1]; out.M[2][0] = a->M[0][2]; out.M[2][1] = a->M[1][2]; out.M[2][2] = a->M[2][2]; out.M[2][3] = a->M[3][2]; out.M[3][0] = a->M[0][3]; out.M[3][1] = a->M[1][3]; out.M[3][2] = a->M[2][3]; out.M[3][3] = a->M[3][3]; return out; } // Returns a 3x3 minor of a 4x4 matrix. static inline float ovrMatrix4f_Minor( const ovrMatrix4f * m, int r0, int r1, int r2, int c0, int c1, int c2 ) { return m->M[r0][c0] * ( m->M[r1][c1] * m->M[r2][c2] - m->M[r2][c1] * m->M[r1][c2] ) - m->M[r0][c1] * ( m->M[r1][c0] * m->M[r2][c2] - m->M[r2][c0] * m->M[r1][c2] ) + m->M[r0][c2] * ( m->M[r1][c0] * m->M[r2][c1] - m->M[r2][c0] * m->M[r1][c1] ); } // Returns the inverse of a 4x4 matrix. static inline ovrMatrix4f ovrMatrix4f_Inverse( const ovrMatrix4f * m ) { const float rcpDet = 1.0f / ( m->M[0][0] * ovrMatrix4f_Minor( m, 1, 2, 3, 1, 2, 3 ) - m->M[0][1] * ovrMatrix4f_Minor( m, 1, 2, 3, 0, 2, 3 ) + m->M[0][2] * ovrMatrix4f_Minor( m, 1, 2, 3, 0, 1, 3 ) - m->M[0][3] * ovrMatrix4f_Minor( m, 1, 2, 3, 0, 1, 2 ) ); ovrMatrix4f out; out.M[0][0] = ovrMatrix4f_Minor( m, 1, 2, 3, 1, 2, 3 ) * rcpDet; out.M[0][1] = -ovrMatrix4f_Minor( m, 0, 2, 3, 1, 2, 3 ) * rcpDet; out.M[0][2] = ovrMatrix4f_Minor( m, 0, 1, 3, 1, 2, 3 ) * rcpDet; out.M[0][3] = -ovrMatrix4f_Minor( m, 0, 1, 2, 1, 2, 3 ) * rcpDet; out.M[1][0] = -ovrMatrix4f_Minor( m, 1, 2, 3, 0, 2, 3 ) * rcpDet; out.M[1][1] = ovrMatrix4f_Minor( m, 0, 2, 3, 0, 2, 3 ) * rcpDet; out.M[1][2] = -ovrMatrix4f_Minor( m, 0, 1, 3, 0, 2, 3 ) * rcpDet; out.M[1][3] = ovrMatrix4f_Minor( m, 0, 1, 2, 0, 2, 3 ) * rcpDet; out.M[2][0] = ovrMatrix4f_Minor( m, 1, 2, 3, 0, 1, 3 ) * rcpDet; out.M[2][1] = -ovrMatrix4f_Minor( m, 0, 2, 3, 0, 1, 3 ) * rcpDet; out.M[2][2] = ovrMatrix4f_Minor( m, 0, 1, 3, 0, 1, 3 ) * rcpDet; out.M[2][3] = -ovrMatrix4f_Minor( m, 0, 1, 2, 0, 1, 3 ) * rcpDet; out.M[3][0] = -ovrMatrix4f_Minor( m, 1, 2, 3, 0, 1, 2 ) * rcpDet; out.M[3][1] = ovrMatrix4f_Minor( m, 0, 2, 3, 0, 1, 2 ) * rcpDet; out.M[3][2] = -ovrMatrix4f_Minor( m, 0, 1, 3, 0, 1, 2 ) * rcpDet; out.M[3][3] = ovrMatrix4f_Minor( m, 0, 1, 2, 0, 1, 2 ) * rcpDet; return out; } // Returns a 4x4 identity matrix. static inline ovrMatrix4f ovrMatrix4f_CreateIdentity() { ovrMatrix4f out; out.M[0][0] = 1.0f; out.M[0][1] = 0.0f; out.M[0][2] = 0.0f; out.M[0][3] = 0.0f; out.M[1][0] = 0.0f; out.M[1][1] = 1.0f; out.M[1][2] = 0.0f; out.M[1][3] = 0.0f; out.M[2][0] = 0.0f; out.M[2][1] = 0.0f; out.M[2][2] = 1.0f; out.M[2][3] = 0.0f; out.M[3][0] = 0.0f; out.M[3][1] = 0.0f; out.M[3][2] = 0.0f; out.M[3][3] = 1.0f; return out; } // Returns a 4x4 homogeneous translation matrix. static inline ovrMatrix4f ovrMatrix4f_CreateTranslation( const float x, const float y, const float z ) { ovrMatrix4f out; out.M[0][0] = 1.0f; out.M[0][1] = 0.0f; out.M[0][2] = 0.0f; out.M[0][3] = x; out.M[1][0] = 0.0f; out.M[1][1] = 1.0f; out.M[1][2] = 0.0f; out.M[1][3] = y; out.M[2][0] = 0.0f; out.M[2][1] = 0.0f; out.M[2][2] = 1.0f; out.M[2][3] = z; out.M[3][0] = 0.0f; out.M[3][1] = 0.0f; out.M[3][2] = 0.0f; out.M[3][3] = 1.0f; return out; } // Returns a 4x4 homogeneous rotation matrix. static inline ovrMatrix4f ovrMatrix4f_CreateRotation( const float radiansX, const float radiansY, const float radiansZ ) { const float sinX = sinf( radiansX ); const float cosX = cosf( radiansX ); const ovrMatrix4f rotationX = { { { 1, 0, 0, 0 }, { 0, cosX, -sinX, 0 }, { 0, sinX, cosX, 0 }, { 0, 0, 0, 1 } } }; const float sinY = sinf( radiansY ); const float cosY = cosf( radiansY ); const ovrMatrix4f rotationY = { { { cosY, 0, sinY, 0 }, { 0, 1, 0, 0 }, { -sinY, 0, cosY, 0 }, { 0, 0, 0, 1 } } }; const float sinZ = sinf( radiansZ ); const float cosZ = cosf( radiansZ ); const ovrMatrix4f rotationZ = { { { cosZ, -sinZ, 0, 0 }, { sinZ, cosZ, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } } }; const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply( &rotationY, &rotationX ); return ovrMatrix4f_Multiply( &rotationZ, &rotationXY ); } // Returns a projection matrix based on the specified dimensions. // The far plane is placed at infinity if farZ <= nearZ. // An infinite projection matrix is preferred for rasterization because, except for // things *right* up against the near plane, it always provides better precision: // "Tightening the Precision of Perspective Rendering" // Paul Upchurch, Mathieu Desbrun // Journal of Graphics Tools, Volume 16, Issue 1, 2012 static inline ovrMatrix4f ovrMatrix4f_CreateProjection( const float minX, const float maxX, float const minY, const float maxY, const float nearZ, const float farZ ) { const float width = maxX - minX; const float height = maxY - minY; const float offsetZ = nearZ; // set to zero for a [0,1] clip space ovrMatrix4f out; if ( farZ <= nearZ ) { // place the far plane at infinity out.M[0][0] = 2 * nearZ / width; out.M[0][1] = 0; out.M[0][2] = ( maxX + minX ) / width; out.M[0][3] = 0; out.M[1][0] = 0; out.M[1][1] = 2 * nearZ / height; out.M[1][2] = ( maxY + minY ) / height; out.M[1][3] = 0; out.M[2][0] = 0; out.M[2][1] = 0; out.M[2][2] = -1; out.M[2][3] = -( nearZ + offsetZ ); out.M[3][0] = 0; out.M[3][1] = 0; out.M[3][2] = -1; out.M[3][3] = 0; } else { // normal projection out.M[0][0] = 2 * nearZ / width; out.M[0][1] = 0; out.M[0][2] = ( maxX + minX ) / width; out.M[0][3] = 0; out.M[1][0] = 0; out.M[1][1] = 2 * nearZ / height; out.M[1][2] = ( maxY + minY ) / height; out.M[1][3] = 0; out.M[2][0] = 0; out.M[2][1] = 0; out.M[2][2] = -( farZ + offsetZ ) / ( farZ - nearZ ); out.M[2][3] = -( farZ * ( nearZ + offsetZ ) ) / ( farZ - nearZ ); out.M[3][0] = 0; out.M[3][1] = 0; out.M[3][2] = -1; out.M[3][3] = 0; } return out; } // Returns a projection matrix based on the given FOV. static inline ovrMatrix4f ovrMatrix4f_CreateProjectionFov( const float fovDegreesX, const float fovDegreesY, const float offsetX, const float offsetY, const float nearZ, const float farZ ) { const float halfWidth = nearZ * tanf( fovDegreesX * ( VRAPI_PI / 180.0f * 0.5f ) ); const float halfHeight = nearZ * tanf( fovDegreesY * ( VRAPI_PI / 180.0f * 0.5f ) ); const float minX = offsetX - halfWidth; const float maxX = offsetX + halfWidth; const float minY = offsetY - halfHeight; const float maxY = offsetY + halfHeight; return ovrMatrix4f_CreateProjection( minX, maxX, minY, maxY, nearZ, farZ ); } // Returns the 4x4 rotation matrix for the given quaternion. static inline ovrMatrix4f ovrMatrix4f_CreateFromQuaternion( const ovrQuatf * q ) { const float ww = q->w * q->w; const float xx = q->x * q->x; const float yy = q->y * q->y; const float zz = q->z * q->z; ovrMatrix4f out; out.M[0][0] = ww + xx - yy - zz; out.M[0][1] = 2 * ( q->x * q->y - q->w * q->z ); out.M[0][2] = 2 * ( q->x * q->z + q->w * q->y ); out.M[0][3] = 0; out.M[1][0] = 2 * ( q->x * q->y + q->w * q->z ); out.M[1][1] = ww - xx + yy - zz; out.M[1][2] = 2 * ( q->y * q->z - q->w * q->x ); out.M[1][3] = 0; out.M[2][0] = 2 * ( q->x * q->z - q->w * q->y ); out.M[2][1] = 2 * ( q->y * q->z + q->w * q->x ); out.M[2][2] = ww - xx - yy + zz; out.M[2][3] = 0; out.M[3][0] = 0; out.M[3][1] = 0; out.M[3][2] = 0; out.M[3][3] = 1; return out; } // Convert a standard projection matrix into a TexCoordsFromTanAngles matrix for // the primary time warp surface. static inline ovrMatrix4f ovrMatrix4f_TanAngleMatrixFromProjection( const ovrMatrix4f * projection ) { /* A projection matrix goes from a view point to NDC, or -1 to 1 space. Scale and bias to convert that to a 0 to 1 space. const ovrMatrix3f m = { { { projection->M[0][0], 0.0f, projection->M[0][2] }, { 0.0f, projection->M[1][1], projection->M[1][2] }, { 0.0f, 0.0f, -1.0f } } }; // Note that there is no Y-flip because eye buffers have 0,0 = left-bottom. const ovrMatrix3f s = ovrMatrix3f_CreateScaling( 0.5f, 0.5f ); const ovrMatrix3f t = ovrMatrix3f_CreateTranslation( 0.5f, 0.5f ); const ovrMatrix3f r0 = ovrMatrix3f_Multiply( &s, &m ); const ovrMatrix3f r1 = ovrMatrix3f_Multiply( &t, &r0 ); return r1; clipZ = ( z * projection[2][2] + projection[2][3] ) / ( projection[3][2] * z ) z = projection[2][3] / ( clipZ * projection[3][2] - projection[2][2] ) z = ( projection[2][3] / projection[3][2] ) / ( clipZ - projection[2][2] / projection[3][2] ) */ const ovrMatrix4f tanAngleMatrix = { { { 0.5f * projection->M[0][0], 0.0f, 0.5f * projection->M[0][2] - 0.5f, 0.0f }, { 0.0f, 0.5f * projection->M[1][1], 0.5f * projection->M[1][2] - 0.5f, 0.0f }, { 0.0f, 0.0f, -1.0f, 0.0f }, // Store the values to convert a clip-Z to a linear depth in the unused matrix elements. { projection->M[2][2], projection->M[2][3], projection->M[3][2], 1.0f } } }; return tanAngleMatrix; } // If a simple quad defined as a -1 to 1 XY unit square is transformed to // the camera view with the given modelView matrix, it can alternately be // drawn as a time warp overlay image to take advantage of the full window // resolution, which is usually higher than the eye buffer textures, and // avoids resampling both into the eye buffer, and again to the screen. // This is used for high quality movie screens and user interface planes. // // Note that this is NOT an MVP matrix -- the "projection" is handled // by the distortion process. // // The exact composition of the overlay image and the base image is // determined by the warp program, you may still need to draw the geometry // into the eye buffer to punch a hole in the alpha channel to let the // overlay/underlay show through. // // This utility functions converts a model-view matrix that would normally // draw a -1 to 1 unit square to the view into a TexCoordsFromTanAngles matrix // for an overlay surface. // // The resulting z value should be straight ahead distance to the plane. // The x and y values will be pre-multiplied by z for projective texturing. static inline ovrMatrix4f ovrMatrix4f_TanAngleMatrixFromUnitSquare( const ovrMatrix4f * modelView ) { /* // Take the inverse of the view matrix because the view matrix transforms the unit square // from world space into view space, while the matrix needed here is the one that transforms // the unit square from view space to world space. const ovrMatrix4f inv = ovrMatrix4f_Inverse( modelView ); // This matrix calculates the projection onto the (-1, 1) X and Y axes of the unit square, // of the intersection of the vector (tanX, tanY, -1) with the plane described by the matrix // that transforms the unit square into world space. const ovrMatrix3f m = { { { inv.M[0][0] * inv.M[2][3] - inv.M[0][3] * inv.M[2][0], inv.M[0][1] * inv.M[2][3] - inv.M[0][3] * inv.M[2][1], inv.M[0][2] * inv.M[2][3] - inv.M[0][3] * inv.M[2][2] }, { inv.M[1][0] * inv.M[2][3] - inv.M[1][3] * inv.M[2][0], inv.M[1][1] * inv.M[2][3] - inv.M[1][3] * inv.M[2][1], inv.M[1][2] * inv.M[2][3] - inv.M[1][3] * inv.M[2][2] }, { - inv.M[2][0], - inv.M[2][1], - inv.M[2][2] } } }; // Flip the Y because textures have 0,0 = left-top as opposed to left-bottom. const ovrMatrix3f f = ovrMatrix3f_CreateScaling( 1.0f, -1.0f ); const ovrMatrix3f s = ovrMatrix3f_CreateScaling( 0.5f, 0.5f ); const ovrMatrix3f t = ovrMatrix3f_CreateTranslation( 0.5f, 0.5f ); const ovrMatrix3f r0 = ovrMatrix3f_Multiply( &f, &m ); const ovrMatrix3f r1 = ovrMatrix3f_Multiply( &s, &r0 ); const ovrMatrix3f r2 = ovrMatrix3f_Multiply( &t, &r1 ); return r2; */ const ovrMatrix4f inv = ovrMatrix4f_Inverse( modelView ); ovrMatrix4f m; m.M[0][0] = + 0.5f * ( inv.M[0][0] * inv.M[2][3] - inv.M[0][3] * inv.M[2][0] ) - 0.5f * inv.M[2][0]; m.M[0][1] = + 0.5f * ( inv.M[0][1] * inv.M[2][3] - inv.M[0][3] * inv.M[2][1] ) - 0.5f * inv.M[2][1]; m.M[0][2] = + 0.5f * ( inv.M[0][2] * inv.M[2][3] - inv.M[0][3] * inv.M[2][2] ) - 0.5f * inv.M[2][2]; m.M[0][3] = 0.0f; m.M[1][0] = - 0.5f * ( inv.M[1][0] * inv.M[2][3] - inv.M[1][3] * inv.M[2][0] ) - 0.5f * inv.M[2][0]; m.M[1][1] = - 0.5f * ( inv.M[1][1] * inv.M[2][3] - inv.M[1][3] * inv.M[2][1] ) - 0.5f * inv.M[2][1]; m.M[1][2] = - 0.5f * ( inv.M[1][2] * inv.M[2][3] - inv.M[1][3] * inv.M[2][2] ) - 0.5f * inv.M[2][2]; m.M[1][3] = 0.0f; m.M[2][0] = - inv.M[2][0]; m.M[2][1] = - inv.M[2][1]; m.M[2][2] = - inv.M[2][2]; m.M[2][3] = 0.0f; m.M[3][0] = 0.0f; m.M[3][1] = 0.0f; m.M[3][2] = 0.0f; m.M[3][3] = 1.0f; return m; } // Utility function to calculate external velocity for smooth stick yaw turning. // To reduce judder in FPS style experiences when the application framerate is // lower than the vsync rate, the rotation from a joypad can be applied to the // view space distorted eye vectors before applying the time warp. static inline ovrMatrix4f ovrMatrix4f_CalculateExternalVelocity( const ovrMatrix4f * viewMatrix, const float yawRadiansPerSecond ) { const float angle = yawRadiansPerSecond * ( -1.0f / 60.0f ); const float sinHalfAngle = sinf( angle * 0.5f ); const float cosHalfAngle = cosf( angle * 0.5f ); // Yaw is always going to be around the world Y axis ovrQuatf quat; quat.x = viewMatrix->M[0][1] * sinHalfAngle; quat.y = viewMatrix->M[1][1] * sinHalfAngle; quat.z = viewMatrix->M[2][1] * sinHalfAngle; quat.w = cosHalfAngle; return ovrMatrix4f_CreateFromQuaternion( &quat ); } //----------------------------------------------------------------- // Default initialization helper functions. //----------------------------------------------------------------- // Utility function to default initialize the ovrInitParms. static inline ovrInitParms vrapi_DefaultInitParms( const ovrJava * java ) { ovrInitParms parms; memset( &parms, 0, sizeof( parms ) ); parms.Type = VRAPI_STRUCTURE_TYPE_INIT_PARMS; parms.ProductVersion = VRAPI_PRODUCT_VERSION; parms.MajorVersion = VRAPI_MAJOR_VERSION; parms.MinorVersion = VRAPI_MINOR_VERSION; parms.PatchVersion = VRAPI_PATCH_VERSION; parms.GraphicsAPI = VRAPI_GRAPHICS_API_OPENGL_ES_2; parms.Java = *java; return parms; } // Utility function to default initialize the ovrModeParms. static inline ovrModeParms vrapi_DefaultModeParms( const ovrJava * java ) { ovrModeParms parms; memset( &parms, 0, sizeof( parms ) ); parms.Type = VRAPI_STRUCTURE_TYPE_MODE_PARMS; parms.AllowPowerSave = true; parms.ResetWindowFullscreen = true; parms.Java = *java; return parms; } // Utility function to default initialize the ovrPerformanceParms. static inline ovrPerformanceParms vrapi_DefaultPerformanceParms() { ovrPerformanceParms parms; parms.CpuLevel = 2; parms.GpuLevel = 2; parms.MainThreadTid = 0; parms.RenderThreadTid = 0; return parms; } typedef enum { VRAPI_FRAME_INIT_DEFAULT, VRAPI_FRAME_INIT_BLACK, VRAPI_FRAME_INIT_BLACK_FLUSH, VRAPI_FRAME_INIT_BLACK_FINAL, VRAPI_FRAME_INIT_LOADING_ICON, VRAPI_FRAME_INIT_LOADING_ICON_FLUSH, VRAPI_FRAME_INIT_MESSAGE, VRAPI_FRAME_INIT_MESSAGE_FLUSH } ovrFrameInit; // Utility function to default initialize the ovrFrameParms. static inline ovrFrameParms vrapi_DefaultFrameParms( const ovrJava * java, const ovrFrameInit init, const double currentTime, ovrTextureSwapChain * textureSwapChain ) { const ovrMatrix4f projectionMatrix = ovrMatrix4f_CreateProjectionFov( 90.0f, 90.0f, 0.0f, 0.0f, 0.1f, 0.0f ); const ovrMatrix4f texCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( &projectionMatrix ); ovrFrameParms parms; memset( &parms, 0, sizeof( parms ) ); parms.Type = VRAPI_STRUCTURE_TYPE_FRAME_PARMS; for ( int layer = 0; layer < VRAPI_FRAME_LAYER_TYPE_MAX; layer++ ) { parms.Layers[layer].ProgramParms[2] = 1.0f; // color scale for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) { parms.Layers[layer].Textures[eye].TexCoordsFromTanAngles = texCoordsFromTanAngles; parms.Layers[layer].Textures[eye].TextureRect.width = 1.0f; parms.Layers[layer].Textures[eye].TextureRect.height = 1.0f; parms.Layers[layer].Textures[eye].HeadPose.Pose.Orientation.w = 1.0f; parms.Layers[layer].Textures[eye].HeadPose.TimeInSeconds = currentTime; } } parms.LayerCount = 1; parms.MinimumVsyncs = 1; parms.ExtraLatencyMode = VRAPI_EXTRA_LATENCY_MODE_OFF; parms.ExternalVelocity.M[0][0] = 1.0f; parms.ExternalVelocity.M[1][1] = 1.0f; parms.ExternalVelocity.M[2][2] = 1.0f; parms.ExternalVelocity.M[3][3] = 1.0f; parms.PerformanceParms = vrapi_DefaultPerformanceParms(); parms.Java = *java; parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].SrcBlend = VRAPI_FRAME_LAYER_BLEND_ONE; parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].DstBlend = VRAPI_FRAME_LAYER_BLEND_ZERO; parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Flags = 0; parms.Layers[VRAPI_FRAME_LAYER_TYPE_OVERLAY].SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA; parms.Layers[VRAPI_FRAME_LAYER_TYPE_OVERLAY].DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA; parms.Layers[VRAPI_FRAME_LAYER_TYPE_OVERLAY].Flags = 0; switch ( init ) { case VRAPI_FRAME_INIT_DEFAULT: { break; } case VRAPI_FRAME_INIT_BLACK: case VRAPI_FRAME_INIT_BLACK_FLUSH: case VRAPI_FRAME_INIT_BLACK_FINAL: { parms.Flags = VRAPI_FRAME_FLAG_INHIBIT_SRGB_FRAMEBUFFER; for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) { parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain = (ovrTextureSwapChain *)VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_BLACK; } break; } case VRAPI_FRAME_INIT_LOADING_ICON: case VRAPI_FRAME_INIT_LOADING_ICON_FLUSH: { parms.LayerCount = 2; parms.Flags = VRAPI_FRAME_FLAG_INHIBIT_SRGB_FRAMEBUFFER; parms.Layers[VRAPI_FRAME_LAYER_TYPE_OVERLAY].Flags = VRAPI_FRAME_LAYER_FLAG_SPIN; parms.Layers[1].ProgramParms[0] = 1.0f; // rotation in radians per second parms.Layers[1].ProgramParms[1] = 16.0f; // icon size factor smaller than fullscreen for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) { parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain = (ovrTextureSwapChain *)VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_BLACK; parms.Layers[VRAPI_FRAME_LAYER_TYPE_OVERLAY].Textures[eye].ColorTextureSwapChain = ( textureSwapChain != NULL ) ? textureSwapChain : (ovrTextureSwapChain *)VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_LOADING_ICON; } break; } case VRAPI_FRAME_INIT_MESSAGE: case VRAPI_FRAME_INIT_MESSAGE_FLUSH: { parms.LayerCount = 2; parms.Flags = VRAPI_FRAME_FLAG_INHIBIT_SRGB_FRAMEBUFFER; parms.Layers[1].ProgramParms[0] = 0.0f; // rotation in radians per second parms.Layers[1].ProgramParms[1] = 2.0f; // message size factor smaller than fullscreen for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) { parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain = (ovrTextureSwapChain *)VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_BLACK; parms.Layers[VRAPI_FRAME_LAYER_TYPE_OVERLAY].Textures[eye].ColorTextureSwapChain = ( textureSwapChain != NULL ) ? textureSwapChain : (ovrTextureSwapChain *)VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_LOADING_ICON; } break; } } if ( init == VRAPI_FRAME_INIT_BLACK_FLUSH || init == VRAPI_FRAME_INIT_LOADING_ICON_FLUSH || init == VRAPI_FRAME_INIT_MESSAGE_FLUSH ) { parms.Flags |= VRAPI_FRAME_FLAG_FLUSH; } if ( init == VRAPI_FRAME_INIT_BLACK_FINAL ) { parms.Flags |= VRAPI_FRAME_FLAG_FLUSH | VRAPI_FRAME_FLAG_FINAL; } return parms; } //----------------------------------------------------------------- // Head Model //----------------------------------------------------------------- // Utility function to default initialize the ovrHeadModelParms. static inline ovrHeadModelParms vrapi_DefaultHeadModelParms() { ovrHeadModelParms parms; memset( &parms, 0, sizeof( parms ) ); parms.InterpupillaryDistance = 0.0640f; // average interpupillary distance parms.EyeHeight = 1.6750f; // average eye height above the ground when standing parms.HeadModelDepth = 0.0805f; parms.HeadModelHeight = 0.0750f; return parms; } //----------------------------------------------------------------- // Eye view matrix helper functions. //----------------------------------------------------------------- // Apply the head-on-a-stick model if head tracking is not available. static inline ovrTracking vrapi_ApplyHeadModel( const ovrHeadModelParms * headModelParms, const ovrTracking * tracking ) { if ( ( tracking->Status & VRAPI_TRACKING_STATUS_POSITION_TRACKED ) == 0 ) { // Calculate the head position based on the head orientation using a head-on-a-stick model. const ovrHeadModelParms * p = headModelParms; const ovrMatrix4f m = ovrMatrix4f_CreateFromQuaternion( &tracking->HeadPose.Pose.Orientation ); ovrTracking newTracking = *tracking; newTracking.HeadPose.Pose.Position.x = m.M[0][1] * p->HeadModelHeight - m.M[0][2] * p->HeadModelDepth; newTracking.HeadPose.Pose.Position.y = m.M[1][1] * p->HeadModelHeight - m.M[1][2] * p->HeadModelDepth - p->HeadModelHeight; newTracking.HeadPose.Pose.Position.z = m.M[2][1] * p->HeadModelHeight - m.M[2][2] * p->HeadModelDepth; return newTracking; } return *tracking; } // Utility function to get the center eye transform. // Pass in NULL for 'input' if there is no additional controller input. static inline ovrMatrix4f vrapi_GetCenterEyeTransform( const ovrHeadModelParms * headModelParms, const ovrTracking * tracking, const ovrMatrix4f * input ) { VRAPI_UNUSED( headModelParms ); // Controller input is expected to be applied relative to the head in neutral position, which means // ovrTracking::HeadPose.Pose.Position should be relative to the center of the head in neutral position. const ovrMatrix4f centerEyeRotation = ovrMatrix4f_CreateFromQuaternion( &tracking->HeadPose.Pose.Orientation ); const ovrVector3f centerEyeOffset = tracking->HeadPose.Pose.Position; const ovrMatrix4f centerEyeTranslation = ovrMatrix4f_CreateTranslation( centerEyeOffset.x, centerEyeOffset.y, centerEyeOffset.z ); const ovrMatrix4f centerEyeTransform = ovrMatrix4f_Multiply( ¢erEyeTranslation, ¢erEyeRotation ); return ( input == NULL ) ? centerEyeTransform : ovrMatrix4f_Multiply( input, ¢erEyeTransform ); } // Utility function to get the center eye view matrix. // Pass in NULL for 'input' if there is no additional controller input. static inline ovrMatrix4f vrapi_GetCenterEyeViewMatrix( const ovrHeadModelParms * headModelParms, const ovrTracking * tracking, const ovrMatrix4f * input ) { const ovrMatrix4f centerEyeTransform = vrapi_GetCenterEyeTransform( headModelParms, tracking, input ); return ovrMatrix4f_Inverse( ¢erEyeTransform ); } // Utility function to get the eye view matrix based on the center eye view matrix and the IPD. static inline ovrMatrix4f vrapi_GetEyeViewMatrix( const ovrHeadModelParms * headModelParms, const ovrMatrix4f * centerEyeViewMatrix, const int eye ) { const float eyeOffset = ( eye ? -0.5f : 0.5f ) * headModelParms->InterpupillaryDistance; const ovrMatrix4f eyeOffsetMatrix = ovrMatrix4f_CreateTranslation( eyeOffset, 0.0f, 0.0f ); return ovrMatrix4f_Multiply( &eyeOffsetMatrix, centerEyeViewMatrix ); } #endif // OVR_VrApi_Helpers_h