OpenXR stuff should be a little more usable now.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5850 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-05-09 13:02:49 +00:00
parent 13d524a30b
commit f35928f4fd
25 changed files with 360 additions and 353 deletions

View File

@ -49,7 +49,8 @@ static cvar_t cl_sendchatstate = CVARD("cl_sendchatstate", "1", "Announce your c
cvar_t cl_prydoncursor = CVAR("cl_prydoncursor", ""); //for dp protocol
cvar_t cl_instantrotate = CVARF("cl_instantrotate", "1", CVAR_SEMICHEAT);
cvar_t in_xflip = {"in_xflip", "0"};
cvar_t in_xflip = CVAR("in_xflip", "0");
cvar_t in_vraim = CVARD("in_vraim", "1", "When set to 1, the 'view' angle sent to the server is controlled by your vr headset instead of separately. This is for fallback behaviour and blocks mouse+joy+gamepad aiming.");
cvar_t prox_inmenu = CVAR("prox_inmenu", "0");
@ -915,7 +916,7 @@ void CL_BaseMove (usercmd_t *cmd, int pnum, float priortime, float extratime)
float nscale = extratime?extratime / (extratime+priortime):0;
float oscale = 1 - nscale;
cmd->fservertime = cl.time*1000;
cmd->fservertime = cl.time;
cmd->servertime = cl.time*1000;
//
@ -940,7 +941,7 @@ void CL_BaseMove (usercmd_t *cmd, int pnum, float priortime, float extratime)
CL_GatherButtons(cmd, pnum);
}
static void CL_ClampPitch (int pnum, float frametime)
void CL_ClampPitch (int pnum, float frametime)
{
float mat[16];
float roll;
@ -1097,6 +1098,30 @@ static void CL_ClampPitch (int pnum, float frametime)
pv->viewangles[YAW] *= 360;
VectorClear(pv->viewanglechange);
if (in_vraim.ival && (pv->vrdev[VRDEV_HEAD].status&VRSTATUS_ANG))
{ //overcomplicated code to replace the pitch+roll angles and add to the yaw angle.
#if 0
matrix3x4 base, head, res;
vec3_t na = {0, pv->viewangles[YAW], 0};
vec3_t f,l,u,o;
Matrix3x4_RM_FromAngles(na, vec3_origin, base[0]);
for (i=0 ; i<3 ; i++)
na[i] = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[i]);
Matrix3x4_RM_FromAngles(na, pv->vrdev[VRDEV_HEAD].origin, head[0]);
Matrix3x4_Multiply(head[0], base[0], res[0]);
Matrix3x4_RM_ToVectors(res[0], f,l,u,o);
VectorAngles(f,u,pv->aimangles,false);
for (i=0 ; i<3 ; i++)
cmd->angles[i] = ANGLE2SHORT(na[i]);
#else
pv->aimangles[PITCH] = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[PITCH]);
pv->aimangles[YAW] = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[YAW]) + pv->viewangles[YAW];
pv->aimangles[ROLL] = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[ROLL]);
#endif
}
else
VectorCopy(pv->viewangles, pv->aimangles);
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
{
@ -1129,6 +1154,11 @@ static void CL_ClampPitch (int pnum, float frametime)
pv->viewangles[PITCH] = cl.maxpitch;
if (pv->viewangles[PITCH] < cl.minpitch)
pv->viewangles[PITCH] = cl.minpitch;
if (pv->aimangles[PITCH] > cl.maxpitch)
pv->aimangles[PITCH] = cl.maxpitch;
if (pv->aimangles[PITCH] < cl.minpitch)
pv->aimangles[PITCH] = cl.minpitch;
}
// if (cl.viewangles[pnum][ROLL] > 50)
@ -1169,7 +1199,10 @@ static void CL_FinishMove (usercmd_t *cmd, int pnum)
CL_GatherButtons(cmd, pnum);
for (i=0 ; i<3 ; i++)
cmd->angles[i] = ((int)(cl.playerview[pnum].viewangles[i]*65536.0/360)&65535);
cmd->angles[i] = (int)(ANGLE2SHORT(cl.playerview[pnum].aimangles[i]))&65535;
cmd->vr[VRDEV_LEFT] = cl.playerview[pnum].vrdev[VRDEV_LEFT];
cmd->vr[VRDEV_RIGHT] = cl.playerview[pnum].vrdev[VRDEV_RIGHT];
cmd->vr[VRDEV_HEAD] = cl.playerview[pnum].vrdev[VRDEV_HEAD];
if (in_impulsespending[pnum] && !cl.paused)
{
@ -2559,6 +2592,7 @@ void CL_InitInput (void)
Cvar_Register (&cl_fastaccel, inputnetworkcvargroup);
Cvar_Register (&in_xflip, inputnetworkcvargroup);
Cvar_Register (&in_vraim, inputnetworkcvargroup);
Cvar_Register (&cl_nodelta, inputnetworkcvargroup);
Cvar_Register (&prox_inmenu, inputnetworkcvargroup);

View File

@ -5978,55 +5978,6 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
return true;
}
void CL_UpdateHeadAngles(void)
{
/*FIXME: no idea what I'm doing with this. lets just not break anything for now
//identity, for now
vec3_t headchange[3] =
{
{1,0,0},
{0,1,0},
{0,0,1}
};
vec3_t tmp[3], tmp2[3];
playerview_t *pv = &cl.playerview[0];
tmp2[0][0] = 0;
tmp2[0][1] = host_frametime*90;
tmp2[0][2] = 0;
AngleVectorsFLU(tmp2[0], headchange[0], headchange[1], headchange[2]);
switch(cl_headmode.ival)
{
case 3: //head angles change both
R_ConcatRotations(headchange, r_refdef.headaxis, tmp);
break;
case 2: //head changes are entirely relative to the 'view' angle
R_ConcatRotations(headchange, r_refdef.headaxis, tmp);
memcpy(r_refdef.headaxis, tmp, sizeof(r_refdef.headaxis));
break;
case 1: //head changes change the view angle directly.
AngleVectorsFLU(pv->viewangles, tmp[0], tmp[1], tmp[2]);
R_ConcatRotations(headchange, tmp, tmp2);
VectorAngles(tmp2[0], tmp2[2], pv->viewangles);
pv->viewangles[0] *= r_meshpitch.value;
pv->viewangles[2] *= r_meshroll.value;
//fall through
default:
case 0: //off
VectorSet(r_refdef.headaxis[0], 1, 0, 0);
VectorSet(r_refdef.headaxis[1], 0, 1, 0);
VectorSet(r_refdef.headaxis[2], 0, 0, 1);
break;
}
*/
VectorSet(r_refdef.headaxis[0], 1, 0, 0);
VectorSet(r_refdef.headaxis[1], 0, 1, 0);
VectorSet(r_refdef.headaxis[2], 0, 0, 1);
}
/*
==================
Host_Frame
@ -6363,8 +6314,6 @@ double Host_Frame (double time)
if (emscriptenfte_getvrframedata())
r_refdef.stereomethod = STEREO_WEBVR;
#endif
CL_UpdateHeadAngles();
{
RSpeedMark();
vid.ime_allow = false;

View File

@ -1036,7 +1036,7 @@ void CL_PredictMovePNum (int seat)
else
{
lerpangles = (cls.demoplayback == DPB_QUAKEWORLD);
VectorCopy (pv->viewangles, pv->simangles);
VectorCopy (pv->aimangles, pv->simangles);
}
}

View File

@ -661,6 +661,7 @@ struct playerview_s
// the client maintains its own idea of view angles, which are
// sent to the server each frame. And only reset at level change
// and teleport times
vec3_t aimangles; //angles actually being sent to the server (different due to in_vraim)
vec3_t viewangles; //current angles
vec3_t viewanglechange; //angles set by input code this frame
vec3_t intermissionangles; //absolute angles for intermission
@ -773,6 +774,8 @@ struct playerview_s
size_t reverbtype;
vec3_t velocity;
} audio;
struct vrdevinfo_s vrdev[VRDEV_COUNT];
};
//

View File

@ -1092,6 +1092,7 @@ qboolean IN_SetHandPosition(const char *devname, vec3_t org, vec3_t ang, vec3_t
{
int dtype;
int seat;
struct vrdevinfo_s *dev;
if (!strncmp(devname, "left", 4))
{
seat = atoi(devname+4);
@ -1111,23 +1112,35 @@ qboolean IN_SetHandPosition(const char *devname, vec3_t org, vec3_t ang, vec3_t
return false; //no idea what you're talking about.
if (seat < 0 || seat >= MAX_SPLITS)
return false; //duuuude!
cl_pendingcmd[seat].vr[dtype].status =
dev = &cl.playerview[seat].vrdev[dtype];
if (org)
VectorCopy(org, dev->origin);
else
VectorClear(dev->origin);
if (ang)
{
dev->angles[0] = ANGLE2SHORT(ang[0]),
dev->angles[1] = ANGLE2SHORT(ang[1]),
dev->angles[2] = ANGLE2SHORT(ang[2]);
}
else
VectorClear(dev->angles);
if (vel)
VectorCopy(vel, dev->velocity);
else
VectorClear(dev->velocity);
if (avel)
dev->avelocity[0] = ANGLE2SHORT(avel[0]),
dev->avelocity[1] = ANGLE2SHORT(avel[1]),
dev->avelocity[2] = ANGLE2SHORT(avel[2]);
else
VectorClear(dev->avelocity);
dev->status =
(org ?VRSTATUS_ORG:0)|
(ang ?VRSTATUS_ANG:0)|
(vel ?VRSTATUS_VEL:0)|
(avel?VRSTATUS_AVEL:0);
if (org)
VectorCopy(org, cl_pendingcmd[seat].vr[dtype].origin);
if (ang)
cl_pendingcmd[seat].vr[dtype].angles[0] = ANGLE2SHORT(ang[0]),
cl_pendingcmd[seat].vr[dtype].angles[1] = ANGLE2SHORT(ang[1]),
cl_pendingcmd[seat].vr[dtype].angles[2] = ANGLE2SHORT(ang[2]);
if (vel)
VectorCopy(vel, cl_pendingcmd[seat].vr[dtype].velocity);
if (avel)
cl_pendingcmd[seat].vr[dtype].avelocity[0] = ANGLE2SHORT(avel[0]),
cl_pendingcmd[seat].vr[dtype].avelocity[1] = ANGLE2SHORT(avel[1]),
cl_pendingcmd[seat].vr[dtype].avelocity[2] = ANGLE2SHORT(avel[2]);
return true;
}

View File

@ -90,6 +90,7 @@ cvar_t pr_csqc_formenus = CVAR("pr_csqc_formenus", "0");
#endif
static cvar_t dpcompat_csqcinputeventtypes = CVARD("dpcompat_csqcinputeventtypes", "999999", "Specifies the first csqc input event that the mod does not recognise. This should never have been a thing, but some mods are simply too buggy.");
extern cvar_t dpcompat_stats;
extern cvar_t in_vraim;
// standard effect cvars/sounds
extern cvar_t r_explosionlight;
@ -2244,6 +2245,12 @@ nogameaccess:
*r = r_refdef.viewangles[parametertype-VF_ANGLES_X];
break;
case VF_VRBASEORIENTATION:
if (csqc_nogameaccess && prinst == csqc_world.progs)
goto nogameaccess;
VectorCopy(r_refdef.base_angles, r);
break;
case VF_CL_VIEWANGLES_V:
if (csqc_nogameaccess && prinst == csqc_world.progs)
goto nogameaccess;
@ -2510,6 +2517,13 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
r_refdef.viewangles[parametertype-VF_ANGLES_X] = *p;
break;
case VF_VRBASEORIENTATION:
in_vraim.ival = 0; //csqc mod with explicit vr stuff.
r_refdef.base_known = true;
VectorCopy(p, r_refdef.base_angles);
VectorCopy(G_VECTOR(OFS_PARM2), r_refdef.base_origin);
break;
case VF_CL_VIEWANGLES_V:
if (csqc_playerview)
VectorCopy(p, csqc_playerview->viewangles);
@ -3823,7 +3837,7 @@ static void cs_set_input_state (usercmd_t *cmd)
if (csqcg.input_servertime)
*csqcg.input_servertime = cmd->fservertime;
if (csqcg.input_clienttime)
*csqcg.input_clienttime = cmd->fclienttime/1000.0f;
*csqcg.input_clienttime = cmd->fclienttime;
if (csqcg.input_cursor_screen)
{
@ -3922,7 +3936,10 @@ static void cs_get_input_state (usercmd_t *cmd)
if (csqcg.input_weapon)
cmd->weapon = *csqcg.input_weapon;
if (csqcg.input_servertime)
{
cmd->fservertime = *csqcg.input_servertime;
cmd->servertime = *csqcg.input_servertime*1000;
}
if (csqcg.input_cursor_screen)
Vector2Copy(csqcg.input_cursor_screen, cmd->cursor_screen);
@ -4064,6 +4081,11 @@ static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct global
if (!cmd->msec)
*cmd = cl.outframes[(f-1)&UPDATE_MASK].cmd[seat];
cmd->msec = (realtime - cl.outframes[(f-1)&UPDATE_MASK].senttime)*1000;
//make sure we have the latest info...
cmd->vr[VRDEV_LEFT] = csqc_playerview->vrdev[VRDEV_LEFT];
cmd->vr[VRDEV_RIGHT] = csqc_playerview->vrdev[VRDEV_RIGHT];
cmd->vr[VRDEV_HEAD] = csqc_playerview->vrdev[VRDEV_HEAD];
}
else
{
@ -7752,6 +7774,8 @@ void CSQC_Shutdown(void)
memset(&csqc_world, 0, sizeof(csqc_world));
memset(&csqcg, 0, sizeof(csqcg));
in_vraim.ival = in_vraim.value; //csqc mod with explicit vr stuff.
if (csqc_deprecated_warned>1)
{
if (!cl_csqc_nodeprecate.ival)

View File

@ -260,10 +260,12 @@ typedef struct
vec3_t vieworg; /*logical view center*/
vec3_t viewangles;
vec3_t viewaxis[3]; /*forward, left, up (NOT RIGHT)*/
vec3_t headaxis[3]; /*this is for head mounted displays. this is relative to the view*/
vec3_t eyeoffset; /*world space, for vr screenies*/
vec2_t projectionoffset; /*for off-centre rendering*/
qboolean base_known; /*otherwise we do some fallback behaviour (ie: viewangles.0y0 and forcing input_angles)*/
vec3_t base_angles, base_origin; /*for vr output, overrides per-eye viewangles according to that eye's matrix.*/
float fov_x, fov_y, afov;
float fovv_x, fovv_y; //viewmodel fovs
float mindist, maxdist; //maxdist may be 0, for 'infinite', in which case mindist probably isn't valid either.

