bullet3/examples/StandaloneMain/hellovr_opengl_main.cpp
Erwin Coumans 0df8887990 improve pybullet performance of loadURDF/SDF/MJCF and 'createCollisionShape'/'createMultiBody' for GUI/VR/SHARED_MEMORY,
use p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,0) before loading and
p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,1) afterwards
2017-07-14 23:12:16 +01:00

2408 lines
70 KiB
C++

#ifdef BT_ENABLE_VR
#ifdef BT_USE_CUSTOM_PROFILER
#endif
//========= Copyright Valve Corporation ============//
#include "../OpenGLWindow/SimpleOpenGL3App.h"
#include "../OpenGLWindow/OpenGLInclude.h"
#include "Bullet3Common/b3Quaternion.h"
#include "Bullet3Common/b3Transform.h"
#include "Bullet3Common/b3CommandLineArgs.h"
#include "../Utils/b3Clock.h"
#include "../Utils/ChromeTraceUtil.h"
#include "../ExampleBrowser/OpenGLGuiHelper.h"
#include "../CommonInterfaces/CommonExampleInterface.h"
#include "../CommonInterfaces/CommonGUIHelperInterface.h"
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h"
#include "LinearMath/btIDebugDraw.h"
int gSharedMemoryKey = -1;
int gDebugDrawFlags = 0;
bool gDisplayDistortion = false;
bool gDisableDesktopGL = false;
#include <stdio.h>
#include <string>
#include <cstdlib>
#include <openvr.h>
#include "lodepng.h"
#include "Matrices.h"
#include "pathtools.h"
CommonExampleInterface* sExample;
int sPrevPacketNum=0;
OpenGLGuiHelper* sGuiPtr = 0;
static vr::VRControllerState_t sPrevStates[vr::k_unMaxTrackedDeviceCount] = { 0 };
#if defined(POSIX)
#include "unistd.h"
#endif
#ifdef _WIN32
#include <Windows.h>
#endif
void ThreadSleep( unsigned long nMilliseconds )
{
#if defined(_WIN32)
::Sleep( nMilliseconds );
#elif defined(POSIX)
usleep( nMilliseconds * 1000 );
#endif
}
class CGLRenderModel
{
public:
CGLRenderModel( const std::string & sRenderModelName );
~CGLRenderModel();
bool BInit( const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture );
void Cleanup();
void Draw();
const std::string & GetName() const { return m_sModelName; }
private:
GLuint m_glVertBuffer;
GLuint m_glIndexBuffer;
GLuint m_glVertArray;
GLuint m_glTexture;
GLsizei m_unVertexCount;
std::string m_sModelName;
};
static bool g_bPrintf = true;
//-----------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
class CMainApplication
{
public:
CMainApplication( int argc, char *argv[] );
virtual ~CMainApplication();
bool BInit();
bool BInitGL();
bool BInitCompositor();
void getControllerTransform(int unDevice, b3Transform& tr);
void SetupRenderModels();
void Shutdown();
void RunMainLoop();
bool HandleInput();
void ProcessVREvent( const vr::VREvent_t & event );
void RenderFrame();
bool SetupTexturemaps();
void SetupScene();
void AddCubeToScene( Matrix4 mat, std::vector<float> &vertdata );
void AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector<float> &vertdata );
void DrawControllers();
bool SetupStereoRenderTargets();
void SetupDistortion();
void SetupCameras();
void RenderStereoTargets();
void RenderDistortion();
void RenderScene( vr::Hmd_Eye nEye );
Matrix4 GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye );
Matrix4 GetHMDMatrixPoseEye( vr::Hmd_Eye nEye );
Matrix4 GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye );
void UpdateHMDMatrixPose();
Matrix4 ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose );
GLuint CompileGLShader( const char *pchShaderName, const char *pchVertexShader, const char *pchFragmentShader );
bool CreateAllShaders();
void SetupRenderModelForTrackedDevice( vr::TrackedDeviceIndex_t unTrackedDeviceIndex );
CGLRenderModel *FindOrLoadRenderModel( const char *pchRenderModelName );
SimpleOpenGL3App* getApp() { return m_app;}
private:
bool m_bDebugOpenGL;
bool m_bVerbose;
bool m_bPerf;
bool m_bVblank;
bool m_bGlFinishHack;
vr::IVRSystem *m_pHMD;
vr::IVRRenderModels *m_pRenderModels;
std::string m_strDriver;
std::string m_strDisplay;
vr::TrackedDevicePose_t m_rTrackedDevicePose[ vr::k_unMaxTrackedDeviceCount ];
Matrix4 m_rmat4DevicePose[ vr::k_unMaxTrackedDeviceCount ];
bool m_rbShowTrackedDevice[ vr::k_unMaxTrackedDeviceCount ];
private:
SimpleOpenGL3App* m_app;
uint32_t m_nWindowWidth;
uint32_t m_nWindowHeight;
bool m_hasContext;
b3Clock m_clock;
private: // OpenGL bookkeeping
int m_iTrackedControllerCount;
int m_iTrackedControllerCount_Last;
int m_iValidPoseCount;
int m_iValidPoseCount_Last;
bool m_bShowCubes;
std::string m_strPoseClasses; // what classes we saw poses for this frame
char m_rDevClassChar[ vr::k_unMaxTrackedDeviceCount ]; // for each device, a character representing its class
int m_iSceneVolumeWidth;
int m_iSceneVolumeHeight;
int m_iSceneVolumeDepth;
float m_fScaleSpacing;
float m_fScale;
int m_iSceneVolumeInit; // if you want something other than the default 20x20x20
float m_fNearClip;
float m_fFarClip;
GLuint m_iTexture;
unsigned int m_uiVertcount;
GLuint m_glSceneVertBuffer;
GLuint m_unSceneVAO;
GLuint m_unLensVAO;
GLuint m_glIDVertBuffer;
GLuint m_glIDIndexBuffer;
unsigned int m_uiIndexSize;
GLuint m_glControllerVertBuffer;
GLuint m_unControllerVAO;
unsigned int m_uiControllerVertcount;
Matrix4 m_mat4HMDPose;
Matrix4 m_mat4eyePosLeft;
Matrix4 m_mat4eyePosRight;
Matrix4 m_mat4ProjectionCenter;
Matrix4 m_mat4ProjectionLeft;
Matrix4 m_mat4ProjectionRight;
struct VertexDataScene
{
Vector3 position;
Vector2 texCoord;
};
struct VertexDataLens
{
Vector2 position;
Vector2 texCoordRed;
Vector2 texCoordGreen;
Vector2 texCoordBlue;
};
GLuint m_unSceneProgramID;
GLuint m_unLensProgramID;
GLuint m_unControllerTransformProgramID;
GLuint m_unRenderModelProgramID;
GLint m_nSceneMatrixLocation;
GLint m_nControllerMatrixLocation;
GLint m_nRenderModelMatrixLocation;
struct FramebufferDesc
{
GLuint m_nDepthBufferId;
GLuint m_nRenderTextureId;
GLuint m_nRenderFramebufferId;
GLuint m_nResolveTextureId;
GLuint m_nResolveFramebufferId;
};
FramebufferDesc leftEyeDesc;
FramebufferDesc rightEyeDesc;
bool CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc );
uint32_t m_nRenderWidth;
uint32_t m_nRenderHeight;
std::vector< CGLRenderModel * > m_vecRenderModels;
CGLRenderModel *m_rTrackedDeviceToRenderModel[ vr::k_unMaxTrackedDeviceCount ];
};
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CMainApplication::CMainApplication( int argc, char *argv[] )
: m_app(NULL)
, m_hasContext(false)
, m_nWindowWidth( 1280 )
, m_nWindowHeight( 720 )
, m_unSceneProgramID( 0 )
, m_unLensProgramID( 0 )
, m_unControllerTransformProgramID( 0 )
, m_unRenderModelProgramID( 0 )
, m_pHMD( NULL )
, m_pRenderModels( NULL )
, m_bDebugOpenGL( false )
, m_bVerbose( false )
, m_bPerf( false )
, m_bVblank( false )
, m_bGlFinishHack( true )
, m_glControllerVertBuffer( 0 )
, m_unControllerVAO( 0 )
, m_unLensVAO( 0 )
, m_unSceneVAO( 0 )
, m_nSceneMatrixLocation( -1 )
, m_nControllerMatrixLocation( -1 )
, m_nRenderModelMatrixLocation( -1 )
, m_iTrackedControllerCount( 0 )
, m_iTrackedControllerCount_Last( -1 )
, m_iValidPoseCount( 0 )
, m_iValidPoseCount_Last( -1 )
, m_iSceneVolumeInit( 20 )
, m_strPoseClasses("")
, m_bShowCubes( false )
{
for( int i = 1; i < argc; i++ )
{
if( !stricmp( argv[i], "-gldebug" ) )
{
m_bDebugOpenGL = true;
}
else if( !stricmp( argv[i], "-verbose" ) )
{
m_bVerbose = true;
}
else if( !stricmp( argv[i], "-novblank" ) )
{
m_bVblank = false;
}
else if( !stricmp( argv[i], "-noglfinishhack" ) )
{
m_bGlFinishHack = false;
}
else if( !stricmp( argv[i], "-noprintf" ) )
{
g_bPrintf = false;
}
else if ( !stricmp( argv[i], "-cubevolume" ) && ( argc > i + 1 ) && ( *argv[ i + 1 ] != '-' ) )
{
m_iSceneVolumeInit = atoi( argv[ i + 1 ] );
i++;
}
}
// other initialization tasks are done in BInit
memset(m_rDevClassChar, 0, sizeof(m_rDevClassChar));
};
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CMainApplication::~CMainApplication()
{
// work is done in Shutdown
b3Printf( "Shutdown" );
}
//-----------------------------------------------------------------------------
// Purpose: Helper to get a string from a tracked device property and turn it
// into a std::string
//-----------------------------------------------------------------------------
std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL )
{
uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError );
if( unRequiredBufferLen == 0 )
return "";
char *pchBuffer = new char[ unRequiredBufferLen ];
unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
std::string sResult = pchBuffer;
delete [] pchBuffer;
return sResult;
}
b3KeyboardCallback prevKeyboardCallback = 0;
void MyKeyboardCallback(int key, int state)
{
if (key == 'p')
{
if (state)
{
b3ChromeUtilsStartTimings();
}
else
{
b3ChromeUtilsStopTimingsAndWriteJsonFile("timings");
}
}
if (sExample)
{
sExample->keyboardCallback(key,state);
}
if (prevKeyboardCallback)
prevKeyboardCallback(key,state);
}
#include "../SharedMemory/SharedMemoryPublic.h"
extern bool useShadowMap;
static bool gEnableVRRenderControllers=true;
static bool gEnableVRRendering = true;
void VRPhysicsServerVisualizerFlagCallback(int flag, bool enable)
{
if (flag == COV_ENABLE_SHADOWS)
{
useShadowMap = enable;
}
if (flag == COV_ENABLE_GUI)
{
//there is no regular GUI here, but disable the
}
if (flag == COV_ENABLE_VR_RENDER_CONTROLLERS)
{
gEnableVRRenderControllers = enable;
}
if (flag == COV_ENABLE_RENDERING)
{
gEnableVRRendering = enable;
}
if (flag == COV_ENABLE_WIREFRAME)
{
if (enable)
{
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
//gDebugDrawFlags |= btIDebugDraw::DBG_DrawWireframe;
} else
{
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
//gDebugDrawFlags &= ~btIDebugDraw::DBG_DrawWireframe;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::BInit()
{
// Loading the SteamVR Runtime
vr::EVRInitError eError = vr::VRInitError_None;
m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Scene );
if ( eError != vr::VRInitError_None )
{
m_pHMD = NULL;
char buf[1024];
sprintf_s( buf, sizeof( buf ), "Unable to init VR runtime: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) );
b3Warning( "VR_Init Failed %s", buf);
return false;
}
m_pRenderModels = (vr::IVRRenderModels *)vr::VR_GetGenericInterface( vr::IVRRenderModels_Version, &eError );
if( !m_pRenderModels )
{
m_pHMD = NULL;
vr::VR_Shutdown();
char buf[1024];
sprintf_s( buf, sizeof( buf ), "Unable to get render model interface: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) );
b3Warning( "VR_Init Failed %s", buf);
return false;
}
// int nWindowPosX = 700;
// int nWindowPosY = 100;
m_nWindowWidth = 1280;
m_nWindowHeight = 720;
/*
//SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 0 );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 0 );
if( m_bDebugOpenGL )
SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG );
*/
m_app = new SimpleOpenGL3App("SimpleOpenGL3App",m_nWindowWidth,m_nWindowHeight,true);
sGuiPtr = new OpenGLGuiHelper(m_app,false);
sGuiPtr->setVisualizerFlagCallback(VRPhysicsServerVisualizerFlagCallback);
sGuiPtr->setVRMode(true);
//sGuiPtr = new DummyGUIHelper;
prevKeyboardCallback = m_app->m_window->getKeyboardCallback();
m_app->m_window->setKeyboardCallback(MyKeyboardCallback);
CommonExampleOptions options(sGuiPtr);
sExample = StandaloneExampleCreateFunc(options);
sExample->initPhysics();
sExample->resetCamera();
#if 0
int cubeIndex = m_app->registerCubeShape(1,1,1);
b3Quaternion orn(0,0,0,1);
{
b3Vector3 color=b3MakeVector3(0.3,0.3,0.6);
b3Vector3 pos = b3MakeVector3(0,0,0);
b3Vector3 scaling=b3MakeVector3 (1,.1,1);
m_app->m_renderer->registerGraphicsInstance(cubeIndex,pos,orn,color,scaling);
}
{
b3Vector3 color=b3MakeVector3(0.3,0.6,0.3);
b3Vector3 pos = b3MakeVector3(0,0.3,0);
b3Vector3 scaling=b3MakeVector3 (.1,.1,.1);
m_app->m_renderer->registerGraphicsInstance(cubeIndex,pos,orn,color,scaling);
}
#endif
m_app->m_renderer->writeTransforms();
/* if (m_pWindow == NULL)
{
printf( "%s - Window could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() );
return false;
}
*/
/*m_pContext = SDL_GL_CreateContext(m_pWindow);
if (m_pContext == NULL)
{
printf( "%s - OpenGL context could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() );
return false;
}
glewExperimental = GL_TRUE;
GLenum nGlewError = glewInit();
if (nGlewError != GLEW_OK)
{
printf( "%s - Error initializing GLEW! %s\n", __FUNCTION__, glewGetErrorString( nGlewError ) );
return false;
}
glGetError(); // to clear the error caused deep in GLEW
if ( SDL_GL_SetSwapInterval( m_bVblank ? 1 : 0 ) < 0 )
{
printf( "%s - Warning: Unable to set VSync! SDL Error: %s\n", __FUNCTION__, SDL_GetError() );
return false;
}
*/
m_strDriver = "No Driver";
m_strDisplay = "No Display";
m_strDriver = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String );
m_strDisplay = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String );
std::string strWindowTitle = "hellovr_bullet - " + m_strDriver + " " + m_strDisplay;
m_app->m_window->setWindowTitle(strWindowTitle.c_str() );
// cube array
m_iSceneVolumeWidth = m_iSceneVolumeInit;
m_iSceneVolumeHeight = m_iSceneVolumeInit;
m_iSceneVolumeDepth = m_iSceneVolumeInit;
m_fScale = 0.3f;
m_fScaleSpacing = 4.0f;
m_fNearClip = 0.1f;
m_fFarClip = 3000.0f;
m_iTexture = 0;
m_uiVertcount = 0;
// m_MillisecondsTimer.start(1, this);
// m_SecondsTimer.start(1000, this);
if (!BInitGL())
{
printf("%s - Unable to initialize OpenGL!\n", __FUNCTION__);
return false;
}
if (!BInitCompositor())
{
printf("%s - Failed to initialize VR Compositor!\n", __FUNCTION__);
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
/*void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* message, const void* userParam)
{
b3Printf( "GL Error: %s\n", message );
}
*/
static void APIENTRY DebugCallback (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam)
{
b3Printf( "GL Error: %s\n", message );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::BInitGL()
{
if( m_bDebugOpenGL )
{
const GLvoid *userParam=0;
glDebugMessageCallback(DebugCallback, userParam);
glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE );
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
if( !CreateAllShaders() )
return false;
SetupTexturemaps();
SetupScene();
SetupCameras();
SetupStereoRenderTargets();
SetupDistortion();
SetupRenderModels();
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::BInitCompositor()
{
vr::EVRInitError peError = vr::VRInitError_None;
if ( !vr::VRCompositor() )
{
printf( "Compositor initialization failed. See log file for details\n" );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::Shutdown()
{
if( m_pHMD )
{
vr::VR_Shutdown();
m_pHMD = NULL;
}
for( std::vector< CGLRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ )
{
delete (*i);
}
m_vecRenderModels.clear();
if( m_hasContext)
{
if (m_glSceneVertBuffer)
{
glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE );
glDebugMessageCallback(nullptr, nullptr);
glDeleteBuffers(1, &m_glSceneVertBuffer);
glDeleteBuffers(1, &m_glIDVertBuffer);
glDeleteBuffers(1, &m_glIDIndexBuffer);
}
if ( m_unSceneProgramID )
{
glDeleteProgram( m_unSceneProgramID );
}
if ( m_unControllerTransformProgramID )
{
glDeleteProgram( m_unControllerTransformProgramID );
}
if ( m_unRenderModelProgramID )
{
glDeleteProgram( m_unRenderModelProgramID );
}
if ( m_unLensProgramID )
{
glDeleteProgram( m_unLensProgramID );
}
glDeleteRenderbuffers( 1, &leftEyeDesc.m_nDepthBufferId );
glDeleteTextures( 1, &leftEyeDesc.m_nRenderTextureId );
glDeleteFramebuffers( 1, &leftEyeDesc.m_nRenderFramebufferId );
glDeleteTextures( 1, &leftEyeDesc.m_nResolveTextureId );
glDeleteFramebuffers( 1, &leftEyeDesc.m_nResolveFramebufferId );
glDeleteRenderbuffers( 1, &rightEyeDesc.m_nDepthBufferId );
glDeleteTextures( 1, &rightEyeDesc.m_nRenderTextureId );
glDeleteFramebuffers( 1, &rightEyeDesc.m_nRenderFramebufferId );
glDeleteTextures( 1, &rightEyeDesc.m_nResolveTextureId );
glDeleteFramebuffers( 1, &rightEyeDesc.m_nResolveFramebufferId );
if( m_unLensVAO != 0 )
{
glDeleteVertexArrays( 1, &m_unLensVAO );
}
if( m_unSceneVAO != 0 )
{
glDeleteVertexArrays( 1, &m_unSceneVAO );
}
if( m_unControllerVAO != 0 )
{
glDeleteVertexArrays( 1, &m_unControllerVAO );
}
}
if (sExample)
{
sExample->exitPhysics();
delete sExample;
}
delete m_app;
m_app=0;
}
void CMainApplication::getControllerTransform(int unDevice, b3Transform& tr)
{
const Matrix4 & matOrg = m_rmat4DevicePose[unDevice];
tr.setIdentity();
tr.setOrigin(b3MakeVector3(matOrg[12],matOrg[13],matOrg[14]));//pos[1]));
b3Matrix3x3 bmat;
for (int i=0;i<3;i++)
{
for (int j=0;j<3;j++)
{
bmat[i][j] = matOrg[i+4*j];
}
}
tr.setBasis(bmat);
b3Transform y2z;
y2z.setIdentity();
y2z.setRotation(b3Quaternion(0,B3_HALF_PI,0));
tr = y2z*tr;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::HandleInput()
{
bool bRet = false;
// Process SteamVR events
vr::VREvent_t event;
while( m_pHMD->PollNextEvent( &event, sizeof( event ) ) )
{
ProcessVREvent( event );
}
// Process SteamVR controller state
for( vr::TrackedDeviceIndex_t unDevice = 0; unDevice < vr::k_unMaxTrackedDeviceCount; unDevice++ )
{
vr::VRControllerState_t state;
if( m_pHMD->GetControllerState( unDevice, &state ,sizeof(vr::VRControllerState_t)) )
{
b3Transform tr;
getControllerTransform(unDevice, tr);
float pos[3] = { tr.getOrigin()[0], tr.getOrigin()[1], tr.getOrigin()[2] };
b3Quaternion born = tr.getRotation();
float orn[4] = { born[0], born[1], born[2], born[3] };
//we need to have the 'move' events, so no early out here
//if (sPrevStates[unDevice].unPacketNum != state.unPacketNum)
if( m_pHMD->GetTrackedDeviceClass( unDevice) == vr::TrackedDeviceClass_HMD )
{
Matrix4 rotYtoZ = rotYtoZ.identity();
//some Bullet apps (especially robotics related) require Z as up-axis)
if (m_app->getUpAxis()==2)
{
rotYtoZ.rotateX(-90);
}
Matrix4 viewMatCenter = m_mat4HMDPose * rotYtoZ;
const float* mat = viewMatCenter.invertAffine().get();
pos[0] = mat[12];
pos[1] = mat[13];
pos[2] = mat[14];
b3Matrix3x3 bmat;
for (int i=0;i<3;i++)
{
for (int j=0;j<3;j++)
{
bmat[i][j] = mat[i+4*j];
}
}
b3Quaternion orn2;
bmat.getRotation(orn2);
orn[0] = orn2[0];
orn[1] = orn2[1];
orn[2] = orn2[2];
orn[3] = orn2[3];
sExample->vrHMDMoveCallback(unDevice, pos,orn);
}
if( m_pHMD->GetTrackedDeviceClass( unDevice) == vr::TrackedDeviceClass_GenericTracker )
{
sExample->vrGenericTrackerMoveCallback(unDevice, pos,orn);
}
if( m_pHMD->GetTrackedDeviceClass( unDevice) == vr::TrackedDeviceClass_Controller )
{
sPrevStates[unDevice].unPacketNum = state.unPacketNum;
for (int button = 0; button < vr::k_EButton_Max; button++)
{
uint64_t trigger = vr::ButtonMaskFromId((vr::EVRButtonId)button);
bool isTrigger = (state.ulButtonPressed&trigger) != 0;
if (isTrigger)
{
//pressed now, not pressed before -> raise a button down event
if ((sPrevStates[unDevice].ulButtonPressed&trigger)==0)
{
// printf("Device PRESSED: %d, button %d\n", unDevice, button);
if (button==2)
{
//glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
///todo(erwincoumans) can't use reguar debug drawer, because physics/graphics are not in sync
///so it can (and likely will) cause crashes
///add a special debug drawer that deals with this
//gDebugDrawFlags = btIDebugDraw::DBG_DrawWireframe+btIDebugDraw::DBG_DrawContactPoints+
//btIDebugDraw::DBG_DrawConstraintLimits+
//btIDebugDraw::DBG_DrawConstraints
//;
//gDebugDrawFlags = btIDebugDraw::DBG_DrawFrames;
}
sExample->vrControllerButtonCallback(unDevice, button, 1, pos, orn);
}
else
{
// printf("Device MOVED: %d\n", unDevice);
sExample->vrControllerMoveCallback(unDevice, pos, orn, state.rAxis[1].x);
}
}
else
{
if( m_pHMD->GetTrackedDeviceClass( unDevice) == vr::TrackedDeviceClass_Controller )
{
// printf("Device RELEASED: %d, button %d\n", unDevice,button);
//not pressed now, but pressed before -> raise a button up event
if ((sPrevStates[unDevice].ulButtonPressed&trigger) != 0)
{
if (button==2)
{
gDebugDrawFlags = 0;
}
sExample->vrControllerButtonCallback(unDevice, button, 0, pos, orn);
} else
{
sExample->vrControllerMoveCallback(unDevice, pos, orn, state.rAxis[1].x);
}
}
}
}
}
// m_rbShowTrackedDevice[ unDevice ] = state.ulButtonPressed == 0;
}
sPrevStates[unDevice] = state;
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RunMainLoop()
{
bool bQuit = false;
while ( !bQuit && !m_app->m_window->requestedExit())
{
b3ChromeUtilsEnableProfiling();
if (gEnableVRRendering)
{
B3_PROFILE("main");
bQuit = HandleInput();
RenderFrame();
} else
{
b3Clock::usleep(0);
sExample->updateGraphics();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Processes a single VR event
//-----------------------------------------------------------------------------
void CMainApplication::ProcessVREvent( const vr::VREvent_t & event )
{
switch( event.eventType )
{
case vr::VREvent_TrackedDeviceActivated:
{
SetupRenderModelForTrackedDevice( event.trackedDeviceIndex );
b3Printf( "Device %u attached. Setting up render model.\n", event.trackedDeviceIndex );
}
break;
case vr::VREvent_TrackedDeviceDeactivated:
{
b3Printf( "Device %u detached.\n", event.trackedDeviceIndex );
}
break;
case vr::VREvent_TrackedDeviceUpdated:
{
b3Printf( "Device %u updated.\n", event.trackedDeviceIndex );
}
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderFrame()
{
// for now as fast as possible
if ( m_pHMD )
{
{
B3_PROFILE("DrawControllers");
DrawControllers();
}
RenderStereoTargets();
if (!gDisableDesktopGL)
{
if (gDisplayDistortion)
{
B3_PROFILE("RenderDistortion");
RenderDistortion();
} else
{
//todo: should use framebuffer_multisample_blit_scaled
//See https://twitter.com/id_aa_carmack/status/268488838425481217?lang=en
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDisable( GL_MULTISAMPLE );
glBindFramebuffer(GL_READ_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId );
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight,
GL_COLOR_BUFFER_BIT,
GL_LINEAR );
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
}
}
vr::Texture_t leftEyeTexture = {(void*)leftEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
vr::VRCompositor()->Submit(vr::Eye_Left, &leftEyeTexture );
vr::Texture_t rightEyeTexture = {(void*)rightEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
vr::VRCompositor()->Submit(vr::Eye_Right, &rightEyeTexture );
}
if ( m_bVblank && m_bGlFinishHack )
{
B3_PROFILE("bGlFinishHack");
//$ HACKHACK. From gpuview profiling, it looks like there is a bug where two renders and a present
// happen right before and after the vsync causing all kinds of jittering issues. This glFinish()
// appears to clear that up. Temporary fix while I try to get nvidia to investigate this problem.
// 1/29/2014 mikesart
//glFinish();
}
// SwapWindow
{
B3_PROFILE("m_app->swapBuffer");
if (!gDisableDesktopGL)
{
m_app->swapBuffer();
}
//SDL_GL_SwapWindow( m_pWindow );
}
// Clear
{
B3_PROFILE("glClearColor");
// We want to make sure the glFinish waits for the entire present to complete, not just the submission
// of the command. So, we do a clear here right here so the glFinish will wait fully for the swap.
glClearColor( 0, 0, 0, 1 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}
// Flush and wait for swap.
if ( m_bVblank )
{
B3_PROFILE("glFlushglFinish");
glFlush();
glFinish();
}
// Spew out the controller and pose count whenever they change.
if ( m_iTrackedControllerCount != m_iTrackedControllerCount_Last || m_iValidPoseCount != m_iValidPoseCount_Last )
{
B3_PROFILE("debug pose");
m_iValidPoseCount_Last = m_iValidPoseCount;
m_iTrackedControllerCount_Last = m_iTrackedControllerCount;
b3Printf( "PoseCount:%d(%s) Controllers:%d\n", m_iValidPoseCount, m_strPoseClasses.c_str(), m_iTrackedControllerCount );
}
{
B3_PROFILE("UpdateHMDMatrixPose");
UpdateHMDMatrixPose();
}
}
//-----------------------------------------------------------------------------
// Purpose: Compiles a GL shader program and returns the handle. Returns 0 if
// the shader couldn't be compiled for some reason.
//-----------------------------------------------------------------------------
GLuint CMainApplication::CompileGLShader( const char *pchShaderName, const char *pchVertexShader, const char *pchFragmentShader )
{
GLuint unProgramID = glCreateProgram();
GLuint nSceneVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource( nSceneVertexShader, 1, &pchVertexShader, NULL);
glCompileShader( nSceneVertexShader );
GLint vShaderCompiled = GL_FALSE;
glGetShaderiv( nSceneVertexShader, GL_COMPILE_STATUS, &vShaderCompiled);
if ( vShaderCompiled != GL_TRUE)
{
b3Printf("%s - Unable to compile vertex shader %d!\n", pchShaderName, nSceneVertexShader);
glDeleteProgram( unProgramID );
glDeleteShader( nSceneVertexShader );
return 0;
}
glAttachShader( unProgramID, nSceneVertexShader);
glDeleteShader( nSceneVertexShader ); // the program hangs onto this once it's attached
GLuint nSceneFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource( nSceneFragmentShader, 1, &pchFragmentShader, NULL);
glCompileShader( nSceneFragmentShader );
GLint fShaderCompiled = GL_FALSE;
glGetShaderiv( nSceneFragmentShader, GL_COMPILE_STATUS, &fShaderCompiled);
if (fShaderCompiled != GL_TRUE)
{
b3Printf("%s - Unable to compile fragment shader %d!\n", pchShaderName, nSceneFragmentShader );
glDeleteProgram( unProgramID );
glDeleteShader( nSceneFragmentShader );
return 0;
}
glAttachShader( unProgramID, nSceneFragmentShader );
glDeleteShader( nSceneFragmentShader ); // the program hangs onto this once it's attached
glLinkProgram( unProgramID );
GLint programSuccess = GL_TRUE;
glGetProgramiv( unProgramID, GL_LINK_STATUS, &programSuccess);
if ( programSuccess != GL_TRUE )
{
b3Printf("%s - Error linking program %d!\n", pchShaderName, unProgramID);
glDeleteProgram( unProgramID );
return 0;
}
glUseProgram( unProgramID );
glUseProgram( 0 );
return unProgramID;
}
//-----------------------------------------------------------------------------
// Purpose: Creates all the shaders used by HelloVR SDL
//-----------------------------------------------------------------------------
bool CMainApplication::CreateAllShaders()
{
m_unSceneProgramID = CompileGLShader(
"Scene",
// Vertex Shader
"#version 410\n"
"uniform mat4 matrix;\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec2 v2UVcoordsIn;\n"
"layout(location = 2) in vec3 v3NormalIn;\n"
"out vec2 v2UVcoords;\n"
"void main()\n"
"{\n"
" v2UVcoords = v2UVcoordsIn;\n"
" gl_Position = matrix * position;\n"
"}\n",
// Fragment Shader
"#version 410 core\n"
"uniform sampler2D mytexture;\n"
"in vec2 v2UVcoords;\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
" outputColor = texture(mytexture, v2UVcoords);\n"
"}\n"
);
m_nSceneMatrixLocation = glGetUniformLocation( m_unSceneProgramID, "matrix" );
if( m_nSceneMatrixLocation == -1 )
{
b3Printf( "Unable to find matrix uniform in scene shader\n" );
return false;
}
m_unControllerTransformProgramID = CompileGLShader(
"Controller",
// vertex shader
"#version 410\n"
"uniform mat4 matrix;\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec3 v3ColorIn;\n"
"out vec4 v4Color;\n"
"void main()\n"
"{\n"
" v4Color.xyz = v3ColorIn; v4Color.a = 1.0;\n"
" gl_Position = matrix * position;\n"
"}\n",
// fragment shader
"#version 410\n"
"in vec4 v4Color;\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
" outputColor = v4Color;\n"
"}\n"
);
m_nControllerMatrixLocation = glGetUniformLocation( m_unControllerTransformProgramID, "matrix" );
if( m_nControllerMatrixLocation == -1 )
{
b3Printf( "Unable to find matrix uniform in controller shader\n" );
return false;
}
m_unRenderModelProgramID = CompileGLShader(
"render model",
// vertex shader
"#version 410\n"
"uniform mat4 matrix;\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec3 v3NormalIn;\n"
"layout(location = 2) in vec2 v2TexCoordsIn;\n"
"out vec2 v2TexCoord;\n"
"void main()\n"
"{\n"
" v2TexCoord = v2TexCoordsIn;\n"
" gl_Position = matrix * vec4(position.xyz, 1);\n"
"}\n",
//fragment shader
"#version 410 core\n"
"uniform sampler2D diffuse;\n"
"in vec2 v2TexCoord;\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
" outputColor = texture( diffuse, v2TexCoord);\n"
"}\n"
);
m_nRenderModelMatrixLocation = glGetUniformLocation( m_unRenderModelProgramID, "matrix" );
if( m_nRenderModelMatrixLocation == -1 )
{
b3Printf( "Unable to find matrix uniform in render model shader\n" );
return false;
}
m_unLensProgramID = CompileGLShader(
"Distortion",
// vertex shader
"#version 410 core\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec2 v2UVredIn;\n"
"layout(location = 2) in vec2 v2UVGreenIn;\n"
"layout(location = 3) in vec2 v2UVblueIn;\n"
"noperspective out vec2 v2UVred;\n"
"noperspective out vec2 v2UVgreen;\n"
"noperspective out vec2 v2UVblue;\n"
"void main()\n"
"{\n"
" v2UVred = v2UVredIn;\n"
" v2UVgreen = v2UVGreenIn;\n"
" v2UVblue = v2UVblueIn;\n"
" gl_Position = position;\n"
"}\n",
// fragment shader
"#version 410 core\n"
"uniform sampler2D mytexture;\n"
"noperspective in vec2 v2UVred;\n"
"noperspective in vec2 v2UVgreen;\n"
"noperspective in vec2 v2UVblue;\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
" float fBoundsCheck = ( (dot( vec2( lessThan( v2UVgreen.xy, vec2(0.05, 0.05)) ), vec2(1.0, 1.0))+dot( vec2( greaterThan( v2UVgreen.xy, vec2( 0.95, 0.95)) ), vec2(1.0, 1.0))) );\n"
" if( fBoundsCheck > 1.0 )\n"
" { outputColor = vec4( 0, 0, 0, 1.0 ); }\n"
" else\n"
" {\n"
" float red = texture(mytexture, v2UVred).x;\n"
" float green = texture(mytexture, v2UVgreen).y;\n"
" float blue = texture(mytexture, v2UVblue).z;\n"
" outputColor = vec4( red, green, blue, 1.0 ); }\n"
"}\n"
);
return m_unSceneProgramID != 0
&& m_unControllerTransformProgramID != 0
&& m_unRenderModelProgramID != 0
&& m_unLensProgramID != 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::SetupTexturemaps()
{
std::string sExecutableDirectory = Path_StripFilename( Path_GetExecutablePath() );
std::string strFullPath = Path_MakeAbsolute( "../cube_texture.png", sExecutableDirectory );
std::vector<unsigned char> imageRGBA;
unsigned nImageWidth, nImageHeight;
unsigned nError = lodepng::decode( imageRGBA, nImageWidth, nImageHeight, strFullPath.c_str() );
if ( nError != 0 )
return false;
glGenTextures(1, &m_iTexture );
glBindTexture( GL_TEXTURE_2D, m_iTexture );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nImageWidth, nImageHeight,
0, GL_RGBA, GL_UNSIGNED_BYTE, &imageRGBA[0] );
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
glBindTexture( GL_TEXTURE_2D, 0 );
return ( m_iTexture != 0 );
}
//-----------------------------------------------------------------------------
// Purpose: create a sea of cubes
//-----------------------------------------------------------------------------
void CMainApplication::SetupScene()
{
if ( !m_pHMD )
return;
std::vector<float> vertdataarray;
Matrix4 matScale;
matScale.scale( m_fScale, m_fScale, m_fScale );
Matrix4 matTransform;
matTransform.translate(
-( (float)m_iSceneVolumeWidth * m_fScaleSpacing ) / 2.f,
-( (float)m_iSceneVolumeHeight * m_fScaleSpacing ) / 2.f,
-( (float)m_iSceneVolumeDepth * m_fScaleSpacing ) / 2.f);
Matrix4 mat = matScale * matTransform;
for( int z = 0; z< m_iSceneVolumeDepth; z++ )
{
for( int y = 0; y< m_iSceneVolumeHeight; y++ )
{
for( int x = 0; x< m_iSceneVolumeWidth; x++ )
{
AddCubeToScene( mat, vertdataarray );
mat = mat * Matrix4().translate( m_fScaleSpacing, 0, 0 );
}
mat = mat * Matrix4().translate( -((float)m_iSceneVolumeWidth) * m_fScaleSpacing, m_fScaleSpacing, 0 );
}
mat = mat * Matrix4().translate( 0, -((float)m_iSceneVolumeHeight) * m_fScaleSpacing, m_fScaleSpacing );
}
m_uiVertcount = vertdataarray.size()/5;
glGenVertexArrays( 1, &m_unSceneVAO );
glBindVertexArray( m_unSceneVAO );
glGenBuffers( 1, &m_glSceneVertBuffer );
glBindBuffer( GL_ARRAY_BUFFER, m_glSceneVertBuffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(float) * vertdataarray.size(), &vertdataarray[0], GL_STATIC_DRAW);
glBindBuffer( GL_ARRAY_BUFFER, m_glSceneVertBuffer );
GLsizei stride = sizeof(VertexDataScene);
uintptr_t offset = 0;
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride , (const void *)offset);
offset += sizeof(Vector3);
glEnableVertexAttribArray( 1 );
glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, stride, (const void *)offset);
glBindVertexArray( 0 );
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
m_hasContext = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector<float> &vertdata )
{
vertdata.push_back( fl0 );
vertdata.push_back( fl1 );
vertdata.push_back( fl2 );
vertdata.push_back( fl3 );
vertdata.push_back( fl4 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::AddCubeToScene( Matrix4 mat, std::vector<float> &vertdata )
{
// Matrix4 mat( outermat.data() );
Vector4 A = mat * Vector4( 0, 0, 0, 1 );
Vector4 B = mat * Vector4( 1, 0, 0, 1 );
Vector4 C = mat * Vector4( 1, 1, 0, 1 );
Vector4 D = mat * Vector4( 0, 1, 0, 1 );
Vector4 E = mat * Vector4( 0, 0, 1, 1 );
Vector4 F = mat * Vector4( 1, 0, 1, 1 );
Vector4 G = mat * Vector4( 1, 1, 1, 1 );
Vector4 H = mat * Vector4( 0, 1, 1, 1 );
// triangles instead of quads
AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); //Front
AddCubeVertex( F.x, F.y, F.z, 1, 1, vertdata );
AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata );
AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata );
AddCubeVertex( H.x, H.y, H.z, 0, 0, vertdata );
AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata );
AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); //Back
AddCubeVertex( A.x, A.y, A.z, 1, 1, vertdata );
AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata );
AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata );
AddCubeVertex( C.x, C.y, C.z, 0, 0, vertdata );
AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata );
AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); //Top
AddCubeVertex( G.x, G.y, G.z, 1, 1, vertdata );
AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata );
AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata );
AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata );
AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata );
AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Bottom
AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata );
AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata );
AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata );
AddCubeVertex( E.x, E.y, E.z, 0, 0, vertdata );
AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata );
AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Left
AddCubeVertex( E.x, E.y, E.z, 1, 1, vertdata );
AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata );
AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata );
AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata );
AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata );
AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); //Right
AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata );
AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata );
AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata );
AddCubeVertex( G.x, G.y, G.z, 0, 0, vertdata );
AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata );
}
//-----------------------------------------------------------------------------
// Purpose: Draw all of the controllers as X/Y/Z lines
//-----------------------------------------------------------------------------
extern int gGraspingController;
void CMainApplication::DrawControllers()
{
// don't draw controllers if somebody else has input focus
if( m_pHMD->IsInputFocusCapturedByAnotherProcess() )
return;
std::vector<float> vertdataarray;
m_uiControllerVertcount = 0;
m_iTrackedControllerCount = 0;
for ( vr::TrackedDeviceIndex_t unTrackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; ++unTrackedDevice )
{
if ( !m_pHMD->IsTrackedDeviceConnected( unTrackedDevice ) )
continue;
if( m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) != vr::TrackedDeviceClass_Controller )
continue;
m_iTrackedControllerCount += 1;
if( !m_rTrackedDevicePose[ unTrackedDevice ].bPoseIsValid )
continue;
const Matrix4 & mat = m_rmat4DevicePose[unTrackedDevice];
Vector4 center = mat * Vector4( 0, 0, 0, 1 );
for ( int i = 0; i < 3; ++i )
{
Vector3 color( 0, 0, 0 );
Vector4 point( 0, 0, 0, 1 );
point[i] += 0.05f; // offset in X, Y, Z
color[i] = 1.0; // R, G, B
point = mat * point;
vertdataarray.push_back( center.x );
vertdataarray.push_back( center.y );
vertdataarray.push_back( center.z );
vertdataarray.push_back( color.x );
vertdataarray.push_back( color.y );
vertdataarray.push_back( color.z );
vertdataarray.push_back( point.x );
vertdataarray.push_back( point.y );
vertdataarray.push_back( point.z );
vertdataarray.push_back( color.x );
vertdataarray.push_back( color.y );
vertdataarray.push_back( color.z );
m_uiControllerVertcount += 2;
}
Vector4 start = mat * Vector4( 0, 0, -0.02f, 1 );
Vector4 end = mat * Vector4( 0, 0, -39.f, 1 );
Vector3 color( .92f, .92f, .71f );
vertdataarray.push_back( start.x );vertdataarray.push_back( start.y );vertdataarray.push_back( start.z );
vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z );
vertdataarray.push_back( end.x );vertdataarray.push_back( end.y );vertdataarray.push_back( end.z );
vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z );
m_uiControllerVertcount += 2;
}
// Setup the VAO the first time through.
if ( m_unControllerVAO == 0 )
{
glGenVertexArrays( 1, &m_unControllerVAO );
glBindVertexArray( m_unControllerVAO );
glGenBuffers( 1, &m_glControllerVertBuffer );
glBindBuffer( GL_ARRAY_BUFFER, m_glControllerVertBuffer );
GLuint stride = 2 * 3 * sizeof( float );
GLuint offset = 0;
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset);
offset += sizeof( Vector3 );
glEnableVertexAttribArray( 1 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset);
glBindVertexArray( 0 );
}
glBindBuffer( GL_ARRAY_BUFFER, m_glControllerVertBuffer );
// set vertex data if we have some
if( vertdataarray.size() > 0 )
{
//$ TODO: Use glBufferSubData for this...
glBufferData( GL_ARRAY_BUFFER, sizeof(float) * vertdataarray.size(), &vertdataarray[0], GL_STREAM_DRAW );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::SetupCameras()
{
m_mat4ProjectionLeft = GetHMDMatrixProjectionEye( vr::Eye_Left );
m_mat4ProjectionRight = GetHMDMatrixProjectionEye( vr::Eye_Right );
m_mat4eyePosLeft = GetHMDMatrixPoseEye( vr::Eye_Left );
m_mat4eyePosRight = GetHMDMatrixPoseEye( vr::Eye_Right );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc )
{
glGenFramebuffers(1, &framebufferDesc.m_nRenderFramebufferId );
glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nRenderFramebufferId);
glGenRenderbuffers(1, &framebufferDesc.m_nDepthBufferId);
glBindRenderbuffer(GL_RENDERBUFFER, framebufferDesc.m_nDepthBufferId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, nWidth, nHeight );
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, framebufferDesc.m_nDepthBufferId );
glGenTextures(1, &framebufferDesc.m_nRenderTextureId );
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebufferDesc.m_nRenderTextureId );
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, nWidth, nHeight, true);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, framebufferDesc.m_nRenderTextureId, 0);
glGenFramebuffers(1, &framebufferDesc.m_nResolveFramebufferId );
glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nResolveFramebufferId);
glGenTextures(1, &framebufferDesc.m_nResolveTextureId );
glBindTexture(GL_TEXTURE_2D, framebufferDesc.m_nResolveTextureId );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferDesc.m_nResolveTextureId, 0);
// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
return false;
}
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::SetupStereoRenderTargets()
{
if ( !m_pHMD )
return false;
m_pHMD->GetRecommendedRenderTargetSize( &m_nRenderWidth, &m_nRenderHeight );
CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, leftEyeDesc );
CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, rightEyeDesc );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::SetupDistortion()
{
if ( !m_pHMD )
return;
GLushort m_iLensGridSegmentCountH = 43;
GLushort m_iLensGridSegmentCountV = 43;
float w = (float)( 1.0/float(m_iLensGridSegmentCountH-1));
float h = (float)( 1.0/float(m_iLensGridSegmentCountV-1));
float u, v = 0;
std::vector<VertexDataLens> vVerts(0);
VertexDataLens vert;
//left eye distortion verts
float Xoffset = -1;
for( int y=0; y<m_iLensGridSegmentCountV; y++ )
{
for( int x=0; x<m_iLensGridSegmentCountH; x++ )
{
u = x*w; v = 1-y*h;
vert.position = Vector2( Xoffset+u, -1+2*y*h );
vr::DistortionCoordinates_t dc0;
bool result = m_pHMD->ComputeDistortion(vr::Eye_Left, u, v,&dc0);
btAssert(result);
vert.texCoordRed = Vector2(dc0.rfRed[0], 1 - dc0.rfRed[1]);
vert.texCoordGreen = Vector2(dc0.rfGreen[0], 1 - dc0.rfGreen[1]);
vert.texCoordBlue = Vector2(dc0.rfBlue[0], 1 - dc0.rfBlue[1]);
vVerts.push_back( vert );
}
}
//right eye distortion verts
Xoffset = 0;
for( int y=0; y<m_iLensGridSegmentCountV; y++ )
{
for( int x=0; x<m_iLensGridSegmentCountH; x++ )
{
u = x*w; v = 1-y*h;
vert.position = Vector2( Xoffset+u, -1+2*y*h );
vr::DistortionCoordinates_t dc0;
bool result = m_pHMD->ComputeDistortion( vr::Eye_Right, u, v,&dc0 );
btAssert(result);
vert.texCoordRed = Vector2(dc0.rfRed[0], 1 - dc0.rfRed[1]);
vert.texCoordGreen = Vector2(dc0.rfGreen[0], 1 - dc0.rfGreen[1]);
vert.texCoordBlue = Vector2(dc0.rfBlue[0], 1 - dc0.rfBlue[1]);
vVerts.push_back( vert );
}
}
std::vector<GLushort> vIndices;
GLushort a,b,c,d;
GLushort offset = 0;
for( GLushort y=0; y<m_iLensGridSegmentCountV-1; y++ )
{
for( GLushort x=0; x<m_iLensGridSegmentCountH-1; x++ )
{
a = m_iLensGridSegmentCountH*y+x +offset;
b = m_iLensGridSegmentCountH*y+x+1 +offset;
c = (y+1)*m_iLensGridSegmentCountH+x+1 +offset;
d = (y+1)*m_iLensGridSegmentCountH+x +offset;
vIndices.push_back( a );
vIndices.push_back( b );
vIndices.push_back( c );
vIndices.push_back( a );
vIndices.push_back( c );
vIndices.push_back( d );
}
}
offset = (m_iLensGridSegmentCountH)*(m_iLensGridSegmentCountV);
for( GLushort y=0; y<m_iLensGridSegmentCountV-1; y++ )
{
for( GLushort x=0; x<m_iLensGridSegmentCountH-1; x++ )
{
a = m_iLensGridSegmentCountH*y+x +offset;
b = m_iLensGridSegmentCountH*y+x+1 +offset;
c = (y+1)*m_iLensGridSegmentCountH+x+1 +offset;
d = (y+1)*m_iLensGridSegmentCountH+x +offset;
vIndices.push_back( a );
vIndices.push_back( b );
vIndices.push_back( c );
vIndices.push_back( a );
vIndices.push_back( c );
vIndices.push_back( d );
}
}
m_uiIndexSize = vIndices.size();
glGenVertexArrays( 1, &m_unLensVAO );
glBindVertexArray( m_unLensVAO );
glGenBuffers( 1, &m_glIDVertBuffer );
glBindBuffer( GL_ARRAY_BUFFER, m_glIDVertBuffer );
glBufferData( GL_ARRAY_BUFFER, vVerts.size()*sizeof(VertexDataLens), &vVerts[0], GL_STATIC_DRAW );
glGenBuffers( 1, &m_glIDIndexBuffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_glIDIndexBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, vIndices.size()*sizeof(GLushort), &vIndices[0], GL_STATIC_DRAW );
glEnableVertexAttribArray( 0 );
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof( VertexDataLens, position ) );
glEnableVertexAttribArray( 1 );
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof( VertexDataLens, texCoordRed ) );
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof( VertexDataLens, texCoordGreen ) );
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof( VertexDataLens, texCoordBlue ) );
glBindVertexArray( 0 );
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderStereoTargets()
{
B3_PROFILE("CMainApplication::RenderStereoTargets");
btScalar dtSec = btScalar(m_clock.getTimeInSeconds());
dtSec = btMin(dtSec,btScalar(0.1));
sExample->stepSimulation(dtSec);
m_clock.reset();
glClearColor( 0.15f, 0.15f, 0.18f, 1.0f ); // nice background color, but not black
glEnable( GL_MULTISAMPLE );
m_app->m_instancingRenderer->init();
Matrix4 rotYtoZ = rotYtoZ.identity();
//some Bullet apps (especially robotics related) require Z as up-axis)
if (m_app->getUpAxis()==2)
{
rotYtoZ.rotateX(-90);
}
// Left Eye
{
Matrix4 viewMatLeft = m_mat4eyePosLeft * m_mat4HMDPose * rotYtoZ;
Matrix4 viewMatCenter = m_mat4HMDPose * rotYtoZ;
//0,1,2,3
//4,5,6,7,
//8,9,10,11
//12,13,14,15
//m_mat4eyePosLeft.get()[10]
//m_app->m_instancingRenderer->getActiveCamera()->setCameraTargetPosition(
// m_mat4eyePosLeft.get()[3],
// m_mat4eyePosLeft.get()[7],
// m_mat4eyePosLeft.get()[11]);
Matrix4 m;
m = viewMatCenter;
const float* mat = m.invertAffine().get();
/*printf("camera:\n,%f,%f,%f,%f\n%f,%f,%f,%f\n%f,%f,%f,%f\n%f,%f,%f,%f",
mat[0],mat[1],mat[2],mat[3],
mat[4],mat[5],mat[6],mat[7],
mat[8],mat[9],mat[10],mat[11],
mat[12],mat[13],mat[14],mat[15]);
*/
float dist=1;
m_app->m_instancingRenderer->getActiveCamera()->setCameraTargetPosition(
mat[12]-dist*mat[8],
mat[13]-dist*mat[9],
mat[14]-dist*mat[10]
);
m_app->m_instancingRenderer->getActiveCamera()->setCameraUpVector(mat[0],mat[1],mat[2]);
m_app->m_instancingRenderer->getActiveCamera()->setVRCamera(viewMatLeft.get(),m_mat4ProjectionLeft.get());
m_app->m_instancingRenderer->updateCamera(m_app->getUpAxis());
}
glBindFramebuffer( GL_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId );
glViewport(0, 0, m_nRenderWidth, m_nRenderHeight );
m_app->m_window->startRendering();
RenderScene( vr::Eye_Left );
m_app->m_instancingRenderer->setRenderFrameBuffer((unsigned int)leftEyeDesc.m_nRenderFramebufferId);
if (gDebugDrawFlags)
{
sExample->physicsDebugDraw(gDebugDrawFlags);
}
//else
{
sExample->renderScene();
}
//m_app->m_instancingRenderer->renderScene();
DrawGridData gridUp;
gridUp.upAxis = m_app->getUpAxis();
// m_app->drawGrid(gridUp);
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDisable( GL_MULTISAMPLE );
glBindFramebuffer(GL_READ_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, leftEyeDesc.m_nResolveFramebufferId );
glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight,
GL_COLOR_BUFFER_BIT,
GL_LINEAR );
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
glEnable( GL_MULTISAMPLE );
// Right Eye
{
Matrix4 viewMatRight = m_mat4eyePosRight * m_mat4HMDPose * rotYtoZ;
m_app->m_instancingRenderer->getActiveCamera()->setVRCamera(viewMatRight.get(),m_mat4ProjectionRight.get());
m_app->m_instancingRenderer->updateCamera(m_app->getUpAxis());
}
glBindFramebuffer( GL_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId );
glViewport(0, 0, m_nRenderWidth, m_nRenderHeight );
m_app->m_window->startRendering();
RenderScene( vr::Eye_Right );
m_app->m_instancingRenderer->setRenderFrameBuffer((unsigned int)rightEyeDesc.m_nRenderFramebufferId);
//m_app->m_renderer->renderScene();
if (gDebugDrawFlags)
{
sExample->physicsDebugDraw(gDebugDrawFlags);
}
//else
{
sExample->renderScene();
}
//m_app->drawGrid(gridUp);
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDisable( GL_MULTISAMPLE );
glBindFramebuffer(GL_READ_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId );
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rightEyeDesc.m_nResolveFramebufferId );
glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight,
GL_COLOR_BUFFER_BIT,
GL_LINEAR );
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderScene( vr::Hmd_Eye nEye )
{
B3_PROFILE("RenderScene");
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
if( m_bShowCubes )
{
glUseProgram( m_unSceneProgramID );
glUniformMatrix4fv( m_nSceneMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix( nEye ).get() );
glBindVertexArray( m_unSceneVAO );
glBindTexture( GL_TEXTURE_2D, m_iTexture );
glDrawArrays( GL_TRIANGLES, 0, m_uiVertcount );
glBindVertexArray( 0 );
}
bool bIsInputCapturedByAnotherProcess = m_pHMD->IsInputFocusCapturedByAnotherProcess();
if (gEnableVRRenderControllers)
{
if( !bIsInputCapturedByAnotherProcess )
{
// draw the controller axis lines
glUseProgram( m_unControllerTransformProgramID );
glUniformMatrix4fv( m_nControllerMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix( nEye ).get() );
glBindVertexArray( m_unControllerVAO );
glDrawArrays( GL_LINES, 0, m_uiControllerVertcount );
glBindVertexArray( 0 );
}
// ----- Render Model rendering -----
glUseProgram( m_unRenderModelProgramID );
for( uint32_t unTrackedDevice = 0; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; unTrackedDevice++ )
{
if( !m_rTrackedDeviceToRenderModel[ unTrackedDevice ] || !m_rbShowTrackedDevice[ unTrackedDevice ] )
continue;
const vr::TrackedDevicePose_t & pose = m_rTrackedDevicePose[ unTrackedDevice ];
if( !pose.bPoseIsValid )
continue;
if( bIsInputCapturedByAnotherProcess && m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) == vr::TrackedDeviceClass_Controller )
continue;
const Matrix4 & matDeviceToTracking = m_rmat4DevicePose[ unTrackedDevice ];
Matrix4 matMVP = GetCurrentViewProjectionMatrix( nEye ) * matDeviceToTracking;
glUniformMatrix4fv( m_nRenderModelMatrixLocation, 1, GL_FALSE, matMVP.get() );
m_rTrackedDeviceToRenderModel[ unTrackedDevice ]->Draw();
}
}
glUseProgram( 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderDistortion()
{
glDisable(GL_DEPTH_TEST);
glViewport( 0, 0, m_nWindowWidth, m_nWindowHeight );
glBindVertexArray( m_unLensVAO );
glUseProgram( m_unLensProgramID );
//render left lens (first half of index array )
glBindTexture(GL_TEXTURE_2D, leftEyeDesc.m_nResolveTextureId );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glDrawElements( GL_TRIANGLES, m_uiIndexSize/2, GL_UNSIGNED_SHORT, 0 );
//render right lens (second half of index array )
glBindTexture(GL_TEXTURE_2D, rightEyeDesc.m_nResolveTextureId );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glDrawElements( GL_TRIANGLES, m_uiIndexSize/2, GL_UNSIGNED_SHORT, (const void *)(m_uiIndexSize) );
glBindVertexArray( 0 );
glUseProgram( 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye )
{
if ( !m_pHMD )
return Matrix4();
vr::HmdMatrix44_t mat = m_pHMD->GetProjectionMatrix( nEye, m_fNearClip, m_fFarClip);
return Matrix4(
mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0],
mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1],
mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2],
mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]
);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::GetHMDMatrixPoseEye( vr::Hmd_Eye nEye )
{
if ( !m_pHMD )
return Matrix4();
vr::HmdMatrix34_t matEyeRight = m_pHMD->GetEyeToHeadTransform( nEye );
Matrix4 matrixObj(
matEyeRight.m[0][0], matEyeRight.m[1][0], matEyeRight.m[2][0], 0.0,
matEyeRight.m[0][1], matEyeRight.m[1][1], matEyeRight.m[2][1], 0.0,
matEyeRight.m[0][2], matEyeRight.m[1][2], matEyeRight.m[2][2], 0.0,
matEyeRight.m[0][3], matEyeRight.m[1][3], matEyeRight.m[2][3], 1.0f
);
return matrixObj.invert();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye )
{
Matrix4 matMVP;
if( nEye == vr::Eye_Left )
{
matMVP = m_mat4ProjectionLeft * m_mat4eyePosLeft * m_mat4HMDPose;
}
else if( nEye == vr::Eye_Right )
{
matMVP = m_mat4ProjectionRight * m_mat4eyePosRight * m_mat4HMDPose;
}
return matMVP;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::UpdateHMDMatrixPose()
{
if (!m_pHMD)
return;
{
B3_PROFILE("WaitGetPoses");
vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0);
}
m_iValidPoseCount = 0;
m_strPoseClasses = "";
{
B3_PROFILE("for loop");
for (int nDevice = 0; nDevice < vr::k_unMaxTrackedDeviceCount; ++nDevice)
{
if (m_rTrackedDevicePose[nDevice].bPoseIsValid)
{
m_iValidPoseCount++;
m_rmat4DevicePose[nDevice] = ConvertSteamVRMatrixToMatrix4(m_rTrackedDevicePose[nDevice].mDeviceToAbsoluteTracking);
if (m_rDevClassChar[nDevice] == 0)
{
switch (m_pHMD->GetTrackedDeviceClass(nDevice))
{
case vr::TrackedDeviceClass_Controller: m_rDevClassChar[nDevice] = 'C'; break;
case vr::TrackedDeviceClass_HMD: m_rDevClassChar[nDevice] = 'H'; break;
case vr::TrackedDeviceClass_Invalid: m_rDevClassChar[nDevice] = 'I'; break;
case vr::TrackedDeviceClass_TrackingReference: m_rDevClassChar[nDevice] = 'T'; break;
default: m_rDevClassChar[nDevice] = '?'; break;
}
}
m_strPoseClasses += m_rDevClassChar[nDevice];
}
}
}
{
B3_PROFILE("m_mat4HMDPose invert");
if (m_rTrackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid)
{
m_mat4HMDPose = m_rmat4DevicePose[vr::k_unTrackedDeviceIndex_Hmd].invert();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Finds a render model we've already loaded or loads a new one
//-----------------------------------------------------------------------------
CGLRenderModel *CMainApplication::FindOrLoadRenderModel( const char *pchRenderModelName )
{
CGLRenderModel *pRenderModel = NULL;
for( std::vector< CGLRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ )
{
if( !stricmp( (*i)->GetName().c_str(), pchRenderModelName ) )
{
pRenderModel = *i;
break;
}
}
// load the model if we didn't find one
if( !pRenderModel )
{
vr::RenderModel_t *pModel;
vr::EVRRenderModelError error;
while ( 1 )
{
error = vr::VRRenderModels()->LoadRenderModel_Async( pchRenderModelName, &pModel );
if ( error != vr::VRRenderModelError_Loading )
break;
ThreadSleep( 1 );
}
if ( error != vr::VRRenderModelError_None )
{
b3Printf( "Unable to load render model %s - %s\n", pchRenderModelName, vr::VRRenderModels()->GetRenderModelErrorNameFromEnum( error ) );
return NULL; // move on to the next tracked device
}
vr::RenderModel_TextureMap_t *pTexture;
while ( 1 )
{
error = vr::VRRenderModels()->LoadTexture_Async( pModel->diffuseTextureId, &pTexture );
if ( error != vr::VRRenderModelError_Loading )
break;
ThreadSleep( 1 );
}
if ( error != vr::VRRenderModelError_None )
{
b3Printf( "Unable to load render texture id:%d for render model %s\n", pModel->diffuseTextureId, pchRenderModelName );
vr::VRRenderModels()->FreeRenderModel( pModel );
return NULL; // move on to the next tracked device
}
pRenderModel = new CGLRenderModel( pchRenderModelName );
if ( !pRenderModel->BInit( *pModel, *pTexture ) )
{
b3Printf( "Unable to create GL model from render model %s\n", pchRenderModelName );
delete pRenderModel;
pRenderModel = NULL;
}
else
{
m_vecRenderModels.push_back( pRenderModel );
}
vr::VRRenderModels()->FreeRenderModel( pModel );
vr::VRRenderModels()->FreeTexture( pTexture );
}
return pRenderModel;
}
//-----------------------------------------------------------------------------
// Purpose: Create/destroy GL a Render Model for a single tracked device
//-----------------------------------------------------------------------------
void CMainApplication::SetupRenderModelForTrackedDevice( vr::TrackedDeviceIndex_t unTrackedDeviceIndex )
{
if( unTrackedDeviceIndex >= vr::k_unMaxTrackedDeviceCount )
return;
// try to find a model we've already set up
std::string sRenderModelName = GetTrackedDeviceString( m_pHMD, unTrackedDeviceIndex, vr::Prop_RenderModelName_String );
CGLRenderModel *pRenderModel = FindOrLoadRenderModel( sRenderModelName.c_str() );
if( !pRenderModel )
{
std::string sTrackingSystemName = GetTrackedDeviceString( m_pHMD, unTrackedDeviceIndex, vr::Prop_TrackingSystemName_String );
b3Printf( "Unable to load render model for tracked device %d (%s.%s)", unTrackedDeviceIndex, sTrackingSystemName.c_str(), sRenderModelName.c_str() );
}
else
{
m_rTrackedDeviceToRenderModel[ unTrackedDeviceIndex ] = pRenderModel;
m_rbShowTrackedDevice[ unTrackedDeviceIndex ] = true;
}
}
//-----------------------------------------------------------------------------
// Purpose: Create/destroy GL Render Models
//-----------------------------------------------------------------------------
void CMainApplication::SetupRenderModels()
{
memset( m_rTrackedDeviceToRenderModel, 0, sizeof( m_rTrackedDeviceToRenderModel ) );
if( !m_pHMD )
return;
for( uint32_t unTrackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; unTrackedDevice++ )
{
if( !m_pHMD->IsTrackedDeviceConnected( unTrackedDevice ) )
continue;
SetupRenderModelForTrackedDevice( unTrackedDevice );
}
}
//-----------------------------------------------------------------------------
// Purpose: Converts a SteamVR matrix to our local matrix class
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose )
{
Matrix4 matrixObj(
matPose.m[0][0], matPose.m[1][0], matPose.m[2][0], 0.0,
matPose.m[0][1], matPose.m[1][1], matPose.m[2][1], 0.0,
matPose.m[0][2], matPose.m[1][2], matPose.m[2][2], 0.0,
matPose.m[0][3], matPose.m[1][3], matPose.m[2][3], 1.0f
);
return matrixObj;
}
//-----------------------------------------------------------------------------
// Purpose: Create/destroy GL Render Models
//-----------------------------------------------------------------------------
CGLRenderModel::CGLRenderModel( const std::string & sRenderModelName )
: m_sModelName( sRenderModelName )
{
m_glIndexBuffer = 0;
m_glVertArray = 0;
m_glVertBuffer = 0;
m_glTexture = 0;
}
CGLRenderModel::~CGLRenderModel()
{
Cleanup();
}
//-----------------------------------------------------------------------------
// Purpose: Allocates and populates the GL resources for a render model
//-----------------------------------------------------------------------------
bool CGLRenderModel::BInit( const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture )
{
// create and bind a VAO to hold state for this model
glGenVertexArrays( 1, &m_glVertArray );
glBindVertexArray( m_glVertArray );
// Populate a vertex buffer
glGenBuffers( 1, &m_glVertBuffer );
glBindBuffer( GL_ARRAY_BUFFER, m_glVertBuffer );
glBufferData( GL_ARRAY_BUFFER, sizeof( vr::RenderModel_Vertex_t ) * vrModel.unVertexCount, vrModel.rVertexData, GL_STATIC_DRAW );
// Identify the components in the vertex buffer
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, vPosition ) );
glEnableVertexAttribArray( 1 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, vNormal ) );
glEnableVertexAttribArray( 2 );
glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, rfTextureCoord ) );
// Create and populate the index buffer
glGenBuffers( 1, &m_glIndexBuffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( uint16_t ) * vrModel.unTriangleCount * 3, vrModel.rIndexData, GL_STATIC_DRAW );
glBindVertexArray( 0 );
// create and populate the texture
glGenTextures(1, &m_glTexture );
glBindTexture( GL_TEXTURE_2D, m_glTexture );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, vrDiffuseTexture.unWidth, vrDiffuseTexture.unHeight,
0, GL_RGBA, GL_UNSIGNED_BYTE, vrDiffuseTexture.rubTextureMapData );
// If this renders black ask McJohn what's wrong.
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
GLfloat fLargest;
glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest );
glBindTexture( GL_TEXTURE_2D, 0 );
m_unVertexCount = vrModel.unTriangleCount * 3;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Frees the GL resources for a render model
//-----------------------------------------------------------------------------
void CGLRenderModel::Cleanup()
{
if( m_glVertBuffer )
{
glDeleteBuffers(1, &m_glIndexBuffer);
glDeleteVertexArrays( 1, &m_glVertArray );
glDeleteBuffers(1, &m_glVertBuffer);
m_glIndexBuffer = 0;
m_glVertArray = 0;
m_glVertBuffer = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose: Draws the render model
//-----------------------------------------------------------------------------
void CGLRenderModel::Draw()
{
glBindVertexArray( m_glVertArray );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, m_glTexture );
glDrawElements( GL_TRIANGLES, m_unVertexCount, GL_UNSIGNED_SHORT, 0 );
glBindVertexArray( 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
b3CommandLineArgs args(argc,argv);
if (args.CheckCmdLineFlag("disable_desktop_gl"))
{
gDisableDesktopGL = true;
}
if (args.CheckCmdLineFlag("tracing"))
{
b3ChromeUtilsStartTimings();
b3ChromeUtilsEnableProfiling();
}
#ifdef BT_USE_CUSTOM_PROFILER
b3SetCustomEnterProfileZoneFunc(dcEnter);
b3SetCustomLeaveProfileZoneFunc(dcLeave);
#endif
CMainApplication *pMainApplication = new CMainApplication( argc, argv );
if (!pMainApplication->BInit())
{
pMainApplication->Shutdown();
return 1;
}
if (sExample)
{
//until we have a proper VR gui, always assume we want the hard-coded default robot assets
char* newargv[2];
char* t0 = (char*)"--robotassets";
newargv[0] = t0;
newargv[1] = t0;
sExample->processCommandLineArgs(2,newargv);
sExample->processCommandLineArgs(argc,argv);
}
char* gVideoFileName = 0;
args.GetCmdLineArgument("mp4",gVideoFileName);
if (gVideoFileName)
pMainApplication->getApp()->dumpFramesToVideo(gVideoFileName);
//request disable VSYNC
typedef bool (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int);
PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
wglSwapIntervalEXT =
(PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress("wglSwapIntervalEXT");
if (wglSwapIntervalEXT)
wglSwapIntervalEXT(0);
pMainApplication->RunMainLoop();
pMainApplication->Shutdown();
if (args.CheckCmdLineFlag("tracing"))
{
b3ChromeUtilsStopTimingsAndWriteJsonFile("timings");
}
return 0;
}
#endif //BT_ENABLE_VR