2020-09-08 22:10:45 +00:00
|
|
|
/************************************************************************************
|
|
|
|
|
|
|
|
Filename : Q2VR_SurfaceView.c based on VrCubeWorld_SurfaceView.c
|
|
|
|
Content : This sample uses a plain Android SurfaceView and handles all
|
|
|
|
Activity and Surface life cycle events in native code. This sample
|
|
|
|
does not use the application framework and also does not use LibOVR.
|
|
|
|
This sample only uses the VrApi.
|
|
|
|
Created : March, 2015
|
|
|
|
Authors : J.M.P. van Waveren / Simon Brown
|
|
|
|
|
|
|
|
Copyright : Copyright 2015 Oculus VR, LLC. All Rights reserved.
|
|
|
|
|
|
|
|
*************************************************************************************/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <sys/prctl.h> // for prctl( PR_SET_NAME )
|
|
|
|
#include <android/log.h>
|
|
|
|
#include <android/native_window_jni.h> // for native window JNI
|
|
|
|
#include <android/input.h>
|
|
|
|
|
|
|
|
#include "argtable3.h"
|
|
|
|
#include "VrInput.h"
|
|
|
|
|
|
|
|
#include <EGL/egl.h>
|
|
|
|
#include <EGL/eglext.h>
|
|
|
|
#include <GLES3/gl3.h>
|
|
|
|
#include <GLES3/gl3ext.h>
|
|
|
|
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <SDL2/SDL_main.h>
|
2020-09-11 21:41:18 +00:00
|
|
|
#include <SDL2/SDL_mutex.h>
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
#include "VrApi_Helpers.h"
|
|
|
|
#include "VrApi_SystemUtils.h"
|
|
|
|
#include "VrApi_Input.h"
|
|
|
|
#include "VrCompositor.h"
|
|
|
|
#include "VrInput.h"
|
2020-09-09 22:16:24 +00:00
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
#include "../../../../../../VrApi/Include/VrApi_Types.h"
|
|
|
|
#include "../../../../../../VrApi/Include/VrApi.h"
|
|
|
|
|
|
|
|
|
|
|
|
//Define all variables here that were externs in the VrCommon.h
|
2020-09-08 22:21:43 +00:00
|
|
|
bool Doom3Quest_initialised;
|
2020-09-08 22:10:45 +00:00
|
|
|
float playerYaw;
|
2020-10-14 18:14:31 +00:00
|
|
|
float vrFOV = 0.0f;
|
2020-09-08 22:10:45 +00:00
|
|
|
bool vr_moveuseoffhand;
|
|
|
|
float vr_snapturn_angle;
|
|
|
|
bool vr_switchsticks;
|
|
|
|
bool vr_secondarybuttonmappings;
|
|
|
|
bool vr_twohandedweapons;
|
|
|
|
bool shutdown;
|
|
|
|
|
|
|
|
#if !defined( EGL_OPENGL_ES3_BIT_KHR )
|
|
|
|
#define EGL_OPENGL_ES3_BIT_KHR 0x0040
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// EXT_texture_border_clamp
|
|
|
|
#ifndef GL_CLAMP_TO_BORDER
|
|
|
|
#define GL_CLAMP_TO_BORDER 0x812D
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef GL_TEXTURE_BORDER_COLOR
|
|
|
|
#define GL_TEXTURE_BORDER_COLOR 0x1004
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// Must use EGLSyncKHR because the VrApi still supports OpenGL ES 2.0
|
|
|
|
#define EGL_SYNC
|
|
|
|
|
|
|
|
#if defined EGL_SYNC
|
|
|
|
// EGL_KHR_reusable_sync
|
|
|
|
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
|
|
|
|
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
|
|
|
|
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
|
|
|
|
PFNEGLSIGNALSYNCKHRPROC eglSignalSyncKHR;
|
|
|
|
PFNEGLGETSYNCATTRIBKHRPROC eglGetSyncAttribKHR;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//Let's go to the maximum!
|
|
|
|
int CPU_LEVEL = 4;
|
|
|
|
int GPU_LEVEL = 4;
|
|
|
|
int NUM_MULTI_SAMPLES = 1;
|
|
|
|
int FFR = 0;
|
2020-09-15 23:02:29 +00:00
|
|
|
float SS_MULTIPLIER = 1.0f;
|
|
|
|
int DISPLAY_REFRESH = 60;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-15 23:02:29 +00:00
|
|
|
vrClientInfo vr;
|
|
|
|
vrClientInfo *pVRClientInfo;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
jclass clazz;
|
|
|
|
|
|
|
|
float radians(float deg) {
|
|
|
|
return (deg * M_PI) / 180.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
float degrees(float rad) {
|
|
|
|
return (rad * 180.0) / M_PI;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* global arg_xxx structs */
|
|
|
|
struct arg_dbl *ss;
|
|
|
|
struct arg_int *cpu;
|
|
|
|
struct arg_int *gpu;
|
|
|
|
struct arg_int *msaa;
|
|
|
|
struct arg_int *ffr;
|
|
|
|
struct arg_int *refresh;
|
|
|
|
struct arg_end *end;
|
|
|
|
|
|
|
|
char **argv;
|
|
|
|
int argc=0;
|
|
|
|
|
|
|
|
enum control_scheme {
|
|
|
|
RIGHT_HANDED_DEFAULT = 0,
|
2020-09-23 18:34:14 +00:00
|
|
|
LEFT_HANDED_DEFAULT = 10
|
2020-09-08 22:10:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
System Clock Time in millis
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
double GetTimeInMilliSeconds()
|
|
|
|
{
|
|
|
|
struct timespec now;
|
|
|
|
clock_gettime( CLOCK_MONOTONIC, &now );
|
|
|
|
return ( now.tv_sec * 1e9 + now.tv_nsec ) * (double)(1e-6);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-16 21:41:35 +00:00
|
|
|
bool forceVirtualScreen = false;
|
|
|
|
bool inMenu = false;
|
|
|
|
bool inGameGuiActive = false;
|
|
|
|
bool objectiveSystemActive = false;
|
|
|
|
bool inCinematic = false;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-16 21:41:35 +00:00
|
|
|
void Doom3Quest_setUseScreenLayer(int screen)
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
2020-09-16 21:41:35 +00:00
|
|
|
inMenu = screen & 0x1;
|
|
|
|
inGameGuiActive = !!(screen & 0x2);
|
|
|
|
objectiveSystemActive = !!(screen & 0x4);
|
|
|
|
inCinematic = !!(screen & 0x8);
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
bool Doom3Quest_useScreenLayer()
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
2020-09-26 10:30:46 +00:00
|
|
|
return inMenu || inCinematic || forceVirtualScreen;
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void UnEscapeQuotes( char *arg )
|
|
|
|
{
|
|
|
|
char *last = NULL;
|
|
|
|
while( *arg ) {
|
|
|
|
if( *arg == '"' && *last == '\\' ) {
|
|
|
|
char *c_curr = arg;
|
|
|
|
char *c_last = last;
|
|
|
|
while( *c_curr ) {
|
|
|
|
*c_last = *c_curr;
|
|
|
|
c_last = c_curr;
|
|
|
|
c_curr++;
|
|
|
|
}
|
|
|
|
*c_last = '\0';
|
|
|
|
}
|
|
|
|
last = arg;
|
|
|
|
arg++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ParseCommandLine(char *cmdline, char **argv)
|
|
|
|
{
|
|
|
|
char *bufp;
|
|
|
|
char *lastp = NULL;
|
|
|
|
int argc, last_argc;
|
|
|
|
argc = last_argc = 0;
|
|
|
|
for ( bufp = cmdline; *bufp; ) {
|
|
|
|
while ( isspace(*bufp) ) {
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
if ( *bufp == '"' ) {
|
|
|
|
++bufp;
|
|
|
|
if ( *bufp ) {
|
|
|
|
if ( argv ) {
|
|
|
|
argv[argc] = bufp;
|
|
|
|
}
|
|
|
|
++argc;
|
|
|
|
}
|
|
|
|
while ( *bufp && ( *bufp != '"' || *lastp == '\\' ) ) {
|
|
|
|
lastp = bufp;
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( *bufp ) {
|
|
|
|
if ( argv ) {
|
|
|
|
argv[argc] = bufp;
|
|
|
|
}
|
|
|
|
++argc;
|
|
|
|
}
|
|
|
|
while ( *bufp && ! isspace(*bufp) ) {
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( *bufp ) {
|
|
|
|
if ( argv ) {
|
|
|
|
*bufp = '\0';
|
|
|
|
}
|
|
|
|
++bufp;
|
|
|
|
}
|
|
|
|
if( argv && last_argc != argc ) {
|
|
|
|
UnEscapeQuotes( argv[last_argc] );
|
|
|
|
}
|
|
|
|
last_argc = argc;
|
|
|
|
}
|
|
|
|
if ( argv ) {
|
|
|
|
argv[argc] = NULL;
|
|
|
|
}
|
|
|
|
return(argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
OpenGL-ES Utility Functions
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2
|
|
|
|
bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp
|
|
|
|
} OpenGLExtensions_t;
|
|
|
|
|
|
|
|
OpenGLExtensions_t glExtensions;
|
|
|
|
|
|
|
|
static void EglInitExtensions()
|
|
|
|
{
|
|
|
|
#if defined EGL_SYNC
|
|
|
|
eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress( "eglCreateSyncKHR" );
|
|
|
|
eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress( "eglDestroySyncKHR" );
|
|
|
|
eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress( "eglClientWaitSyncKHR" );
|
|
|
|
eglSignalSyncKHR = (PFNEGLSIGNALSYNCKHRPROC) eglGetProcAddress( "eglSignalSyncKHR" );
|
|
|
|
eglGetSyncAttribKHR = (PFNEGLGETSYNCATTRIBKHRPROC) eglGetProcAddress( "eglGetSyncAttribKHR" );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const char * allExtensions = (const char *)glGetString( GL_EXTENSIONS );
|
|
|
|
if ( allExtensions != NULL )
|
|
|
|
{
|
|
|
|
glExtensions.multi_view = strstr( allExtensions, "GL_OVR_multiview2" ) &&
|
|
|
|
strstr( allExtensions, "GL_OVR_multiview_multisampled_render_to_texture" );
|
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
glExtensions.EXT_texture_border_clamp = strstr( allExtensions, "GL_EXT_texture_border_clamp" ) ||
|
|
|
|
strstr( allExtensions, "GL_OES_texture_border_clamp" );
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * EglErrorString( const EGLint error )
|
|
|
|
{
|
|
|
|
switch ( error )
|
|
|
|
{
|
|
|
|
case EGL_SUCCESS: return "EGL_SUCCESS";
|
|
|
|
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
|
|
|
|
case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
|
|
|
|
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
|
|
|
|
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
|
|
|
|
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
|
|
|
|
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
|
|
|
|
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
|
|
|
|
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
|
|
|
|
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
|
|
|
|
case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
|
|
|
|
case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
|
|
|
|
case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
|
|
|
|
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
|
|
|
|
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
|
|
|
|
default: return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * GlFrameBufferStatusString( GLenum status )
|
|
|
|
{
|
|
|
|
switch ( status )
|
|
|
|
{
|
|
|
|
case GL_FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED";
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED";
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
|
|
|
|
default: return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrEgl
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
EGLint MajorVersion;
|
|
|
|
EGLint MinorVersion;
|
|
|
|
EGLDisplay Display;
|
|
|
|
EGLConfig Config;
|
|
|
|
EGLSurface TinySurface;
|
|
|
|
EGLSurface MainSurface;
|
|
|
|
EGLContext Context;
|
|
|
|
} ovrEgl;
|
|
|
|
|
|
|
|
static void ovrEgl_Clear( ovrEgl * egl )
|
|
|
|
{
|
|
|
|
egl->MajorVersion = 0;
|
|
|
|
egl->MinorVersion = 0;
|
|
|
|
egl->Display = 0;
|
|
|
|
egl->Config = 0;
|
|
|
|
egl->TinySurface = EGL_NO_SURFACE;
|
|
|
|
egl->MainSurface = EGL_NO_SURFACE;
|
|
|
|
egl->Context = EGL_NO_CONTEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrEgl_CreateContext( ovrEgl * egl, const ovrEgl * shareEgl )
|
|
|
|
{
|
|
|
|
if ( egl->Display != 0 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
egl->Display = eglGetDisplay( EGL_DEFAULT_DISPLAY );
|
|
|
|
eglInitialize( egl->Display, &egl->MajorVersion, &egl->MinorVersion );
|
|
|
|
// Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample
|
|
|
|
// flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in
|
|
|
|
// settings, and that is completely wasted for our warp target.
|
|
|
|
const int MAX_CONFIGS = 1024;
|
|
|
|
EGLConfig configs[MAX_CONFIGS];
|
|
|
|
EGLint numConfigs = 0;
|
|
|
|
if ( eglGetConfigs( egl->Display, configs, MAX_CONFIGS, &numConfigs ) == EGL_FALSE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglGetConfigs() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const EGLint configAttribs[] =
|
|
|
|
{
|
|
|
|
EGL_RED_SIZE, 8,
|
|
|
|
EGL_GREEN_SIZE, 8,
|
|
|
|
EGL_BLUE_SIZE, 8,
|
|
|
|
EGL_ALPHA_SIZE, 8, // need alpha for the multi-pass timewarp compositor
|
|
|
|
EGL_DEPTH_SIZE, 24,
|
|
|
|
EGL_STENCIL_SIZE, 8,
|
|
|
|
EGL_SAMPLES, 0,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
egl->Config = 0;
|
|
|
|
for ( int i = 0; i < numConfigs; i++ )
|
|
|
|
{
|
|
|
|
EGLint value = 0;
|
|
|
|
|
|
|
|
eglGetConfigAttrib( egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value );
|
|
|
|
if ( ( value & EGL_OPENGL_ES3_BIT_KHR ) != EGL_OPENGL_ES3_BIT_KHR )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The pbuffer config also needs to be compatible with normal window rendering
|
|
|
|
// so it can share textures with the window context.
|
|
|
|
eglGetConfigAttrib( egl->Display, configs[i], EGL_SURFACE_TYPE, &value );
|
|
|
|
if ( ( value & ( EGL_WINDOW_BIT | EGL_PBUFFER_BIT ) ) != ( EGL_WINDOW_BIT | EGL_PBUFFER_BIT ) )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int j = 0;
|
|
|
|
for ( ; configAttribs[j] != EGL_NONE; j += 2 )
|
|
|
|
{
|
|
|
|
eglGetConfigAttrib( egl->Display, configs[i], configAttribs[j], &value );
|
|
|
|
if ( value != configAttribs[j + 1] )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( configAttribs[j] == EGL_NONE )
|
|
|
|
{
|
|
|
|
egl->Config = configs[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( egl->Config == 0 )
|
|
|
|
{
|
|
|
|
ALOGE( " eglChooseConfig() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
EGLint contextAttribs[] =
|
|
|
|
{
|
|
|
|
EGL_CONTEXT_CLIENT_VERSION, 3,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
ALOGV( " Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )" );
|
|
|
|
egl->Context = eglCreateContext( egl->Display, egl->Config, ( shareEgl != NULL ) ? shareEgl->Context : EGL_NO_CONTEXT, contextAttribs );
|
|
|
|
if ( egl->Context == EGL_NO_CONTEXT )
|
|
|
|
{
|
|
|
|
ALOGE( " eglCreateContext() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const EGLint surfaceAttribs[] =
|
|
|
|
{
|
|
|
|
EGL_WIDTH, 16,
|
|
|
|
EGL_HEIGHT, 16,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
ALOGV( " TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )" );
|
|
|
|
egl->TinySurface = eglCreatePbufferSurface( egl->Display, egl->Config, surfaceAttribs );
|
|
|
|
if ( egl->TinySurface == EGL_NO_SURFACE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglCreatePbufferSurface() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
eglDestroyContext( egl->Display, egl->Context );
|
|
|
|
egl->Context = EGL_NO_CONTEXT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ALOGV( " eglMakeCurrent( Display, TinySurface, TinySurface, Context )" );
|
|
|
|
if ( eglMakeCurrent( egl->Display, egl->TinySurface, egl->TinySurface, egl->Context ) == EGL_FALSE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglMakeCurrent() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
eglDestroySurface( egl->Display, egl->TinySurface );
|
|
|
|
eglDestroyContext( egl->Display, egl->Context );
|
|
|
|
egl->Context = EGL_NO_CONTEXT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrEgl_DestroyContext( ovrEgl * egl )
|
|
|
|
{
|
|
|
|
if ( egl->Display != 0 )
|
|
|
|
{
|
|
|
|
ALOGE( " eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )" );
|
|
|
|
if ( eglMakeCurrent( egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ) == EGL_FALSE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglMakeCurrent() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( egl->Context != EGL_NO_CONTEXT )
|
|
|
|
{
|
|
|
|
ALOGE( " eglDestroyContext( Display, Context )" );
|
|
|
|
if ( eglDestroyContext( egl->Display, egl->Context ) == EGL_FALSE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglDestroyContext() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
}
|
|
|
|
egl->Context = EGL_NO_CONTEXT;
|
|
|
|
}
|
|
|
|
if ( egl->TinySurface != EGL_NO_SURFACE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglDestroySurface( Display, TinySurface )" );
|
|
|
|
if ( eglDestroySurface( egl->Display, egl->TinySurface ) == EGL_FALSE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglDestroySurface() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
}
|
|
|
|
egl->TinySurface = EGL_NO_SURFACE;
|
|
|
|
}
|
|
|
|
if ( egl->Display != 0 )
|
|
|
|
{
|
|
|
|
ALOGE( " eglTerminate( Display )" );
|
|
|
|
if ( eglTerminate( egl->Display ) == EGL_FALSE )
|
|
|
|
{
|
|
|
|
ALOGE( " eglTerminate() failed: %s", EglErrorString( eglGetError() ) );
|
|
|
|
}
|
|
|
|
egl->Display = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrFramebuffer
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static void ovrFramebuffer_Clear( ovrFramebuffer * frameBuffer )
|
|
|
|
{
|
|
|
|
frameBuffer->Width = 0;
|
|
|
|
frameBuffer->Height = 0;
|
|
|
|
frameBuffer->Multisamples = 0;
|
|
|
|
frameBuffer->TextureSwapChainLength = 0;
|
|
|
|
frameBuffer->ProcessingTextureSwapChainIndex = 0;
|
|
|
|
frameBuffer->ReadyTextureSwapChainIndex = 0;
|
|
|
|
frameBuffer->ColorTextureSwapChain = NULL;
|
|
|
|
frameBuffer->DepthBuffers = NULL;
|
|
|
|
frameBuffer->FrameBuffers = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
|
|
|
typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
|
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
#if !defined(GL_OVR_multiview)
|
|
|
|
/// static const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR = 0x9630;
|
|
|
|
/// static const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR = 0x9632;
|
|
|
|
/// static const int GL_MAX_VIEWS_OVR = 0x9631;
|
|
|
|
typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(
|
|
|
|
GLenum target,
|
|
|
|
GLenum attachment,
|
|
|
|
GLuint texture,
|
|
|
|
GLint level,
|
|
|
|
GLint baseViewIndex,
|
|
|
|
GLsizei numViews);
|
|
|
|
#endif
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
#if !defined(GL_OVR_multiview_multisampled_render_to_texture)
|
|
|
|
typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(
|
|
|
|
GLenum target,
|
|
|
|
GLenum attachment,
|
|
|
|
GLuint texture,
|
|
|
|
GLint level,
|
|
|
|
GLsizei samples,
|
|
|
|
GLint baseViewIndex,
|
|
|
|
GLsizei numViews);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static bool ovrFramebuffer_Create(
|
|
|
|
ovrFramebuffer* frameBuffer,
|
|
|
|
const bool useMultiview,
|
|
|
|
const GLenum colorFormat,
|
|
|
|
const int width,
|
|
|
|
const int height,
|
|
|
|
const int multisamples) {
|
|
|
|
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT =
|
|
|
|
(PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)eglGetProcAddress(
|
|
|
|
"glRenderbufferStorageMultisampleEXT");
|
|
|
|
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT =
|
|
|
|
(PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress(
|
|
|
|
"glFramebufferTexture2DMultisampleEXT");
|
|
|
|
|
|
|
|
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR =
|
|
|
|
(PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress(
|
|
|
|
"glFramebufferTextureMultiviewOVR");
|
|
|
|
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR =
|
|
|
|
(PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)eglGetProcAddress(
|
|
|
|
"glFramebufferTextureMultisampleMultiviewOVR");
|
|
|
|
|
|
|
|
frameBuffer->Width = width;
|
2020-09-08 22:10:45 +00:00
|
|
|
frameBuffer->Height = height;
|
|
|
|
frameBuffer->Multisamples = multisamples;
|
2020-10-07 18:05:09 +00:00
|
|
|
frameBuffer->UseMultiview =
|
|
|
|
(useMultiview && (glFramebufferTextureMultiviewOVR != NULL)) ? true : false;
|
|
|
|
|
|
|
|
frameBuffer->ColorTextureSwapChain = vrapi_CreateTextureSwapChain3(
|
|
|
|
frameBuffer->UseMultiview ? VRAPI_TEXTURE_TYPE_2D_ARRAY : VRAPI_TEXTURE_TYPE_2D,
|
|
|
|
colorFormat,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
1,
|
|
|
|
3);
|
|
|
|
frameBuffer->TextureSwapChainLength =
|
|
|
|
vrapi_GetTextureSwapChainLength(frameBuffer->ColorTextureSwapChain);
|
|
|
|
frameBuffer->DepthBuffers =
|
|
|
|
(GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
|
|
|
frameBuffer->FrameBuffers =
|
|
|
|
(GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
|
|
|
|
|
|
|
ALOGV(" frameBuffer->UseMultiview = %d", frameBuffer->UseMultiview);
|
|
|
|
|
|
|
|
for (int i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
|
2020-09-08 22:10:45 +00:00
|
|
|
// Create the color buffer texture.
|
2020-10-07 18:05:09 +00:00
|
|
|
const GLuint colorTexture =
|
|
|
|
vrapi_GetTextureSwapChainHandle(frameBuffer->ColorTextureSwapChain, i);
|
|
|
|
GLenum colorTextureTarget = frameBuffer->UseMultiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
|
|
|
GL(glBindTexture(colorTextureTarget, colorTexture));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER));
|
|
|
|
GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
|
|
GL(glTexParameterfv(colorTextureTarget, GL_TEXTURE_BORDER_COLOR, borderColor));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
|
|
GL(glBindTexture(colorTextureTarget, 0));
|
|
|
|
|
|
|
|
if (frameBuffer->UseMultiview) {
|
|
|
|
// Create the depth buffer texture.
|
|
|
|
GL(glGenTextures(1, &frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glBindTexture(GL_TEXTURE_2D_ARRAY, frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH_COMPONENT24, width, height, 2));
|
|
|
|
GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0));
|
|
|
|
|
|
|
|
// Create the frame buffer.
|
|
|
|
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
|
|
|
|
if (multisamples > 1 && (glFramebufferTextureMultisampleMultiviewOVR != NULL)) {
|
|
|
|
GL(glFramebufferTextureMultisampleMultiviewOVR(
|
|
|
|
GL_DRAW_FRAMEBUFFER,
|
|
|
|
GL_DEPTH_ATTACHMENT,
|
|
|
|
frameBuffer->DepthBuffers[i],
|
|
|
|
0 /* level */,
|
|
|
|
multisamples /* samples */,
|
|
|
|
0 /* baseViewIndex */,
|
|
|
|
2 /* numViews */));
|
|
|
|
GL(glFramebufferTextureMultisampleMultiviewOVR(
|
|
|
|
GL_DRAW_FRAMEBUFFER,
|
|
|
|
GL_COLOR_ATTACHMENT0,
|
|
|
|
colorTexture,
|
|
|
|
0 /* level */,
|
|
|
|
multisamples /* samples */,
|
|
|
|
0 /* baseViewIndex */,
|
|
|
|
2 /* numViews */));
|
|
|
|
} else {
|
|
|
|
GL(glFramebufferTextureMultiviewOVR(
|
|
|
|
GL_DRAW_FRAMEBUFFER,
|
|
|
|
GL_DEPTH_ATTACHMENT,
|
|
|
|
frameBuffer->DepthBuffers[i],
|
|
|
|
0 /* level */,
|
|
|
|
0 /* baseViewIndex */,
|
|
|
|
2 /* numViews */));
|
|
|
|
GL(glFramebufferTextureMultiviewOVR(
|
|
|
|
GL_DRAW_FRAMEBUFFER,
|
|
|
|
GL_COLOR_ATTACHMENT0,
|
|
|
|
colorTexture,
|
|
|
|
0 /* level */,
|
|
|
|
0 /* baseViewIndex */,
|
|
|
|
2 /* numViews */));
|
|
|
|
}
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
|
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
|
|
|
if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
ALOGE(
|
|
|
|
"Incomplete frame buffer object: %s",
|
|
|
|
GlFrameBufferStatusString(renderFramebufferStatus));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (multisamples > 1 && glRenderbufferStorageMultisampleEXT != NULL &&
|
|
|
|
glFramebufferTexture2DMultisampleEXT != NULL) {
|
|
|
|
// Create multisampled depth buffer.
|
|
|
|
GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glRenderbufferStorageMultisampleEXT(
|
|
|
|
GL_RENDERBUFFER, multisamples, GL_DEPTH_COMPONENT24, width, height));
|
|
|
|
GL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
|
|
|
|
|
|
|
// Create the frame buffer.
|
|
|
|
// NOTE: glFramebufferTexture2DMultisampleEXT only works with GL_FRAMEBUFFER.
|
|
|
|
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glFramebufferTexture2DMultisampleEXT(
|
|
|
|
GL_FRAMEBUFFER,
|
|
|
|
GL_COLOR_ATTACHMENT0,
|
|
|
|
GL_TEXTURE_2D,
|
|
|
|
colorTexture,
|
|
|
|
0,
|
|
|
|
multisamples));
|
|
|
|
GL(glFramebufferRenderbuffer(
|
|
|
|
GL_FRAMEBUFFER,
|
|
|
|
GL_DEPTH_ATTACHMENT,
|
|
|
|
GL_RENDERBUFFER,
|
|
|
|
frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
|
|
GL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
|
|
|
if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
ALOGE(
|
|
|
|
"Incomplete frame buffer object: %s",
|
|
|
|
GlFrameBufferStatusString(renderFramebufferStatus));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Create depth buffer.
|
|
|
|
GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height));
|
|
|
|
GL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
|
|
|
|
|
|
|
// Create the frame buffer.
|
|
|
|
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glFramebufferRenderbuffer(
|
|
|
|
GL_DRAW_FRAMEBUFFER,
|
|
|
|
GL_DEPTH_ATTACHMENT,
|
|
|
|
GL_RENDERBUFFER,
|
|
|
|
frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glFramebufferTexture2D(
|
|
|
|
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0));
|
|
|
|
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
|
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
|
|
|
if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
ALOGE(
|
|
|
|
"Incomplete frame buffer object: %s",
|
|
|
|
GlFrameBufferStatusString(renderFramebufferStatus));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) {
|
|
|
|
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers));
|
|
|
|
if (frameBuffer->UseMultiview) {
|
|
|
|
GL(glDeleteTextures(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
|
|
|
|
} else {
|
|
|
|
GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
|
|
|
|
}
|
|
|
|
vrapi_DestroyTextureSwapChain(frameBuffer->ColorTextureSwapChain);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
free(frameBuffer->DepthBuffers);
|
|
|
|
free(frameBuffer->FrameBuffers);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
ovrFramebuffer_Clear(frameBuffer);
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_SetCurrent( ovrFramebuffer * frameBuffer )
|
|
|
|
{
|
|
|
|
GL( glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->ProcessingTextureSwapChainIndex] ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_SetNone()
|
|
|
|
{
|
|
|
|
GL( glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_Resolve( ovrFramebuffer * frameBuffer )
|
|
|
|
{
|
|
|
|
// Discard the depth buffer, so the tiler won't need to write it back out to memory.
|
|
|
|
const GLenum depthAttachment[1] = { GL_DEPTH_ATTACHMENT };
|
|
|
|
glInvalidateFramebuffer( GL_DRAW_FRAMEBUFFER, 1, depthAttachment );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_Advance( ovrFramebuffer * frameBuffer )
|
|
|
|
{
|
|
|
|
// Advance to the next texture from the set.
|
|
|
|
frameBuffer->ReadyTextureSwapChainIndex = frameBuffer->ProcessingTextureSwapChainIndex;
|
|
|
|
frameBuffer->ProcessingTextureSwapChainIndex = ( frameBuffer->ProcessingTextureSwapChainIndex + 1 ) % frameBuffer->TextureSwapChainLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ovrFramebuffer_ClearEdgeTexels( ovrFramebuffer * frameBuffer )
|
|
|
|
{
|
|
|
|
GL( glEnable( GL_SCISSOR_TEST ) );
|
|
|
|
GL( glViewport( 0, 0, frameBuffer->Width, frameBuffer->Height ) );
|
|
|
|
|
|
|
|
// Explicitly clear the border texels to black because OpenGL-ES does not support GL_CLAMP_TO_BORDER.
|
|
|
|
// Clear to fully opaque black.
|
|
|
|
GL( glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) );
|
|
|
|
|
|
|
|
// bottom
|
|
|
|
GL( glScissor( 0, 0, frameBuffer->Width, 1 ) );
|
|
|
|
GL( glClear( GL_COLOR_BUFFER_BIT ) );
|
|
|
|
// top
|
|
|
|
GL( glScissor( 0, frameBuffer->Height - 1, frameBuffer->Width, 1 ) );
|
|
|
|
GL( glClear( GL_COLOR_BUFFER_BIT ) );
|
|
|
|
// left
|
|
|
|
GL( glScissor( 0, 0, 1, frameBuffer->Height ) );
|
|
|
|
GL( glClear( GL_COLOR_BUFFER_BIT ) );
|
|
|
|
// right
|
|
|
|
GL( glScissor( frameBuffer->Width - 1, 0, 1, frameBuffer->Height ) );
|
|
|
|
GL( glClear( GL_COLOR_BUFFER_BIT ) );
|
|
|
|
|
|
|
|
|
|
|
|
GL( glScissor( 0, 0, 0, 0 ) );
|
|
|
|
GL( glDisable( GL_SCISSOR_TEST ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrRenderer
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void ovrRenderer_Clear( ovrRenderer * renderer )
|
|
|
|
{
|
2020-10-07 18:05:09 +00:00
|
|
|
ovrFramebuffer_Clear( &renderer->FrameBuffer );
|
2020-09-08 22:10:45 +00:00
|
|
|
renderer->ProjectionMatrix = ovrMatrix4f_CreateIdentity();
|
|
|
|
renderer->NumBuffers = VRAPI_FRAME_LAYER_EYE_MAX;
|
|
|
|
}
|
|
|
|
|
2020-10-14 18:14:31 +00:00
|
|
|
float Doom3Quest_GetFOV();
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
void ovrRenderer_Create( int width, int height, ovrRenderer * renderer, const ovrJava * java )
|
|
|
|
{
|
2020-10-07 18:05:09 +00:00
|
|
|
renderer->NumBuffers = 1; // Multiview
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
//Now using a symmetrical render target, based on the horizontal FOV
|
2020-10-14 18:14:31 +00:00
|
|
|
Doom3Quest_GetFOV();
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
// Create the multi view frame buffer
|
|
|
|
ovrFramebuffer_Create( &renderer->FrameBuffer,
|
|
|
|
true,
|
|
|
|
GL_RGBA8,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
NUM_MULTI_SAMPLES );
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
// Setup the projection matrix.
|
|
|
|
renderer->ProjectionMatrix = ovrMatrix4f_CreateProjectionFov(
|
|
|
|
vrFOV, vrFOV, 0.0f, 0.0f, 1.0f, 0.0f );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void ovrRenderer_Destroy( ovrRenderer * renderer )
|
|
|
|
{
|
2020-10-07 18:05:09 +00:00
|
|
|
ovrFramebuffer_Destroy( &renderer->FrameBuffer );
|
2020-09-08 22:10:45 +00:00
|
|
|
renderer->ProjectionMatrix = ovrMatrix4f_CreateIdentity();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef EPSILON
|
|
|
|
#define EPSILON 0.001f
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static ovrVector3f normalizeVec(ovrVector3f vec) {
|
|
|
|
//NOTE: leave w-component untouched
|
|
|
|
//@@const float EPSILON = 0.000001f;
|
|
|
|
float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z;
|
|
|
|
//@@if(xxyyzz < EPSILON)
|
|
|
|
//@@ return *this; // do nothing if it is zero vector
|
|
|
|
|
|
|
|
//float invLength = invSqrt(xxyyzz);
|
|
|
|
ovrVector3f result;
|
|
|
|
float invLength = 1.0f / sqrtf(xxyyzz);
|
|
|
|
result.x = vec.x * invLength;
|
|
|
|
result.y = vec.y * invLength;
|
|
|
|
result.z = vec.z * invLength;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NormalizeAngles(vec3_t angles)
|
|
|
|
{
|
|
|
|
while (angles[0] >= 90) angles[0] -= 180;
|
|
|
|
while (angles[1] >= 180) angles[1] -= 360;
|
|
|
|
while (angles[2] >= 180) angles[2] -= 360;
|
|
|
|
while (angles[0] < -90) angles[0] += 180;
|
|
|
|
while (angles[1] < -180) angles[1] += 360;
|
|
|
|
while (angles[2] < -180) angles[2] += 360;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetAnglesFromVectors(const ovrVector3f forward, const ovrVector3f right, const ovrVector3f up, vec3_t angles)
|
|
|
|
{
|
|
|
|
float sr, sp, sy, cr, cp, cy;
|
|
|
|
|
|
|
|
sp = -forward.z;
|
|
|
|
|
|
|
|
float cp_x_cy = forward.x;
|
|
|
|
float cp_x_sy = forward.y;
|
|
|
|
float cp_x_sr = -right.z;
|
|
|
|
float cp_x_cr = up.z;
|
|
|
|
|
|
|
|
float yaw = atan2(cp_x_sy, cp_x_cy);
|
|
|
|
float roll = atan2(cp_x_sr, cp_x_cr);
|
|
|
|
|
|
|
|
cy = cos(yaw);
|
|
|
|
sy = sin(yaw);
|
|
|
|
cr = cos(roll);
|
|
|
|
sr = sin(roll);
|
|
|
|
|
|
|
|
if (fabs(cy) > EPSILON)
|
|
|
|
{
|
|
|
|
cp = cp_x_cy / cy;
|
|
|
|
}
|
|
|
|
else if (fabs(sy) > EPSILON)
|
|
|
|
{
|
|
|
|
cp = cp_x_sy / sy;
|
|
|
|
}
|
|
|
|
else if (fabs(sr) > EPSILON)
|
|
|
|
{
|
|
|
|
cp = cp_x_sr / sr;
|
|
|
|
}
|
|
|
|
else if (fabs(cr) > EPSILON)
|
|
|
|
{
|
|
|
|
cp = cp_x_cr / cr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cp = cos(asin(sp));
|
|
|
|
}
|
|
|
|
|
|
|
|
float pitch = atan2(sp, cp);
|
|
|
|
|
|
|
|
angles[0] = pitch / (M_PI*2.f / 360.f);
|
|
|
|
angles[1] = yaw / (M_PI*2.f / 360.f);
|
|
|
|
angles[2] = roll / (M_PI*2.f / 360.f);
|
|
|
|
|
|
|
|
NormalizeAngles(angles);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QuatToYawPitchRoll(ovrQuatf q, vec3_t rotation, vec3_t out) {
|
|
|
|
|
|
|
|
ovrMatrix4f mat = ovrMatrix4f_CreateFromQuaternion( &q );
|
|
|
|
|
|
|
|
if (rotation[0] != 0.0f || rotation[1] != 0.0f || rotation[2] != 0.0f)
|
|
|
|
{
|
|
|
|
ovrMatrix4f rot = ovrMatrix4f_CreateRotation(radians(rotation[0]), radians(rotation[1]), radians(rotation[2]));
|
|
|
|
mat = ovrMatrix4f_Multiply(&mat, &rot);
|
|
|
|
}
|
|
|
|
|
|
|
|
ovrVector4f v1 = {0, 0, -1, 0};
|
|
|
|
ovrVector4f v2 = {1, 0, 0, 0};
|
|
|
|
ovrVector4f v3 = {0, 1, 0, 0};
|
|
|
|
|
|
|
|
ovrVector4f forwardInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v1);
|
|
|
|
ovrVector4f rightInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v2);
|
|
|
|
ovrVector4f upInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v3);
|
|
|
|
|
|
|
|
ovrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y};
|
|
|
|
ovrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y};
|
|
|
|
ovrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y};
|
|
|
|
|
|
|
|
ovrVector3f forwardNormal = normalizeVec(forward);
|
|
|
|
ovrVector3f rightNormal = normalizeVec(right);
|
|
|
|
ovrVector3f upNormal = normalizeVec(up);
|
|
|
|
|
|
|
|
GetAnglesFromVectors(forwardNormal, rightNormal, upNormal, out);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-09 22:16:24 +00:00
|
|
|
void updateHMDOrientation()
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
2020-09-09 22:16:24 +00:00
|
|
|
//Position
|
|
|
|
VectorSubtract(vr.hmdposition_last, vr.hmdposition, vr.hmdposition_delta);
|
|
|
|
|
|
|
|
//Keep this for our records
|
|
|
|
VectorCopy(vr.hmdposition, vr.hmdposition_last);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-09 22:16:24 +00:00
|
|
|
//Orientation
|
|
|
|
VectorSubtract(vr.hmdorientation_last, vr.hmdorientation, vr.hmdorientation_delta);
|
|
|
|
|
|
|
|
//Keep this for our records
|
|
|
|
VectorCopy(vr.hmdorientation, vr.hmdorientation_last);
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void setHMDPosition( float x, float y, float z, float yaw )
|
|
|
|
{
|
2020-09-09 22:16:24 +00:00
|
|
|
VectorSet(vr.hmdposition, x, y, z);
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
========================
|
2020-09-08 22:21:43 +00:00
|
|
|
Doom3Quest_Vibrate
|
2020-09-08 22:10:45 +00:00
|
|
|
========================
|
|
|
|
*/
|
|
|
|
|
|
|
|
//0 = left, 1 = right
|
|
|
|
float vibration_channel_duration[2] = {0.0f, 0.0f};
|
|
|
|
float vibration_channel_intensity[2] = {0.0f, 0.0f};
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
void Doom3Quest_Vibrate(int duration, int channel, float intensity )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
if (vibration_channel_duration[channel] > 0.0f)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (vibration_channel_duration[channel] == -1.0f && duration != 0.0f)
|
|
|
|
return;
|
|
|
|
|
|
|
|
vibration_channel_duration[channel] = duration;
|
|
|
|
vibration_channel_intensity[channel] = intensity;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VR_Doom3Main(int argc, char** argv);
|
|
|
|
|
|
|
|
void VR_GetMove( float *joy_forward, float *joy_side, float *hmd_forward, float *hmd_side, float *up, float *yaw, float *pitch, float *roll )
|
|
|
|
{
|
|
|
|
*joy_forward = remote_movementForward;
|
|
|
|
*hmd_forward = positional_movementForward;
|
|
|
|
*up = remote_movementUp;
|
|
|
|
*joy_side = remote_movementSideways;
|
|
|
|
*hmd_side = positional_movementSideways;
|
2020-09-09 22:16:24 +00:00
|
|
|
*yaw = vr.hmdorientation[YAW] + snapTurn;
|
|
|
|
*pitch = vr.hmdorientation[PITCH];
|
|
|
|
*roll = vr.hmdorientation[ROLL];
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrRenderThread
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrApp
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
2020-09-11 21:41:18 +00:00
|
|
|
#define MAX_TRACKING_SAMPLES 4
|
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
ovrJava Java;
|
|
|
|
ovrEgl Egl;
|
|
|
|
ANativeWindow * NativeWindow;
|
|
|
|
bool Resumed;
|
|
|
|
ovrMobile * Ovr;
|
|
|
|
ovrScene Scene;
|
2020-09-11 21:41:18 +00:00
|
|
|
SDL_mutex * RenderThreadFrameIndex_Mutex;
|
2020-09-09 22:16:24 +00:00
|
|
|
long long RenderThreadFrameIndex;
|
2020-09-11 21:41:18 +00:00
|
|
|
long long MainThreadFrameIndex;
|
|
|
|
double DisplayTime[MAX_TRACKING_SAMPLES];
|
|
|
|
ovrTracking2 Tracking[MAX_TRACKING_SAMPLES];
|
2020-09-08 22:10:45 +00:00
|
|
|
int SwapInterval;
|
|
|
|
int CpuLevel;
|
|
|
|
int GpuLevel;
|
|
|
|
int MainThreadTid;
|
|
|
|
int RenderThreadTid;
|
|
|
|
ovrLayer_Union2 Layers[ovrMaxLayerCount];
|
|
|
|
int LayerCount;
|
|
|
|
ovrRenderer Renderer;
|
|
|
|
} ovrApp;
|
|
|
|
|
|
|
|
static void ovrApp_Clear( ovrApp * app )
|
|
|
|
{
|
|
|
|
app->Java.Vm = NULL;
|
|
|
|
app->Java.Env = NULL;
|
|
|
|
app->Java.ActivityObject = NULL;
|
|
|
|
app->Ovr = NULL;
|
2020-09-11 21:41:18 +00:00
|
|
|
app->RenderThreadFrameIndex_Mutex = SDL_CreateMutex();
|
2020-09-09 22:16:24 +00:00
|
|
|
app->RenderThreadFrameIndex = 1;
|
2020-09-11 21:41:18 +00:00
|
|
|
app->MainThreadFrameIndex = 1;
|
|
|
|
memset(app->DisplayTime, 0, MAX_TRACKING_SAMPLES * sizeof(double));
|
|
|
|
memset(app->Tracking, 0, MAX_TRACKING_SAMPLES * sizeof(ovrTracking2));
|
2020-09-08 22:10:45 +00:00
|
|
|
app->SwapInterval = 1;
|
|
|
|
app->CpuLevel = 4;
|
|
|
|
app->GpuLevel = 4;
|
|
|
|
app->MainThreadTid = 0;
|
|
|
|
app->RenderThreadTid = 0;
|
|
|
|
|
|
|
|
ovrEgl_Clear( &app->Egl );
|
|
|
|
|
|
|
|
ovrScene_Clear( &app->Scene );
|
|
|
|
ovrRenderer_Clear( &app->Renderer );
|
|
|
|
}
|
|
|
|
|
|
|
|
static ovrApp gAppState;
|
|
|
|
static ovrJava java;
|
|
|
|
static bool destroyed = false;
|
|
|
|
|
2020-10-14 18:14:31 +00:00
|
|
|
float Doom3Quest_GetFOV()
|
|
|
|
{
|
|
|
|
if (vrFOV == 0.0f) {
|
|
|
|
vrFOV = vrapi_GetSystemPropertyInt(&gAppState.Java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
|
|
|
|
}
|
|
|
|
|
|
|
|
return vrFOV;
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
static void ovrApp_HandleVrModeChanges( ovrApp * app )
|
|
|
|
{
|
|
|
|
if ( app->Resumed != false && app->NativeWindow != NULL )
|
|
|
|
{
|
|
|
|
if ( app->Ovr == NULL )
|
|
|
|
{
|
|
|
|
ovrJava sJava = java;
|
|
|
|
sJava.Env = NULL;
|
|
|
|
(*sJava.Vm)->AttachCurrentThread(sJava.Vm, &sJava.Env, NULL);
|
|
|
|
|
|
|
|
ovrModeParms parms = vrapi_DefaultModeParms( &sJava );
|
|
|
|
// Must reset the FLAG_FULLSCREEN window flag when using a SurfaceView
|
|
|
|
parms.Flags |= VRAPI_MODE_FLAG_RESET_WINDOW_FULLSCREEN;
|
|
|
|
|
|
|
|
parms.Flags |= VRAPI_MODE_FLAG_NATIVE_WINDOW;
|
|
|
|
parms.Display = (size_t)app->Egl.Display;
|
|
|
|
parms.WindowSurface = (size_t)app->NativeWindow;
|
|
|
|
parms.ShareContext = (size_t)app->Egl.Context;
|
|
|
|
|
|
|
|
ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
|
|
|
|
|
|
|
|
ALOGV( " vrapi_EnterVrMode()" );
|
|
|
|
|
|
|
|
app->Ovr = vrapi_EnterVrMode( &parms );
|
|
|
|
|
|
|
|
ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
|
|
|
|
|
|
|
|
// If entering VR mode failed then the ANativeWindow was not valid.
|
|
|
|
if ( app->Ovr == NULL )
|
|
|
|
{
|
|
|
|
ALOGE( "Invalid ANativeWindow!" );
|
|
|
|
app->NativeWindow = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set performance parameters once we have entered VR mode and have a valid ovrMobile.
|
|
|
|
if ( app->Ovr != NULL )
|
|
|
|
{
|
|
|
|
vrapi_SetClockLevels( app->Ovr, app->CpuLevel, app->GpuLevel );
|
|
|
|
|
|
|
|
ALOGV( " vrapi_SetClockLevels( %d, %d )", app->CpuLevel, app->GpuLevel );
|
|
|
|
|
|
|
|
vrapi_SetPerfThread( app->Ovr, VRAPI_PERF_THREAD_TYPE_MAIN, app->MainThreadTid );
|
|
|
|
|
|
|
|
ALOGV( " vrapi_SetPerfThread( MAIN, %d )", app->MainThreadTid );
|
|
|
|
|
|
|
|
vrapi_SetPerfThread( app->Ovr, VRAPI_PERF_THREAD_TYPE_RENDERER, app->RenderThreadTid );
|
|
|
|
|
|
|
|
ALOGV( " vrapi_SetPerfThread( RENDERER, %d )", app->RenderThreadTid );
|
2020-09-26 10:30:46 +00:00
|
|
|
|
|
|
|
vrapi_SetExtraLatencyMode(app->Ovr, VRAPI_EXTRA_LATENCY_MODE_ON);
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
2020-09-26 10:30:46 +00:00
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( app->Ovr != NULL )
|
|
|
|
{
|
|
|
|
ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
|
|
|
|
|
|
|
|
ALOGV( " vrapi_LeaveVrMode()" );
|
|
|
|
|
|
|
|
vrapi_LeaveVrMode( app->Ovr );
|
|
|
|
app->Ovr = NULL;
|
|
|
|
|
|
|
|
ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrMessageQueue
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
MQ_WAIT_NONE, // don't wait
|
|
|
|
MQ_WAIT_RECEIVED, // wait until the consumer thread has received the message
|
|
|
|
MQ_WAIT_PROCESSED // wait until the consumer thread has processed the message
|
|
|
|
} ovrMQWait;
|
|
|
|
|
|
|
|
#define MAX_MESSAGE_PARMS 8
|
|
|
|
#define MAX_MESSAGES 1024
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int Id;
|
|
|
|
ovrMQWait Wait;
|
|
|
|
long long Parms[MAX_MESSAGE_PARMS];
|
|
|
|
} ovrMessage;
|
|
|
|
|
|
|
|
static void ovrMessage_Init( ovrMessage * message, const int id, const int wait )
|
|
|
|
{
|
|
|
|
message->Id = id;
|
|
|
|
message->Wait = wait;
|
|
|
|
memset( message->Parms, 0, sizeof( message->Parms ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrMessage_SetPointerParm( ovrMessage * message, int index, void * ptr ) { *(void **)&message->Parms[index] = ptr; }
|
|
|
|
static void * ovrMessage_GetPointerParm( ovrMessage * message, int index ) { return *(void **)&message->Parms[index]; }
|
|
|
|
static void ovrMessage_SetIntegerParm( ovrMessage * message, int index, int value ) { message->Parms[index] = value; }
|
|
|
|
static int ovrMessage_GetIntegerParm( ovrMessage * message, int index ) { return (int)message->Parms[index]; }
|
|
|
|
static void ovrMessage_SetFloatParm( ovrMessage * message, int index, float value ) { *(float *)&message->Parms[index] = value; }
|
|
|
|
static float ovrMessage_GetFloatParm( ovrMessage * message, int index ) { return *(float *)&message->Parms[index]; }
|
|
|
|
|
|
|
|
// Cyclic queue with messages.
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
ovrMessage Messages[MAX_MESSAGES];
|
|
|
|
volatile int Head; // dequeue at the head
|
|
|
|
volatile int Tail; // enqueue at the tail
|
|
|
|
ovrMQWait Wait;
|
|
|
|
volatile bool EnabledFlag;
|
|
|
|
volatile bool PostedFlag;
|
|
|
|
volatile bool ReceivedFlag;
|
|
|
|
volatile bool ProcessedFlag;
|
|
|
|
pthread_mutex_t Mutex;
|
|
|
|
pthread_cond_t PostedCondition;
|
|
|
|
pthread_cond_t ReceivedCondition;
|
|
|
|
pthread_cond_t ProcessedCondition;
|
|
|
|
} ovrMessageQueue;
|
|
|
|
|
|
|
|
static void ovrMessageQueue_Create( ovrMessageQueue * messageQueue )
|
|
|
|
{
|
|
|
|
messageQueue->Head = 0;
|
|
|
|
messageQueue->Tail = 0;
|
|
|
|
messageQueue->Wait = MQ_WAIT_NONE;
|
|
|
|
messageQueue->EnabledFlag = false;
|
|
|
|
messageQueue->PostedFlag = false;
|
|
|
|
messageQueue->ReceivedFlag = false;
|
|
|
|
messageQueue->ProcessedFlag = false;
|
|
|
|
|
|
|
|
pthread_mutexattr_t attr;
|
|
|
|
pthread_mutexattr_init( &attr );
|
|
|
|
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
|
|
|
|
pthread_mutex_init( &messageQueue->Mutex, &attr );
|
|
|
|
pthread_mutexattr_destroy( &attr );
|
|
|
|
pthread_cond_init( &messageQueue->PostedCondition, NULL );
|
|
|
|
pthread_cond_init( &messageQueue->ReceivedCondition, NULL );
|
|
|
|
pthread_cond_init( &messageQueue->ProcessedCondition, NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrMessageQueue_Destroy( ovrMessageQueue * messageQueue )
|
|
|
|
{
|
|
|
|
pthread_mutex_destroy( &messageQueue->Mutex );
|
|
|
|
pthread_cond_destroy( &messageQueue->PostedCondition );
|
|
|
|
pthread_cond_destroy( &messageQueue->ReceivedCondition );
|
|
|
|
pthread_cond_destroy( &messageQueue->ProcessedCondition );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrMessageQueue_Enable( ovrMessageQueue * messageQueue, const bool set )
|
|
|
|
{
|
|
|
|
messageQueue->EnabledFlag = set;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrMessageQueue_PostMessage( ovrMessageQueue * messageQueue, const ovrMessage * message )
|
|
|
|
{
|
|
|
|
if ( !messageQueue->EnabledFlag )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while ( messageQueue->Tail - messageQueue->Head >= MAX_MESSAGES )
|
|
|
|
{
|
|
|
|
usleep( 1000 );
|
|
|
|
}
|
|
|
|
pthread_mutex_lock( &messageQueue->Mutex );
|
|
|
|
messageQueue->Messages[messageQueue->Tail & ( MAX_MESSAGES - 1 )] = *message;
|
|
|
|
messageQueue->Tail++;
|
|
|
|
messageQueue->PostedFlag = true;
|
|
|
|
pthread_cond_broadcast( &messageQueue->PostedCondition );
|
|
|
|
if ( message->Wait == MQ_WAIT_RECEIVED )
|
|
|
|
{
|
|
|
|
while ( !messageQueue->ReceivedFlag )
|
|
|
|
{
|
|
|
|
pthread_cond_wait( &messageQueue->ReceivedCondition, &messageQueue->Mutex );
|
|
|
|
}
|
|
|
|
messageQueue->ReceivedFlag = false;
|
|
|
|
}
|
|
|
|
else if ( message->Wait == MQ_WAIT_PROCESSED )
|
|
|
|
{
|
|
|
|
while ( !messageQueue->ProcessedFlag )
|
|
|
|
{
|
|
|
|
pthread_cond_wait( &messageQueue->ProcessedCondition, &messageQueue->Mutex );
|
|
|
|
}
|
|
|
|
messageQueue->ProcessedFlag = false;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock( &messageQueue->Mutex );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrMessageQueue_SleepUntilMessage( ovrMessageQueue * messageQueue )
|
|
|
|
{
|
|
|
|
if ( messageQueue->Wait == MQ_WAIT_PROCESSED )
|
|
|
|
{
|
|
|
|
messageQueue->ProcessedFlag = true;
|
|
|
|
pthread_cond_broadcast( &messageQueue->ProcessedCondition );
|
|
|
|
messageQueue->Wait = MQ_WAIT_NONE;
|
|
|
|
}
|
|
|
|
pthread_mutex_lock( &messageQueue->Mutex );
|
|
|
|
if ( messageQueue->Tail > messageQueue->Head )
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock( &messageQueue->Mutex );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while ( !messageQueue->PostedFlag )
|
|
|
|
{
|
|
|
|
pthread_cond_wait( &messageQueue->PostedCondition, &messageQueue->Mutex );
|
|
|
|
}
|
|
|
|
messageQueue->PostedFlag = false;
|
|
|
|
pthread_mutex_unlock( &messageQueue->Mutex );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ovrMessageQueue_GetNextMessage( ovrMessageQueue * messageQueue, ovrMessage * message, bool waitForMessages )
|
|
|
|
{
|
|
|
|
if ( messageQueue->Wait == MQ_WAIT_PROCESSED )
|
|
|
|
{
|
|
|
|
messageQueue->ProcessedFlag = true;
|
|
|
|
pthread_cond_broadcast( &messageQueue->ProcessedCondition );
|
|
|
|
messageQueue->Wait = MQ_WAIT_NONE;
|
|
|
|
}
|
|
|
|
if ( waitForMessages )
|
|
|
|
{
|
|
|
|
ovrMessageQueue_SleepUntilMessage( messageQueue );
|
|
|
|
}
|
|
|
|
pthread_mutex_lock( &messageQueue->Mutex );
|
|
|
|
if ( messageQueue->Tail <= messageQueue->Head )
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock( &messageQueue->Mutex );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*message = messageQueue->Messages[messageQueue->Head & ( MAX_MESSAGES - 1 )];
|
|
|
|
messageQueue->Head++;
|
|
|
|
pthread_mutex_unlock( &messageQueue->Mutex );
|
|
|
|
if ( message->Wait == MQ_WAIT_RECEIVED )
|
|
|
|
{
|
|
|
|
messageQueue->ReceivedFlag = true;
|
|
|
|
pthread_cond_broadcast( &messageQueue->ReceivedCondition );
|
|
|
|
}
|
|
|
|
else if ( message->Wait == MQ_WAIT_PROCESSED )
|
|
|
|
{
|
|
|
|
messageQueue->Wait = MQ_WAIT_PROCESSED;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrAppThread
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MESSAGE_ON_CREATE,
|
|
|
|
MESSAGE_ON_START,
|
|
|
|
MESSAGE_ON_RESUME,
|
|
|
|
MESSAGE_ON_PAUSE,
|
|
|
|
MESSAGE_ON_STOP,
|
|
|
|
MESSAGE_ON_DESTROY,
|
|
|
|
MESSAGE_ON_SURFACE_CREATED,
|
|
|
|
MESSAGE_ON_SURFACE_DESTROYED
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
JavaVM * JavaVm;
|
|
|
|
jobject ActivityObject;
|
|
|
|
jclass ActivityClass;
|
|
|
|
pthread_t Thread;
|
|
|
|
ovrMessageQueue MessageQueue;
|
|
|
|
ANativeWindow * NativeWindow;
|
|
|
|
} ovrAppThread;
|
|
|
|
|
|
|
|
long shutdownCountdown;
|
|
|
|
|
|
|
|
int m_width;
|
|
|
|
int m_height;
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
void Doom3Quest_GetScreenRes(int *width, int *height)
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
*width = m_width;
|
|
|
|
*height = m_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Android_MessageBox(const char *title, const char *text)
|
|
|
|
{
|
|
|
|
ALOGE("%s %s", title, text);
|
|
|
|
}
|
|
|
|
|
|
|
|
//void initialize_gl4es();
|
|
|
|
|
|
|
|
void VR_Init()
|
|
|
|
{
|
|
|
|
//Initialise all our variables
|
2020-09-22 22:43:30 +00:00
|
|
|
screenYaw = 0.0f;
|
2020-09-08 22:10:45 +00:00
|
|
|
remote_movementSideways = 0.0f;
|
|
|
|
remote_movementForward = 0.0f;
|
|
|
|
remote_movementUp = 0.0f;
|
|
|
|
positional_movementSideways = 0.0f;
|
|
|
|
positional_movementForward = 0.0f;
|
|
|
|
snapTurn = 0.0f;
|
2020-09-16 21:41:35 +00:00
|
|
|
vr.visible_hud = true;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
//init randomiser
|
|
|
|
srand(time(NULL));
|
|
|
|
|
|
|
|
shutdown = false;
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
long renderThreadCPUTime = 0;
|
2020-09-26 10:30:46 +00:00
|
|
|
|
2020-10-08 20:16:18 +00:00
|
|
|
void Doom3Quest_prepareEyeBuffer( )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
2020-09-08 22:21:43 +00:00
|
|
|
ovrRenderer *renderer = Doom3Quest_useScreenLayer() ? &gAppState.Scene.CylinderRenderer : &gAppState.Renderer;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
ovrFramebuffer *frameBuffer = &(renderer->FrameBuffer);
|
2020-09-08 22:10:45 +00:00
|
|
|
ovrFramebuffer_SetCurrent(frameBuffer);
|
|
|
|
|
2020-10-08 20:16:18 +00:00
|
|
|
renderThreadCPUTime = GetTimeInMilliSeconds();
|
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
GL(glEnable(GL_SCISSOR_TEST));
|
|
|
|
GL(glDepthMask(GL_TRUE));
|
|
|
|
GL(glEnable(GL_DEPTH_TEST));
|
|
|
|
GL(glDepthFunc(GL_LEQUAL));
|
|
|
|
|
|
|
|
//Weusing the size of the render target
|
|
|
|
GL(glViewport(0, 0, frameBuffer->Width, frameBuffer->Height));
|
|
|
|
GL(glScissor(0, 0, frameBuffer->Width, frameBuffer->Height));
|
|
|
|
|
|
|
|
GL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
|
|
|
|
GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
|
|
|
GL(glDisable(GL_SCISSOR_TEST));
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
void Doom3Quest_finishEyeBuffer( )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
2020-10-07 18:05:09 +00:00
|
|
|
renderThreadCPUTime = GetTimeInMilliSeconds() - renderThreadCPUTime;
|
2020-09-26 10:30:46 +00:00
|
|
|
|
|
|
|
{
|
2020-10-07 18:05:09 +00:00
|
|
|
__android_log_print(ANDROID_LOG_INFO, "Doom3Quest", "RENDER THREAD TOTAL CPU TIME: %i", renderThreadCPUTime);
|
2020-09-26 10:30:46 +00:00
|
|
|
}
|
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
GLCheckErrors( __LINE__ );
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
ovrRenderer *renderer = Doom3Quest_useScreenLayer() ? &gAppState.Scene.CylinderRenderer : &gAppState.Renderer;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-10-07 18:05:09 +00:00
|
|
|
ovrFramebuffer *frameBuffer = &(renderer->FrameBuffer);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
//Clear edge to prevent smearing
|
|
|
|
ovrFramebuffer_ClearEdgeTexels(frameBuffer);
|
|
|
|
ovrFramebuffer_Resolve(frameBuffer);
|
|
|
|
ovrFramebuffer_Advance(frameBuffer);
|
|
|
|
|
|
|
|
ovrFramebuffer_SetNone();
|
|
|
|
}
|
|
|
|
|
|
|
|
static ovrAppThread * gAppThread = NULL;
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
bool Doom3Quest_processMessageQueue() {
|
2020-09-08 22:10:45 +00:00
|
|
|
for ( ; ; )
|
|
|
|
{
|
|
|
|
ovrMessage message;
|
|
|
|
const bool waitForMessages = ( gAppState.Ovr == NULL && destroyed == false );
|
|
|
|
if ( !ovrMessageQueue_GetNextMessage( &gAppThread->MessageQueue, &message, waitForMessages ) )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( message.Id )
|
|
|
|
{
|
|
|
|
case MESSAGE_ON_CREATE:
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_ON_START:
|
|
|
|
{
|
2020-09-08 22:21:43 +00:00
|
|
|
if (!Doom3Quest_initialised)
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
//Set command line arguments here
|
|
|
|
if (argc != 0)
|
|
|
|
{
|
|
|
|
//TODO
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int argc = 1; char *argv[] = { "qzdoom" };
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
Doom3Quest_initialised = true;
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_ON_RESUME:
|
|
|
|
{
|
|
|
|
//If we get here, then user has opted not to quit
|
|
|
|
gAppState.Resumed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_ON_PAUSE:
|
|
|
|
{
|
|
|
|
gAppState.Resumed = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_ON_STOP:
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_ON_DESTROY:
|
|
|
|
{
|
|
|
|
gAppState.NativeWindow = NULL;
|
|
|
|
destroyed = true;
|
|
|
|
shutdown = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_ON_SURFACE_CREATED: { gAppState.NativeWindow = (ANativeWindow *)ovrMessage_GetPointerParm( &message, 0 ); break; }
|
|
|
|
case MESSAGE_ON_SURFACE_DESTROYED: { gAppState.NativeWindow = NULL; break; }
|
|
|
|
}
|
|
|
|
|
|
|
|
ovrApp_HandleVrModeChanges( &gAppState );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void shutdownVR() {
|
2020-09-11 21:41:18 +00:00
|
|
|
SDL_DestroyMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
2020-09-08 22:10:45 +00:00
|
|
|
ovrRenderer_Destroy( &gAppState.Renderer );
|
|
|
|
ovrEgl_DestroyContext( &gAppState.Egl );
|
|
|
|
(*java.Vm)->DetachCurrentThread( java.Vm );
|
|
|
|
vrapi_Shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
void showLoadingIcon();
|
|
|
|
void jni_shutdown();
|
|
|
|
|
|
|
|
/* Called before SDL_main() to initialize JNI bindings in SDL library */
|
|
|
|
extern void SDL_Android_Init(JNIEnv* env, jclass cls);
|
|
|
|
|
|
|
|
//Calld on the main thread before the rendering thread is started
|
|
|
|
void DeactivateContext()
|
|
|
|
{
|
|
|
|
eglMakeCurrent( gAppState.Egl.Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
|
|
|
|
}
|
|
|
|
|
|
|
|
//Caled by the rendering thread to take charge of the context
|
|
|
|
void ActivateContext()
|
|
|
|
{
|
|
|
|
eglMakeCurrent( gAppState.Egl.Display, gAppState.Egl.TinySurface, gAppState.Egl.TinySurface, gAppState.Egl.Context );
|
|
|
|
|
|
|
|
gAppState.RenderThreadTid = gettid();
|
|
|
|
}
|
|
|
|
|
|
|
|
void * AppThreadFunction(void * parm ) {
|
|
|
|
gAppThread = (ovrAppThread *) parm;
|
|
|
|
|
|
|
|
java.Vm = gAppThread->JavaVm;
|
|
|
|
(*java.Vm)->AttachCurrentThread(java.Vm, &java.Env, NULL);
|
|
|
|
java.ActivityObject = gAppThread->ActivityObject;
|
|
|
|
|
|
|
|
jclass cls = (*java.Env)->GetObjectClass(java.Env, java.ActivityObject);
|
|
|
|
|
|
|
|
/* This interface could expand with ABI negotiation, callbacks, etc. */
|
|
|
|
SDL_Android_Init(java.Env, cls);
|
|
|
|
|
|
|
|
SDL_SetMainReady();
|
|
|
|
|
|
|
|
pVRClientInfo = &vr;
|
|
|
|
|
|
|
|
// Note that AttachCurrentThread will reset the thread name.
|
|
|
|
prctl(PR_SET_NAME, (long) "OVR::Main", 0, 0, 0);
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
Doom3Quest_initialised = false;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
const ovrInitParms initParms = vrapi_DefaultInitParms(&java);
|
|
|
|
int32_t initResult = vrapi_Initialize(&initParms);
|
|
|
|
if (initResult != VRAPI_INITIALIZE_SUCCESS) {
|
|
|
|
// If intialization failed, vrapi_* function calls will not be available.
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2020-09-16 21:41:35 +00:00
|
|
|
VR_Init();
|
|
|
|
|
2020-09-08 22:10:45 +00:00
|
|
|
ovrApp_Clear(&gAppState);
|
|
|
|
gAppState.Java = java;
|
|
|
|
|
|
|
|
// This app will handle android gamepad events itself.
|
|
|
|
vrapi_SetPropertyInt(&gAppState.Java, VRAPI_EAT_NATIVE_GAMEPAD_EVENTS, 0);
|
|
|
|
|
|
|
|
//Set FFR property
|
|
|
|
vrapi_SetPropertyInt( &gAppState.Java, VRAPI_FOVEATION_LEVEL, FFR );
|
|
|
|
|
|
|
|
//Using a symmetrical render target
|
|
|
|
m_height = m_width = (int)(vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH) * SS_MULTIPLIER);
|
|
|
|
|
|
|
|
gAppState.CpuLevel = CPU_LEVEL;
|
|
|
|
gAppState.GpuLevel = GPU_LEVEL;
|
|
|
|
gAppState.MainThreadTid = gettid();
|
|
|
|
|
|
|
|
ovrEgl_CreateContext(&gAppState.Egl, NULL);
|
|
|
|
|
|
|
|
EglInitExtensions();
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
chdir("/sdcard/Doom3Quest");
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
//First handle any messages in the queue
|
|
|
|
while ( gAppState.Ovr == NULL ) {
|
2020-09-08 22:21:43 +00:00
|
|
|
Doom3Quest_processMessageQueue();
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ovrRenderer_Create(m_width, m_height, &gAppState.Renderer, &java);
|
|
|
|
|
|
|
|
if ( gAppState.Ovr == NULL )
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the scene if not yet created.
|
|
|
|
ovrScene_Create( m_width, m_height, &gAppState.Scene, &java );
|
|
|
|
|
|
|
|
//Set the screen refresh
|
|
|
|
vrapi_SetDisplayRefreshRate(gAppState.Ovr, DISPLAY_REFRESH);
|
|
|
|
|
|
|
|
//Run loading loop until we are ready to start QzDoom
|
2020-09-08 22:21:43 +00:00
|
|
|
while (!destroyed && !Doom3Quest_initialised) {
|
|
|
|
Doom3Quest_processMessageQueue();
|
2020-09-11 21:41:18 +00:00
|
|
|
Doom3Quest_getHMDOrientation();
|
2020-09-08 22:10:45 +00:00
|
|
|
showLoadingIcon();
|
|
|
|
}
|
|
|
|
|
|
|
|
//Should now be all set up and ready - start the Doom3 main loop
|
|
|
|
VR_Doom3Main(argc, argv);
|
|
|
|
|
|
|
|
//Take the context back
|
|
|
|
ActivateContext();
|
|
|
|
|
|
|
|
//We are done, shutdown cleanly
|
|
|
|
shutdownVR();
|
|
|
|
|
|
|
|
//Ask Java to shut down
|
|
|
|
jni_shutdown();
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//All the stuff we want to do each frame
|
2020-09-23 18:34:14 +00:00
|
|
|
void Doom3Quest_FrameSetup(int controlscheme)
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
//Use floor based tracking space
|
|
|
|
vrapi_SetTrackingSpace(gAppState.Ovr, VRAPI_TRACKING_SPACE_LOCAL_FLOOR);
|
2020-09-09 22:16:24 +00:00
|
|
|
|
2020-09-22 22:43:30 +00:00
|
|
|
if (!Doom3Quest_useScreenLayer())
|
|
|
|
{
|
|
|
|
screenYaw = vr.hmdorientation[YAW];
|
|
|
|
}
|
|
|
|
|
2020-09-09 22:16:24 +00:00
|
|
|
Doom3Quest_processHaptics();
|
|
|
|
Doom3Quest_getHMDOrientation();
|
2020-09-23 18:34:14 +00:00
|
|
|
Doom3Quest_getTrackedRemotesOrientation(controlscheme);
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
void Doom3Quest_processHaptics() {//Handle haptics
|
2020-09-08 22:10:45 +00:00
|
|
|
static float lastFrameTime = 0.0f;
|
|
|
|
float timestamp = (float)(GetTimeInMilliSeconds());
|
|
|
|
float frametime = timestamp - lastFrameTime;
|
|
|
|
lastFrameTime = timestamp;
|
|
|
|
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
|
|
if (vibration_channel_duration[i] > 0.0f ||
|
|
|
|
vibration_channel_duration[i] == -1.0f) {
|
|
|
|
vrapi_SetHapticVibrationSimple(gAppState.Ovr, controllerIDs[i],
|
|
|
|
vibration_channel_intensity[i]);
|
|
|
|
|
|
|
|
if (vibration_channel_duration[i] != -1.0f) {
|
|
|
|
vibration_channel_duration[i] -= frametime;
|
|
|
|
|
|
|
|
if (vibration_channel_duration[i] < 0.0f) {
|
|
|
|
vibration_channel_duration[i] = 0.0f;
|
|
|
|
vibration_channel_intensity[i] = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vrapi_SetHapticVibrationSimple(gAppState.Ovr, controllerIDs[i], 0.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void showLoadingIcon()
|
|
|
|
{
|
|
|
|
int frameFlags = 0;
|
|
|
|
frameFlags |= VRAPI_FRAME_FLAG_FLUSH;
|
|
|
|
|
|
|
|
ovrLayerProjection2 blackLayer = vrapi_DefaultLayerBlackProjection2();
|
|
|
|
blackLayer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_INHIBIT_SRGB_FRAMEBUFFER;
|
|
|
|
|
|
|
|
ovrLayerLoadingIcon2 iconLayer = vrapi_DefaultLayerLoadingIcon2();
|
|
|
|
iconLayer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_INHIBIT_SRGB_FRAMEBUFFER;
|
|
|
|
|
|
|
|
const ovrLayerHeader2 * layers[] =
|
|
|
|
{
|
|
|
|
&blackLayer.Header,
|
|
|
|
&iconLayer.Header,
|
|
|
|
};
|
|
|
|
|
|
|
|
ovrSubmitFrameDescription2 frameDesc = {};
|
2020-09-11 21:41:18 +00:00
|
|
|
{
|
|
|
|
SDL_LockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-11 21:41:18 +00:00
|
|
|
frameDesc.Flags = frameFlags;
|
|
|
|
frameDesc.SwapInterval = 1;
|
|
|
|
frameDesc.FrameIndex = gAppState.RenderThreadFrameIndex;
|
|
|
|
frameDesc.DisplayTime = gAppState.DisplayTime[gAppState.RenderThreadFrameIndex %
|
|
|
|
MAX_TRACKING_SAMPLES];
|
|
|
|
frameDesc.LayerCount = 2;
|
|
|
|
frameDesc.Layers = layers;
|
|
|
|
|
|
|
|
gAppState.RenderThreadFrameIndex++;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-11 21:41:18 +00:00
|
|
|
SDL_UnlockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
vrapi_SubmitFrame2( gAppState.Ovr, &frameDesc );
|
2020-09-09 22:16:24 +00:00
|
|
|
}
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-09 22:16:24 +00:00
|
|
|
void Doom3Quest_getHMDOrientation() {
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-11 21:41:18 +00:00
|
|
|
//Update the main thread frame index in a thread safe way
|
|
|
|
{
|
|
|
|
SDL_LockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
|
|
|
|
gAppState.MainThreadFrameIndex = gAppState.RenderThreadFrameIndex + 1;
|
|
|
|
|
|
|
|
SDL_UnlockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
gAppState.DisplayTime[gAppState.MainThreadFrameIndex % MAX_TRACKING_SAMPLES] = vrapi_GetPredictedDisplayTime(gAppState.Ovr, gAppState.MainThreadFrameIndex);
|
|
|
|
|
|
|
|
ovrTracking2 *tracking = &gAppState.Tracking[gAppState.MainThreadFrameIndex % MAX_TRACKING_SAMPLES];
|
|
|
|
*tracking = vrapi_GetPredictedTracking2(gAppState.Ovr, gAppState.DisplayTime[gAppState.MainThreadFrameIndex % MAX_TRACKING_SAMPLES]);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-23 18:34:14 +00:00
|
|
|
//Don't update game with tracking if we are in big screen mode
|
|
|
|
if (!Doom3Quest_useScreenLayer()) {
|
|
|
|
|
|
|
|
const ovrQuatf quatHmd = tracking->HeadPose.Pose.Orientation;
|
|
|
|
const ovrVector3f positionHmd = tracking->HeadPose.Pose.Position;
|
2020-09-08 22:10:45 +00:00
|
|
|
|
2020-09-23 18:34:14 +00:00
|
|
|
vec3_t rotation = {0};
|
|
|
|
QuatToYawPitchRoll(quatHmd, rotation, vr.hmdorientation);
|
|
|
|
setHMDPosition(positionHmd.x, positionHmd.y, positionHmd.z, vr.hmdorientation[YAW]);
|
|
|
|
updateHMDOrientation();
|
|
|
|
}
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-23 18:34:14 +00:00
|
|
|
void Doom3Quest_getTrackedRemotesOrientation(int controlscheme) {
|
2020-09-09 22:16:24 +00:00
|
|
|
|
|
|
|
//Get info for tracked remotes
|
2020-09-11 21:41:18 +00:00
|
|
|
acquireTrackedRemotesData(gAppState.Ovr, gAppState.DisplayTime[gAppState.MainThreadFrameIndex % MAX_TRACKING_SAMPLES]);
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
//Call additional control schemes here
|
2020-09-23 22:06:34 +00:00
|
|
|
if (controlscheme == RIGHT_HANDED_DEFAULT) {
|
|
|
|
HandleInput_Default(controlscheme, &rightTrackedRemoteState_new,
|
|
|
|
&rightTrackedRemoteState_old, &rightRemoteTracking_new,
|
|
|
|
&leftTrackedRemoteState_new, &leftTrackedRemoteState_old,
|
|
|
|
&leftRemoteTracking_new,
|
|
|
|
ovrButton_A, ovrButton_B, ovrButton_X, ovrButton_Y);
|
|
|
|
} else {
|
|
|
|
//Left handed
|
|
|
|
HandleInput_Default(controlscheme, &leftTrackedRemoteState_new, &leftTrackedRemoteState_old,
|
|
|
|
&leftRemoteTracking_new,
|
|
|
|
&rightTrackedRemoteState_new, &rightTrackedRemoteState_old,
|
|
|
|
&rightRemoteTracking_new,
|
|
|
|
ovrButton_X, ovrButton_Y, ovrButton_A, ovrButton_B);
|
|
|
|
}
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
void Doom3Quest_submitFrame()
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ovrSubmitFrameDescription2 frameDesc = {0};
|
2020-09-09 22:16:24 +00:00
|
|
|
|
2020-09-11 21:41:18 +00:00
|
|
|
long long renderThreadFrameIndex;
|
|
|
|
{
|
|
|
|
SDL_LockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
renderThreadFrameIndex = gAppState.RenderThreadFrameIndex;
|
|
|
|
SDL_UnlockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
}
|
2020-09-09 22:16:24 +00:00
|
|
|
|
|
|
|
if (!Doom3Quest_useScreenLayer()) {
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2();
|
2020-09-11 21:41:18 +00:00
|
|
|
layer.HeadPose = gAppState.Tracking[renderThreadFrameIndex % MAX_TRACKING_SAMPLES].HeadPose;
|
2020-09-08 22:10:45 +00:00
|
|
|
for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
|
|
|
|
{
|
2020-10-07 18:05:09 +00:00
|
|
|
ovrFramebuffer * frameBuffer = &gAppState.Renderer.FrameBuffer;
|
2020-09-08 22:10:45 +00:00
|
|
|
layer.Textures[eye].ColorSwapChain = frameBuffer->ColorTextureSwapChain;
|
|
|
|
layer.Textures[eye].SwapChainIndex = frameBuffer->ReadyTextureSwapChainIndex;
|
|
|
|
|
|
|
|
ovrMatrix4f projectionMatrix;
|
|
|
|
projectionMatrix = ovrMatrix4f_CreateProjectionFov(vrFOV, vrFOV,
|
|
|
|
0.0f, 0.0f, 0.1f, 0.0f);
|
|
|
|
|
|
|
|
layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection(&projectionMatrix);
|
|
|
|
|
|
|
|
layer.Textures[eye].TextureRect.x = 0;
|
|
|
|
layer.Textures[eye].TextureRect.y = 0;
|
|
|
|
layer.Textures[eye].TextureRect.width = 1.0f;
|
|
|
|
layer.Textures[eye].TextureRect.height = 1.0f;
|
|
|
|
}
|
|
|
|
layer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_CHROMATIC_ABERRATION_CORRECTION;
|
|
|
|
|
|
|
|
// Set up the description for this frame.
|
|
|
|
const ovrLayerHeader2 *layers[] =
|
|
|
|
{
|
|
|
|
&layer.Header
|
|
|
|
};
|
|
|
|
|
|
|
|
frameDesc.Flags = 0;
|
|
|
|
frameDesc.SwapInterval = gAppState.SwapInterval;
|
2020-09-11 21:41:18 +00:00
|
|
|
frameDesc.FrameIndex = renderThreadFrameIndex;
|
|
|
|
frameDesc.DisplayTime = gAppState.DisplayTime[renderThreadFrameIndex % MAX_TRACKING_SAMPLES];
|
2020-09-08 22:10:45 +00:00
|
|
|
frameDesc.LayerCount = 1;
|
|
|
|
frameDesc.Layers = layers;
|
|
|
|
|
|
|
|
// Hand over the eye images to the time warp.
|
|
|
|
vrapi_SubmitFrame2(gAppState.Ovr, &frameDesc);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Set-up the compositor layers for this frame.
|
|
|
|
// NOTE: Multiple independent layers are allowed, but they need to be added
|
|
|
|
// in a depth consistent order.
|
|
|
|
memset( gAppState.Layers, 0, sizeof( ovrLayer_Union2 ) * ovrMaxLayerCount );
|
|
|
|
gAppState.LayerCount = 0;
|
|
|
|
|
|
|
|
// Add a simple cylindrical layer
|
|
|
|
gAppState.Layers[gAppState.LayerCount++].Cylinder =
|
|
|
|
BuildCylinderLayer(&gAppState.Scene.CylinderRenderer,
|
2020-09-11 21:41:18 +00:00
|
|
|
gAppState.Scene.CylinderWidth, gAppState.Scene.CylinderHeight, &gAppState.Tracking[renderThreadFrameIndex % MAX_TRACKING_SAMPLES], radians(playerYaw) );
|
2020-09-08 22:10:45 +00:00
|
|
|
|
|
|
|
// Compose the layers for this frame.
|
|
|
|
const ovrLayerHeader2 * layerHeaders[ovrMaxLayerCount] = { 0 };
|
|
|
|
for ( int i = 0; i < gAppState.LayerCount; i++ )
|
|
|
|
{
|
|
|
|
layerHeaders[i] = &gAppState.Layers[i].Header;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up the description for this frame.
|
|
|
|
frameDesc.Flags = 0;
|
|
|
|
frameDesc.SwapInterval = gAppState.SwapInterval;
|
2020-09-11 21:41:18 +00:00
|
|
|
frameDesc.FrameIndex = renderThreadFrameIndex;
|
|
|
|
frameDesc.DisplayTime = gAppState.DisplayTime[renderThreadFrameIndex % MAX_TRACKING_SAMPLES];
|
2020-09-08 22:10:45 +00:00
|
|
|
frameDesc.LayerCount = gAppState.LayerCount;
|
|
|
|
frameDesc.Layers = layerHeaders;
|
|
|
|
|
|
|
|
// Hand over the eye images to the time warp.
|
|
|
|
vrapi_SubmitFrame2(gAppState.Ovr, &frameDesc);
|
|
|
|
}
|
|
|
|
|
2020-09-11 21:41:18 +00:00
|
|
|
{
|
|
|
|
SDL_LockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
gAppState.RenderThreadFrameIndex++;
|
|
|
|
SDL_UnlockMutex(gAppState.RenderThreadFrameIndex_Mutex);
|
|
|
|
}
|
2020-09-08 22:10:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ovrAppThread_Create( ovrAppThread * appThread, JNIEnv * env, jobject activityObject, jclass activityClass )
|
|
|
|
{
|
|
|
|
(*env)->GetJavaVM( env, &appThread->JavaVm );
|
|
|
|
appThread->ActivityObject = (*env)->NewGlobalRef( env, activityObject );
|
|
|
|
appThread->ActivityClass = (*env)->NewGlobalRef( env, activityClass );
|
|
|
|
appThread->Thread = 0;
|
|
|
|
appThread->NativeWindow = NULL;
|
|
|
|
ovrMessageQueue_Create( &appThread->MessageQueue );
|
|
|
|
|
|
|
|
const int createErr = pthread_create( &appThread->Thread, NULL, AppThreadFunction, appThread );
|
|
|
|
if ( createErr != 0 )
|
|
|
|
{
|
|
|
|
ALOGE( "pthread_create returned %i", createErr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ovrAppThread_Destroy( ovrAppThread * appThread, JNIEnv * env )
|
|
|
|
{
|
|
|
|
pthread_join( appThread->Thread, NULL );
|
|
|
|
(*env)->DeleteGlobalRef( env, appThread->ActivityObject );
|
|
|
|
(*env)->DeleteGlobalRef( env, appThread->ActivityClass );
|
|
|
|
ovrMessageQueue_Destroy( &appThread->MessageQueue );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
Activity lifecycle
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
jmethodID android_shutdown;
|
|
|
|
static JavaVM *jVM;
|
|
|
|
static jobject shutdownCallbackObj=0;
|
|
|
|
|
|
|
|
void jni_shutdown()
|
|
|
|
{
|
|
|
|
ALOGV("Calling: jni_shutdown");
|
|
|
|
JNIEnv *env;
|
|
|
|
jobject tmp;
|
|
|
|
if (((*jVM)->GetEnv(jVM, (void**) &env, JNI_VERSION_1_4))<0)
|
|
|
|
{
|
|
|
|
(*jVM)->AttachCurrentThread(jVM,&env, NULL);
|
|
|
|
}
|
|
|
|
return (*env)->CallVoidMethod(env, shutdownCallbackObj, android_shutdown);
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT jint JNICALL SDL_JNI_OnLoad(JavaVM* vm, void* reserved);
|
|
|
|
|
|
|
|
int JNI_OnLoad(JavaVM* vm, void* reserved)
|
|
|
|
{
|
|
|
|
JNIEnv *env;
|
|
|
|
jVM = vm;
|
|
|
|
if((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
|
|
|
|
{
|
|
|
|
ALOGE("Failed JNI_OnLoad");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SDL_JNI_OnLoad(vm, reserved);
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT jlong JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onCreate( JNIEnv * env, jclass activityClass, jobject activity,
|
2020-09-08 22:10:45 +00:00
|
|
|
jstring commandLineParams)
|
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onCreate()" );
|
|
|
|
|
|
|
|
/* the global arg_xxx structs are initialised within the argtable */
|
|
|
|
void *argtable[] = {
|
|
|
|
ss = arg_dbl0("s", "supersampling", "<double>", "super sampling value (e.g. 1.0)"),
|
|
|
|
cpu = arg_int0("c", "cpu", "<int>", "CPU perf index 1-4 (default: 2)"),
|
|
|
|
gpu = arg_int0("g", "gpu", "<int>", "GPU perf index 1-4 (default: 3)"),
|
|
|
|
msaa = arg_int0("m", "msaa", "<int>", "MSAA 1-4 (default: 1)"),
|
|
|
|
ffr = arg_int0("f", "ffr", "<int>", "FFR 0-4 (default: 0)"),
|
|
|
|
refresh = arg_int0("r", "refresh", "<int>", "Display Refresh 60 or 72 (default: 72)"),
|
|
|
|
end = arg_end(20)
|
|
|
|
};
|
|
|
|
|
|
|
|
jboolean iscopy;
|
|
|
|
const char *arg = (*env)->GetStringUTFChars(env, commandLineParams, &iscopy);
|
|
|
|
|
|
|
|
|
|
|
|
char *cmdLine = NULL;
|
|
|
|
if (arg && strlen(arg))
|
|
|
|
{
|
|
|
|
cmdLine = strdup(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
(*env)->ReleaseStringUTFChars(env, commandLineParams, arg);
|
|
|
|
|
|
|
|
ALOGV("Command line %s", cmdLine);
|
|
|
|
argv = malloc(sizeof(char*) * 255);
|
|
|
|
argc = ParseCommandLine(strdup(cmdLine), argv);
|
|
|
|
|
|
|
|
/* verify the argtable[] entries were allocated sucessfully */
|
|
|
|
if (arg_nullcheck(argtable) == 0) {
|
|
|
|
/* Parse the command line as defined by argtable[] */
|
|
|
|
arg_parse(argc, argv, argtable);
|
|
|
|
|
|
|
|
if (ss->count > 0 && ss->dval[0] > 0.0)
|
|
|
|
{
|
|
|
|
SS_MULTIPLIER = ss->dval[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpu->count > 0 && cpu->ival[0] > 0 && cpu->ival[0] < 10)
|
|
|
|
{
|
|
|
|
CPU_LEVEL = cpu->ival[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gpu->count > 0 && gpu->ival[0] > 0 && gpu->ival[0] < 10)
|
|
|
|
{
|
|
|
|
GPU_LEVEL = gpu->ival[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msaa->count > 0 && msaa->ival[0] > 0 && msaa->ival[0] < 5)
|
|
|
|
{
|
|
|
|
NUM_MULTI_SAMPLES = msaa->ival[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ffr->count > 0 && ffr->ival[0] >= 0 && ffr->ival[0] <= 4)
|
|
|
|
{
|
|
|
|
FFR = ffr->ival[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (refresh->count > 0 && (refresh->ival[0] == 60 || refresh->ival[0] == 72))
|
|
|
|
{
|
|
|
|
DISPLAY_REFRESH = refresh->ival[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *) malloc( sizeof( ovrAppThread ) );
|
|
|
|
ovrAppThread_Create( appThread, env, activity, activityClass );
|
|
|
|
|
|
|
|
ovrMessageQueue_Enable( &appThread->MessageQueue, true );
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_CREATE, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
|
|
|
|
return (jlong)((size_t)appThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onStart( JNIEnv * env, jobject obj, jlong handle, jobject obj1)
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onStart()" );
|
|
|
|
|
|
|
|
shutdownCallbackObj = (jobject)(*env)->NewGlobalRef(env, obj1);
|
|
|
|
jclass callbackClass = (*env)->GetObjectClass(env, shutdownCallbackObj);
|
|
|
|
|
|
|
|
android_shutdown = (*env)->GetMethodID(env,callbackClass,"shutdown","()V");
|
|
|
|
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_START, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onResume( JNIEnv * env, jobject obj, jlong handle )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onResume()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_RESUME, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onPause( JNIEnv * env, jobject obj, jlong handle )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onPause()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_PAUSE, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onStop( JNIEnv * env, jobject obj, jlong handle )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onStop()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_STOP, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onDestroy( JNIEnv * env, jobject obj, jlong handle )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onDestroy()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_DESTROY, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
ovrMessageQueue_Enable( &appThread->MessageQueue, false );
|
|
|
|
|
|
|
|
ovrAppThread_Destroy( appThread, env );
|
|
|
|
free( appThread );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
Surface lifecycle
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onSurfaceCreated( JNIEnv * env, jobject obj, jlong handle, jobject surface )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onSurfaceCreated()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
|
|
|
|
ANativeWindow * newNativeWindow = ANativeWindow_fromSurface( env, surface );
|
|
|
|
if ( ANativeWindow_getWidth( newNativeWindow ) < ANativeWindow_getHeight( newNativeWindow ) )
|
|
|
|
{
|
|
|
|
// An app that is relaunched after pressing the home button gets an initial surface with
|
|
|
|
// the wrong orientation even though android:screenOrientation="landscape" is set in the
|
|
|
|
// manifest. The choreographer callback will also never be called for this surface because
|
|
|
|
// the surface is immediately replaced with a new surface with the correct orientation.
|
|
|
|
ALOGE( " Surface not in landscape mode!" );
|
|
|
|
}
|
|
|
|
|
|
|
|
ALOGV( " NativeWindow = ANativeWindow_fromSurface( env, surface )" );
|
|
|
|
appThread->NativeWindow = newNativeWindow;
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_SURFACE_CREATED, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessage_SetPointerParm( &message, 0, appThread->NativeWindow );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onSurfaceChanged( JNIEnv * env, jobject obj, jlong handle, jobject surface )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onSurfaceChanged()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
|
|
|
|
ANativeWindow * newNativeWindow = ANativeWindow_fromSurface( env, surface );
|
|
|
|
if ( ANativeWindow_getWidth( newNativeWindow ) < ANativeWindow_getHeight( newNativeWindow ) )
|
|
|
|
{
|
|
|
|
// An app that is relaunched after pressing the home button gets an initial surface with
|
|
|
|
// the wrong orientation even though android:screenOrientation="landscape" is set in the
|
|
|
|
// manifest. The choreographer callback will also never be called for this surface because
|
|
|
|
// the surface is immediately replaced with a new surface with the correct orientation.
|
|
|
|
ALOGE( " Surface not in landscape mode!" );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( newNativeWindow != appThread->NativeWindow )
|
|
|
|
{
|
|
|
|
if ( appThread->NativeWindow != NULL )
|
|
|
|
{
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_SURFACE_DESTROYED, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
ALOGV( " ANativeWindow_release( NativeWindow )" );
|
|
|
|
ANativeWindow_release( appThread->NativeWindow );
|
|
|
|
appThread->NativeWindow = NULL;
|
|
|
|
}
|
|
|
|
if ( newNativeWindow != NULL )
|
|
|
|
{
|
|
|
|
ALOGV( " NativeWindow = ANativeWindow_fromSurface( env, surface )" );
|
|
|
|
appThread->NativeWindow = newNativeWindow;
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_SURFACE_CREATED, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessage_SetPointerParm( &message, 0, appThread->NativeWindow );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( newNativeWindow != NULL )
|
|
|
|
{
|
|
|
|
ANativeWindow_release( newNativeWindow );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:21:43 +00:00
|
|
|
JNIEXPORT void JNICALL Java_com_drbeef_doom3quest_GLES3JNILib_onSurfaceDestroyed( JNIEnv * env, jobject obj, jlong handle )
|
2020-09-08 22:10:45 +00:00
|
|
|
{
|
|
|
|
ALOGV( " GLES3JNILib::onSurfaceDestroyed()" );
|
|
|
|
ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
|
|
|
|
ovrMessage message;
|
|
|
|
ovrMessage_Init( &message, MESSAGE_ON_SURFACE_DESTROYED, MQ_WAIT_PROCESSED );
|
|
|
|
ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
|
|
|
|
ALOGV( " ANativeWindow_release( NativeWindow )" );
|
|
|
|
ANativeWindow_release( appThread->NativeWindow );
|
|
|
|
appThread->NativeWindow = NULL;
|
|
|
|
}
|
|
|
|
|