View File

@ -326,7 +326,11 @@ void V_DriftPitch (playerview_t *pv)
return;
}
#ifdef QUAKESTATS
delta = pv->statsf[STAT_IDEALPITCH] - pv->viewangles[PITCH];
#else
delta = 0 - pv->viewangles[PITCH];
#endif
if (!delta)
{

View File

@ -74,7 +74,7 @@ typedef struct plugvrfuncs_s
qboolean (*Prepare) (vrsetup_t *setupinfo); //called before graphics context init
qboolean (*Init) (vrsetup_t *setupinfo, rendererstate_t *info); //called after graphics context init
qboolean (*SyncFrame)(double *frametime); //called in the client's main loop, to block/tweak frame times. True means the game should render as fast as possible.
qboolean (*Render) (void(*rendereye)(texid_t tex, vec4_t fovoverride, matrix3x4 axisorg));
qboolean (*Render) (void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3_t angorg[2]));
void (*Shutdown) (void);
#define plugvrfuncs_name "VR"
} plugvrfuncs_t;

View File

@ -245,10 +245,10 @@ typedef struct
int position;
int size;
} texwadlump_t;
int numwadtextures;
static int numwadtextures;
static texwadlump_t texwadlump[TEXWAD_MAXIMAGES];
wadfile_t *openwadfiles;
static wadfile_t *openwadfiles;
void Wads_Flush (void)
{

View File

@ -772,6 +772,39 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define fte_alignof(type) sizeof(qintptr_t)
#endif
//WARNING: FTE_CONSTRUCTOR things are unordered.
#ifdef __cplusplus
//use standard constructors in any c++ code...
#define FTE_CONSTRUCTOR(fn) \
static void fn(void); \
class atinit_##fn {atinit_##fn(void){fn();}}; \
static void fn(void)
#elif _MSC_VER
#pragma section(".CRT$XCU",read)
#if _MSC_VER >= 1500 //use '/include' so it doesn't get stripped from linker optimisations
#define INITIALIZER2_(f,p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
__pragma(comment(linker,"/include:" p #f "_")) \
static void f(void)
#else // '/include' doesn't exist, hope there's no linker optimisations.
#define INITIALIZER2_(f,p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
static void f(void)
#endif
#ifdef _WIN64
#define INITIALIZER(f) INITIALIZER2_(f,"")
#else
#define INITIALIZER(f) INITIALIZER2_(f,"_")
#endif
#else
//assume gcc/clang...
#define FTE_CONSTRUCTOR(fn) \
__attribute__((constructor)) static void fn(void)
#endif
//safeswitch(foo){safedefault: break;}
//switch, but errors for any omitted enum values despite the presence of a default case.
//(gcc will generally give warnings without the default, but sometimes you don't have control over the source of your enumeration values)

View File

@ -500,7 +500,7 @@ typedef struct
#define FTECONTENTS_EMPTY 0x00000000
#define FTECONTENTS_SOLID 0x00000001
//q2window 0x00000002
#define FTECONTENTS_WINDOW 0x00000002 //solid to bullets, but not sight/agro
//q2aux 0x00000004
#define FTECONTENTS_LAVA 0x00000008
#define FTECONTENTS_SLIME 0x00000010
@ -656,8 +656,8 @@ typedef struct
// content masks. Allow q2contents_window in here
//#define MASK_ALL (-1)
#define MASK_WORLDSOLID (FTECONTENTS_SOLID|Q2CONTENTS_WINDOW) /*default trace type for something simple that ignores non-bsp stuff*/
#define MASK_POINTSOLID (FTECONTENTS_SOLID|Q2CONTENTS_WINDOW|FTECONTENTS_BODY) /*default trace type for an entity of no size*/
#define MASK_WORLDSOLID (FTECONTENTS_SOLID|FTECONTENTS_WINDOW) /*default trace type for something simple that ignores non-bsp stuff*/
#define MASK_POINTSOLID (FTECONTENTS_SOLID|FTECONTENTS_WINDOW|FTECONTENTS_BODY) /*default trace type for an entity of no size*/
#define MASK_BOXSOLID (FTECONTENTS_SOLID|FTECONTENTS_PLAYERCLIP|Q2CONTENTS_WINDOW|FTECONTENTS_BODY) /*default trace type for an entity that does have size*/
#define MASK_PLAYERSOLID MASK_BOXSOLID
//#define MASK_DEADSOLID (Q2CONTENTS_SOLID|Q2CONTENTS_PLAYERCLIP|Q2CONTENTS_WINDOW)

View File

@ -787,12 +787,12 @@ typedef enum
VF_SKYROOM_CAMERA = 222,
VF_PIXELPSCALE = 223, //[dpi_x, dpi_y, dpi_y/dpi_x]
VF_PROJECTIONOFFSET = 224, //allows for off-axis projections.
VF_VRBASEORIENTATION= 225, //specifies the worldspace coords+angles of the VR room space.
//WARNING: update fteqcc when new entries are added.
VF_DP_CLEARSCREEN = 201, //misnomer - NOTOVERLAY would be a better name. when set to false prevents any and all post-proc things that might write colour values in areas with no geometry there.
//fuck DP and their complete lack of respect for existing implemenetations
VF_DP_FOG_DENSITY = 202, //misassigned
VF_DP_FOG_DENSITY = 202, //misassigned - fuck DP and their complete lack of respect for existing implemenetations
VF_DP_FOG_COLOR = 203, //misassigned
VF_DP_FOG_COLOR_R = 204, //misassigned
VF_DP_FOG_COLOR_G = 205, //misassigned
@ -802,7 +802,7 @@ typedef enum
VF_DP_FOG_END = 209, //misassigned
VF_DP_FOG_HEIGHT = 210, //misassigned
VF_DP_FOG_FADEDEPTH = 211, //misassigned
VF_DP_MAINVIEW = 400, // defective. should be a viewid instead, allowing for per-view motionblur instead of disabling it outright
VF_DP_MAINVIEW = 400, // defective. should have been a 1-based viewid instead, allowing for per-view motionblur instead of disabling it outright
VF_DP_MINFPS_QUALITY = 401, //multiplier for lod and culling to try to reduce costs.
} viewflags;

View File

@ -1189,6 +1189,22 @@ typedef struct
size_t bonedatamax;
} packet_entities_t;
struct vrdevinfo_s
{
unsigned int status;
#define VRSTATUS_ORG (1u<<0)
#define VRSTATUS_ANG (1u<<1)
#define VRSTATUS_VEL (1u<<2)
#define VRSTATUS_AVEL (1u<<3)
short angles[3];
short avelocity[3];
vec3_t origin;
vec3_t velocity;
#define VRDEV_LEFT 0
#define VRDEV_RIGHT 1
#define VRDEV_HEAD 2
#define VRDEV_COUNT 3
};
typedef struct usercmd_s
{
//the first members of this structure MUST match the q2 version
@ -1214,21 +1230,7 @@ typedef struct usercmd_s
unsigned int cursor_entitynumber;
//vr things
struct
{
unsigned int status;
#define VRSTATUS_ORG (1u<<0)
#define VRSTATUS_ANG (1u<<1)
#define VRSTATUS_VEL (1u<<2)
#define VRSTATUS_AVEL (1u<<3)
short angles[3];
short avelocity[3];
vec3_t origin;
vec3_t velocity;
#define VRDEV_LEFT 0
#define VRDEV_RIGHT 1
#define VRDEV_HEAD 2
} vr[3]; //left, right, head.
struct vrdevinfo_s vr[VRDEV_COUNT]; //left, right, head.
} usercmd_t;
typedef struct q2usercmd_s

View File

@ -88,6 +88,7 @@ Zone block
#else
#define VALGRIND_MAKE_MEM_UNDEFINED(ptr,sz) //as an alternative to memzero..
#define VALGRIND_MAKE_MEM_NOACCESS(ptr,sz)
#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(ptr,sz) //undo VALGRIND_MAKE_MEM_UNDEFINED, to make sure we don't read past the end of buffers.
#endif
void Memory_Init (void);

View File

@ -435,7 +435,7 @@ void R_RotateForEntity (float *m, float *modelview, const entity_t *e, const mod
R_SetupGL
=============
*/
static void R_SetupGL (matrix3x4 eyematrix, vec4_t fovoverrides, float projmatrix[16]/*for webvr*/, texid_t fbo)
static void R_SetupGL (vec3_t eyeangorg[2], vec4_t fovoverrides, float projmatrix[16]/*for webvr*/, texid_t fbo)
{
int x, x2, y2, y, w, h;
vec3_t newa;
@ -450,14 +450,32 @@ static void R_SetupGL (matrix3x4 eyematrix, vec4_t fovoverrides, float projmatri
newa[0] = r_refdef.viewangles[0];
newa[1] = r_refdef.viewangles[1];
newa[2] = r_refdef.viewangles[2] + gl_screenangle.value;
if (eyematrix)
if (eyeangorg)
{
matrix3x4 headmatrix;
extern cvar_t in_vraim;
matrix3x4 basematrix;
matrix3x4 eyematrix;
matrix3x4 viewmatrix;
Matrix3x4_RM_FromAngles(newa, r_refdef.vieworg, headmatrix[0]);
Matrix3x4_Multiply(headmatrix[0], eyematrix[0], viewmatrix[0]);
Matrix3x4_RM_ToVectors(viewmatrix[0], vpn, vright, vup, r_origin);
VectorNegate(vright, vright);
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]);
Matrix3x4_Multiply(eyematrix[0], basematrix[0], viewmatrix[0]);
Matrix3x4_RM_ToVectors(viewmatrix[0], vpn, vright, vup, r_origin);
VectorNegate(vright, vright);
}
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.
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
{
@ -717,7 +735,7 @@ static void R_RenderScene_Internal(void)
depthcleared = false; //whatever is in the depth buffer is no longer useful.
}
static void R_RenderEyeScene (texid_t rendertarget, vec4_t fovoverride, matrix3x4 eyematrix)
static void R_RenderEyeScene (texid_t rendertarget, vec4_t fovoverride, vec3_t eyeangorg[2])
{
extern qboolean depthcleared;
refdef_t refdef = r_refdef;
@ -725,6 +743,11 @@ static void R_RenderEyeScene (texid_t rendertarget, vec4_t fovoverride, matrix3x
int ph = vid.fbpheight;
int r = 0;
extern void CL_ClampPitch (int pnum, float frametime);
/*the vr code tends to be somewhat laggy with its head angles, leaving it to the last minute, so redo this to reduce latency*/
if ((size_t)(refdef.playerview-cl.playerview) < MAX_SPLITS)
CL_ClampPitch (refdef.playerview-cl.playerview, 0);
if (rendertarget)
{
r = GLBE_FBO_Update(&fbo_vr, FBO_RB_DEPTH, &rendertarget, 1, r_nulltex, rendertarget->width, rendertarget->height, 0);
@ -735,10 +758,10 @@ static void R_RenderEyeScene (texid_t rendertarget, vec4_t fovoverride, matrix3x
vid.fbpheight = rendertarget->height;
}
R_SetupGL (eyematrix, fovoverride, NULL, rendertarget);
R_SetupGL (eyeangorg, fovoverride, NULL, rendertarget);
R_RenderScene_Internal();
//if (eyematrix)
/*//if (eyematrix)
{
vec3_t newa, newo;
matrix3x4 headmatrix; //position of the head in local space
@ -774,7 +797,7 @@ static void R_RenderEyeScene (texid_t rendertarget, vec4_t fovoverride, matrix3x
if (R2D_Flush)
R2D_Flush();
GL_SetShaderState2D(false);
}
}*/
if (rendertarget)
{
@ -795,8 +818,7 @@ static void R_RenderScene (void)
int i;
int cull = r_refdef.flipcull;
unsigned int colourmask = r_refdef.colourmask;
vec3_t ang, org;
matrix3x4 eyematrix;
vec3_t eyeangorg[2];
extern qboolean depthcleared;
r_refdef.colourmask = 0u;
@ -914,12 +936,11 @@ static void R_RenderScene (void)
}
r_framecount++; //view position changes, if only slightly. which means we need to rebuild vis info. :(
ang[0] = 0;
ang[1] = r_stereo_convergence.value * (i?0.5:-0.5);
ang[2] = 0;
VectorSet(org, 0, stereooffset[i], 0);
Matrix3x4_RM_FromAngles(ang, org, eyematrix[0]);
R_SetupGL (eyematrix, NULL, NULL, NULL);
eyeangorg[0][0] = 0;
eyeangorg[0][1] = r_stereo_convergence.value * (i?0.5:-0.5);
eyeangorg[0][2] = 0;
VectorSet(eyeangorg[1], 0, stereooffset[i], 0);
R_SetupGL (eyeangorg, NULL, NULL, NULL);
R_RenderScene_Internal ();
}

View File

@ -58,7 +58,7 @@ cvar_t r_shadow_realtime_world = CVARFD ("r_shadow_realtime_world", "0", CVAR
cvar_t r_shadow_realtime_world_shadows = CVARF ("r_shadow_realtime_world_shadows", "1", CVAR_ARCHIVE);
cvar_t r_shadow_realtime_world_lightmaps = CVARFD ("r_shadow_realtime_world_lightmaps", "0", 0, "Specifies how much of the map's normal lightmap to retain when using world realtime lights. 0 completely replaces lighting.");
cvar_t r_shadow_realtime_world_importlightentitiesfrommap = CVARFD ("r_shadow_realtime_world_importlightentitiesfrommap", "0", CVAR_ARCHIVE, "Controls default loading of map-based realtime lights.\n0: Load explicit .rtlight files only.\n1: Load explicit lights then try fallback to parsing the entities lump.\n2: Load only the entities lump.");
cvar_t r_shadow_realtime_dlight = CVARFD ("r_shadow_realtime_dlight", "1", CVAR_ARCHIVE, "Enables the use of dynamic realtime lights, allowing explosions to use bumpmaps etc properly.");
cvar_t r_shadow_realtime_dlight = CVARAFD ("r_shadow_realtime_dlight", "1", "r_shadow_realtime_dynamic", CVAR_ARCHIVE, "Enables the use of dynamic realtime lights, allowing explosions to use bumpmaps etc properly.");
cvar_t r_shadow_realtime_dlight_shadows = CVARFD ("r_shadow_realtime_dlight_shadows", "1", CVAR_ARCHIVE, "Allows dynamic realtime lights to cast shadows as they move.");
cvar_t r_shadow_realtime_dlight_ambient = CVAR ("r_shadow_realtime_dlight_ambient", "0");
cvar_t r_shadow_realtime_dlight_diffuse = CVAR ("r_shadow_realtime_dlight_diffuse", "1");

View File

@ -1594,7 +1594,10 @@ void DL_DeThread(void)
{
dl->threadenable = false;
if (dl->threadctx)
{
Sys_WaitOnThread(dl->threadctx);
dl->threadctx = NULL;
}
}
#endif
}

View File

@ -6147,7 +6147,7 @@ static void QCC_VerifyArgs_setviewprop (const char *funcname, QCC_ref_t **arglis
{"VF_CL_VIEWANGLES_X", 35, ev_float},
{"VF_CL_VIEWANGLES_X", 36, ev_float},
{"VF_PERSPECTIVE", 200, ev_float},
//201
// {"VF_DP_CLEARSCENE", 201, ev_float},
{"VF_ACTIVESEAT", 202, ev_float, ev_float},
{"VF_AFOV", 203, ev_float},
// {"VF_SCREENVSIZE", 204, ev_vector},
@ -6171,6 +6171,10 @@ static void QCC_VerifyArgs_setviewprop (const char *funcname, QCC_ref_t **arglis
{"VF_SKYROOM_CAMERA", 222, ev_vector},
// {"VF_PIXELPSCALE", 223, ev_vector},
{"VF_PROJECTIONOFFSET", 224, ev_vector},
{"VF_VRBASEORIENTATION",225, ev_vector, ev_vector},
{"VF_DP_MAINVIEW", 400, ev_float},
// {"VF_DP_MINFPS_QUALITY", 401, ev_float},
};
char temp[256];

View File

@ -5069,8 +5069,6 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
if (QCC_PR_CheckToken("="))
{
paramlist[numparms].defltvalue = QCC_PR_ParseDefaultInitialiser(paramlist[numparms].type);
if (!paramlist[numparms].defltvalue.sym->constant)
QCC_PR_ParseError(0, "Default initialiser is not constant\n");
QCC_FreeTemp(paramlist[numparms].defltvalue);
}
numparms++;

View File

@ -10291,9 +10291,109 @@ void SV_SetEntityButtons(edict_t *ent, unsigned int buttonbits)
}
}
static void SV_SetSSQCInputs(usercmd_t *ucmd)
{
pr_global_struct->input_timelength = ucmd->msec/1000.0f * sv.gamespeed;
pr_global_struct->input_impulse = ucmd->impulse;
//precision inaccuracies. :(
#define ANGLE2SHORT(x) (x) * (65536/360.0)
if (sv_player->v->fixangle)
{
(pr_global_struct->input_angles)[0] = sv_player->v->v_angle[0];
(pr_global_struct->input_angles)[1] = sv_player->v->v_angle[1];
(pr_global_struct->input_angles)[2] = sv_player->v->v_angle[2];
}
else
{
(pr_global_struct->input_angles)[0] = SHORT2ANGLE(ucmd->angles[0]);
(pr_global_struct->input_angles)[1] = SHORT2ANGLE(ucmd->angles[1]);
(pr_global_struct->input_angles)[2] = SHORT2ANGLE(ucmd->angles[2]);
}
(pr_global_struct->input_movevalues)[0] = ucmd->forwardmove;
(pr_global_struct->input_movevalues)[1] = ucmd->sidemove;
(pr_global_struct->input_movevalues)[2] = ucmd->upmove;
pr_global_struct->input_buttons = ucmd->buttons;
if (pr_global_ptrs->input_weapon)
pr_global_struct->input_weapon = ucmd->weapon;
if (pr_global_ptrs->input_lightlevel)
pr_global_struct->input_lightlevel = ucmd->lightlevel;
if (pr_global_ptrs->input_cursor_screen)
VectorSet(pr_global_struct->input_cursor_screen, ucmd->cursor_screen[0], ucmd->cursor_screen[1], 0);
if (pr_global_ptrs->input_cursor_trace_start)
VectorCopy(ucmd->cursor_start, pr_global_struct->input_cursor_trace_start);
if (pr_global_ptrs->input_cursor_trace_endpos)
VectorCopy(ucmd->cursor_impact, pr_global_struct->input_cursor_trace_endpos);
if (pr_global_ptrs->input_cursor_entitynumber)
pr_global_struct->input_cursor_entitynumber = ucmd->cursor_entitynumber;
if (pr_global_ptrs->input_head_status)
pr_global_struct->input_head_status = ucmd->vr[VRDEV_HEAD].status;
if (pr_global_ptrs->input_head_origin)
VectorCopy(ucmd->vr[VRDEV_HEAD].origin, pr_global_struct->input_head_origin);
if (pr_global_ptrs->input_head_velocity)
VectorCopy(ucmd->vr[VRDEV_HEAD].velocity, pr_global_struct->input_head_velocity);
if (pr_global_ptrs->input_head_angles)
{
(pr_global_struct->input_head_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[0]);
(pr_global_struct->input_head_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[1]);
(pr_global_struct->input_head_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[2]);
}
if (pr_global_ptrs->input_head_avelocity)
{
(pr_global_struct->input_head_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[0]);
(pr_global_struct->input_head_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[1]);
(pr_global_struct->input_head_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[2]);
}
if (pr_global_ptrs->input_left_status)
pr_global_struct->input_left_status = ucmd->vr[VRDEV_LEFT].status;
if (pr_global_ptrs->input_left_origin)
VectorCopy(ucmd->vr[VRDEV_LEFT].origin, pr_global_struct->input_left_origin);
if (pr_global_ptrs->input_left_velocity)
VectorCopy(ucmd->vr[VRDEV_LEFT].velocity, pr_global_struct->input_left_velocity);
if (pr_global_ptrs->input_left_angles)
{
(pr_global_struct->input_left_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[0]);
(pr_global_struct->input_left_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[1]);
(pr_global_struct->input_left_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[2]);
}
if (pr_global_ptrs->input_left_avelocity)
{
(pr_global_struct->input_left_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[0]);
(pr_global_struct->input_left_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[1]);
(pr_global_struct->input_left_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[2]);
}
if (pr_global_ptrs->input_right_status)
pr_global_struct->input_right_status = ucmd->vr[VRDEV_RIGHT].status;
if (pr_global_ptrs->input_right_origin)
VectorCopy(ucmd->vr[VRDEV_RIGHT].origin, pr_global_struct->input_right_origin);
if (pr_global_ptrs->input_right_velocity)
VectorCopy(ucmd->vr[VRDEV_RIGHT].velocity, pr_global_struct->input_right_velocity);
if (pr_global_ptrs->input_right_angles)
{
(pr_global_struct->input_right_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[0]);
(pr_global_struct->input_right_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[1]);
(pr_global_struct->input_right_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[2]);
}
if (pr_global_ptrs->input_right_avelocity)
{
(pr_global_struct->input_right_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[0]);
(pr_global_struct->input_right_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[1]);
(pr_global_struct->input_right_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[2]);
}
}
//EXT_CSQC_1 (called when a movement command is received. runs full acceleration + movement)
qboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd)
{
if (ucmd->vr[VRDEV_HEAD].status & VRSTATUS_ANG)
sv_player->xv->idealpitch = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[0]);
else
sv_player->xv->idealpitch = 0;
SV_SetSSQCInputs(ucmd); //make sure its available for PlayerPreThink.
if (gfuncs.RunClientCommand)
{
vec3_t startangle;
@ -10352,107 +10452,6 @@ qboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd)
V_CalcRoll (sv_player->v->angles, sv_player->v->velocity)*4;
}
pr_global_struct->input_timelength = ucmd->msec/1000.0f * sv.gamespeed;
pr_global_struct->input_impulse = ucmd->impulse;
//precision inaccuracies. :(
#define ANGLE2SHORT(x) (x) * (65536/360.0)
if (sv_player->v->fixangle)
{
(pr_global_struct->input_angles)[0] = sv_player->v->v_angle[0];
(pr_global_struct->input_angles)[1] = sv_player->v->v_angle[1];
(pr_global_struct->input_angles)[2] = sv_player->v->v_angle[2];
}
else
{
(pr_global_struct->input_angles)[0] = SHORT2ANGLE(ucmd->angles[0]);
(pr_global_struct->input_angles)[1] = SHORT2ANGLE(ucmd->angles[1]);
(pr_global_struct->input_angles)[2] = SHORT2ANGLE(ucmd->angles[2]);
}
(pr_global_struct->input_movevalues)[0] = ucmd->forwardmove;
(pr_global_struct->input_movevalues)[1] = ucmd->sidemove;
(pr_global_struct->input_movevalues)[2] = ucmd->upmove;
pr_global_struct->input_buttons = ucmd->buttons;
if (pr_global_ptrs->input_weapon)
pr_global_struct->input_weapon = ucmd->weapon;
if (pr_global_ptrs->input_lightlevel)
pr_global_struct->input_lightlevel = ucmd->lightlevel;
if (pr_global_ptrs->input_cursor_screen)
VectorSet(pr_global_struct->input_cursor_screen, ucmd->cursor_screen[0], ucmd->cursor_screen[1], 0);
if (pr_global_ptrs->input_cursor_trace_start)
VectorCopy(ucmd->cursor_start, pr_global_struct->input_cursor_trace_start);
if (pr_global_ptrs->input_cursor_trace_endpos)
VectorCopy(ucmd->cursor_impact, pr_global_struct->input_cursor_trace_endpos);
if (pr_global_ptrs->input_cursor_entitynumber)
pr_global_struct->input_cursor_entitynumber = ucmd->cursor_entitynumber;
if (pr_global_ptrs->input_head_status)
pr_global_struct->input_head_status = ucmd->vr[VRDEV_HEAD].status;
if (pr_global_ptrs->input_head_origin)
VectorCopy(ucmd->vr[VRDEV_HEAD].origin, pr_global_struct->input_head_origin);
if (pr_global_ptrs->input_head_velocity)
VectorCopy(ucmd->vr[VRDEV_HEAD].velocity, pr_global_struct->input_head_velocity);
if (pr_global_ptrs->input_head_angles)
{
(pr_global_struct->input_head_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[0]);
(pr_global_struct->input_head_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[1]);
(pr_global_struct->input_head_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[2]);
}
if (pr_global_ptrs->input_head_avelocity)
{
(pr_global_struct->input_head_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[0]);
(pr_global_struct->input_head_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[1]);
(pr_global_struct->input_head_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[2]);
}
if (pr_global_ptrs->input_left_status)
pr_global_struct->input_left_status = ucmd->vr[VRDEV_LEFT].status;
if (pr_global_ptrs->input_left_origin)
VectorCopy(ucmd->vr[VRDEV_LEFT].origin, pr_global_struct->input_left_origin);
if (pr_global_ptrs->input_left_velocity)
VectorCopy(ucmd->vr[VRDEV_LEFT].velocity, pr_global_struct->input_left_velocity);
if (pr_global_ptrs->input_left_angles)
{
(pr_global_struct->input_left_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[0]);
(pr_global_struct->input_left_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[1]);
(pr_global_struct->input_left_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[2]);
}
if (pr_global_ptrs->input_left_avelocity)
{
(pr_global_struct->input_left_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[0]);
(pr_global_struct->input_left_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[1]);
(pr_global_struct->input_left_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[2]);
}
if (pr_global_ptrs->input_right_status)
pr_global_struct->input_right_status = ucmd->vr[VRDEV_RIGHT].status;
if (pr_global_ptrs->input_right_origin)
VectorCopy(ucmd->vr[VRDEV_RIGHT].origin, pr_global_struct->input_right_origin);
if (pr_global_ptrs->input_right_velocity)
VectorCopy(ucmd->vr[VRDEV_RIGHT].velocity, pr_global_struct->input_right_velocity);
if (pr_global_ptrs->input_right_angles)
{
(pr_global_struct->input_right_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[0]);
(pr_global_struct->input_right_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[1]);
(pr_global_struct->input_right_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[2]);
}
if (pr_global_ptrs->input_right_avelocity)
{
(pr_global_struct->input_right_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[0]);
(pr_global_struct->input_right_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[1]);
(pr_global_struct->input_right_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[2]);
}
//prethink should be consistant with what the engine normally does
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict);
PR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPreThink);

