8dadfb4878
Cmake: Add FTE_WERROR option, defaults to true in debug builds and off in release builds (in case future compilers have issues). Cmake: Pull in libXscreensaver so we don't get interrupted by screensavers when playing demos. Make: Added `make webcl-rel` for a web build without server bloat (eg for sites focused on demo playback. Yes, this means you XantoM). fteqcc: Include the decompiler in fteqcc (non-gui) builds ('-d' arg). fteqcc: Decompiler can now mostly handle hexen2 mods without any unknown opcodes. Allow ezHud and OpenSSL to be compiled as in-engine plugins, potentially for web and windows ports respectively. Web: Fix support for ogg vorbis. Add support for voip. Web: Added basic support for WebXR. QTV: Don't try seeking on unseekable qtv streams. Don't spam when developer 1 is set. QTV: add support for some eztv extensions. MVD: added hack to use ktx's vweps in mvd where mvdsv doesn't bother to record the info. qwfwd: hack around a hack in qwfwd, allowing it to work again. recording: favour qwd in single player, instead of mvd. Protocol: reduce client memory used for precache names. Bump maximum precache counts - some people are just abusive, yes you Orl. hexen2: add enough clientside protocol compat to play the demo included with h2mp. lacks effects. in_xflip: restored this setting. fs_hidesyspaths: new cvar, defaults to enabled so you won't find your username or whatever turning up in screenshots or the like. change it to 0 before debuging stuff eg via 'path'. gl_overbright_models: Added cvar to match QS. netchan: Added MTU determination, we'll no longer fail to connect when routers stupidly drop icmp packets. Win: try a few other versions of xinput too. CSQC: Added a CSQC_GenerateMaterial function, to give the csqc a chance to generate custom materials. MenuQC: Added support for the skeletal objects API.
664 lines
20 KiB
C
664 lines
20 KiB
C
#include "quakedef.h"
|
|
#include "glquake.h"
|
|
#include "vr.h"
|
|
#include "shader.h"
|
|
vfsfile_t *FSWEB_OpenTempHandle(int f);
|
|
|
|
extern cvar_t gl_lateswap;
|
|
extern qboolean gammaworks;
|
|
|
|
extern qboolean vid_isfullscreen;
|
|
|
|
qboolean mouseactive;
|
|
extern qboolean mouseusedforgui;
|
|
|
|
static int gamepaddeviceids[] = {DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET};
|
|
static int keyboardid[] = {0};
|
|
static int mouseid[] = {0};
|
|
|
|
static cvar_t *xr_enable; //refrains from starting up when 0 and closes it too, and forces it off too
|
|
static cvar_t *xr_metresize;
|
|
static cvar_t *xr_skipregularview;
|
|
|
|
static void WebXR_Toggle_f(void)
|
|
{
|
|
emscriptenfte_xr_setup(-3); //toggle it.
|
|
}
|
|
static void WebXR_Start_f(void)
|
|
{
|
|
emscriptenfte_xr_setup(-2); //start it (any mode)
|
|
}
|
|
static void WebXR_End_f(void)
|
|
{
|
|
emscriptenfte_xr_setup(-1); //end it (if running)
|
|
}
|
|
static void WebXR_Start_Inline_f(void)
|
|
{
|
|
emscriptenfte_xr_setup(-1); //end it (if running)
|
|
emscriptenfte_xr_setup(0); //start it (inline mode)
|
|
}
|
|
static void WebXR_Start_VR_f(void)
|
|
{
|
|
emscriptenfte_xr_setup(-1); //end it (if running)
|
|
emscriptenfte_xr_setup(1); //start it (inline mode)
|
|
}
|
|
static void WebXR_Start_AR_f(void)
|
|
{
|
|
emscriptenfte_xr_setup(-1); //end it (if running)
|
|
emscriptenfte_xr_setup(2); //start it (inline mode)
|
|
}
|
|
static void WebXR_Info_f(void)
|
|
{
|
|
int modes = emscriptenfte_xr_issupported();
|
|
if (modes == 0)
|
|
{
|
|
Con_Printf(S_COLOR_RED"WebXR is unavailable\n");
|
|
return;
|
|
}
|
|
if (modes < 0)
|
|
Con_Printf("WebXR availability is unknown\n");
|
|
else
|
|
{
|
|
Con_Printf("WebXR-inline is %savailable\n", (modes & (1<<0))?S_COLOR_GREEN:S_COLOR_RED"un");
|
|
Con_Printf("WebXR-immersive-vr is %savailable\n", (modes & (1<<1))?S_COLOR_GREEN:S_COLOR_RED"un");
|
|
Con_Printf("WebXR-immersive-ar is %savailable\n", (modes & (1<<2))?S_COLOR_GREEN:S_COLOR_RED"un");
|
|
|
|
if (emscriptenfte_xr_isactive())
|
|
Con_Printf("WebXR is active\n");
|
|
else
|
|
Con_Printf("WebXR is inactive\n");
|
|
}
|
|
}
|
|
|
|
static qboolean WebXR_Prepare (vrsetup_t *setupinfo)
|
|
{ //called before graphics context init. basically just checks if we can do vr.
|
|
xr_enable = Cvar_Get2("xr_enable", "1", CVAR_SEMICHEAT, "Controls whether to use webxr rendering or not.", "WebXR configuration");
|
|
xr_metresize = Cvar_Get2("xr_metresize", "26.24671916", CVAR_ARCHIVE, "Size of a metre in game units", "WebXR configuration");
|
|
xr_skipregularview = Cvar_Get2("xr_skipregularview", "1", CVAR_ARCHIVE, "Skip rendering the regular view when OpenXR is active", "WebXR configuration");
|
|
|
|
return xr_enable->ival;//emscriptenfte_xr_issupported() != 0;
|
|
}
|
|
static qboolean WebXR_Init (vrsetup_t *setupinfo, rendererstate_t *info)
|
|
{ //called after graphics context init.
|
|
Cmd_AddCommand("xr_toggle", WebXR_Toggle_f);
|
|
Cmd_AddCommand("xr_start", WebXR_Start_f);
|
|
Cmd_AddCommand("xr_end", WebXR_End_f);
|
|
Cmd_AddCommand("xr_start_inline", WebXR_Start_Inline_f);
|
|
Cmd_AddCommand("xr_start_vr", WebXR_Start_VR_f);
|
|
Cmd_AddCommand("xr_start_ar", WebXR_Start_AR_f);
|
|
Cmd_AddCommand("xr_info", WebXR_Info_f);
|
|
return true; //we don't have much control here, so we have no failure paths here. probably the session isn't even created.
|
|
}
|
|
static unsigned int WebXR_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.
|
|
return emscriptenfte_xr_isactive(); //we're using openxr's session's sync stuff when this is active.
|
|
}
|
|
static void WebXR_MatrixToQuake(const float in[16], float out[12])
|
|
{
|
|
float tempmat[16], tempmat2[16];
|
|
const float fixupmat[16] = { 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1};
|
|
const float reorient[16] = { 0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1};
|
|
|
|
Matrix4_Multiply(in, fixupmat, tempmat2);//we want z-up...
|
|
Matrix4_Multiply(reorient, tempmat2, tempmat);//rotate it.
|
|
|
|
//transpose it cos smaller? urgh.
|
|
out[ 0] = tempmat[0];
|
|
out[ 1] = tempmat[4];
|
|
out[ 2] = tempmat[8];
|
|
out[ 3] = tempmat[12];
|
|
out[ 4] = tempmat[1];
|
|
out[ 5] = tempmat[5];
|
|
out[ 6] = tempmat[9];
|
|
out[ 7] = tempmat[13];
|
|
out[ 8] = tempmat[2];
|
|
out[ 9] = tempmat[6];
|
|
out[10] = tempmat[10];
|
|
out[11] = tempmat[14];
|
|
|
|
//fix up offset scaling parts
|
|
out[3] *= xr_metresize->value;
|
|
out[7] *= xr_metresize->value;
|
|
out[11]*= xr_metresize->value;
|
|
}
|
|
static qboolean WebXR_Render (void(*rendereye)(texid_t tex, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12]))
|
|
{ //calls rendereye for each view we're meant to be drawing.
|
|
//webxr uses separate viewpoints on the same fbo
|
|
struct webxrinfo_s eye[16];
|
|
fbostate_t fbo;
|
|
int oldfbo = 0;
|
|
float eyematrix[12];
|
|
pxrect_t vp;
|
|
|
|
int e, eyes = emscriptenfte_xr_geteyeinfo(countof(eye), eye);
|
|
if (!eyes)
|
|
return false; //erk? lets just do normal drawing.
|
|
|
|
for (e = 0; e < eyes; e++)
|
|
{
|
|
fbo.fbo = eye[e].fbo;
|
|
if (!e)
|
|
oldfbo = GLBE_FBO_Push(&fbo);
|
|
else
|
|
GLBE_FBO_Push(&fbo);
|
|
|
|
if (!eye[e].viewport[2] || !eye[e].viewport[3])
|
|
continue; //no pixels getting drawn... don't waste time (emulators that feel an urge to give a dodgy eye to report two eyes instead of one)
|
|
|
|
vp.x = eye[e].viewport[0];
|
|
vp.width = eye[e].viewport[2];
|
|
vp.height = eye[e].viewport[3];
|
|
vp.maxheight = vp.y+vp.height; //negatives suck.
|
|
vp.y = vp.maxheight-eye[e].viewport[1]-vp.height; //opengl sucks
|
|
|
|
WebXR_MatrixToQuake(eye[e].transform, eyematrix);
|
|
|
|
//really just a pointer to R_RenderEyeScene...
|
|
rendereye(NULL, &vp, NULL, eye[e].projmatrix, eyematrix);
|
|
}
|
|
GLBE_FBO_Pop(oldfbo);
|
|
|
|
if (!xr_enable->ival)
|
|
emscriptenfte_xr_shutdown();
|
|
|
|
if (eye[e].fbo==0)
|
|
return true; //always skip the non-vr screen when we're fighting over the same FB.
|
|
return xr_skipregularview->ival; //skip non-vr rendering.
|
|
}
|
|
static void WebXR_Shutdown (void)
|
|
{
|
|
Cmd_RemoveCommand("xr_toggle");
|
|
Cmd_RemoveCommand("xr_start");
|
|
Cmd_RemoveCommand("xr_end");
|
|
Cmd_RemoveCommand("xr_start_inline");
|
|
Cmd_RemoveCommand("xr_start_vr");
|
|
Cmd_RemoveCommand("xr_start_ar");
|
|
Cmd_RemoveCommand("xr_info");
|
|
emscriptenfte_xr_shutdown();
|
|
}
|
|
static struct plugvrfuncs_s webxrfuncs = {
|
|
"WebXR",
|
|
WebXR_Prepare,
|
|
WebXR_Init,
|
|
WebXR_SyncFrame,
|
|
WebXR_Render,
|
|
WebXR_Shutdown,
|
|
};
|
|
|
|
static void *GLVID_getwebglfunction(char *functionname)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void IN_GamePadButtonEvent(int joydevid, int button, int ispressed, int isstandardmapping)
|
|
{
|
|
//note that the gamepad API handles 'buttons' as float values, so triggers are here instead of as 'axis' values (unlike other APIs). on the plus side, we're no longer responsible for figuring out the required threshold value to denote a 'press', but we're not tracking half-presses and that's our fault and we don't care.
|
|
static const int standardmapping[] =
|
|
{ //the order of these keys is different from that of xinput
|
|
//however, the quake button codes should be the same. I really ought to define some K_ aliases for them.
|
|
K_GP_A,
|
|
K_GP_B,
|
|
K_GP_X,
|
|
K_GP_Y,
|
|
K_GP_LEFT_SHOULDER,
|
|
K_GP_RIGHT_SHOULDER,
|
|
K_GP_LEFT_TRIGGER,
|
|
K_GP_RIGHT_TRIGGER,
|
|
K_GP_BACK,
|
|
K_GP_START,
|
|
K_GP_LEFT_STICK,
|
|
K_GP_RIGHT_STICK,
|
|
K_GP_DPAD_UP,
|
|
K_GP_DPAD_DOWN,
|
|
K_GP_DPAD_LEFT,
|
|
K_GP_DPAD_RIGHT,
|
|
K_GP_GUIDE,
|
|
//K_GP_UNKNOWN
|
|
};
|
|
|
|
if (joydevid < 0)
|
|
{
|
|
static const int standardxrmapping[] =
|
|
{
|
|
//Right Left
|
|
K_GP_RIGHT_TRIGGER, K_GP_LEFT_TRIGGER, //Primary trigger/button
|
|
K_GP_RIGHT_SHOULDER, K_GP_LEFT_SHOULDER, //Primary squeeze
|
|
K_GP_TOUCHPAD, K_GP_MISC1, //Primary touchpad
|
|
K_GP_RIGHT_STICK, K_GP_LEFT_STICK, //Primary thumbstick
|
|
|
|
//'Additional inputs may be exposed after', which should be in some pseudo-prioritised order with the awkward ones last.
|
|
K_GP_A, K_GP_DPAD_DOWN,
|
|
K_GP_B, K_GP_DPAD_RIGHT,
|
|
K_GP_X, K_GP_DPAD_LEFT,
|
|
K_GP_Y, K_GP_DPAD_UP,
|
|
};
|
|
button = button*2 + (joydevid != -1); //munge them into a single array cos I cba with all the extra conditionals
|
|
if (button < countof(standardxrmapping))
|
|
button = standardxrmapping[button];
|
|
else
|
|
return; //err...
|
|
|
|
joydevid = countof(gamepaddeviceids)-1;
|
|
}
|
|
else if (isstandardmapping && button < countof(standardmapping))
|
|
button = standardmapping[button];
|
|
else if (button < 32+4)
|
|
button = K_JOY1+button;
|
|
else
|
|
return; //err...
|
|
|
|
if (joydevid < countof(gamepaddeviceids))
|
|
{
|
|
if (joydevid == gamepaddeviceids[joydevid])
|
|
{
|
|
if (!ispressed)
|
|
return; //don't send axis events until its enabled.
|
|
gamepaddeviceids[joydevid] = joydevid;
|
|
}
|
|
joydevid = gamepaddeviceids[joydevid];
|
|
}
|
|
|
|
IN_KeyEvent(joydevid, ispressed, button, 0);
|
|
}
|
|
|
|
static void IN_GamePadAxisEvent(int joydevid, int axis, float value, int isstandardmapping)
|
|
{
|
|
static const int standardmapping[] = {GPAXIS_LT_RIGHT,GPAXIS_LT_DOWN,GPAXIS_RT_RIGHT,GPAXIS_RT_DOWN};
|
|
if (joydevid < 0)
|
|
{
|
|
static const int standardxrmapping[] =
|
|
{
|
|
//Right Left
|
|
-1, -1, //Primary touchpad X
|
|
-1, -1, //Primary touchpad Y
|
|
GPAXIS_RT_RIGHT, GPAXIS_LT_RIGHT, //Primary thumbstick X
|
|
GPAXIS_RT_DOWN, GPAXIS_LT_DOWN, //Primary thumbstick Y
|
|
|
|
//'Additional inputs may be exposed after', which should be in some pseudo-prioritised order with the awkward ones last.
|
|
};
|
|
axis = axis*2 + (joydevid != -1); //munge them into a single array cos I cba with all the extra conditionals
|
|
if (axis < countof(standardxrmapping))
|
|
axis = standardxrmapping[axis];
|
|
else
|
|
return; //err...
|
|
|
|
joydevid = countof(gamepaddeviceids)-1;
|
|
}
|
|
else if (isstandardmapping)
|
|
{
|
|
if (axis < countof(standardmapping))
|
|
axis = standardmapping[axis];
|
|
}
|
|
else
|
|
return; //random mappings? erk?
|
|
|
|
if (joydevid < countof(gamepaddeviceids))
|
|
{
|
|
joydevid = gamepaddeviceids[joydevid];
|
|
if (joydevid == DEVID_UNSET)
|
|
return; //don't send axis events until its enabled.
|
|
}
|
|
else
|
|
IN_JoystickAxisEvent(joydevid, axis, value);
|
|
}
|
|
|
|
static void IN_GamePadOrientationEvent(int joydevid, float px,float py,float pz, float qx,float qy,float qz,float qw)
|
|
{ //(some) vr controllers only
|
|
vec3_t org, ang;
|
|
const char *dev;
|
|
|
|
const float sqw = qw * qw;
|
|
const float sqx = qx * qx;
|
|
const float sqy = qy * qy;
|
|
const float sqz = qz * qz;
|
|
|
|
ang[PITCH] = -asin(-2 * (qy * qz - qw * qx)) * (180/M_PI);
|
|
ang[YAW] = atan2(2 * (qx * qz + qw * qy), sqw - sqx - sqy + sqz) * (180/M_PI);
|
|
ang[ROLL] = -atan2(2 * (qx * qy + qw * qz), sqw - sqx + sqy - sqz) * (180/M_PI);
|
|
|
|
org[0] = -pz * xr_metresize->value;
|
|
org[1] = -px * xr_metresize->value;
|
|
org[2] = py * xr_metresize->value;
|
|
|
|
if (joydevid == -1)
|
|
dev = "right";
|
|
else if (joydevid == -2)
|
|
dev = "left";
|
|
else if (joydevid == -3)
|
|
dev = "head";
|
|
else if (joydevid == -4)
|
|
dev = "gaze";
|
|
else
|
|
return;
|
|
|
|
IN_SetHandPosition(dev, org, ang, NULL, NULL);
|
|
}
|
|
|
|
static void VID_Resized(int width, int height)
|
|
{
|
|
extern cvar_t vid_conautoscale, vid_conwidth;
|
|
vid.pixelwidth = width;
|
|
vid.pixelheight = height;
|
|
//Con_Printf("Resized: %i %i\n", vid.pixelwidth, vid.pixelheight);
|
|
|
|
Cvar_ForceCallback(&vid_conautoscale);
|
|
Cvar_ForceCallback(&vid_conwidth);
|
|
}
|
|
static unsigned int domkeytoquake(unsigned int code)
|
|
{
|
|
static const unsigned short tab[256] =
|
|
{
|
|
/* 0*/ 0,0,0,0,0,0,0,0, K_BACKSPACE,K_TAB,0,0,0,K_ENTER,0,0,
|
|
/* 16*/ K_SHIFT,K_CTRL,K_ALT,K_PAUSE,K_CAPSLOCK,0,0,0,0,0,0,K_ESCAPE,0,0,0,0,
|
|
/* 32*/ ' ',K_PGUP,K_PGDN,K_END,K_HOME,K_LEFTARROW,K_UPARROW,K_RIGHTARROW, K_DOWNARROW,0,0,0,K_PRINTSCREEN,K_INS,K_DEL,0,
|
|
/* 48*/ '0','1','2','3','4','5','6','7', '8','9',0,';',0,'=',0,0,
|
|
|
|
/* 64*/ 0,'a','b','c','d','e','f','g', 'h','i','j','k','l','m','n','o',
|
|
/* 80*/ 'p','q','r','s','t','u','v','w', 'x','y','z',K_LWIN,K_RWIN,K_APP,0,0,
|
|
/* 96*/ K_KP_INS,K_KP_END,K_KP_DOWNARROW,K_KP_PGDN,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_HOME, K_KP_UPARROW,K_KP_PGDN,K_KP_STAR,K_KP_PLUS,0,K_KP_MINUS,K_KP_DEL,K_KP_SLASH,
|
|
/*112*/ K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8,K_F9,K_F10,K_F11,K_F12,0,0,0,0,
|
|
/*128*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
|
/*144*/ K_KP_NUMLOCK,K_SCRLCK,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
|
/*160*/ 0,0,0,'#',0,0,0,0, 0,0,0,0,0,'-',0,0,
|
|
/*176*/ 0,0,0,0,0,0,0,0, 0,0,';','=',',','-','.','/',
|
|
/*192*/ '`',0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
|
/*208*/ 0,0,0,0,0,0,0,0, 0,0,0,'[','\\',']','\'','`',
|
|
/*224*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
|
/*240*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
|
};
|
|
if (!code)
|
|
return 0;
|
|
if (code >= sizeof(tab)/sizeof(tab[0]))
|
|
{
|
|
Con_DPrintf("You just pressed key %u, but I don't know what its meant to be\n", code);
|
|
return 0;
|
|
}
|
|
if (!tab[code])
|
|
Con_DPrintf("You just pressed key %u, but I don't know what its meant to be\n", code);
|
|
|
|
// Con_DPrintf("You just pressed dom key %u, which is quake key %u\n", code, tab[code]);
|
|
return tab[code];
|
|
}
|
|
static int DOM_KeyEvent(unsigned int devid, int down, int scan, int uni)
|
|
{
|
|
extern int shift_down;
|
|
// Con_Printf("Key %s %i %i:%c\n", down?"down":"up", scan, uni, uni?(char)uni:' ');
|
|
if (shift_down)
|
|
{
|
|
scan = domkeytoquake(scan);
|
|
}
|
|
else
|
|
{
|
|
scan = domkeytoquake(scan);
|
|
}
|
|
IN_KeyEvent(keyboardid[devid], down, scan, uni);
|
|
//Chars which don't map to some printable ascii value get preventDefaulted.
|
|
//This is to stop fucking annoying fucking things like backspace randomly destroying the page and thus game.
|
|
//And it has to be conditional, or we don't get any unicode chars at all.
|
|
//The behaviour browsers seem to give is retardedly unhelpful, and just results in hacks to detect keys that appear to map to ascii...
|
|
//Preventing the browser from leaving the page etc should NOT mean I can no longer get ascii/unicode values, only that the browser stops trying to do something random due to the event.
|
|
//If you are the person that decreed that this is the holy way, then please castrate yourself now.
|
|
// if (scan == K_BACKSPACE || scan == K_LCTRL || scan == K_LALT || scan == K_LSHIFT || scan == K_RCTRL || scan == K_RALT || scan == K_RSHIFT)
|
|
return true;
|
|
// return false;
|
|
}
|
|
static int RemapTouchId(int id, qboolean final)
|
|
{
|
|
static int touchids[8];
|
|
int i;
|
|
if (!id)
|
|
return id;
|
|
for (i = 1; i < countof(touchids); i++)
|
|
if (touchids[i] == id)
|
|
{
|
|
if (final)
|
|
touchids[i] = 0;
|
|
return i;
|
|
}
|
|
for (i = 1; i < countof(touchids); i++)
|
|
if (touchids[i] == 0)
|
|
{
|
|
if (!final)
|
|
touchids[i] = id;
|
|
return i;
|
|
}
|
|
return id;
|
|
}
|
|
static void DOM_ButtonEvent(unsigned int devid, int down, int button)
|
|
{
|
|
devid = RemapTouchId(devid, !down);
|
|
if (down == 2)
|
|
{
|
|
//fixme: the event is a float. we ignore that.
|
|
while(button < 0)
|
|
{
|
|
IN_KeyEvent(mouseid[devid], true, K_MWHEELUP, 0);
|
|
IN_KeyEvent(mouseid[devid], false, K_MWHEELUP, 0);
|
|
button += 1;
|
|
}
|
|
while(button > 0)
|
|
{
|
|
IN_KeyEvent(mouseid[devid], true, K_MWHEELDOWN, 0);
|
|
IN_KeyEvent(mouseid[devid], false, K_MWHEELUP, 0);
|
|
button -= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//swap buttons 2 and 3, so rmb is still +forward by default and not +mlook.
|
|
if (button == 2)
|
|
button = 1;
|
|
else if (button == 1)
|
|
button = 2;
|
|
|
|
if (button < 0)
|
|
button = K_TOUCH;
|
|
else
|
|
button += K_MOUSE1;
|
|
IN_KeyEvent(mouseid[devid], down, button, 0);
|
|
}
|
|
}
|
|
static void DOM_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size)
|
|
{
|
|
devid = RemapTouchId(devid, false);
|
|
IN_MouseMove(mouseid[devid], abs, x, y, z, size);
|
|
}
|
|
|
|
static void DOM_LoadFile(char *loc, char *mime, int handle)
|
|
{
|
|
vfsfile_t *file = NULL;
|
|
if (handle != -1)
|
|
file = FSWEB_OpenTempHandle(handle);
|
|
else
|
|
{
|
|
char str[1024];
|
|
if (!strcmp(mime, "joinurl") || !strcmp(mime, "observeurl") || !strcmp(mime, "connecturl"))
|
|
{
|
|
extern cvar_t spectator;
|
|
if (!strcmp(mime, "joinurl"))
|
|
Cvar_Set(&spectator, "0");
|
|
if (!strcmp(mime, "observeurl"))
|
|
Cvar_Set(&spectator, "1");
|
|
Cbuf_AddText(va("connect %s\n", COM_QuotedString(loc, str, sizeof(str), false)), RESTRICT_INSECURE);
|
|
return;
|
|
}
|
|
if (!strcmp(mime, "demourl"))
|
|
{
|
|
Cbuf_AddText(va("qtvplay %s\n", COM_QuotedString(loc, str, sizeof(str), false)), RESTRICT_INSECURE);
|
|
return;
|
|
}
|
|
}
|
|
//try and open it. generally downloading it from the server.
|
|
if (!Host_RunFile(loc, strlen(loc), file))
|
|
{
|
|
if (file)
|
|
VFS_CLOSE(file);
|
|
}
|
|
}
|
|
static void DOM_CbufAddText(const char *text)
|
|
{
|
|
Cbuf_AddText(text, RESTRICT_LOCAL);
|
|
}
|
|
static int VID_ShouldSwitchToFullscreen(void)
|
|
{ //if false, mouse grabs won't work and we'll be forced to touchscreen mode.
|
|
//we can only go fullscreen when the user clicks something.
|
|
//this means that the user will get pissed off at the fullscreen state changing when they first click on the menus after it loading up.
|
|
//this is confounded by escape bringing up the menu. <ESC>GRR IT CHANGED MODE!<options>WTF IT CHANGED AGAIN FUCKING PIECE OF SHIT!.
|
|
//annoying, but that's web browsers for you. the best thing we can do is to not regrab until they next click while actually back in the game.
|
|
extern cvar_t vid_fullscreen;
|
|
return !!vid_fullscreen.value && !Key_MouseShouldBeFree();
|
|
}
|
|
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
|
|
{
|
|
vid_isfullscreen = true;
|
|
|
|
if (!emscriptenfte_setupcanvas(
|
|
info->width,
|
|
info->height,
|
|
VID_Resized,
|
|
DOM_MouseMove,
|
|
DOM_ButtonEvent,
|
|
DOM_KeyEvent,
|
|
DOM_LoadFile,
|
|
DOM_CbufAddText,
|
|
IN_GamePadButtonEvent,
|
|
IN_GamePadAxisEvent,
|
|
IN_GamePadOrientationEvent,
|
|
VID_ShouldSwitchToFullscreen
|
|
))
|
|
{
|
|
Con_Printf("Couldn't set up canvas\n");
|
|
return false;
|
|
}
|
|
|
|
vid.activeapp = true;
|
|
|
|
if (info->vr && !info->vr->Prepare(NULL))
|
|
info->vr = NULL; //not available.
|
|
|
|
if (!GL_Init(info, GLVID_getwebglfunction))
|
|
return false;
|
|
if (info->vr && !info->vr->Init(NULL, info))
|
|
return false;
|
|
vid.vr = info->vr;
|
|
|
|
qglViewport (0, 0, vid.pixelwidth, vid.pixelheight);
|
|
|
|
VID_Resized(vid.pixelwidth, vid.pixelheight);
|
|
|
|
mouseactive = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void GLVID_DeInit (void)
|
|
{
|
|
vid.activeapp = false;
|
|
|
|
emscriptenfte_setupcanvas(-1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
GL_ForgetPointers();
|
|
}
|
|
|
|
|
|
void GLVID_SwapBuffers (void)
|
|
{
|
|
//webgl doesn't support swapbuffers.
|
|
//you can't use it for loading screens.
|
|
//such things must result in waiting until the following frame.
|
|
//although there IS a swapped-buffers event, which we should probably use in preference to requestanimationframe or whatever the call is.
|
|
|
|
/*
|
|
if (!vid_isfullscreen)
|
|
{
|
|
if (!in_windowed_mouse.value)
|
|
{
|
|
if (mouseactive)
|
|
{
|
|
IN_DeactivateMouse ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((key_dest == key_game||mouseusedforgui) && vid.activeapp)
|
|
IN_ActivateMouse ();
|
|
else if (!(key_dest == key_game || mouseusedforgui) || !vid.activeapp)
|
|
IN_DeactivateMouse ();
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramps)
|
|
{
|
|
gammaworks = false;
|
|
return gammaworks;
|
|
}
|
|
|
|
void GLVID_SetCaption(const char *text)
|
|
{
|
|
emscriptenfte_settitle(text);
|
|
}
|
|
|
|
void Sys_SendKeyEvents(void)
|
|
{
|
|
/*most callbacks happen outside our code, we don't need to poll for events - except for joysticks*/
|
|
qboolean shouldbefree = Key_MouseShouldBeFree();
|
|
emscriptenfte_updatepointerlock(in_windowed_mouse.ival && !shouldbefree, shouldbefree);
|
|
emscriptenfte_polljoyevents();
|
|
}
|
|
/*various stuff for joysticks, which we don't support in this port*/
|
|
void INS_Shutdown (void)
|
|
{
|
|
}
|
|
void INS_ReInit (void)
|
|
{
|
|
}
|
|
void INS_Move(void)
|
|
{
|
|
}
|
|
void INS_Init (void)
|
|
{
|
|
//mneh, handy enough
|
|
R_RegisterVRDriver(NULL, &webxrfuncs);
|
|
}
|
|
void INS_Accumulate(void)
|
|
{
|
|
}
|
|
void INS_Commands (void)
|
|
{
|
|
}
|
|
void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))
|
|
{
|
|
size_t i;
|
|
char foobar[64];
|
|
for (i = 0; i < countof(gamepaddeviceids); i++)
|
|
{
|
|
Q_snprintfz(foobar, sizeof(foobar), "gp%i", (int)i);
|
|
callback(ctx, "gamepad", foobar, &gamepaddeviceids[i]);
|
|
}
|
|
for (i = 0; i < countof(mouseid); i++)
|
|
{
|
|
Q_snprintfz(foobar, sizeof(foobar), "m%i", (int)i);
|
|
callback(ctx, "mouse", foobar, &mouseid[i]);
|
|
}
|
|
for (i = 0; i < countof(keyboardid); i++)
|
|
{
|
|
Q_snprintfz(foobar, sizeof(foobar), "kb%i", (int)i);
|
|
callback(ctx, "keyboard", foobar, &keyboardid[i]);
|
|
}
|
|
}
|
|
|
|
void INS_Rumble(int joy, quint16_t amp_low, quint16_t amp_high, quint32_t duration)
|
|
{
|
|
}
|
|
void INS_RumbleTriggers(int joy, quint16_t left, quint16_t right, quint32_t duration)
|
|
{
|
|
}
|
|
void INS_SetLEDColor(int id, vec3_t color)
|
|
{
|
|
}
|
|
void INS_SetTriggerFX(int id, const void *data, size_t size)
|
|
{
|
|
}
|
|
|