2022-01-29 15:45:18 +00:00
|
|
|
#include "vr_base.h"
|
2020-09-06 11:40:32 +00:00
|
|
|
#include "vr_renderer.h"
|
|
|
|
|
|
|
|
#include "../qcommon/q_shared.h"
|
|
|
|
#include "../qcommon/qcommon.h"
|
|
|
|
#include "../client/client.h"
|
2022-02-01 23:35:05 +00:00
|
|
|
#include "../VrApi/Include/VrApi_Types.h"
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-02-02 23:31:59 +00:00
|
|
|
#include "vr_clientinfo.h"
|
2022-02-03 21:32:09 +00:00
|
|
|
#include "vr_types.h"
|
2022-02-02 23:31:59 +00:00
|
|
|
//#include "../SDL2/include/SDL_opengles2_gl2.h"
|
|
|
|
|
2020-09-06 11:40:32 +00:00
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
2022-01-29 15:45:18 +00:00
|
|
|
#include <VrApi.h>
|
2020-09-06 11:40:32 +00:00
|
|
|
#include <VrApi_Helpers.h>
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#define ENABLE_GL_DEBUG 1
|
|
|
|
#define ENABLE_GL_DEBUG_VERBOSE 0
|
|
|
|
#if ENABLE_GL_DEBUG
|
|
|
|
#include <GLES3/gl32.h>
|
|
|
|
#endif
|
|
|
|
|
2022-01-30 23:36:14 +00:00
|
|
|
#define SUPER_SAMPLE 1.2f
|
|
|
|
|
2022-02-02 23:31:59 +00:00
|
|
|
extern vr_clientinfo_t vr;
|
|
|
|
|
2020-09-06 11:40:32 +00:00
|
|
|
void APIENTRY VR_GLDebugLog(GLenum source, GLenum type, GLuint id,
|
|
|
|
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
|
|
|
|
{
|
|
|
|
if (type == GL_DEBUG_TYPE_ERROR || type == GL_DEBUG_TYPE_PERFORMANCE || ENABLE_GL_DEBUG_VERBOSE)
|
|
|
|
{
|
|
|
|
Com_Printf("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
|
|
|
|
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-01 23:35:05 +00:00
|
|
|
void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight)
|
2022-01-29 15:45:18 +00:00
|
|
|
{
|
|
|
|
static int width = 0;
|
|
|
|
static int height = 0;
|
|
|
|
|
|
|
|
if (engine)
|
|
|
|
{
|
2022-02-03 21:32:09 +00:00
|
|
|
*pWidth = width = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH) * SUPER_SAMPLE;
|
|
|
|
*pHeight = height = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT) * SUPER_SAMPLE;
|
2022-01-29 15:45:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//use cached values
|
|
|
|
*pWidth = width;
|
|
|
|
*pHeight = height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 11:40:32 +00:00
|
|
|
void VR_InitRenderer( engine_t* engine ) {
|
|
|
|
#if ENABLE_GL_DEBUG
|
|
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
|
|
glDebugMessageCallback(VR_GLDebugLog, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int eyeW, eyeH;
|
2022-02-01 23:35:05 +00:00
|
|
|
VR_GetResolution(engine, &eyeW, &eyeH);
|
2022-01-29 15:45:18 +00:00
|
|
|
|
2020-09-06 11:40:32 +00:00
|
|
|
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
|
|
|
|
framebuffer_t* framebuffer = &engine->framebuffers[eye];
|
|
|
|
framebuffer->colorTexture = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8,
|
|
|
|
eyeW, eyeH, 1, 3);
|
|
|
|
framebuffer->swapchainLength = vrapi_GetTextureSwapChainLength(framebuffer->colorTexture);
|
|
|
|
framebuffer->depthBuffers = (GLuint*)malloc(framebuffer->swapchainLength * sizeof(GLuint));
|
|
|
|
framebuffer->framebuffers = (GLuint*)malloc(framebuffer->swapchainLength * sizeof(GLuint));
|
|
|
|
|
|
|
|
for (int index = 0; index < framebuffer->swapchainLength; ++index) {
|
|
|
|
GLuint colorTexture;
|
|
|
|
GLenum framebufferStatus;
|
|
|
|
|
|
|
|
colorTexture = vrapi_GetTextureSwapChainHandle(framebuffer->colorTexture, index);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, colorTexture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
glGenRenderbuffers(1, &framebuffer->depthBuffers[index]);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, framebuffer->depthBuffers[index]);
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, eyeW, eyeH);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
|
|
|
|
glGenFramebuffers(1, &framebuffer->framebuffers[index]);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->framebuffers[index]);
|
|
|
|
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
|
|
|
|
framebuffer->depthBuffers[index]);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
|
|
|
|
framebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
|
|
|
|
assert(framebufferStatus == GL_FRAMEBUFFER_COMPLETE);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VR_DestroyRenderer( engine_t* engine ) {
|
|
|
|
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye)
|
|
|
|
{
|
|
|
|
if (engine->framebuffers[eye].swapchainLength > 0) {
|
|
|
|
glDeleteFramebuffers(engine->framebuffers[eye].swapchainLength,
|
|
|
|
engine->framebuffers[eye].depthBuffers);
|
|
|
|
free(engine->framebuffers[eye].depthBuffers);
|
|
|
|
free(engine->framebuffers[eye].framebuffers);
|
|
|
|
|
|
|
|
vrapi_DestroyTextureSwapChain(engine->framebuffers[eye].colorTexture);
|
|
|
|
|
|
|
|
memset(&engine->framebuffers[eye], 0, sizeof(engine->framebuffers[eye]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Assumes landscape cylinder shape.
|
|
|
|
static ovrMatrix4f CylinderModelMatrix( const int texWidth, const int texHeight,
|
|
|
|
const ovrVector3f translation,
|
|
|
|
const float rotateYaw,
|
|
|
|
const float rotatePitch,
|
|
|
|
const float radius,
|
|
|
|
const float density )
|
|
|
|
{
|
|
|
|
const ovrMatrix4f scaleMatrix = ovrMatrix4f_CreateScale( radius, radius * (float)texHeight * VRAPI_PI / density, radius );
|
|
|
|
const ovrMatrix4f transMatrix = ovrMatrix4f_CreateTranslation( translation.x, translation.y, translation.z );
|
|
|
|
const ovrMatrix4f rotXMatrix = ovrMatrix4f_CreateRotation( rotateYaw, 0.0f, 0.0f );
|
|
|
|
const ovrMatrix4f rotYMatrix = ovrMatrix4f_CreateRotation( 0.0f, rotatePitch, 0.0f );
|
|
|
|
|
|
|
|
const ovrMatrix4f m0 = ovrMatrix4f_Multiply( &transMatrix, &scaleMatrix );
|
|
|
|
const ovrMatrix4f m1 = ovrMatrix4f_Multiply( &rotXMatrix, &m0 );
|
|
|
|
const ovrMatrix4f m2 = ovrMatrix4f_Multiply( &rotYMatrix, &m1 );
|
|
|
|
|
|
|
|
return m2;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern cvar_t *vr_screen_dist;
|
|
|
|
|
|
|
|
ovrLayerCylinder2 BuildCylinderLayer(engine_t* engine, const int textureWidth, const int textureHeight,
|
|
|
|
const ovrTracking2 * tracking, float rotatePitch )
|
|
|
|
{
|
|
|
|
ovrLayerCylinder2 layer = vrapi_DefaultLayerCylinder2();
|
|
|
|
|
|
|
|
const float fadeLevel = 1.0f;
|
|
|
|
layer.Header.ColorScale.x =
|
|
|
|
layer.Header.ColorScale.y =
|
|
|
|
layer.Header.ColorScale.z =
|
|
|
|
layer.Header.ColorScale.w = fadeLevel;
|
|
|
|
layer.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA;
|
|
|
|
layer.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA;
|
|
|
|
|
|
|
|
//layer.Header.Flags = VRAPI_FRAME_LAYER_FLAG_CLIP_TO_TEXTURE_RECT;
|
|
|
|
|
|
|
|
layer.HeadPose = tracking->HeadPose;
|
|
|
|
|
|
|
|
const float density = 4500.0f;
|
|
|
|
const float rotateYaw = 0.0f;
|
2022-02-02 23:31:59 +00:00
|
|
|
const float radius = 12.0f;
|
|
|
|
const float distance = -16.0f;
|
2022-01-29 15:45:18 +00:00
|
|
|
|
|
|
|
const ovrVector3f translation = { 0.0f, 1.0f, distance };
|
|
|
|
|
|
|
|
ovrMatrix4f cylinderTransform =
|
|
|
|
CylinderModelMatrix( textureWidth, textureHeight, translation,
|
|
|
|
rotateYaw, rotatePitch, radius, density );
|
|
|
|
|
|
|
|
const float circScale = density * 0.5f / textureWidth;
|
|
|
|
const float circBias = -circScale * ( 0.5f * ( 1.0f - 1.0f / circScale ) );
|
|
|
|
|
|
|
|
for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
|
|
|
|
{
|
|
|
|
ovrMatrix4f modelViewMatrix = ovrMatrix4f_Multiply( &tracking->Eye[eye].ViewMatrix, &cylinderTransform );
|
|
|
|
layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_Inverse( &modelViewMatrix );
|
|
|
|
layer.Textures[eye].ColorSwapChain = engine->framebuffers[eye].colorTexture;
|
|
|
|
layer.Textures[eye].SwapChainIndex = engine->framebuffers[eye].swapchainIndex;
|
|
|
|
|
|
|
|
// Texcoord scale and bias is just a representation of the aspect ratio. The positioning
|
|
|
|
// of the cylinder is handled entirely by the TexCoordsFromTanAngles matrix.
|
|
|
|
|
|
|
|
const float texScaleX = circScale;
|
|
|
|
const float texBiasX = circBias;
|
|
|
|
const float texScaleY = -0.5f;
|
|
|
|
const float texBiasY = texScaleY * ( 0.5f * ( 1.0f - ( 1.0f / texScaleY ) ) );
|
|
|
|
|
|
|
|
layer.Textures[eye].TextureMatrix.M[0][0] = texScaleX;
|
|
|
|
layer.Textures[eye].TextureMatrix.M[0][2] = texBiasX;
|
|
|
|
layer.Textures[eye].TextureMatrix.M[1][1] = texScaleY;
|
|
|
|
layer.Textures[eye].TextureMatrix.M[1][2] = -texBiasY;
|
|
|
|
|
|
|
|
layer.Textures[eye].TextureRect.width = 1.0f;
|
|
|
|
layer.Textures[eye].TextureRect.height = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
2022-02-02 23:31:59 +00:00
|
|
|
void VR_ClearFrameBuffer( GLuint frameBuffer, int width, int height)
|
|
|
|
{
|
|
|
|
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBuffer );
|
|
|
|
|
|
|
|
glEnable( GL_SCISSOR_TEST );
|
|
|
|
glViewport( 0, 0, width, height );
|
|
|
|
|
2022-02-09 23:55:05 +00:00
|
|
|
if (Cvar_VariableIntegerValue("vr_deathCam"))
|
|
|
|
{
|
|
|
|
//Blood red.. ish
|
|
|
|
glClearColor( 0.12f, 0.0f, 0.05f, 1.0f );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Black
|
|
|
|
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
|
|
|
|
}
|
|
|
|
|
2022-02-02 23:31:59 +00:00
|
|
|
glScissor( 0, 0, width, height );
|
|
|
|
glClear( GL_COLOR_BUFFER_BIT );
|
|
|
|
|
|
|
|
glScissor( 0, 0, 0, 0 );
|
|
|
|
glDisable( GL_SCISSOR_TEST );
|
|
|
|
|
|
|
|
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
|
|
|
|
}
|
|
|
|
|
2020-09-06 11:40:32 +00:00
|
|
|
void VR_DrawFrame( engine_t* engine ) {
|
|
|
|
|
|
|
|
if (!engine->ovr)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
++engine->frameIndex;
|
2022-01-30 23:36:14 +00:00
|
|
|
engine->predictedDisplayTime = vrapi_GetPredictedDisplayTime(engine->ovr, engine->frameIndex);
|
|
|
|
engine->tracking = vrapi_GetPredictedTracking2(engine->ovr, engine->predictedDisplayTime);
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-02-05 15:06:53 +00:00
|
|
|
float fov_y = vrapi_GetSystemPropertyInt( engine->ovr, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y);
|
|
|
|
float fov_x = vrapi_GetSystemPropertyInt( engine->ovr, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
|
|
|
|
const ovrMatrix4f projectionMatrix = ovrMatrix4f_CreateProjectionFov(
|
|
|
|
fov_x, fov_y, 0.0f, 0.0f, 1.0f, 0.0f );
|
|
|
|
|
2022-02-02 23:31:59 +00:00
|
|
|
static int playerYaw = 0;
|
|
|
|
|
|
|
|
int eyeW, eyeH;
|
|
|
|
VR_GetResolution(engine, &eyeW, &eyeH);
|
|
|
|
|
|
|
|
if (VR_useScreenLayer())
|
2022-01-29 15:45:18 +00:00
|
|
|
{
|
|
|
|
static ovrLayer_Union2 cylinderLayer;
|
|
|
|
memset( &cylinderLayer, 0, sizeof( ovrLayer_Union2 ) );
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
// Add a simple cylindrical layer
|
|
|
|
cylinderLayer.Cylinder =
|
2022-02-03 21:32:09 +00:00
|
|
|
BuildCylinderLayer(engine, eyeW, eyeW * 0.75f, &engine->tracking, radians(playerYaw) );
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
const ovrLayerHeader2* layers[] = {
|
|
|
|
&cylinderLayer.Header
|
|
|
|
};
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
// Set up the description for this frame.
|
|
|
|
ovrSubmitFrameDescription2 frameDesc = { 0 };
|
|
|
|
frameDesc.Flags = 0;
|
|
|
|
frameDesc.SwapInterval = 1;
|
|
|
|
frameDesc.FrameIndex = engine->frameIndex;
|
2022-01-30 23:36:14 +00:00
|
|
|
frameDesc.DisplayTime = engine->predictedDisplayTime;
|
2022-01-29 15:45:18 +00:00
|
|
|
frameDesc.LayerCount = 1;
|
|
|
|
frameDesc.Layers = layers;
|
|
|
|
|
|
|
|
const framebuffer_t* framebuffers = engine->framebuffers;
|
2022-02-02 23:31:59 +00:00
|
|
|
|
2022-02-05 15:06:53 +00:00
|
|
|
re.SetVRHeadsetParms(&projectionMatrix,
|
2022-01-29 15:45:18 +00:00
|
|
|
framebuffers[0].framebuffers[framebuffers[0].swapchainIndex],
|
|
|
|
framebuffers[1].framebuffers[framebuffers[1].swapchainIndex]);
|
2022-02-02 23:31:59 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
Com_Frame();
|
|
|
|
|
|
|
|
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
|
|
|
|
engine->framebuffers[eye].swapchainIndex = (engine->framebuffers[eye].swapchainIndex + 1) %
|
|
|
|
engine->framebuffers[eye].swapchainLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hand over the eye images to the time warp.
|
|
|
|
vrapi_SubmitFrame2(engine->ovr, &frameDesc);
|
2020-09-06 11:40:32 +00:00
|
|
|
}
|
2022-01-29 15:45:18 +00:00
|
|
|
else
|
|
|
|
{
|
2022-02-02 23:31:59 +00:00
|
|
|
playerYaw = vr.hmdorientation[YAW];
|
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2();
|
2022-01-30 23:36:14 +00:00
|
|
|
layer.HeadPose = engine->tracking.HeadPose;
|
2022-01-29 15:45:18 +00:00
|
|
|
|
|
|
|
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
|
|
|
|
layer.Textures[eye].ColorSwapChain = engine->framebuffers[eye].colorTexture;
|
|
|
|
layer.Textures[eye].SwapChainIndex = engine->framebuffers[eye].swapchainIndex;
|
2022-02-05 15:06:53 +00:00
|
|
|
layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection(&projectionMatrix);
|
2022-01-29 15:45:18 +00:00
|
|
|
}
|
|
|
|
|
2022-02-01 23:35:05 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
const framebuffer_t* framebuffers = engine->framebuffers;
|
2022-02-02 23:31:59 +00:00
|
|
|
|
|
|
|
VR_ClearFrameBuffer(framebuffers[0].framebuffers[framebuffers[0].swapchainIndex], eyeW, eyeH);
|
|
|
|
VR_ClearFrameBuffer(framebuffers[1].framebuffers[framebuffers[1].swapchainIndex], eyeW, eyeH);
|
|
|
|
|
2022-02-05 15:06:53 +00:00
|
|
|
re.SetVRHeadsetParms(&projectionMatrix,
|
2022-01-29 15:45:18 +00:00
|
|
|
framebuffers[0].framebuffers[framebuffers[0].swapchainIndex],
|
|
|
|
framebuffers[1].framebuffers[framebuffers[1].swapchainIndex]);
|
2022-02-02 23:31:59 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
Com_Frame();
|
|
|
|
|
|
|
|
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
|
|
|
|
engine->framebuffers[eye].swapchainIndex = (engine->framebuffers[eye].swapchainIndex + 1) %
|
|
|
|
engine->framebuffers[eye].swapchainLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ovrLayerHeader2* layers[] = {
|
|
|
|
&layer.Header
|
|
|
|
};
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
ovrSubmitFrameDescription2 frameDesc = { 0 };
|
|
|
|
frameDesc.Flags = 0;
|
|
|
|
frameDesc.SwapInterval = 1;
|
|
|
|
frameDesc.FrameIndex = engine->frameIndex;
|
2022-01-30 23:36:14 +00:00
|
|
|
frameDesc.DisplayTime = engine->predictedDisplayTime;
|
2022-01-29 15:45:18 +00:00
|
|
|
frameDesc.LayerCount = 1;
|
|
|
|
frameDesc.Layers = layers;
|
2020-09-06 11:40:32 +00:00
|
|
|
|
2022-01-29 15:45:18 +00:00
|
|
|
vrapi_SubmitFrame2(engine->ovr, &frameDesc);
|
|
|
|
}
|
2020-09-06 11:40:32 +00:00
|
|
|
|
|
|
|
}
|