View File

@ -2271,6 +2271,7 @@ void SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf
}
statsf[STAT_VIEWHEIGHT] = ent->v->view_ofs[2];
statsf[STAT_IDEALPITCH] = ent->xv->idealpitch;
statsf[STAT_PUNCHANGLE_X] = ent->xv->punchangle[0];
statsf[STAT_PUNCHANGLE_Y] = ent->xv->punchangle[1];

View File

@ -8113,9 +8113,9 @@ void SV_ExecuteClientMessage (client_t *cl)
else while (split->lastruncmd < newcmd.servertime)
{
//try to find the oldest (valid) command.
if (split->lastcmd.servertime < oldest.servertime)
if (split->lastruncmd < oldest.servertime)
c = &oldest;
else if (split->lastcmd.servertime < oldcmd.servertime)
else if (split->lastruncmd < oldcmd.servertime)
c = &oldcmd;
else
c = &newcmd;

View File

@ -2656,7 +2656,7 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb)
return true;
}
void VK_R_RenderEye(texid_t image, vec4_t fovoverride, matrix3x4 axisorg)
void VK_R_RenderEye(texid_t image, vec4_t fovoverride, vec3_t eyeangorg[2])
{
struct vk_rendertarg *rt;

View File

@ -126,36 +126,6 @@ static cvar_t *xr_skipregularview;
#define METRES_TO_QUAKE(x) ((x)*xr_metresize->value)
#define QUAKE_TO_METRES(x) ((x)/xr_metresize->value)
static void Matrix3x4_FromAngles (const vec3_t angles, const vec3_t org, float *fte_restrict transform)
{
float angle;
float sr, sp, sy, cr, cp, cy;
angle = angles[YAW] * (M_PI*2 / 360);
sy = sin(angle);
cy = cos(angle);
angle = angles[PITCH] * (M_PI*2 / 360);
sp = sin(angle);
cp = cos(angle);
angle = angles[ROLL] * (M_PI*2 / 360);
sr = sin(angle);
cr = cos(angle);
transform[0+0] = cp*cy;
transform[0+1] = cp*sy;
transform[0+2] = -sp;
transform[0+3] = org[0];
transform[4+0] = (-1*sr*sp*cy+-1*cr*-sy);
transform[4+1] = (-1*sr*sp*sy+-1*cr*cy);
transform[4+2] = -1*sr*cp;
transform[4+3] = org[1];
transform[8+0] = (cr*sp*cy+-sr*-sy);
transform[8+1] = (cr*sp*sy+-sr*cy);
transform[8+2] = cr*cp;
transform[8+3] = org[2];
}
static void XR_PoseToAngOrg(const XrPosef *pose, vec3_t ang, vec3_t org)
{
XrQuaternionf q = pose->orientation;
@ -178,62 +148,6 @@ static void XR_PoseToAngOrg(const XrPosef *pose, vec3_t ang, vec3_t org)
org[2] = METRES_TO_QUAKE(pose->position.z);
#endif
}
static void XR_PoseToTransform(const XrPosef *pose, float *fte_restrict transform)
{
vec3_t ang, org;
XR_PoseToAngOrg(pose, ang, org);
Matrix3x4_FromAngles(ang, org, transform);
}
static void Matrix3x4_Invert_XR (const float *in1, float *fte_restrict out)
{
// we only support uniform scaling, so assume the first row is enough
// (note the lack of sqrt here, because we're trying to undo the scaling,
// this means multiplying by the inverse scale twice - squaring it, which
// makes the sqrt a waste of time)
#if 1
double scale = 1.0 / (in1[0] * in1[0] + in1[1] * in1[1] + in1[2] * in1[2]);
#else
double scale = 3.0 / sqrt
(in1->m[0][0] * in1->m[0][0] + in1->m[0][1] * in1->m[0][1] + in1->m[0][2] * in1->m[0][2]
+ in1->m[1][0] * in1->m[1][0] + in1->m[1][1] * in1->m[1][1] + in1->m[1][2] * in1->m[1][2]
+ in1->m[2][0] * in1->m[2][0] + in1->m[2][1] * in1->m[2][1] + in1->m[2][2] * in1->m[2][2]);
scale *= scale;
#endif
// invert the rotation by transposing and multiplying by the squared
// recipricol of the input matrix scale as described above
out[0] = in1[0] * scale;
out[1] = in1[4] * scale;
out[2] = in1[8] * scale;
out[4] = in1[1] * scale;
out[5] = in1[5] * scale;
out[6] = in1[9] * scale;
out[8] = in1[2] * scale;
out[9] = in1[6] * scale;
out[10] = in1[10] * scale;
// invert the translate
out[3] = -(in1[3] * out[0] + in1[7] * out[1] + in1[11] * out[2]);
out[7] = -(in1[3] * out[4] + in1[7] * out[5] + in1[11] * out[6]);
out[11] = -(in1[3] * out[8] + in1[7] * out[9] + in1[11] * out[10]);
}
static void Matrix3x4_Multiply_XR(const float *a, const float *b, float *fte_restrict out)
{
out[0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2];
out[1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2];
out[2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2];
out[3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + b[3];
out[4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6];
out[5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6];
out[6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6];
out[7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + b[7];
out[8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10];
out[9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10];
out[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10];
out[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + b[11];
}
#define VectorAngles VectorAnglesPluginsSuck
static void VectorAngles(const float *forward, const float *up, float *result, qboolean meshpitch) //up may be NULL
@ -1037,32 +951,14 @@ static int QDECL XR_BindProfileFile(const char *fname, qofs_t fsize, time_t mtim
return false;
}
static void XR_SetupInputs(void)
static const struct
{
unsigned int h;
XrResult res;
//begin instance-level init
{
XrActionSetCreateInfo info = {XR_TYPE_ACTION_SET_CREATE_INFO};
Q_strlcpy(info.actionSetName, "actions", sizeof(info.actionSetName));
Q_strlcpy(info.localizedActionSetName, FULLENGINENAME" Actions", sizeof(info.localizedActionSetName));
info.priority = 0;
xr.actionset.subactionPath = XR_NULL_PATH;
res = xrCreateActionSet(xr.instance, &info, &xr.actionset.actionSet);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to create actionset - %s\n", XR_StringForResult(res));
}
h = 0;
if (fsfuncs)
fsfuncs->EnumerateFiles("oxr_*.binds", XR_BindProfileFile, &h);
if (!h) //no user bindings defined, use fallbacks. probably this needs to be per-mod.
{
//FIXME: set up some proper bindings!
XR_BindProfileStr("khr_simple",
"/interaction_profiles/khr/simple_controller /user/hand/left/ /user/hand/right/\n"
const char *name;
const char *script;
} xr_knownprofiles[] =
{
//FIXME: set up some proper bindings!
{"khr_simple", "/interaction_profiles/khr/simple_controller /user/hand/left/ /user/hand/right/\n"
"+attack_left \"Left Attack\" button input/select/click /user/hand/left\n"
"+attack_right \"Right Attack\" button input/select/click /user/hand/right\n"
"+menu_left \"Left Menu\" button input/menu/click /user/hand/left\n"
@ -1072,10 +968,9 @@ static void XR_SetupInputs(void)
// "grip_pose \"Grip Pose\" pose input/grip/pose\n"
// "aim_pose \"Aim Pose\" pose input/aim/pose\n"
"vibrate \"A Vibrator\" vibration output/haptic\n"
);
},
/* XR_BindProfileStr("valve_index",
"/interaction_profiles/valve/index_controller /user/hand/left/ /user/hand/right/\n"
/* {"valve_index", "/interaction_profiles/valve/index_controller /user/hand/left/ /user/hand/right/\n"
//"unbound \"Unused Button\" button input/system/click\n"
//"unbound \"Unused Button\" button input/system/touch\n"
//"unbound \"Unused Button\" button input/a/click\n"
@ -1096,10 +991,9 @@ static void XR_SetupInputs(void)
//"unbound \"Unused Button\" pose input/grip/pose\n"
//"unbound \"Unused Button\" pose input/aim/pose\n"
//"unbound \"Unused Button\" vibration output/haptic\n"
);
},
*/
/* XR_BindProfileStr("htc_vive",
"/interaction_profiles/htc/vive_controller /user/hand/left/ /user/hand/right/\n"
/* {"htc_vive", "/interaction_profiles/htc/vive_controller /user/hand/left/ /user/hand/right/\n"
//"unbound \"Unused Button\" button input/system/click\n"
//"unbound \"Unused Button\" button input/squeeze/click\n"
//"unbound \"Unused Button\" button input/menu/click\n"
@ -1113,8 +1007,7 @@ static void XR_SetupInputs(void)
//"unbound \"Unused Button\" vibration output/haptic\n"
);
*/
/* XR_BindProfileStr("htc_vive_pro",
"/interaction_profiles/htc/vive_pro /user/head/\n"
/* {"htc_vive_pro", "/interaction_profiles/htc/vive_pro /user/head/\n"
//"unbound \"Unused Button\" button input/system/click\n"
//"unbound \"Unused Button\" button input/volume_up/click\n"
//"unbound \"Unused Button\" button input/volume_down/click\n"
@ -1123,7 +1016,7 @@ static void XR_SetupInputs(void)
*/
//FIXME: map to quake's keys.
XR_BindProfileStr("gamepad", "/interaction_profiles/microsoft/xbox_controller /user/gamepad/\n"
{"gamepad", "/interaction_profiles/microsoft/xbox_controller /user/gamepad/\n"
"togglemenu Menu button input/menu/click\n"
//"unbound \"Unused Button\" button input/view/click\n"
//"unbound \"Unused Button\" button input/a/click\n"
@ -1148,7 +1041,34 @@ static void XR_SetupInputs(void)
//"unbound \"Unused Vibrator\" vibration output/haptic_left_trigger\n"
//"unbound \"Unused Vibrator\" vibration output/haptic_right\n"
//"unbound \"Unused Vibrator\" vibration output/haptic_right_trigger\n"
);
},
};
static void XR_SetupInputs(void)
{
unsigned int h;
XrResult res;
//begin instance-level init
{
XrActionSetCreateInfo info = {XR_TYPE_ACTION_SET_CREATE_INFO};
Q_strlcpy(info.actionSetName, "actions", sizeof(info.actionSetName));
Q_strlcpy(info.localizedActionSetName, FULLENGINENAME" Actions", sizeof(info.localizedActionSetName));
info.priority = 0;
xr.actionset.subactionPath = XR_NULL_PATH;
res = xrCreateActionSet(xr.instance, &info, &xr.actionset.actionSet);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to create actionset - %s\n", XR_StringForResult(res));
}
h = 0;
if (fsfuncs)
fsfuncs->EnumerateFiles("oxr_*.binds", XR_BindProfileFile, &h);
if (!h) //no user bindings defined, use fallbacks. probably this needs to be per-mod.
{
for (h = 0; h < countof(xr_knownprofiles); h++)
XR_BindProfileStr(xr_knownprofiles[h].name, xr_knownprofiles[h].script);
}
//begin session specific. stuff
@ -1295,8 +1215,7 @@ static void XR_UpdateInputs(XrTime time)
(loc.locationFlags&XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)?angles:NULL,
(vel.velocityFlags&XR_SPACE_VELOCITY_LINEAR_VALID_BIT)?lvel:NULL,
(vel.velocityFlags&XR_SPACE_VELOCITY_ANGULAR_VALID_BIT)?avel:NULL))
if (transform[3][0] || transform[3][1] || transform[3][2])
{
{ //custom poses that mods might want to handle themselves...
vec3_t angles;
char cmd[256];
VectorAngles(transform[0], transform[2], angles, false);
@ -1695,7 +1614,7 @@ static qboolean XR_SyncFrame(double *frametime)
return true;
}
static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, matrix3x4 axisorg))
static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3_t angorg[2]))
{
XrFrameEndInfo endframeinfo = {XR_TYPE_FRAME_END_INFO};
unsigned int u;
@ -1762,7 +1681,6 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, matr
XrViewState viewstate = {XR_TYPE_VIEW_STATE};
XrViewLocateInfo locateinfo = {XR_TYPE_VIEW_LOCATE_INFO};
XrView eyeview[MAX_VIEW_COUNT]={};
matrix3x4 transform, eyetransform, inv;
for (u = 0; u < MAX_VIEW_COUNT; u++)
eyeview[u].type = XR_TYPE_VIEW;
@ -1802,8 +1720,6 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, matr
apose.position.y /= xr.viewcount;
apose.position.z /= xr.viewcount;
XR_PoseToAngOrg(&apose, ang, org);
Matrix3x4_FromAngles(ang, org, transform[0]);
Matrix3x4_Invert_XR(transform[0], inv[0]);
inputfuncs->SetHandPosition("head", org, ang, NULL, NULL);
}
@ -1812,6 +1728,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, matr
vec4_t fovoverride;
XrSwapchainImageWaitInfo waitinfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
unsigned int imgidx = 0;
vec3_t orientation[2];
res = xrAcquireSwapchainImage(xr.eye[u].swapchain, NULL, &imgidx);
if (XR_FAILED(res))
Con_Printf("xrAcquireSwapchainImage: %s\n", XR_StringForResult(res));
@ -1822,8 +1739,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, matr
projviews[u].fov = eyeview[u].fov;
projviews[u].subImage = xr.eye[u].subimage;
XR_PoseToTransform(&eyeview[u].pose, transform[0]);
Matrix3x4_Multiply_XR(transform[0], inv[0], eyetransform[0]);
XR_PoseToAngOrg(&eyeview[u].pose, orientation[0], orientation[1]);
fovoverride[0] = eyeview[u].fov.angleLeft * (180/M_PI);
fovoverride[1] = eyeview[u].fov.angleRight * (180/M_PI);
@ -1834,7 +1750,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, matr
res = xrWaitSwapchainImage(xr.eye[u].swapchain, &waitinfo);
if (XR_FAILED(res))
Con_Printf("xrWaitSwapchainImage: %s\n", XR_StringForResult(res));
rendereye(&xr.eye[u].swapimages[imgidx], fovoverride, eyetransform);
rendereye(&xr.eye[u].swapimages[imgidx], fovoverride, orientation);
//GL note: the OpenXR specification says NOTHING about the application having to glFlush or glFinish.
// I take this to mean that the openxr runtime is responsible for setting up barriers or w/e inside ReleaseSwapchainImage.
//VK note: the OpenXR spec does say that it needs to be color_attachment_optimal+owned by queue. which it is.