This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
+528
View File
@@ -0,0 +1,528 @@
/************************************************************************************␍
Filename : VrApi.h␍
Content : Minimum necessary API for mobile VR␍
Created : June 25, 2014␍
Authors : John Carmack, J.M.P. van Waveren␍
Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved.␍
*************************************************************************************/
#ifndef OVR_VrApi_h␍
#define OVR_VrApi_h␍
#include "VrApi_Config.h"␍
#include "VrApi_Version.h"␍
#include "VrApi_Types.h"␍
/*␍
VrApi␍
=====␍
Multiple Android activities that live in the same address space can cooperatively use the VrApi.␍
However, only one activity can be in "VR mode" at a time. The following explains when an activity␍
is expected to enter/leave VR mode.␍
Android Activity life cycle␍
===========================␍
An Android Activity can only be in VR mode while the activity is in the resumed state.␍
The following shows how VR mode fits into the Android Activity life cycle.␍
1. VrActivity::onCreate() <---------+␍
2. VrActivity::onStart() <-------+ |␍
3. VrActivity::onResume() <---+ | |␍
4. vrapi_EnterVrMode() | | |␍
5. vrapi_LeaveVrMode() | | |␍
6. VrActivity::onPause() -----+ | |␍
7. VrActivity::onStop() ---------+ |␍
8. VrActivity::onDestroy() ---------+␍
Android Surface life cycle␍
==========================␍
An Android Activity can only be in VR mode while there is a valid Android Surface.␍
The following shows how VR mode fits into the Android Surface life cycle.␍
1. VrActivity::surfaceCreated() <----+␍
2. VrActivity::surfaceChanged() |␍
3. vrapi_EnterVrMode() |␍
4. vrapi_LeaveVrMode() |␍
5. VrActivity::surfaceDestroyed() ---+␍
Note that the life cycle of a surface is not necessarily tightly coupled with the␍
life cycle of an activity. These two life cycles may interleave in complex ways.␍
Usually surfaceCreated() is called after onResume() and surfaceDestroyed() is called␍
between onPause() and onDestroy(). However, this is not guaranteed and, for instance,␍
surfaceDestroyed() may be called after onDestroy() or even before onPause().␍
An Android Activity is only in the resumed state with a valid Android Surface between␍
surfaceChanged() or onResume(), whichever comes last, and surfaceDestroyed() or onPause(),␍
whichever comes first. In other words, a VR application will typically enter VR mode␍
from surfaceChanged() or onResume(), whichever comes last, and leave VR mode from␍
surfaceDestroyed() or onPause(), whichever comes first.␍
Android VR life cycle␍
=====================␍
// Setup the Java references.␍
ovrJava java;␍
java.Vm = javaVm;␍
java.Env = jniEnv;␍
java.ActivityObject = activityObject;␍
// Initialize the API.␍
const ovrInitParms initParms = vrapi_DefaultInitParms( &java );␍
if ( vrapi_Initialize( &initParms ) != VRAPI_INITIALIZE_SUCCESS )␍
{␍
FAIL( "Failed to initialize VrApi!" );␍
abort();␍
}␍
// Create an EGLContext and get the suggested FOV and suggested␍
// resolution to setup a projection matrix and eye texture swap chains.␍
const float suggestedEyeFovDegreesX = vrapi_GetSystemPropertyFloat( &java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X );␍
const float suggestedEyeFovDegreesY = vrapi_GetSystemPropertyFloat( &java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y );␍
// Setup a projection matrix based on the 'ovrHmdInfo'.␍
const ovrMatrix4f eyeProjectionMatrix = ovrMatrix4f_CreateProjectionFov( suggestedEyeFovDegreesX,␍
suggestedEyeFovDegreesY,␍
0.0f, 0.0f, VRAPI_ZNEAR, 0.0f );␍
const int suggestedEyeTextureWidth = vrapi_GetSystemPropertyInt( &java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH );␍
const int suggestedEyeTextureHeight = vrapi_GetSystemPropertyInt( &java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT );␍
// Allocate a texture swap chain for each eye.␍
ovrTextureSwapChain * colorTextureSwapChain[VRAPI_FRAME_LAYER_EYE_MAX];␍
for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )␍
{␍
colorTextureSwapChain[eye] = vrapi_CreateTextureSwapChain( VRAPI_TEXTURE_TYPE_2D, VRAPI_TEXTURE_FORMAT_8888,␍
suggestedEyeTextureWidth,␍
suggestedEyeTextureHeight,␍
1, true );␍
}␍
// Android Activity/Surface life cycle loop.␍
for ( ; ; )␍
{␍
// Acquire ANativeWindow from Android Surface and create EGLSurface.␍
// Make the EGLContext context current on the surface.␍
// Enter VR mode once the activity is in the resumed state with a␍
// valid EGLSurface and current EGLContext.␍
const ovrModeParms modeParms = vrapi_DefaultModeParms( &java );␍
ovrMobile * ovr = vrapi_EnterVrMode( &modeParms );␍
// Frame loop, possibly running on another thread.␍
for ( long long frameIndex = 1; ; frameIndex++ )␍
{␍
// Get the HMD pose, predicted for the middle of the time period during which␍
// the new eye images will be displayed. The number of frames predicted ahead␍
// depends on the pipeline depth of the engine and the synthesis rate.␍
// The better the prediction, the less black will be pulled in at the edges.␍
const double predictedDisplayTime = vrapi_GetPredictedDisplayTime( ovr, frameIndex );␍
const ovrTracking baseTracking = vrapi_GetPredictedTracking( ovr, predictedDisplayTime );␍
// Apply the head-on-a-stick model if there is no positional tracking.␍
const ovrHeadModelParms headModelParms = vrapi_DefaultHeadModelParms();␍
const ovrTracking tracking = vrapi_ApplyHeadModel( &headModelParms, &baseTracking );␍
// Advance the simulation based on the predicted display time.␍
// Render eye images and setup ovrFrameParms using 'ovrTracking'.␍
const double currentTime = vrapi_GetTimeInSeconds();␍
ovrFrameParms frameParms = vrapi_DefaultFrameParms( &java, VRAPI_FRAME_INIT_DEFAULT, currentTime, NULL );␍
frameParms.FrameIndex = frameIndex;␍
const ovrMatrix4f centerEyeViewMatrix = vrapi_GetCenterEyeViewMatrix( &headModelParms, &tracking, NULL );␍
for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )␍
{␍
const ovrMatrix4f eyeViewMatrix = vrapi_GetEyeViewMatrix( &headModelParms, &centerEyeViewMatrix, eye );␍
const int colorTextureSwapChainIndex = frameIndex % vrapi_GetTextureSwapChainLength( colorTextureSwapChain[eye] );␍
const unsigned int textureId = vrapi_GetTextureSwapChainHandle( colorTextureSwapChain[eye], colorTextureSwapChainIndex );␍
// Render to 'textureId' using the 'eyeViewMatrix' and 'eyeProjectionMatrix'.␍
frameParms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain = colorTextureSwapChain[eye];␍
frameParms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TextureSwapChainIndex = colorTextureSwapChainIndex;␍
frameParms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( &eyeProjectionMatrix );␍
frameParms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].HeadPose = tracking.HeadPose;␍
}␍
// Hand over the eye images to the time warp.␍
vrapi_SubmitFrame( ovr, &frameParms );␍
}␍
// Leave VR mode when the activity is paused, the Android Surface is␍
// destroyed, or when switching to another activity.␍
vrapi_LeaveVrMode( ovr );␍
}␍
// Destroy the texture swap chains.␍
for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )␍
{␍
vrapi_DestroyTextureSwapChain( colorTextureSwapChain[eye] );␍
}␍
// Shut down the API.␍
vrapi_Shutdown();␍
Integration␍
===========␍
The API is designed to work with an Android Activity using a plain Android SurfaceView,␍
where the Activity life cycle and the Surface life cycle are managed completely in native␍
code by sending the life cycle events (onResume, onPause, surfaceChanged etc.) to native code.␍
The API does not work with an Android Activity using a GLSurfaceView. The GLSurfaceView␍
class manages the window surface and EGLSurface and the implementation of GLSurfaceView␍
may unbind the EGLSurface before onPause() gets called. As such, there is no way to␍
leave VR mode before the EGLSurface disappears. Another problem with GLSurfaceView is␍
that it creates the EGLContext using eglChooseConfig(). The Android EGL code pushes in␍
multisample flags in eglChooseConfig() if the user has selected the "force 4x MSAA" option␍
in settings. Using a multisampled front buffer is completely wasted for time warp␍
rendering.␍
Alternatively an Android NativeActivity can be used to avoid manually handling all␍
the life cycle events. However, it is important to select the EGLConfig manually␍
without using eglChooseConfig() to make sure the front buffer is not multisampled.␍
The vrapi_GetSystemProperty* functions can be called at any time from any thread.␍
This allows an application to setup its renderer, possibly running on a separate␍
thread, before entering VR mode.␍
On Android, an application cannot just allocate a new window/frontbuffer and render to it.␍
Android allocates and manages the window/frontbuffer and (after the fact) notifies the␍
application of the state of affairs through life cycle events (surfaceCreated / surfaceChanged␍
/ surfaceDestroyed). The application (or 3rd party engine) typically handles these events.␍
Since the VrApi cannot just allocate a new window/frontbuffer, and the VrApi does not␍
handle the life cycle events, the VrApi somehow has to hijack the Android surface from␍
the application. The easiest way to do this is by having the application first setup an␍
OpenGL ES context that is current on the Android window surface. vrapi_EnterVrMode() is␍
then called from the thread with this OpenGL ESL context, which allows vrapi_EnterVrMode()␍
to swap out the Android window surface and take ownership of the actual frontbuffer that␍
is used for rendering.␍
Sensor input only becomes available after entering VR mode. In part this is because the␍
VrApi supports hybrid apps. The app starts out in non-stereo mode, and only switches to␍
VR mode when the phone is docked into the headset. While not in VR mode, a non-stereo app␍
shoud not be burdened with a SCHED_FIFO device manager thread for sensor input and possibly␍
expensive sensor/vision processing. In other words, there is no sensor input until the␍
phone is docked and the app is in VR mode.␍
Before getting sensor input, the application also needs to know when the images that are␍
going to be synthesized will be displayed, because the sensor input needs to be predicted␍
ahead for that time. As it turns out, it is not trivial to get an accurate predicted␍
display time. Therefore the calculation of this predicted display time is part of the VrApi.␍
An accurate predicted display time can only really be calculated once the rendering loop␍
is up and running and submitting frames regularly. In other words, before getting sensor␍
input, the application needs an accurate predicted display time, which in return requires␍
the renderer to be up and running. As such, it makes sense that sensor input is not␍
available until vrapi_EnterVrMode() has been called. However, once the application is␍
in VR mode, it can call vrapi_GetPredictedDisplayTime() and vrapi_GetPredictedTracking()␍
at any time from any thread.␍
vrapi_SubmitFrame() must be called from the thread with the OpenGL ES context that was␍
used for rendering. The reason for this is that the VrApi allows for one frame of overlap␍
which is essential on tiled mobile GPUs. Because there is one frame of overlap, the eye images␍
have typically not completed rendering by the time they are submitted to vrapi_SubmitFrame().␍
vrapi_SubmitFrame() therefore adds a sync object to the current context which allows the␍
background time warp thread to check when the eye images have completed.␍
Note that vrapi_EnterVrMode() and vrapi_SubmitFrame() can be called from different threads.␍
vrapi_EnterVrMode() needs to be called from a thread with an OpenGL ES context that is current␍
on the Android window surface. This does not need to be the same context that is also used␍
for rendering. vrapi_SubmitFrame() needs to be called from the thread with the OpenGL ES␍
context that was used to render the eye images. If this is a different context than the context␍
used to enter VR mode, then for stereoscopic rendering this context never needs to be current␍
on the Android window surface.␍
Eye Image Synthesis␍
===================␍
vrapi_SubmitFrame() controls the synthesis rate through an application specified␍
ovrFrameParms::MinimumVsyncs. vrapi_SubmitFrame() also controls at which point during␍
a display refresh cycle the calling thread gets released. vrapi_SubmitFrame() only returns␍
when the previous eye images have been consumed by the asynchronous time warp thread,␍
and at least the specified minimum number of V-syncs have passed since the last call␍
to vrapi_SubmitFrame(). The asynchronous time warp thread consumes new eye images and␍
updates the V-sync counter halfway through a display refresh cycle. This is the first␍
time the time warp can start updating the first eye, covering the first half of the␍
display. As a result, vrapi_SubmitFrame() returns and releases the calling thread halfway␍
through a display refresh cycle.␍
Once vrapi_SubmitFrame() returns, synthesis has a full display refresh cycle to generate␍
new eye images up to the next halfway point. At the next halfway point, the time␍
warp has half a display refresh cycle (up to V-sync) to update the first eye. The␍
time warp then effectively waits for V-sync and then has another half a display␍
refresh cycle (up to the next-next halfway point) to update the second eye. The␍
asynchronous time warp uses a high priority GPU context and will eat away cycles␍
from synthesis, so synthesis does not have a full display refresh cycle worth of␍
actual GPU cycles. However, the asynchronous time warp tends to be very fast,␍
leaving most of the GPU time for synthesis.␍
Instead of using the latest sensor sampling, synthesis uses predicted sensor input␍
for the middle of the time period during which the new eye images will be displayed.␍
This predicted time is calculated using vrapi_GetPredictedDisplayTime(). The number␍
of frames predicted ahead depends on the pipeline depth and the minimum number of␍
V-syncs in between eye image rendering. Less than half a display refresh cycle␍
before each eye image will be displayed, the asynchronous time warp will get new␍
predicted sensor input using the very latest sensor sampling. The asynchronous␍
time warp then corrects the eye images using this new sensor input. In other words,␍
the asynchronous time warp will always correct the eye images even if the predicted␍
sensor input for synthesis was not perfect. However, the better the prediction for␍
synthesis, the less black will be pulled in at the edges by the asynchronous time warp.␍
The application can improve the prediction by fetching the latest predicted sensor␍
input right before rendering each eye, and passing a, possibly different, sensor state␍
for each eye to vrapi_SubmitFrame(). However, it is very important that both eyes use a␍
sensor state that is predicted for the exact same display time, so both eyes can be␍
displayed at the same time without causing intra frame motion judder. While the predicted␍
orientation can be updated for each eye, the position must remain the same for both eyes,␍
or the position would seem to judder "backwards in time" if a frame is dropped.␍
Ideally the eye images are only displayed for the MinimumVsyncs display refresh cycles␍
that are centered about the eye image predicted display time. In other words, a set␍
of eye images is first displayed at prediction time minus MinimumVsyncs / 2 display␍
refresh cycles. The eye images should never be shown before this time because that␍
can cause intra frame motion judder. Ideally the eye images are also not shown after␍
the prediction time plus MinimumVsyncs / 2 display refresh cycles, but this may␍
happen if synthesis fails to produce new eye images in time.␍
MinimumVsyncs = 1␍
|-------|-------|-------| - V-syncs␍
| * | * | * | - eye image display periods (* = predicted time in middle of display period)␍
\ / \ / \ /␍
^ \ / ^ | +---- The asynchronous time warp projects the second eye image onto the display.␍
| \ / | +---- The asynchronous time warp projects the first eye image onto the display. ␍
| | |␍
| | +---- Call vrapi_SubmitFrame before this point.␍
| | vrapi_SubmitFrame inserts a GPU fence and hands over eye images to the asynchronous time warp.␍
| | The asynchronous time warp checks the fence and uses the new eye images if rendering has completed.␍
| |␍
| +---- Generate GPU commands and execute commands on GPU.␍
|␍
+---- vrapi_SubmitFrame releases the renderer thread.␍
MinimumVsyncs = 2␍
|-------|-------|-------|-------|-------| - V-syncs␍
* | * | * | - eye image display periods (* = predicted time in middle of display period)␍
\ / \ / \ / \ / \ /␍
^ \ / ^ | | | +---- The asynchronous time warp re-projects the second eye image onto the display.␍
| \ / | | | +---- The asynchronous time warp re-projects the first eye image onto the display. ␍
| \ / | | +---- The asynchronous time warp projects the second eye image onto the display.␍
| \ / | +---- The asynchronous time warp projects the first eye image onto the display.␍
| \ / |␍
| \ / +---- Call vrapi_SubmitFrame before this point.␍
| | vrapi_SubmitFrame inserts a GPU fence and hands over eye images to the asynchronous time warp.␍
| | The asynchronous time warp checks the fence and uses the new eye images if rendering has completed.␍
| |␍
| +---- Generate GPU commands and execute commands on GPU.␍
|␍
+---- vrapi_SubmitFrame releases the renderer thread.␍
MinimumVsyncs = 3␍
|-------|-------|-------|-------|-------|-------|-------| - V-syncs␍
| * | * | - eye image display periods (* = predicted time in middle of display period)␍
\ / \ / \ / \ / \ / \ / \ /␍
^ \ / ^ | | | | | +---- The asynchronous time warp re-projects the second eye image onto the display.␍
| \ / | | | | | +---- The asynchronous time warp re-projects the first eye image onto the display. ␍
| \ / | | | | +---- The asynchronous time warp re-projects the second eye image onto the display.␍
| \ / | | | +---- The asynchronous time warp re-projects the first eye image onto the display. ␍
| \ / | | +---- The asynchronous time warp projects the second eye image onto the display.␍
| \ / | +---- The asynchronous time warp projects the first eye image onto the display.␍
| \ / |␍
| \ / +---- Call vrapi_SubmitFrame before this point.␍
| \ / vrapi_SubmitFrame inserts a GPU fence and hands over eye images to the asynchronous time warp.␍
| \ / The asynchronous time warp checks the fence and uses the new eye images if rendering has completed.␍
| |␍
| +---- Generate GPU commands and execute commands on GPU.␍
| ␍
+---- vrapi_SubmitFrame releases the renderer thread.␍
*/
#if defined( __cplusplus )␍
extern "C" {
#endif␍
// Returns the version + compile time stamp as a string.␍
// Can be called any time from any thread.␍
OVR_VRAPI_EXPORT const char * vrapi_GetVersionString();
// Returns global, absolute high-resolution time in seconds. This is the same value␍
// as used in sensor messages and on Android also the same as Java's system.nanoTime(),␍
// which is what the Choreographer V-sync timestamp is based on.␍
// WARNING: do not use this time as a seed for simulations, animations or other logic.␍
// An animation, for instance, should not be updated based on the "real time" the␍
// animation code is executed. Instead, an animation should be updated based on the␍
// time it will be displayed. Using the "real time" will introduce intra-frame motion␍
// judder when the code is not executed at a consistent point in time every frame.␍
// In other words, for simulations, animations and other logic use the time returned␍
// by vrapi_GetPredictedDisplayTime().␍
// Can be called any time from any thread.␍
OVR_VRAPI_EXPORT double vrapi_GetTimeInSeconds();
//-----------------------------------------------------------------␍
// Initialization/Shutdown␍
//-----------------------------------------------------------------␍
// Initializes the API for application use.␍
// This is lightweight and does not create any threads.␍
// This is typically called from onCreate() or shortly thereafter.␍
// Can be called from any thread.␍
// Returns a non-zero value from ovrInitializeStatus on error.␍
OVR_VRAPI_EXPORT ovrInitializeStatus vrapi_Initialize( const ovrInitParms * initParms );
// Shuts down the API on application exit.␍
// This is typically called from onDestroy() or shortly thereafter.␍
// Can be called from any thread.␍
OVR_VRAPI_EXPORT void vrapi_Shutdown();
//-----------------------------------------------------------------␍
// System properties and status␍
//-----------------------------------------------------------------␍
// Returns a system property. These are constants for a particular device.␍
// This function can be called any time from any thread once the VrApi is initialized.␍
OVR_VRAPI_EXPORT int vrapi_GetSystemPropertyInt( const ovrJava * java, const ovrSystemProperty propType );
OVR_VRAPI_EXPORT float vrapi_GetSystemPropertyFloat( const ovrJava * java, const ovrSystemProperty propType );
// Returns a system status. These are variables that may change at run-time.␍
// This function can be called any time from any thread once the VrApi is initialized.␍
OVR_VRAPI_EXPORT int vrapi_GetSystemStatusInt( const ovrJava * java, const ovrSystemStatus statusType );
OVR_VRAPI_EXPORT float vrapi_GetSystemStatusFloat( const ovrJava * java, const ovrSystemStatus statusType );
//-----------------------------------------------------------------␍
// Enter/Leave VR mode␍
//-----------------------------------------------------------------␍
// Starts up the time warp, V-sync tracking, sensor reading, clock locking,␍
// thread scheduling, and sets video options. The parms are copied, and are␍
// not referenced after the function returns.␍
//␍
// This should be called after vrapi_Initialize(), when the app is both␍
// resumed and has a valid window surface. ␍
//␍
// Must be called from a thread that has an OpenGL ES context current␍
// on the active Android window surface. The context of the calling␍
// thread is used to match the version and config for the context used by␍
// the background time warp thread. The time warp will also hijack the␍
// Android window surface from the context that is current on the calling␍
// thread. On return, the context from the calling thread will be current␍
// on an invisible pbuffer, because the time warp takes ownership of the␍
// Android window surface. Note that this requires the config used by the␍
// calling thread to have an EGL_SURFACE_TYPE with EGL_PBUFFER_BIT.␍
OVR_VRAPI_EXPORT ovrMobile * vrapi_EnterVrMode( const ovrModeParms * parms );
// Shut everything down for window destruction.␍
// The ovrMobile object is freed by this function.␍
//␍
// Must be called from the same thread that called vrapi_EnterVrMode() with␍
// the same OpenGL ES context that was current on the Android window surface␍
// before calling vrapi_EnterVrMode(). By calling this function the time warp␍
// gives up ownership of the Android window surface, and on return, the␍
// context from the calling thread will be current again on the Android␍
// window surface.␍
OVR_VRAPI_EXPORT void vrapi_LeaveVrMode( ovrMobile * ovr );
//-----------------------------------------------------------------␍
// Tracking␍
//-----------------------------------------------------------------␍
// Returns a predicted absolute system time in seconds at which the next set␍
// of eye images will be displayed.␍
//␍
// The predicted time is the middle of the time period during which the new␍
// eye images will be displayed. The number of frames predicted ahead depends␍
// on the pipeline depth of the engine and the minumum number of V-syncs in␍
// between eye image rendering. The better the prediction, the less black will␍
// be pulled in at the edges by the time warp.␍
//␍
// The frameIndex is an application controlled number that uniquely identifies␍
// the new set of eye images for which synthesis is about to start. This same␍
// frameIndex must be passed to vrapi_SubmitFrame() when the new eye images are␍
// submitted to the time warp. The frameIndex is expected to be incremented␍
// once every frame before calling this function.␍
//␍
// Can be called from any thread while in VR mode.␍
OVR_VRAPI_EXPORT double vrapi_GetPredictedDisplayTime( ovrMobile * ovr, long long frameIndex );
// Returns the predicted sensor state based on the specified absolute system time␍
// in seconds. Pass absTime value of 0.0 to request the most recent sensor reading.␍
//␍
// Can be called from any thread while in VR mode.␍
OVR_VRAPI_EXPORT ovrTracking vrapi_GetPredictedTracking( ovrMobile * ovr, double absTimeInSeconds );
// Recenters the orientation on the yaw axis and will recenter the position␍
// when position tracking is available.␍
//␍
// Note that this immediately affects vrapi_GetPredictedTracking() which may␍
// be called asynchronously from the time warp. It is therefore best to␍
// make sure the screen is black before recentering to avoid previous eye␍
// images from being abrubtly warped across the screen.␍
//␍
// Can be called from any thread while in VR mode.␍
OVR_VRAPI_EXPORT void vrapi_RecenterPose( ovrMobile * ovr );
//-----------------------------------------------------------------␍
// Texture Swap Chains␍
//-----------------------------------------------------------------␍
// Create a texture swap chain that can be passed to vrapi_SubmitFrame.␍
// Must be called from a thread with a valid OpenGL ES context current.␍
OVR_VRAPI_EXPORT ovrTextureSwapChain * vrapi_CreateTextureSwapChain( ovrTextureType type, ovrTextureFormat format,
int width, int height, int levels, bool buffered );
// Destroy the given texture swap chain.␍
// Must be called from a thread with a valid OpenGL ES context current.␍
OVR_VRAPI_EXPORT void vrapi_DestroyTextureSwapChain( ovrTextureSwapChain * chain );
// Returns the number of textures in the swap chain.␍
OVR_VRAPI_EXPORT int vrapi_GetTextureSwapChainLength( ovrTextureSwapChain * chain );
// Get the OpenGL name of the texture at the given index.␍
OVR_VRAPI_EXPORT unsigned int vrapi_GetTextureSwapChainHandle( ovrTextureSwapChain * chain, int index );
// Set the OpenGL name of the texture at the given index. NOTE: This is not portable to PC.␍
OVR_VRAPI_EXPORT void vrapi_SetTextureSwapChainHandle( ovrTextureSwapChain * chain, int index, unsigned int handle );
//-----------------------------------------------------------------␍
// Frame Submission␍
//-----------------------------------------------------------------␍
// Accepts new eye images plus poses that will be used for future warps.␍
// The parms are copied, and are not referenced after the function returns.␍
//␍
// This will block until the textures from the previous vrapi_SubmitFrame() have been␍
// consumed by the background thread, to allow one frame of overlap for maximum␍
// GPU utilization, while preventing multiple frames from piling up variable latency.␍
//␍
// This will block until at least MinimumVsyncs have passed since the last␍
// call to vrapi_SubmitFrame() to prevent applications with simple scenes from␍
// generating completely wasted frames.␍
//␍
// IMPORTANT: any dynamic textures that are passed to vrapi_SubmitFrame() must be␍
// triple buffered to avoid flickering and performance problems.␍
//␍
// Note that the config used by the calling thread must have an EGL_SURFACE_TYPE␍
// with EGL_WINDOW_BIT so textures can be shared with the background thread.␍
//␍
// Must be called from the thread with the OpenGL ES context current that was␍
// used to render the eye images, but drawing does not need to be completed.␍
// A sync object will be added to the current context so the background␍
// thread can know when rendering of the eye images has completed.␍
OVR_VRAPI_EXPORT void vrapi_SubmitFrame( ovrMobile * ovr, const ovrFrameParms * parms );
#if defined( __cplusplus )␍
} // extern "C"␍
#endif␍
#endif // OVR_VrApi_h␍
+37
View File
@@ -0,0 +1,37 @@
/************************************************************************************␍
Filename : VrApi_Config.h␍
Content : VrApi preprocessor settings␍
Created : April 23, 2015␍
Authors : James Dolan␍
Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved.␍
*************************************************************************************/
#ifndef OVR_VrApi_Config_h␍
#define OVR_VrApi_Config_h␍
#if defined( _MSC_VER ) || defined( __ICL )␍
#if defined( OVR_VRAPI_ENABLE_EXPORT )␍
#define OVR_VRAPI_EXPORT __declspec(dllexport)␍
#else␍
#define OVR_VRAPI_EXPORT␍
#endif␍
#define OVR_VRAPI_DEPRECATED __declspec(deprecated)␍
#else␍
#if defined( OVR_VRAPI_ENABLE_EXPORT )␍
#define OVR_VRAPI_EXPORT __attribute__((__visibility__("default")))␍
#else␍
#define OVR_VRAPI_EXPORT ␍
#endif␍
#define OVR_VRAPI_DEPRECATED __attribute__ ((deprecated))␍
#endif␍
#endif // !OVR_VrApi_Config_h␍
+640
View File
@@ -0,0 +1,640 @@
/************************************************************************************␍
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( &centerEyeTranslation, &centerEyeRotation );
return ( input == NULL ) ? centerEyeTransform : ovrMatrix4f_Multiply( input, &centerEyeTransform );
}
// 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( &centerEyeTransform );
}
// 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␍
@@ -0,0 +1,84 @@
/************************************************************************************␍
Filename : VrApi_LocalPrefs.h␍
Content : Interface for device-local preferences␍
Created : July 8, 2014␍
Authors : John Carmack␍
Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved.␍
*************************************************************************************/
#ifndef OVR_VrApi_LocalPrefs_h␍
#define OVR_VrApi_LocalPrefs_h␍
#include "VrApi_Config.h"␍
#include "VrApi_Types.h"␍
#if defined( __cplusplus )␍
extern "C" {
#endif␍
// Local preferences are for storing platform-wide settings that are tied to␍
// a device instead of an application or user.␍
// Initially this is just a set of strings stored to /sdcard/.oculusprefs, but it␍
// may move to some other database.␍
//␍
// While it is here, you can easily set one or more values with adb like this:␍
// adb shell "echo dev_enableCapture 1 > /sdcard/.oculusprefs"␍
//␍
// The key / value pairs are just alternate tokens, with no newline required, so␍
// you can set multiple values at once:␍
//␍
// adb shell "echo dev_enableCapture 1 dev_powerLevelState 1 > /sdcard/.oculusprefs"␍
// Enable support for Oculus Remote Monitor to connect to the application.␍
#define LOCAL_PREF_VRAPI_ENABLE_CAPTURE "dev_enableCapture" // "0" or "1"␍
// Use the provided cpu and gpu levels for setting␍
// fixed clock levels.␍
#define LOCAL_PREF_VRAPI_CPU_LEVEL "dev_cpuLevel" // "0", "1", "2", or "3"␍
#define LOCAL_PREF_VRAPI_GPU_LEVEL "dev_gpuLevel" // "0", "1", "2", or "3"␍
// Shipping applications will always want this on, but if you want to draw␍
// directly to the screen for debug tasks, you can run synchronously so the␍
// init thread is still current on the window.␍
#define LOCAL_PREF_VRAPI_ASYNC_TIMEWARP "dev_asyncTimewarp" // "0" or "1"␍
// Optionally force a specific MinimumVsyncs.␍
#define LOCAL_PREF_VRAPI_MINIMUM_VSYNCS "dev_mimumumVsyncs" // "0", "1", "2", "3"␍
// Optionally force a specific extra latency mode.␍
#define LOCAL_PREF_VRAPI_EXTRA_LATENCY_MODE "dev_extraLatencyMode" // "0" = off, "1" = on, "2" = dynamic␍
// For video capture or testing on reference platforms without direct frontbuffer␍
// rendering, direct frontbuffer can be forced off.␍
#define LOCAL_PREF_VRAPI_FRONTBUFFER "dev_frontbuffer" // "0" or "1"␍
// Optional distortion file to override built-in distortion.␍
#define LOCAL_PREF_VRAPI_DISTORTION_FILE_NAME "dev_distortionFileName" // default = ""␍
// Experimental feature that clips the distortion mesh to reduce GPU fill for timewarp+distortion␍
#define LOCAL_PREF_VRAPI_CLIP_DISTORTION_MESH "dev_clipDistortionMesh" // "0" = off, "1" = clip to optics, "2" = clip to framebuffer␍
// Debug option to draw the axis lines after warp.␍
#define LOCAL_PREF_VRAPI_DRAW_CALIBRATION_LINES "dev_drawCalibrationLines" // "0" or "1"␍
#define LOCAL_PREF_VRAPI_GPU_TIMINGS "dev_gpuTimings" // "0" = off, "1" = glBeginQuery/glEndQuery, "2" = glQueryCounter␍
#define LOCAL_PREF_APP_DEBUG_OPTIONS "dev_debugOptions" // "0" or "1"␍
#define LOCAL_PREF_VRAPI_SIMULATE_UNDOCK "dev_simulateUndock" // time to wait before simulating an undock event, < 0 means don't simulate␍
// Query the in-memory preferences for a (case insensitive) key / value pair.␍
// If the returned string is not defaultKeyValue, it will remain valid until the next ovr_UpdateLocalPreferences().␍
OVR_VRAPI_EXPORT const char * ovr_GetLocalPreferenceValueForKey( const char * keyName, const char * defaultKeyValue );
// Updates the in-memory data and synchronously writes it to storage.␍
OVR_VRAPI_EXPORT void ovr_SetLocalPreferenceValueForKey( const char * keyName, const char * keyValue );
#if defined( __cplusplus )␍
} // extern "C"␍
#endif␍
#endif // OVR_VrApi_LocalPrefs_h␍
+530
View File
@@ -0,0 +1,530 @@
/************************************************************************************␍
Filename : VrApi_Types.h␍
Content : Types for minimum necessary API for mobile VR␍
Created : April 30, 2015␍
Authors : J.M.P. van Waveren␍
Copyright : Copyright 2015 Oculus VR, LLC. All Rights reserved.␍
*************************************************************************************/
#ifndef OVR_VrApi_Types_h␍
#define OVR_VrApi_Types_h␍
#include <stdbool.h>␍
#include "VrApi_Config.h" // needed for VRAPI_EXPORT␍
//-----------------------------------------------------------------␍
// Java␍
//-----------------------------------------------------------------␍
#if defined( ANDROID )␍
#include <jni.h>␍
#elif defined( __cplusplus )␍
typedef struct _JNIEnv JNIEnv;
typedef struct _JavaVM JavaVM;
typedef class _jobject * jobject;
#else␍
typedef const struct JNINativeInterface * JNIEnv;
typedef const struct JNIInvokeInterface * JavaVM;
void * jobject;
#endif␍
typedef struct
{
JavaVM * Vm; // Java Virtual Machine␍
JNIEnv * Env; // Thread specific environment␍
jobject ActivityObject; // Java activity object␍
} ovrJava;
//-----------------------------------------------------------------␍
// Basic Types␍
//-----------------------------------------------------------------␍
typedef struct ovrVector3f_
{
float x, y, z;
} ovrVector3f;
// Quaternion.␍
typedef struct ovrQuatf_
{
float x, y, z, w;
} ovrQuatf;
// Row-major 4x4 matrix.␍
typedef struct ovrMatrix4f_
{
float M[4][4];
} ovrMatrix4f;
// Position and orientation together.␍
typedef struct ovrPosef_
{
ovrQuatf Orientation;
ovrVector3f Position;
} ovrPosef;
typedef struct ovrRectf_
{
float x;
float y;
float width;
float height;
} ovrRectf;
typedef enum
{
VRAPI_FALSE = 0,
VRAPI_TRUE
} ovrBooleanResult;
//-----------------------------------------------------------------␍
// Structure Types␍
//-----------------------------------------------------------------␍
typedef enum
{
VRAPI_STRUCTURE_TYPE_INIT_PARMS = 1,
VRAPI_STRUCTURE_TYPE_MODE_PARMS = 2,
VRAPI_STRUCTURE_TYPE_FRAME_PARMS = 3,
} ovrStructureType;
//-----------------------------------------------------------------␍
// System Properties and Status␍
//-----------------------------------------------------------------␍
typedef enum
{
VRAPI_DEVICE_TYPE_NOTE4,
VRAPI_DEVICE_TYPE_NOTE5,
VRAPI_DEVICE_TYPE_S6,
VRAPI_MAX_DEVICE_TYPES
} ovrDeviceType;
typedef enum
{
VRAPI_SYS_PROP_DEVICE_TYPE,
VRAPI_SYS_PROP_MAX_FULLSPEED_FRAMEBUFFER_SAMPLES,
// Physical width and height of the display in pixels.␍
VRAPI_SYS_PROP_DISPLAY_PIXELS_WIDE,
VRAPI_SYS_PROP_DISPLAY_PIXELS_HIGH,
// Refresh rate of the display in cycles per second.␍
// Currently 60Hz.␍
VRAPI_SYS_PROP_DISPLAY_REFRESH_RATE,
// With a display resolution of 2560x1440, the pixels at the center␍
// of each eye cover about 0.06 degrees of visual arc. To wrap a␍
// full 360 degrees, about 6000 pixels would be needed and about one␍
// quarter of that would be needed for ~90 degrees FOV. As such, Eye␍
// images with a resolution of 1536x1536 result in a good 1:1 mapping␍
// in the center, but they need mip-maps for off center pixels. To␍
// avoid the need for mip-maps and for significantly improved rendering␍
// performance this currently returns a conservative 1024x1024.␍
VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH,
VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT,
// This is a product of the lens distortion and the screen size,␍
// but there is no truly correct answer.␍
// There is a tradeoff in resolution and coverage.␍
// Too small of an FOV will leave unrendered pixels visible, but too␍
// large wastes resolution or fill rate. It is unreasonable to␍
// increase it until the corners are completely covered, but we do␍
// want most of the outside edges completely covered.␍
// Applications might choose to render a larger FOV when angular␍
// acceleration is high to reduce black pull in at the edges by␍
// the time warp.␍
// Currently symmetric 90.0 degrees.␍
VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X, // Horizontal field of view in degrees␍
VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y, // Vertical field of view in degrees␍
} ovrSystemProperty;
typedef enum
{
VRAPI_SYS_STATUS_DOCKED, // Device is docked.␍
VRAPI_SYS_STATUS_MOUNTED, // Device is mounted.␍
VRAPI_SYS_STATUS_THROTTLED, // Device is in powersave mode.␍
VRAPI_SYS_STATUS_THROTTLED2, // Device is in extreme powersave mode.␍
VRAPI_SYS_STATUS_THROTTLED_WARNING_LEVEL, // Powersave mode warning required.␍
VRAPI_SYS_STATUS_RENDER_LATENCY_MILLISECONDS, // Average time between render tracking sample and scanout.␍
VRAPI_SYS_STATUS_TIMEWARP_LATENCY_MILLISECONDS, // Average time between timewarp tracking sample and scanout.␍
VRAPI_SYS_STATUS_SCANOUT_LATENCY_MILLISECONDS, // Average time between Vsync and scanout.␍
VRAPI_SYS_STATUS_APP_FRAMES_PER_SECOND, // Number of frames per second delivered through vrapi_SubmitFrame.␍
VRAPI_SYS_STATUS_SCREEN_TEARS_PER_SECOND, // Number of screen tears per second (per eye).␍
VRAPI_SYS_STATUS_EARLY_FRAMES_PER_SECOND, // Number of frames per second delivered a whole display refresh early.␍
VRAPI_SYS_STATUS_STALE_FRAMES_PER_SECOND, // Number of frames per second delivered late.␍
} ovrSystemStatus;
//-----------------------------------------------------------------␍
// Initialization␍
//-----------------------------------------------------------------␍
typedef enum
{
VRAPI_INITIALIZE_SUCCESS = 0,
VRAPI_INITIALIZE_UNKNOWN_ERROR = -1,
VRAPI_INITIALIZE_PERMISSIONS_ERROR = -2,
} ovrInitializeStatus;
typedef enum
{
VRAPI_GRAPHICS_API_OPENGL_ES_2 = ( 0x10000 | 0x0200 ), // OpenGL ES 2.x context␍
VRAPI_GRAPHICS_API_OPENGL_ES_3 = ( 0x10000 | 0x0300 ), // OpenGL ES 3.x context␍
VRAPI_GRAPHICS_API_OPENGL_COMPAT = ( 0x20000 | 0x0100 ), // OpenGL Compatibility Profile␍
VRAPI_GRAPHICS_API_OPENGL_CORE_3 = ( 0x20000 | 0x0300 ), // OpenGL Core Profile 3.x␍
VRAPI_GRAPHICS_API_OPENGL_CORE_4 = ( 0x20000 | 0x0400 ), // OpenGL Core Profile 4.x␍
} ovrGraphicsAPI;
typedef struct
{
ovrStructureType Type;
int ProductVersion;
int MajorVersion;
int MinorVersion;
int PatchVersion;
ovrGraphicsAPI GraphicsAPI;
ovrJava Java;
} ovrInitParms;
//-----------------------------------------------------------------␍
// VR Mode␍
//-----------------------------------------------------------------␍
typedef struct
{
ovrStructureType Type;
// If true, warn and allow the app to continue at 30fps when␍
// throttling occurs.␍
// If false, display the level 2 error message which requires␍
// the user to undock.␍
bool AllowPowerSave;
// When an application with multiple activities moves backwards on␍
// the activity stack, the activity window it returns to is no longer␍
// flagged as fullscreen. As a result, Android will also render␍
// the decor view, which wastes a significant amount of bandwidth.␍
// By setting this flag, the fullscreen flag is reset on the window.␍
// Unfortunately, this causes Android life cycle events that mess up␍
// several NativeActivity codebases like Stratum and UE4, so this␍
// flag should only be set for select applications with multiple␍
// activities. Use "adb shell dumpsys SurfaceFlinger" to verify␍
// that there is only one HWC next to the FB_TARGET.␍
bool ResetWindowFullscreen;
// The Java VM is needed for the time warp thread to create a Java environment.␍
// A Java environment is needed to access various system services. The thread␍
// that enters VR mode is responsible for attaching and detaching the Java␍
// environment. The Java Activity object is needed to get the windowManager,␍
// packageName, systemService, etc.␍
ovrJava Java;
// If not zero, then use this display for asynchronous time warp rendering.␍
// Using EGL this is an EGLDisplay.␍
unsigned long long Display;
// If not zero, then use this window surface for asynchronous time warp rendering␍
// This is expected to be the front buffer.␍
// Using EGL this is an EGLSurface.␍
unsigned long long WindowSurface;
// If not zero, then resources from this context will be shared␍
// with the asynchronous time warp.␍
// Using EGL this is an EGLContext.␍
unsigned long long ShareContext;
} ovrModeParms;
// VR context␍
// To allow multiple Android activities that live in the same address space␍
// to cooperatively use the VrApi, each activity needs to maintain its own␍
// separate contexts for a lot of the video related systems.␍
typedef struct ovrMobile ovrMobile;
//-----------------------------------------------------------------␍
// Tracking␍
//-----------------------------------------------------------------␍
// Full rigid body pose with first and second derivatives.␍
typedef struct ovrRigidBodyPosef_
{
ovrPosef Pose;
ovrVector3f AngularVelocity;
ovrVector3f LinearVelocity;
ovrVector3f AngularAcceleration;
ovrVector3f LinearAcceleration;
double TimeInSeconds; // Absolute time of this pose.␍
double PredictionInSeconds; // Seconds this pose was predicted ahead.␍
} ovrRigidBodyPosef;
// Bit flags describing the current status of sensor tracking.␍
typedef enum
{
VRAPI_TRACKING_STATUS_ORIENTATION_TRACKED = 0x0001, // Orientation is currently tracked.␍
VRAPI_TRACKING_STATUS_POSITION_TRACKED = 0x0002, // Position is currently tracked.␍
VRAPI_TRACKING_STATUS_HMD_CONNECTED = 0x0080 // HMD is available & connected.␍
} ovrTrackingStatus;
// Tracking state at a given absolute time.␍
typedef struct ovrTracking_
{
// Sensor status described by ovrTrackingStatus flags.␍
unsigned int Status;
// Predicted head configuration at the requested absolute time.␍
// The pose describes the head orientation and center eye position.␍
ovrRigidBodyPosef HeadPose;
} ovrTracking;
//-----------------------------------------------------------------␍
// Texture Swap Chain␍
//-----------------------------------------------------------------␍
typedef enum
{
VRAPI_TEXTURE_TYPE_2D, // 2D textures.␍
VRAPI_TEXTURE_TYPE_2D_EXTERNAL, // External 2D texture.␍
VRAPI_TEXTURE_TYPE_2D_ARRAY, // Texture array.␍
VRAPI_TEXTURE_TYPE_CUBE, // Cube maps.␍
VRAPI_TEXTURE_TYPE_MAX
} ovrTextureType;
typedef enum
{
VRAPI_TEXTURE_FORMAT_NONE,
VRAPI_TEXTURE_FORMAT_565,
VRAPI_TEXTURE_FORMAT_5551,
VRAPI_TEXTURE_FORMAT_4444,
VRAPI_TEXTURE_FORMAT_8888,
VRAPI_TEXTURE_FORMAT_8888_sRGB,
VRAPI_TEXTURE_FORMAT_RGBA16F,
VRAPI_TEXTURE_FORMAT_DEPTH_16,
VRAPI_TEXTURE_FORMAT_DEPTH_24,
VRAPI_TEXTURE_FORMAT_DEPTH_24_STENCIL_8,
} ovrTextureFormat;
typedef enum
{
VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_BLACK = 0x1,
VRAPI_DEFAULT_TEXTURE_SWAPCHAIN_LOADING_ICON = 0x2
} ovrDefaultTextureSwapChain;
typedef enum
{
VRAPI_TEXTURE_SWAPCHAIN_FULL_MIP_CHAIN = -1
} ovrTextureSwapChainSettings;
typedef struct ovrTextureSwapChain ovrTextureSwapChain;
//-----------------------------------------------------------------␍
// Frame Submission␍
//-----------------------------------------------------------------␍
typedef enum
{
// To get gamma correct sRGB filtering of the eye textures, the textures must be␍
// allocated with GL_SRGB8_ALPHA8 format and the window surface must be allocated␍
// with these attributes:␍
// EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR␍
//␍
// While we can reallocate textures easily enough, we can't change the window␍
// colorspace without relaunching the entire application, so if you want to␍
// be able to toggle between gamma correct and incorrect, you must allocate␍
// the framebuffer as sRGB, then inhibit that processing when using normal␍
// textures.␍
VRAPI_FRAME_FLAG_INHIBIT_SRGB_FRAMEBUFFER = 1,
// Flush the warp swap pipeline so the images show up immediately.␍
// This is expensive and should only be used when an immediate transition␍
// is needed like displaying black when resetting the HMD orientation.␍
VRAPI_FRAME_FLAG_FLUSH = 2,
// This is the final frame. Do not accept any more frames after this.␍
VRAPI_FRAME_FLAG_FINAL = 4,
// Display continuously changing graph of TimeWarp timing data. By default,␍
// this will display the start and end times of the draw.␍
VRAPI_FRAME_FLAG_TIMEWARP_DEBUG_GRAPH_SHOW = 8,
// Continue to display the timing data, but no new data is collected and displayed.␍
VRAPI_FRAME_FLAG_TIMEWARP_DEBUG_GRAPH_FREEZE = 16,
// Change the TimeWarp graph to display the latency (seconds from eye buffer␍
// orientation time) instead of the draw times.␍
VRAPI_FRAME_FLAG_TIMEWARP_DEBUG_GRAPH_LATENCY_MODE = 32,
} ovrFrameFlags;
typedef enum
{
// Enable writing to the alpha channel␍
VRAPI_FRAME_LAYER_FLAG_WRITE_ALPHA = 1,
// Correct for chromatic aberration. Quality/perf trade-off.␍
VRAPI_FRAME_LAYER_FLAG_CHROMATIC_ABERRATION_CORRECTION = 2,
// Used for some HUDs, but generally considered bad practice.␍
VRAPI_FRAME_LAYER_FLAG_FIXED_TO_VIEW = 4,
// Spin the layer - for loading icons␍
VRAPI_FRAME_LAYER_FLAG_SPIN = 8,
} ovrFrameLayerFlags;
typedef enum
{
VRAPI_FRAME_LAYER_EYE_LEFT,
VRAPI_FRAME_LAYER_EYE_RIGHT,
VRAPI_FRAME_LAYER_EYE_MAX
} ovrFrameLayerEye;
typedef enum
{
VRAPI_FRAME_LAYER_BLEND_ZERO,
VRAPI_FRAME_LAYER_BLEND_ONE,
VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA,
VRAPI_FRAME_LAYER_BLEND_DST_ALPHA,
VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_DST_ALPHA,
VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA
} ovrFrameLayerBlend;
typedef enum
{
VRAPI_FRAME_LAYER_TYPE_WORLD,
VRAPI_FRAME_LAYER_TYPE_OVERLAY,
VRAPI_FRAME_LAYER_TYPE_CURSOR,
VRAPI_FRAME_LAYER_TYPE_USER,
VRAPI_FRAME_LAYER_TYPE_MAX
} ovrFrameLayerType;
typedef enum
{
VRAPI_EXTRA_LATENCY_MODE_OFF,
VRAPI_EXTRA_LATENCY_MODE_ON,
VRAPI_EXTRA_LATENCY_MODE_DYNAMIC
} ovrExtraLatencyMode;
// Note that any layer textures that are dynamic must be triple buffered.␍
typedef struct
{
// Because OpenGL ES does not support clampToBorder, it is the␍
// application's responsibility to make sure that all mip levels␍
// of the primary eye texture have a black border that will show␍
// up when time warp pushes the texture partially off screen.␍
ovrTextureSwapChain * ColorTextureSwapChain;
// The depth texture is optional for positional time warp.␍
ovrTextureSwapChain * DepthTextureSwapChain;
// Index to the texture from the set that should be displayed.␍
int TextureSwapChainIndex;
// Points on the screen are mapped by a distortion correction␍
// function into ( TanX, TanY, -1, 1 ) vectors that are transformed␍
// by this matrix to get ( S, T, Q, _ ) vectors that are looked␍
// up with texture2dproj() to get texels.␍
ovrMatrix4f TexCoordsFromTanAngles;
// Only texels within this range should be drawn.␍
// This is a sub-rectangle of the [(0,0)-(1,1)] texture coordinate range.␍
ovrRectf TextureRect;
// The tracking state for which ModelViewMatrix is correct.␍
// It is ok to update the orientation for each eye, which␍
// can help minimize black edge pull-in, but the position␍
// must remain the same for both eyes, or the position would␍
// seem to judder "backwards in time" if a frame is dropped.␍
ovrRigidBodyPosef HeadPose;
// If not zero, this fence will be used to determine whether or not␍
// rendering to the color and depth texture swap chains has completed.␍
unsigned long long CompletionFence;
} ovrFrameLayerTexture;
typedef struct
{
// Image used for each eye.␍
ovrFrameLayerTexture Textures[VRAPI_FRAME_LAYER_EYE_MAX];
// Program-specific tuning values.␍
float ProgramParms[4];
// Layer blend function.␍
ovrFrameLayerBlend SrcBlend;
ovrFrameLayerBlend DstBlend;
// Combination of ovrFrameLayerFlags flags.␍
int Flags;
} ovrFrameLayer;
typedef struct
{
// These are fixed clock levels in the range [0, 3].␍
int CpuLevel;
int GpuLevel;
// These threads will get SCHED_FIFO.␍
int MainThreadTid;
int RenderThreadTid;
} ovrPerformanceParms;
typedef struct
{
ovrStructureType Type;
// Layers composited in the time warp.␍
ovrFrameLayer Layers[VRAPI_FRAME_LAYER_TYPE_MAX];
int LayerCount;
// Combination of ovrFrameFlags flags.␍
int Flags;
// Application controlled frame index that uniquely identifies this particular frame.␍
// This must be the same frame index that was passed to vrapi_GetPredictedDisplayTime()␍
// when synthesis of this frame started.␍
long long FrameIndex;
// WarpSwap will not return until at least this many V-syncs have␍
// passed since the previous WarpSwap returned.␍
// Setting to 2 will reduce power consumption and may make animation␍
// more regular for applications that can't hold full frame rate.␍
int MinimumVsyncs;
// Latency Mode.␍
ovrExtraLatencyMode ExtraLatencyMode;
// Rotation from a joypad can be added on generated frames to reduce␍
// judder in FPS style experiences when the application framerate is␍
// lower than the V-sync rate.␍
// This will be applied to the view space distorted␍
// eye vectors before applying the rest of the time warp.␍
// This will only be added when the same ovrFrameParms is used for␍
// more than one V-sync.␍
ovrMatrix4f ExternalVelocity;
// jobject that will be updated before each eye for minimal␍
// latency.␍
// IMPORTANT: This should be a JNI weak reference to the object.␍
// The system will try to convert it into a global reference before␍
// calling SurfaceTexture->Update, which allows it to be safely␍
// freed by the application.␍
jobject SurfaceTextureObject;
// CPU/GPU performance parameters.␍
ovrPerformanceParms PerformanceParms;
// For handling HMD events and power level state changes.␍
ovrJava Java;
} ovrFrameParms;
//-----------------------------------------------------------------␍
// Head Model␍
//-----------------------------------------------------------------␍
typedef struct
{
float InterpupillaryDistance; // Distance between eyes.␍
float EyeHeight; // Eye height relative to the ground.␍
float HeadModelDepth; // Eye offset forward from the head center at EyeHeight.␍
float HeadModelHeight; // Neck joint offset down from the head center at EyeHeight.␍
} ovrHeadModelParms;
//-----------------------------------------------------------------␍
// FIXME:VRAPI remove this once all simulation code uses VrFrame::PredictedDisplayTimeInSeconds and perf timing uses LOGCPUTIME␍
//-----------------------------------------------------------------␍
#if defined( __cplusplus )␍
extern "C" {
#endif␍
OVR_VRAPI_EXPORT double vrapi_GetTimeInSeconds();
#if defined( __cplusplus )␍
} // extern "C"␍
#endif␍
#endif // OVR_VrApi_Types_h␍
+29
View File
@@ -0,0 +1,29 @@
/************************************************************************************␍
Filename : VrApi_Version.h␍
Content : API version␍
Copyright : Copyright 2015 Oculus VR, LLC. All Rights reserved.␍
*************************************************************************************/
#ifndef OVR_VrApi_Version_h␍
#define OVR_VrApi_Version_h␍
// At some point we will transition to product version 1 ␍
// and reset the major version back to 1 (first product release, version 1.0).␍
#define VRAPI_PRODUCT_VERSION 1␍
#define VRAPI_MAJOR_VERSION 0␍
#define VRAPI_MINOR_VERSION 0␍
#define VRAPI_PATCH_VERSION 0␍
// Internal build identifier␍
#define VRAPI_BUILD_VERSION 119482␍
// Internal build description␍
#define VRAPI_BUILD_DESCRIPTION ""␍
// Minimum version of the driver required for this API␍
#define VRAPI_DRIVER_VERSION 16693008␍
#endif // OVR_VrApi_Version_h␍