Fix up some openxr issues (especially with vulkan).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6133 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-12-20 10:05:55 +00:00
parent 7e985b431b
commit df701ecfac
6 changed files with 574 additions and 236 deletions

View file

@ -3530,6 +3530,8 @@ void Surf_DrawWorld (void)
/*FIXME: move this away*/
if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife)
Surf_LessenStains();
r_refdef.sceneareas = NULL;
}
}

View file

@ -1382,34 +1382,45 @@ void Matrix4x4_Identity(float *outm)
void Matrix4x4_CM_Projection_Offset(float *proj, float fovl, float fovr, float fovd, float fovu, float neard, float fard, qboolean d3d)
{
double xmin, xmax, ymin, ymax;
double dn = (d3d?0:-1), df = 1; //d3d outputs near as 0, opengl has near as -1. that's the only difference.
double ymax = tan( fovu * M_PI / 180.0 );
double ymin = tan( fovd * M_PI / 180.0 );
double xmin = tan( fovl * M_PI / 180.0 );
double xmax = tan( fovr * M_PI / 180.0 );
float fovy = fovu-fovd;
float fovx = fovr-fovl;
if (fard <= neard)
{ //switch to an infinite projection
const double epsilon = 1.0/(1<<22);
//proj
ymax = neard * tan( fovy * M_PI / 360.0 );
ymin = -ymax;
if (fovx == fovy)
{
xmax = ymax;
xmin = ymin;
}
else
{
xmax = neard * tan( fovx * M_PI / 360.0 );
xmin = -xmax;
}
proj[0] = (2*neard) / (xmax - xmin);
proj[0] = (2) / (xmax - xmin);
proj[4] = 0;
proj[8] = (xmax + xmin) / (xmax - xmin);
proj[12] = 0;
proj[1] = 0;
proj[5] = (2*neard) / (ymax - ymin);
proj[5] = (2) / (ymax - ymin);
proj[9] = (ymax + ymin) / (ymax - ymin);
proj[13] = 0;
proj[2] = 0;
proj[6] = 0;
proj[10] = epsilon-1;
proj[14] = (epsilon-(df-dn))*neard;
proj[3] = 0;
proj[7] = 0;
proj[11] = -1;
proj[15] = 0;
}
else
{
proj[0] = (2) / (xmax - xmin);
proj[4] = 0;
proj[8] = (xmax + xmin) / (xmax - xmin);
proj[12] = 0;
proj[1] = 0;
proj[5] = (2) / (ymax - ymin);
proj[9] = (ymax + ymin) / (ymax - ymin);
proj[13] = 0;
@ -1423,6 +1434,7 @@ void Matrix4x4_CM_Projection_Offset(float *proj, float fovl, float fovr, float f
proj[11] = -1;
proj[15] = 0;
}
}
void Matrix4x4_CM_Projection_Far(float *proj, float fovx, float fovy, float neard, float fard, qboolean d3d)
{

View file

@ -610,23 +610,11 @@ static void R_SetupGL (vec3_t eyeangorg[2], vec4_t fovoverrides, float projmatri
if (r_refdef.useperspective)
{
int stencilshadows = Sh_StencilShadowsActive();
if ((!stencilshadows || !gl_stencilbits) && r_refdef.maxdist)//gl_nv_range_clamp)
{
// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI;
// yfov = (2.0 * tan (scr_fov.value/360*M_PI)) / screenaspect;
// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov.value*2)/M_PI;
// MYgluPerspective (yfov, screenaspect, 4, 4096);
Matrix4x4_CM_Projection_Offset(r_refdef.m_projection_std, fov_l, fov_r, fov_d, fov_u, r_refdef.mindist, r_refdef.maxdist, false);
Matrix4x4_CM_Projection_Far(r_refdef.m_projection_view, fovv_x, fovv_y, r_refdef.mindist, r_refdef.maxdist, false);
}
else
{
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, false);
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_view, fovv_x, fovv_y, r_refdef.mindist, false);
}
float maxdist = r_refdef.maxdist;
if (gl_stencilbits && Sh_StencilShadowsActive())
maxdist = 0; //if we're using stencil shadows then force the maxdist to infinite to ensure the shadow volume is sealed.
Matrix4x4_CM_Projection_Offset(r_refdef.m_projection_std, fov_l, fov_r, fov_d, fov_u, r_refdef.mindist, maxdist, false);
Matrix4x4_CM_Projection_Offset(r_refdef.m_projection_view, -fovv_x/2, fovv_x/2, -fovv_y/2, fovv_y/2, r_refdef.mindist, maxdist, false);
r_refdef.m_projection_std[8] += r_refdef.projectionoffset[0];
r_refdef.m_projection_std[9] += r_refdef.projectionoffset[1];

View file

@ -313,6 +313,7 @@ qboolean R_DrawSkyroom(shader_t *skyshader)
r_refdef.flags &= ~RDF_SKIPSKY;
r_refdef.forcedvis = NULL;
r_refdef.areabitsknown = false; //recalculate areas clientside.
r_refdef.sceneareas = NULL;
if (cl.fog[FOGTYPE_SKYROOM].density)
{

View file

@ -2210,25 +2210,44 @@ void VK_R_DeInit (void)
Image_Shutdown();
}
void VK_SetupViewPortProjection(qboolean flipy)
void VK_SetupViewPortProjection(qboolean flipy, vec3_t *eyeangorg, float *fovoverrides)
{
float fov_x, fov_y;
float fovv_x, fovv_y;
float fov_l, fov_r, fov_d, fov_u;
if (eyeangorg)
{
extern cvar_t in_vraim;
matrix3x4 basematrix;
matrix3x4 eyematrix;
matrix3x4 viewmatrix;
vec3_t newa;
Matrix3x4_RM_FromAngles(eyeangorg[0], eyeangorg[1], eyematrix[0]);
if (r_refdef.base_known)
{ //mod is specifying its own base ang+org.
Matrix3x4_RM_FromAngles(r_refdef.base_angles, r_refdef.base_origin, basematrix[0]);
}
else
{ //mod provides no info.
//client will fiddle with input_angles
newa[0] = newa[2] = 0; //ignore player pitch+roll. sorry. apply the eye's transform on top.
newa[1] = r_refdef.viewangles[1];
if (in_vraim.ival)
newa[1] -= SHORT2ANGLE(r_refdef.playerview->vrdev[VRDEV_HEAD].angles[YAW]);
Matrix3x4_RM_FromAngles(newa, r_refdef.vieworg, basematrix[0]);
}
Matrix3x4_Multiply(eyematrix[0], basematrix[0], viewmatrix[0]);
Matrix3x4_RM_ToVectors(viewmatrix[0], vpn, vright, vup, r_origin);
VectorNegate(vright, vright);
}
else
{
AngleVectors (r_refdef.viewangles, vpn, vright, vup);
VectorCopy (r_refdef.vieworg, r_origin);
fov_x = r_refdef.fov_x;//+sin(cl.time)*5;
fov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;
fovv_x = r_refdef.fovv_x;
fovv_y = r_refdef.fovv_y;
if ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))
{
fov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
fov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
fovv_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
fovv_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
}
// screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;
@ -2247,16 +2266,43 @@ void VK_SetupViewPortProjection(qboolean flipy)
Matrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);
r_refdef.flipcull = 0;
}
if (r_refdef.maxdist)
fov_x = r_refdef.fov_x;//+sin(cl.time)*5;
fov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;
fovv_x = r_refdef.fovv_x;
fovv_y = r_refdef.fovv_y;
if ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))
{
Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist, false);
Matrix4x4_CM_Projection_Far(r_refdef.m_projection_view, fovv_x, fovv_y, r_refdef.mindist, r_refdef.maxdist, false);
fov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
fov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
fovv_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
fovv_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
}
if (fovoverrides)
{
fov_l = fovoverrides[0];
fov_r = fovoverrides[1];
fov_d = fovoverrides[2];
fov_u = fovoverrides[3];
fov_x = fov_r-fov_l;
fov_y = fov_u-fov_d;
fovv_x = fov_x;
fovv_y = fov_y;
r_refdef.flipcull = ((fov_u < fov_d)^(fov_r < fov_l))?SHADER_CULL_FLIP:0;
}
else
{
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, false);
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_view, fovv_x, fovv_y, r_refdef.mindist, false);
fov_l = -fov_x / 2;
fov_r = fov_x / 2;
fov_d = -fov_y / 2;
fov_u = fov_y / 2;
}
Matrix4x4_CM_Projection_Offset(r_refdef.m_projection_std, fov_l, fov_r, fov_d, fov_u, r_refdef.mindist, r_refdef.maxdist, false);
Matrix4x4_CM_Projection_Offset(r_refdef.m_projection_view, -fovv_x/2, fovv_x/2, -fovv_y/2, fovv_y/2, r_refdef.mindist, r_refdef.maxdist, false);
r_refdef.m_projection_view[2+4*0] *= 0.333;
r_refdef.m_projection_view[2+4*1] *= 0.333;
r_refdef.m_projection_view[2+4*2] *= 0.333;
@ -2666,7 +2712,7 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb)
r_refdef.viewangles[2] = saveang[2]+ang[i][2];
VK_SetupViewPortProjection(true);
VK_SetupViewPortProjection(true, NULL, NULL);
/*if (!vk.rendertarg->depthcleared)
{
@ -2752,7 +2798,7 @@ void VK_R_RenderEye(texid_t image, vec4_t fovoverride, vec3_t eyeangorg[2])
{
struct vk_rendertarg *rt;
VK_SetupViewPortProjection(false);
VK_SetupViewPortProjection(false, eyeangorg, fovoverride);
rt = &postproc[postproc_buf++%countof(postproc)];
rt->rpassflags |= RP_VR;
@ -2933,7 +2979,7 @@ void VK_R_RenderView (void)
}
else
{
VK_SetupViewPortProjection(false);
VK_SetupViewPortProjection(false, NULL, NULL);
if (rt != rtscreen)
VKBE_RT_Begin(rt);

View file

@ -55,11 +55,15 @@ static pluginputfuncs_t *inputfuncs;
//and finally include openxr stuff now that its hopefully not going to fail about missing typedefs.
#include <openxr/openxr_platform.h>
#if XR_CURRENT_API_VERSION < XR_MAKE_VERSION(1, 0, 16)
#define XR_ERROR_RUNTIME_UNAVAILABLE -51 //available starting 1.0.16
#endif
#ifdef XR_NO_PROTOTYPES
#define XRFUNCS \
XRFUNC(xrGetInstanceProcAddr) \
XRFUNC(xrResultToString) \
XRFUNC(xrEnumerateApiLayerProperties) \
XRFUNC(xrEnumerateInstanceExtensionProperties) \
XRFUNC(xrCreateInstance) \
XRFUNC(xrGetInstanceProperties) \
@ -108,6 +112,13 @@ XRFUNCS
#undef XRFUNC
#endif
#ifdef XR_EXT_hand_tracking
static PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT;
static PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT;
static PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT;
#endif
#ifdef SVNREVISION
#define APPLICATIONVERSION atoi(STRINGIFY(SVNREVISION))
#define ENGINEVERSION atoi(STRINGIFY(SVNREVISION))
@ -122,6 +133,9 @@ static cvar_t *xr_formfactor;
static cvar_t *xr_viewconfig;
static cvar_t *xr_metresize;
static cvar_t *xr_skipregularview;
static cvar_t *xr_fingertracking;
static void XR_SetupInputs_Instance(void);
#define METRES_TO_QUAKE(x) ((x)*xr_metresize->value)
#define QUAKE_TO_METRES(x) ((x)/xr_metresize->value)
@ -224,6 +238,8 @@ static struct
//session state
XrSession session; //driver context
XrSessionState state;
qboolean beginning;
qboolean ending;
XrSpace space;
struct
{ //basically just swapchain state.
@ -255,6 +271,17 @@ static struct
XrSpace space; //for poses.
qboolean held; //for buttons.
} actions[256];
qboolean inputsdirty; //mostly for printing them.
#ifdef XR_EXT_hand_tracking
struct
{
XrHandTrackerEXT handle;
qboolean active;
XrHandJointLocationEXT jointloc[XR_HAND_JOINT_COUNT_EXT];
XrHandJointVelocityEXT jointvel[XR_HAND_JOINT_COUNT_EXT];
} hand[2];
#endif
} xr;
static qboolean QDECL XR_PluginMayUnload(void)
@ -284,11 +311,23 @@ static void XR_SessionEnded(void)
}
}
#ifdef XR_EXT_hand_tracking
for (u = 0; u < countof(xr.hand); u++)
{
if (xr.hand[u].handle)
xrDestroyHandTrackerEXT(xr.hand[u].handle);
xr.hand[u].handle = XR_NULL_HANDLE;
xr.hand[u].active = false;
}
#endif
if (xr.session)
{
xrDestroySession(xr.session);
xr.session = XR_NULL_HANDLE;
}
xr.state = XR_SESSION_STATE_UNKNOWN;
xr.beginning = false;
}
static void XR_Shutdown(void)
{ //called on any kind of failure
@ -366,20 +405,44 @@ static const char *XR_StringForResult(XrResult res)
case XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: return "XR_ERROR_ACTIONSETS_ALREADY_ATTACHED";
case XR_ERROR_LOCALIZED_NAME_DUPLICATED: return "XR_ERROR_LOCALIZED_NAME_DUPLICATED";
case XR_ERROR_LOCALIZED_NAME_INVALID: return "XR_ERROR_LOCALIZED_NAME_INVALID";
// case XR_ERROR_RUNTIME_UNAVAILABLE: return "XR_ERROR_RUNTIME_UNAVAILABLE";
default:
return va("XrResult %i", res);
}
#endif
}
static XrBool32 XRAPI_CALL XR_DebugPrint(XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes, const XrDebugUtilsMessengerCallbackDataEXT *callbackData, void *userData)
{
char *sev;
switch(messageSeverity)
{
case 1/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT*/: sev = "^8"; break;
case 16/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT*/: sev = ""; break;
case 256/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT*/: sev = CON_WARNING; break;
default:
case 4096/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT*/: sev = CON_ERROR; break;
}
Con_Printf("%s%s: %s\n", sev, callbackData->functionName, callbackData->message);
return XR_FALSE;
}
static qboolean XR_PreInit(vrsetup_t *qreqs)
{
XrResult res;
const char *ext;
const char *xrexts[8], *ext;
const char *xrlayers[8];
uint32_t numext = 0, numlayers = 0;
qboolean havedebugutils = false;
#ifdef XR_EXT_hand_tracking
qboolean havehandtrack = false;
#endif
XR_Shutdown(); //just in case...
if (qreqs->structsize != sizeof(*qreqs))
if (qreqs->structsize != sizeof(*qreqs) || xr_enable->ival < 0)
return false; //nope, get lost.
if (!strncasecmp(xr_formfactor->string, "none", 4))
qreqs->vrplatform = VR_HEADLESS;
@ -425,27 +488,87 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
}
xr.instance = XR_NULL_HANDLE;
if (xr_debug->ival)
{
uint32_t count, u;
struct XrApiLayerProperties *props;
if (XR_SUCCEEDED(xrEnumerateApiLayerProperties(0, &count, NULL)))
{
props = calloc(count, sizeof(*props));
for (u = 0; u < count; u++)
props[u].type = XR_TYPE_API_LAYER_PROPERTIES;
xrEnumerateApiLayerProperties(count, &count, props);
for (u = 0; u < count; u++)
Con_Printf("OpenXR Layer %s: %s\n", props[u].layerName, props[u].description);
if (xr_debug->ival>1)
{
for (u = 0; u < count; u++)
if (!strcmp(props[u].layerName, "XR_APILAYER_LUNARG_core_validation"))
{
xrlayers[numlayers++] = "XR_APILAYER_LUNARG_core_validation";
break;
}
if (u==count)
Con_Printf("OpenXR: Validation layers not found\n");
}
free(props);
}
}
{
unsigned int exts = 0, u=0;
XrExtensionProperties *extlist;
res = xrEnumerateInstanceExtensionProperties(NULL, 0, &exts, NULL);
if (XR_SUCCEEDED(res))
if (res == XR_ERROR_RUNTIME_UNAVAILABLE || res == XR_ERROR_INSTANCE_LOST)
{
Con_Printf(CON_WARNING"OpenXR: no runtime installed\n");
return false;
}
else if (XR_SUCCEEDED(res))
{
extlist = calloc(exts, sizeof(*extlist));
for (u = 0; u < exts; u++)
extlist[u].type = XR_TYPE_EXTENSION_PROPERTIES;
xrEnumerateInstanceExtensionProperties(NULL, exts, &exts, extlist);
//print a list of them all, if we can.
if (xr_debug->ival)
{
Con_Printf("OpenXR:\n");
Con_DPrintf("OpenXR:\n");
for (u = 0; u < exts; u++)
Con_Printf("\t%s\n", extlist[u].extensionName);
Con_DPrintf("\t%s\n", extlist[u].extensionName);
}
//make sure we have an appropriate extension for the API we're using.
if (ext)
{
for (u = 0; u < exts; u++)
if (!strcmp(extlist[u].extensionName, ext))
break;
if (u == exts)
{
Con_Printf(CON_ERROR"OpenXR: instance driver does not support required %s\n", ext);
free(extlist);
return false; //would just give an error on xrCreateInstance anyway.
}
xrexts[numext++] = ext;
}
//look for some interesting extensions
for (u = 0; u < exts; u++)
{
if (!strcmp(extlist[u].extensionName, XR_EXT_DEBUG_UTILS_EXTENSION_NAME) && !havedebugutils && xr_debug->ival)
havedebugutils = true, xrexts[numext++] = XR_EXT_DEBUG_UTILS_EXTENSION_NAME;
#ifdef XR_EXT_hand_tracking
if (!strcmp(extlist[u].extensionName, XR_EXT_HAND_TRACKING_EXTENSION_NAME) && !havehandtrack && xr_fingertracking->ival)
havehandtrack = true, xrexts[numext++] = XR_EXT_HAND_TRACKING_EXTENSION_NAME;
#endif
}
free(extlist);
}
else
@ -453,11 +576,6 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
Con_Printf(CON_ERROR"OpenXR: xrEnumerateInstanceExtensionProperties failed (%s)\n", XR_StringForResult(res));
return false;
}
if (u == exts)
{
Con_Printf(CON_ERROR"OpenXR: instance driver does not support required %s\n", ext);
return false; //would just give an error on xrCreateInstance anyway.
}
}
//create our instance
@ -469,18 +587,37 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
Q_strlcpy(createinfo.applicationInfo.engineName, "FTEQW", sizeof(createinfo.applicationInfo.engineName));
createinfo.applicationInfo.engineVersion = ENGINEVERSION;
createinfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
createinfo.enabledApiLayerCount = 0;
createinfo.enabledApiLayerNames = NULL;
createinfo.enabledExtensionCount = ext?1:0;
createinfo.enabledExtensionNames = &ext;
createinfo.enabledApiLayerCount = numlayers;
createinfo.enabledApiLayerNames = xrlayers;
createinfo.enabledExtensionCount = numext;
createinfo.enabledExtensionNames = xrexts;
res = xrCreateInstance(&createinfo, &xr.instance);
}
if (res == XR_ERROR_RUNTIME_UNAVAILABLE || res == XR_ERROR_INSTANCE_LOST)
{
Con_Printf(CON_WARNING"OpenXR: no runtime installed\n");
return false;
}
if (XR_FAILED(res) || !xr.instance)
{
Con_Printf(CON_ERROR"OpenXR Runtime: xrCreateInstance failed (%s)\n", XR_StringForResult(res));
return false;
}
if (havedebugutils)
{
XrDebugUtilsMessengerEXT messenger1 = XR_NULL_HANDLE;
XrDebugUtilsMessengerCreateInfoEXT cb = {XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
PFN_xrCreateDebugUtilsMessengerEXT xrCreateDebugUtilsMessengerEXT;
cb.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
cb.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
cb.userCallback = XR_DebugPrint;
cb.userData = NULL;
if (!XR_FAILED(xrGetInstanceProcAddr(xr.instance, "xrCreateDebugUtilsMessengerEXT", (PFN_xrVoidFunction*) &xrCreateDebugUtilsMessengerEXT)))
xrCreateDebugUtilsMessengerEXT(xr.instance, &cb, &messenger1);
}
if (xr_debug->ival)
{
XrInstanceProperties props = {XR_TYPE_INSTANCE_PROPERTIES};
@ -511,15 +648,43 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
return false;
}
if (xr_debug->ival)
{
XrSystemProperties props = {XR_TYPE_SYSTEM_PROPERTIES};
#ifdef XR_EXT_hand_tracking
XrSystemHandTrackingPropertiesEXT handtrackprops = {XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT};
if (havehandtrack)
{ //instance might support it, but the specific hardware we're trying to use might not, in which case don't try using it after all.
handtrackprops.next = props.next;
props.next = &handtrackprops;
}
#endif
if (qreqs->vrplatform != VR_HEADLESS)
if (XR_SUCCEEDED(xrGetSystemProperties(xr.instance, xr.systemid, &props)))
{
if (xr_debug->ival)
Con_Printf("OpenXR System: %s\n", props.systemName);
}
#ifdef XR_EXT_hand_tracking
havehandtrack = handtrackprops.supportsHandTracking;
#endif
}
#ifdef XR_EXT_hand_tracking
if (havehandtrack)
{
xrGetInstanceProcAddr(xr.instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*) &xrCreateHandTrackerEXT);
xrGetInstanceProcAddr(xr.instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*) &xrLocateHandJointsEXT);
xrGetInstanceProcAddr(xr.instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*) &xrDestroyHandTrackerEXT);
}
else
{
xrCreateHandTrackerEXT = NULL;
xrLocateHandJointsEXT = NULL;
xrDestroyHandTrackerEXT = NULL;
}
#endif
switch(qreqs->vrplatform)
{
default:
@ -655,10 +820,18 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
}
}
if (qreqs->vrplatform == VR_HEADLESS)
return true;
res = xrEnumerateViewConfigurationViews(xr.instance, xr.systemid, xr.viewtype, 0, &xr.viewcount, NULL);
if (xr.viewcount > MAX_VIEW_COUNT)
xr.viewcount = MAX_VIEW_COUNT; //oh noes! evile!
xr.views = calloc(1,sizeof(*xr.views)*xr.viewcount);
{
uint32_t u;
for (u = 0; u < xr.viewcount; u++)
xr.views[u].type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
}
res = xrEnumerateViewConfigurationViews(xr.instance, xr.systemid, xr.viewtype, xr.viewcount, &xr.viewcount, xr.views);
//caller now knows what device/contextversion/etc to init with
@ -751,6 +924,9 @@ static qboolean XR_Init(vrsetup_t *qreqs, rendererstate_t *info)
break;
#endif
}
XR_SetupInputs_Instance();
return true;
}
@ -949,7 +1125,7 @@ static int QDECL XR_BindProfileFile(const char *fname, qofs_t fsize, time_t mtim
free(buf);
VFS_CLOSE(f);
}
return false;
return true;
}
static const struct
@ -1045,7 +1221,7 @@ static const struct
},
};
static void XR_SetupInputs(void)
static void XR_SetupInputs_Instance(void)
{
unsigned int h;
XrResult res;
@ -1071,8 +1247,11 @@ static void XR_SetupInputs(void)
for (h = 0; h < countof(xr_knownprofiles); h++)
XR_BindProfileStr(xr_knownprofiles[h].name, xr_knownprofiles[h].script);
}
//begin session specific. stuff
}
static void XR_SetupInputs_Session(void)
{
unsigned int h;
XrResult res;
//create action space stuff.
for (h = 0; h < xr.numactions; h++)
@ -1107,7 +1286,25 @@ static void XR_SetupInputs(void)
Con_Printf("openxr: xrAttachSessionActionSets failed - %s\n", XR_StringForResult(res));
}
#if 1
#ifdef XR_EXT_hand_tracking
//create some hand trackers... try to create one for each hand...
//(note: this is more a finger-joint tracker than a hand tracker, though limited controllers generally mean its good only for fingers)
if (xrCreateHandTrackerEXT)
for (h = 0; h < 2; h++)
{
XrHandTrackerCreateInfoEXT info = {XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT};
info.hand = h?XR_HAND_RIGHT_EXT:XR_HAND_LEFT_EXT;
info.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
res = xrCreateHandTrackerEXT(xr.session, &info, &xr.hand[h].handle);
if (XR_FAILED(res))
Con_Printf("openxr: xrCreateHandTrackerEXT failed - %s\n", XR_StringForResult(res));
}
#endif
}
static void XR_PrintInputs(void)
{
XrResult res;
if (xr_debug->ival)
{
XrInteractionProfileState profile = {XR_TYPE_INTERACTION_PROFILE_STATE};
@ -1167,8 +1364,8 @@ static void XR_SetupInputs(void)
Con_Printf("\t%s: error %s\n", xr.actions[u].actname, XR_StringForResult(res));
}
}
#endif
}
static void XR_UpdateInputs(XrTime time)
{
XrResult res;
@ -1297,6 +1494,10 @@ static qboolean XR_Begin(void)
XrResult res;
uint32_t swapfmts;
int64_t *fmts, fmttouse=0;
xr.beginning = false;
xr.ending = false;
{
XrSessionCreateInfo sessioninfo = {XR_TYPE_SESSION_CREATE_INFO};
sessioninfo.next = xr.bindinginfo;
@ -1322,7 +1523,9 @@ static qboolean XR_Begin(void)
xrEnumerateSwapchainFormats(xr.session, 0, &swapfmts, NULL);
fmts = alloca(sizeof(*fmts)*swapfmts);
res = xrEnumerateSwapchainFormats(xr.session, swapfmts, &swapfmts, fmts);
if (!swapfmts)
if (xr.renderer == QR_HEADLESS)
;
else if (!swapfmts)
Con_Printf("OpenXR: No swapchain formats to use (%s)\n", XR_StringForResult(res));
#ifdef XR_USE_GRAPHICS_API_OPENGL
else if (xr.renderer == QR_OPENGL)
@ -1497,6 +1700,7 @@ static qboolean XR_Begin(void)
xr.eye[u].swapimages = calloc(xr.eye[u].numswapimages, sizeof(*xr.eye[u].swapimages));
for (i = 0; i < xr.eye[u].numswapimages; i++)
{
xr.eye[u].swapimages[i].format = swapinfo.format;
xr.eye[u].swapimages[i].num = xrimg[i].image;
xr.eye[u].swapimages[i].width = swapinfo.width;
xr.eye[u].swapimages[i].height = swapinfo.height;
@ -1511,7 +1715,7 @@ static qboolean XR_Begin(void)
if (XR_FAILED(res))
return false;
XR_SetupInputs();
XR_SetupInputs_Session();
return true;
}
@ -1522,6 +1726,8 @@ static void XR_ProcessEvents(void)
XrResult res;
for (;;)
{
ev.type = XR_TYPE_EVENT_DATA_BUFFER;
ev.next = NULL;
res = xrPollEvent(xr.instance, &ev);
if (res == XR_EVENT_UNAVAILABLE || XR_FAILED(res))
return; //nothing interesting here folks
@ -1531,29 +1737,28 @@ static void XR_ProcessEvents(void)
default: //no idea wtf that is
Con_Printf("openxr event %u\n", ev.type);
break;
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
XR_Shutdown(); //we're meant to try restarting, but that's a hassle. FIXME: expect the user to do a vid_restart.
return;
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
break;
case XR_TYPE_EVENT_DATA_EVENTS_LOST:
{
XrEventDataEventsLost *s = (XrEventDataEventsLost*)&ev;
Con_Printf(CON_ERROR"OpenXR: Lost %u events!\n", s->lostEventCount);
}
break;
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
xr.inputsdirty = true;
break;
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
{
XrEventDataSessionStateChanged *s = (XrEventDataSessionStateChanged*)&ev;
switch(s->state)
{
default:
break; //urgh
case XR_SESSION_STATE_READY:
{
XrSessionBeginInfo info = {XR_TYPE_SESSION_BEGIN_INFO};
info.primaryViewConfigurationType = xr.viewtype;
res = xrBeginSession(xr.session, &info);
if (XR_FAILED(res))
Con_Printf("Unable to begin session: %s\n", XR_StringForResult(res));
}
break;
case XR_SESSION_STATE_STOPPING:
res = xrEndSession(xr.session);
if (XR_FAILED(res))
Con_Printf("Unable to end session: %s\n", XR_StringForResult(res));
break;
}
xr.state = s->state;
return; //make sure the outer loop actually sees each state change.
}
break;
}
@ -1572,28 +1777,73 @@ static qboolean XR_SyncFrame(double *frametime)
return true;
}
if (!xr.session)
{
if (xr_enable->ival && !XR_Begin())
{
XR_Shutdown();
return false;
}
}
else
{
if (!xr_enable->ival && !xr.ending)
{ //user doesn't want a session apparently. try and end the current session cleanly.
res = xrRequestExitSession(xr.session);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to request session end: %s\n", XR_StringForResult(res));
xr.ending = true;
}
}
XR_ProcessEvents();
memset(&xr.framestate, 0, sizeof(xr.framestate));
xr.framestate.type = XR_TYPE_FRAME_STATE;
switch(xr.state)
safeswitch(xr.state)
{
case XR_SESSION_STATE_READY:
case XR_SESSION_STATE_FOCUSED:
case XR_SESSION_STATE_SYNCHRONIZED:
case XR_SESSION_STATE_VISIBLE:
xr.framestate.shouldRender = !!xr.session;
case XR_SESSION_STATE_IDLE: //not allowed to progress till the user puts it on their head/etc
xr.beginning = false;
break;
default:
xr.framestate.shouldRender = false;
case XR_SESSION_STATE_READY:
if (!xr.beginning)
{
XrSessionBeginInfo info = {XR_TYPE_SESSION_BEGIN_INFO};
info.primaryViewConfigurationType = xr.viewtype;
res = xrBeginSession(xr.session, &info);
if (XR_FAILED(res))
Con_Printf("Unable to begin session: %s\n", XR_StringForResult(res));
xr.beginning = true; //begin our xr loop... (and/or stop the spam just above)
}
break;
case XR_SESSION_STATE_SYNCHRONIZED: //no rendering or input yet
case XR_SESSION_STATE_VISIBLE: //now generating video frames, but no input yet
case XR_SESSION_STATE_FOCUSED: //we have inputs! (and still generating video frames)
break;
case XR_SESSION_STATE_STOPPING: //going back to idle (user took it off their head). we'll go back to rendering to our window again.
xrEndSession(xr.session);
xr.beginning = false;
break;
case XR_SESSION_STATE_LOSS_PENDING: //terminate for now. recreate later if you want.
XR_SessionEnded(); //destroys the session but not the instance, so it can be started up again if desired.
xr.beginning = false;
break;
case XR_SESSION_STATE_EXITING: //terminate with prejudice.
XR_SessionEnded();
xr.beginning = false;
if (!xr.ending)
XR_Shutdown(); //this doesn't look like one we requested... don't let it start back up again.
break;
case XR_SESSION_STATE_UNKNOWN:
case XR_SESSION_STATE_MAX_ENUM:
safedefault:
xr.beginning = false; //some weird error.
break;
}
if (xr.framestate.shouldRender)
if (xr.beginning)
{
XrTime time;
memset(&xr.framestate, 0, sizeof(xr.framestate));
xr.framestate.type = XR_TYPE_FRAME_STATE;
res = xrWaitFrame(xr.session, NULL, &xr.framestate);
if (XR_FAILED(res))
{
@ -1631,39 +1881,77 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
if (!xr.instance)
return false; //err... noooes!
if (!xr.session)
{
if (!xr_enable->ival)
if (!xr.session || !xr.beginning)
return false;
if (!XR_Begin())
{ //something catasrophic went wrong. don't spam begins.
XR_Shutdown();
}
return false;
}
if (!xr_enable->ival)
if (xr.inputsdirty)
{
res = xrRequestExitSession(xr.session);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to request session end: %s\n", XR_StringForResult(res));
XR_ProcessEvents();
xr.inputsdirty = false;
XR_PrintInputs();
}
switch(xr.state)
#ifdef XR_EXT_hand_tracking
for (u = 0; u < 2; u++)
{
case XR_SESSION_STATE_EXITING:
XR_SessionEnded(); //destroys the session but not the instance, so it can be started up again if desired.
return false;
case XR_SESSION_STATE_FOCUSED:
case XR_SESSION_STATE_SYNCHRONIZED:
case XR_SESSION_STATE_VISIBLE:
case XR_SESSION_STATE_READY:
break;
default:
return false; //not ready.
vec3_t ang, org;
unsigned int j;
static const char *jointnames[] = {
"PALM",
"WRIST",
"THUMB_METACARPAL",
"THUMB_PROXIMAL",
"THUMB_DISTAL",
"THUMB_TIP",
"INDEX_METACARPAL",
"INDEX_PROXIMAL",
"INDEX_INTERMEDIATE",
"INDEX_DISTAL",
"INDEX_TIP",
"MIDDLE_METACARPAL",
"MIDDLE_PROXIMAL",
"MIDDLE_INTERMEDIATE",
"MIDDLE_DISTAL",
"MIDDLE_TIP",
"RING_METACARPAL",
"RING_PROXIMAL",
"RING_INTERMEDIATE",
"RING_DISTAL",
"RING_TIP",
"LITTLE_METACARPAL",
"LITTLE_PROXIMAL",
"LITTLE_INTERMEDIATE",
"LITTLE_DISTAL",
"LITTLE_TIP",
};
XrHandJointsLocateInfoEXT info = {XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT};
XrHandJointLocationsEXT loc = {XR_TYPE_HAND_JOINT_LOCATIONS_EXT};
XrHandJointVelocitiesEXT vel = {XR_TYPE_HAND_JOINT_VELOCITIES_EXT};
loc.next = &vel;
loc.jointCount = countof(xr.hand[u].jointloc);
loc.jointLocations = xr.hand[u].jointloc;
vel.next = &vel;
vel.jointCount = countof(xr.hand[u].jointvel);
vel.jointVelocities = xr.hand[u].jointvel;
if (xr.hand[u].handle)
xrLocateHandJointsEXT(xr.hand[u].handle, &info, &loc);
xr.hand[u].active = loc.isActive;
if (!xr.hand[u].active || !xr_debug->ival)
continue;
for (j = 0; j < countof(jointnames); j++)
{
if (!xr.hand[u].jointloc[j].locationFlags && !xr.hand[u].jointvel[j].velocityFlags)
continue;
XR_PoseToAngOrg(&xr.hand[u].jointloc[j].pose, ang, org);
Con_Printf("%s %s: (%g %g %g) [%g %g %g] %g (%g %g %g) [%g %g %g]\n", u?"Right":"Left", jointnames[j],
ang[0],ang[1],ang[2],org[0],org[1],org[2], xr.hand[u].jointloc[j].radius,
xr.hand[u].jointvel[j].angularVelocity.x,xr.hand[u].jointvel[j].angularVelocity.y,xr.hand[u].jointvel[j].angularVelocity.z,
xr.hand[u].jointvel[j].linearVelocity.x,xr.hand[u].jointvel[j].linearVelocity.y,xr.hand[u].jointvel[j].linearVelocity.z);
}
}
#endif
if (!xr.needrender)
return false; //xrWaitFrame not called?
@ -1803,15 +2091,15 @@ qboolean Plug_Init(void)
#undef XRFUNC
{NULL}};
#ifdef _WIN32
#define XR_LOADER_LIBNAME "openxr_loader"
#define XR_LOADER_LIBNAME "openxr_loader"ARCH_DL_POSTFIX
#else
#define XR_LOADER_LIBNAME "libopenxr_loader"
#define XR_LOADER_LIBNAME "libopenxr_loader"ARCH_DL_POSTFIX".1"
#endif
if (!lib)
lib = plugfuncs->LoadDLL(XR_LOADER_LIBNAME, funcs);
if (!lib)
{
Con_Printf(CON_ERROR"OpenXR: Unable to load "XR_LOADER_LIBNAME ARCH_DL_POSTFIX"\n");
Con_Printf(CON_ERROR"OpenXR: Unable to load "XR_LOADER_LIBNAME"\n");
return false;
}
}
@ -1827,7 +2115,8 @@ qboolean Plug_Init(void)
xr_formfactor = cvarfuncs->GetNVFDG("xr_formfactor", "head", CVAR_ARCHIVE, "Controls which VR system to try to use. Valid options are head, or hand", "OpenXR configuration");
xr_viewconfig = cvarfuncs->GetNVFDG("xr_viewconfig", "", CVAR_ARCHIVE, "Controls the type of view we aim for. Valid options are mono, stereo, or quad", "OpenXR configuration");
xr_metresize = cvarfuncs->GetNVFDG("xr_metresize", "26.24671916", CVAR_ARCHIVE, "Size of a metre in game units", "OpenXR configuration");
xr_skipregularview = cvarfuncs->GetNVFDG("xr_skipregularview", "0", CVAR_ARCHIVE, "Skip rendering the regular view when OpenXR is active.", "OpenXR configuration");
xr_skipregularview = cvarfuncs->GetNVFDG("xr_skipregularview", "1", CVAR_ARCHIVE, "Skip rendering the regular view when OpenXR is active.", "OpenXR configuration");
xr_fingertracking = cvarfuncs->GetNVFDG("xr_fingertracking", "0", 0, "Attempt to track individual finger joints.", "OpenXR configuration");
return true;
}
return false;