From c949500da8629393c5cd4f23b79f6d69598dd332 Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 14 May 2020 15:50:26 +0000 Subject: [PATCH] Implement preliminary openxr plugin. inputs are still useless (can HOPEFULLY result in some prints, but nothing else), and there's no considerations for 2d things. Fix prediction issues with the ftenq protocol. Fix some console image previews. Added mod_precache cvar. set to 0 to significantly reduce memory usage in xonotic... Prevent xonotic's random file writes from forcing full worker syncs, for faster loading. QTV connections are now accepted only from localhost peers by default. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5694 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- CMakeLists.txt | 53 +- engine/Makefile | 2 +- engine/client/cl_main.c | 90 +- engine/client/cl_parse.c | 23 +- engine/client/cl_pred.c | 12 +- engine/client/image.c | 9 +- engine/client/m_items.c | 2 +- engine/client/merged.h | 5 +- engine/client/pr_menu.c | 33 +- engine/client/quakedef.h | 4 + engine/client/render.h | 7 +- engine/client/renderer.c | 127 +- engine/client/vid.h | 3 + engine/client/vr.h | 89 ++ engine/common/bothdefs.h | 5 +- engine/common/cmd.c | 4 +- engine/common/com_mesh.c | 1 + engine/common/common.c | 49 +- engine/common/fs.c | 51 +- engine/common/mathlib.c | 44 + engine/common/mathlib.h | 1 + engine/common/net.h | 2 +- engine/common/net_wins.c | 32 +- engine/common/plugin.c | 36 +- engine/common/pr_bgcmd.c | 6 + engine/common/protocol.h | 2 +- engine/common/sys.h | 2 +- engine/d3d/vid_d3d11.c | 30 +- engine/gl/gl_backend.c | 2 +- engine/gl/gl_bloom.c | 12 +- engine/gl/gl_heightmap.c | 207 +++- engine/gl/gl_model.c | 10 +- engine/gl/gl_rlight.c | 1 + engine/gl/gl_rmain.c | 249 ++-- engine/gl/gl_shader.c | 4 +- engine/gl/gl_shadow.c | 7 +- engine/gl/gl_videgl.c | 22 +- engine/gl/gl_vidlinuxglx.c | 31 +- engine/gl/gl_vidnt.c | 20 +- engine/qclib/qcc_pr_comp.c | 6 +- engine/server/pr_cmds.c | 545 +++++---- engine/server/progs.h | 8 +- engine/server/sv_ccmds.c | 36 +- engine/server/sv_ents.c | 7 +- engine/server/sv_init.c | 68 +- engine/server/sv_main.c | 9 +- engine/server/sv_user.c | 85 +- engine/vk/vk_backend.c | 43 +- engine/vk/vk_init.c | 192 ++- engine/vk/vkrenderer.h | 4 +- plugins/Makefile | 56 +- plugins/cef/cef.c | 347 +++--- plugins/openxr.c | 1609 +++++++++++++++++++++++++ plugins/plugin.h | 6 +- quakec/menusys/menu/servers.qc | 2 +- quakec/menusys/menu/updates.qc | 2 +- quakec/menusys/menusys/mitem_frame.qc | 2 +- 57 files changed, 3441 insertions(+), 875 deletions(-) create mode 100644 engine/client/vr.h create mode 100644 plugins/openxr.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d468d2bd..4e76e386 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1077,7 +1077,57 @@ ELSE() MESSAGE(WARNING "ffmpeg library NOT available. Quake shouldn't be playing fmv anyway.") ENDIF() -#TODO: cef plugin +#openxr plugin +FIND_PACKAGE(PkgConfig) +IF (PKGCONFIG_FOUND) + IF (NOT CMAKE_CROSSCOMPILING) #its picking up the linux headers then complaining that they're missing in mingw. also almost entirely untested so no great loss. + PKG_SEARCH_MODULE(OPENXR openxr) + ENDIF() + IF (OPENXR_FOUND) + ADD_LIBRARY(openxr MODULE + plugins/plugin.c + plugins/openxr.c + ) + TARGET_INCLUDE_DIRECTORIES(openxr PRIVATE ${OPENXR_INCLUDE_DIRS} ) + + SET_TARGET_PROPERTIES(openxr PROPERTIES PREFIX "fteplug_") + SET_TARGET_PROPERTIES(openxr PROPERTIES LINK_FLAGS "-Wl,--no-undefined") + IF (1) #dynamically link + SET_TARGET_PROPERTIES(openxr PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES};XR_NO_PROTOTYPES") + TARGET_LINK_LIBRARIES(openxr ${SYS_LIBS}) + ELSE() #statically link + SET_TARGET_PROPERTIES(openxr PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES}") + TARGET_LINK_LIBRARIES(openxr ${SYS_LIBS} ${OPENXR_LIBRARIES}) + ENDIF() + SET(INSTALLTARGS ${INSTALLTARGS} openxr) + ELSE() + MESSAGE(WARNING "openxr library NOT available. Quake is already a reality anyway.") + ENDIF() +ENDIF() + +#cef plugin +#libcef itself can be obtained from http://opensource.spotify.com/cefbuilds/index.html (minimal builds, which still ends up with a 940mb libcef.so - yes, actual size) +#(be sure to manually strip the binary of its debug info) +IF (0) + FIND_PATH (CEF_PATH include/cef_version.h /tmp/cef/cef_binary_81.3.1+gb2b49f1+chromium-81.0.4044.113_linux64_minimal) + #FIND_LIBRARY(CEF_LIBRARIES cef ${CEF_PATH}/Release) + IF (CEF_PATH) + ADD_LIBRARY(cef MODULE + plugins/plugin.c + plugins/cef/cef.c + ) + TARGET_INCLUDE_DIRECTORIES(cef PRIVATE ${CEF_PATH}/..) + if (CEF_LIBRARIES) + SET_TARGET_PROPERTIES(cef PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES};LIBCEF_STATIC") + TARGET_LINK_LIBRARIES(cef ${SYS_LIBS} ${CEF_LIBRARIES} ${CMAKE_DL_LIBS}) + ELSE() + SET_TARGET_PROPERTIES(cef PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES};LIBCEF_DYNAMIC") + TARGET_LINK_LIBRARIES(cef ${SYS_LIBS} ${CMAKE_DL_LIBS}) + ENDIF() + SET_TARGET_PROPERTIES(cef PROPERTIES PREFIX "fteplug_") + SET_TARGET_PROPERTIES(cef PROPERTIES LINK_FLAGS "-Wl,--no-undefined") + ENDIF() +ENDIF() IF(NOT ANDROID) #XMPP/jabber client plugin @@ -1115,6 +1165,7 @@ ADD_CUSTOM_TARGET(menusys ALL quakec/menusys/menu.src quakec/menusys/menusys/mitems.qc quakec/menusys/menusys/mitems_common.qc + quakec/menusys/menusys/mitem_frame.qc quakec/menusys/menusys/mitem_desktop.qc quakec/menusys/menusys/mitem_exmenu.qc quakec/menusys/menusys/mitem_edittext.qc diff --git a/engine/Makefile b/engine/Makefile index fc952ab0..de9d35ee 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -1718,9 +1718,9 @@ DEPCXX?=$(CXX) ARCH:=$(ARCH) BASELDFLAGS:=-L$(ARCHLIBS) $(BASELDFLAGS) +default: help -include Makefile_private -.default: help all: rel rel: sv-rel m-rel qcc-rel dbg: sv-dbg m-dbg qcc-dbg diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 5effe424..1846bd4f 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cl_master.h" #include "cl_ignore.h" #include "shader.h" +#include "vr.h" #include // callbacks void QDECL CL_Sbar_Callback(struct cvar_s *var, char *oldvalue); @@ -184,6 +185,7 @@ cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOT cvar_t cl_download_mapsrc = CVARFD("cl_download_mapsrc", "", CVAR_ARCHIVE, "Specifies an http location prefix for map downloads. EG: \"http://example.com/path/quakemaps/\""); cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)"); cvar_t requiredownloads = CVARFD("requiredownloads","1", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining."); +cvar_t mod_precache = CVARD("mod_precache","1", "Controls when models are loaded.\n0: Load them only when they're visible.\n1: Load them upfront.\n2: Lazily load them to shorten load times at the risk of brief stuttering during only the start of the map."); cvar_t cl_muzzleflash = CVAR("cl_muzzleflash", "1"); @@ -2695,8 +2697,8 @@ void CL_Packet_f (void) if (Cmd_FromGamecode()) //some mvdsv servers stuffcmd a packet command which lets them know which ip the client is from. { //unfortunatly, 50% of servers are badly configured resulting in them poking local services that THEY MUST NOT HAVE ACCESS TO. - char *addrdesc; - char *realdesc; + const char *addrdesc; + const char *realdesc; if (cls.demoplayback) { Con_DPrintf ("Not sending realip packet from demo\n"); @@ -4091,13 +4093,13 @@ static void CL_Curl_f(void) usage |= 4; else if (!strcmp(arg, "--for")) { - alreadyhave = true; //assume we have it. - for (i++; i < argc-1; i++) + alreadyhave = true; //assume we have a package that satisfies the file name. + for (i++; i < argc-1; i++) //all but the last... { arg = Cmd_Argv(i); if (!CL_CheckDLFile(arg)) { - alreadyhave = false; + alreadyhave = false; //I guess we didn't after all. break; } } @@ -4477,10 +4479,43 @@ void CL_CrashMeError_f(void) Sys_Error("crashme! %s", Cmd_Args()); } + +static char *ShowTime(unsigned int seconds) +{ + char buf[1024]; + char *b = buf; + *b = 0; + + if (seconds > 60) + { + if (seconds > 60*60) + { + if (seconds > 24*60*60) + { + strcpy(b, va("%id ", seconds/(24*60*60))); + b += strlen(b); + seconds %= 24*60*60; + } + + strcpy(b, va("%ih ", seconds/(60*60))); + b += strlen(b); + seconds %= 60*60; + } + strcpy(b, va("%im ", seconds/60)); + b += strlen(b); + seconds %= 60; + } + strcpy(b, va("%is", seconds)); + b += strlen(b); + + return va("%s", buf); +} void CL_Status_f(void) { + extern world_t csqc_world; char adr[128]; float pi, po, bi, bo; + NET_PrintAddresses(cls.sockets); NET_PrintConnectionsStatus(cls.sockets); if (NET_GetRates(cls.sockets, &pi, &po, &bi, &bo)) @@ -4578,6 +4613,42 @@ void CL_Status_f(void) if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) Con_Printf("\treplacement deltas\n"); } + + if (cl.worldmodel) + { + Con_Printf("map uptime : %s\n", ShowTime(cl.time)); + COM_FileBase(cl.worldmodel->name, adr, sizeof(adr)); + Con_Printf ("current map : %s (%s)\n", adr, cl.levelname); + } + + if (csqc_world.progs) + { + extern int num_sfx; + int count = 0, i; + edict_t *e; + for (i = 0; i < csqc_world.num_edicts; i++) + { + e = EDICT_NUM_PB(csqc_world.progs, i); + if (e && e->ereftype == ER_FREE && sv.time - e->freetime > 0.5) + continue; //free, and older than the zombie time + count++; + } + Con_Printf("entities : %i/%i/%i (mem: %.1f%%)\n", count, csqc_world.num_edicts, csqc_world.max_edicts, 100*(float)(csqc_world.progs->stringtablesize/(double)csqc_world.progs->stringtablemaxsize)); + for (count = 1; count < MAX_PRECACHE_MODELS; count++) + if (!*cl.model_csqcname[count]) + break; + Con_Printf("models : %i/%i\n", count, MAX_PRECACHE_MODELS); + Con_Printf("sounds : %i/\n", num_sfx); //there is a limit, its just private. :( + + for (count = 1; count < MAX_SSPARTICLESPRE; count++) + if (!cl.particle_csname[count]) + break; + if (count!=1) + Con_Printf("particles : %i/%i\n", count, MAX_SSPARTICLESPRE); + if (cl.csqcdebug) + Con_Printf("csqc debug : true\n"); + } + Con_Printf("gamedir : %s\n", FS_GetGamedir(true)); } void CL_Demo_SetSpeed_f(void) @@ -4771,6 +4842,7 @@ void CL_Init (void) Cvar_Register (&cl_teamchatsound, cl_controlgroup); Cvar_Register (&requiredownloads, cl_controlgroup); + Cvar_Register (&mod_precache, cl_controlgroup); Cvar_Register (&cl_standardchat, cl_controlgroup); Cvar_Register (&msg_filter, cl_controlgroup); Cvar_Register (&msg_filter_frags, cl_controlgroup); @@ -5894,6 +5966,7 @@ double Host_Frame (double time) qboolean idle; extern int r_blockvidrestart; static qboolean hadwork; + qboolean vrsync; RSpeedLocals(); @@ -5902,7 +5975,8 @@ double Host_Frame (double time) return 0; // something bad happened, or the server disconnected } - newrealtime = Media_TweekCaptureFrameTime(realtime, time); + vrsync = vid.vr?vid.vr->SyncFrame(&time):false; //fiddle with frame timings + newrealtime = Media_TweekCaptureFrameTime(realtime, time); //fiddle with time some more time = newrealtime - realtime; realtime = newrealtime; @@ -5945,7 +6019,7 @@ double Host_Frame (double time) if (!cls.timedemo) CL_ReadPackets (); - if (idle && cl_idlefps.value > 0) + if (idle && cl_idlefps.value > 0 && !vrsync) { double idlesec = 1.0 / cl_idlefps.value; if (idlesec > 0.1) @@ -6020,7 +6094,7 @@ double Host_Frame (double time) #ifdef HAVE_MEDIA_ENCODER && Media_Capturing() != 2 #endif - ) + && !vrsync) { // realtime += spare/1000; //don't use it all! double newspare = CL_FilterTime((spare/1000 + realtime - oldrealtime)*1000, maxfps, 1.5, maxfpsignoreserver); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 1fe0d3cc..25ce2718 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -276,14 +276,14 @@ static const char *svc_nqstrings[] = "NEW PROTOCOL(75)", //75 "NEW PROTOCOL(76)", //76 "NEW PROTOCOL(77)", //77 - "nqsvcfte_updatestatstring(78)", //78 - "nqsvcfte_updatestatfloat(79)", //79 + "nqsvcfte_updatestatstring", //78 + "nqsvcfte_updatestatfloat", //79 "NEW PROTOCOL(80)", //80 "NEW PROTOCOL(81)", //81 "NEW PROTOCOL(82)", //82 - "nqsvcfte_cgamepacket(83)", //83 + "nqsvcfte_cgamepacket", //83 "nqsvcfte_voicechat", //84 - "nqsvcfte_setangledelta(85)", //85 + "nqsvcfte_setangledelta", //85 "nqsvcfte_updateentities", //86 "NEW PROTOCOL(87)", //87 "NEW PROTOCOL(88)", //88 @@ -291,7 +291,7 @@ static const char *svc_nqstrings[] = }; #endif -extern cvar_t requiredownloads, cl_standardchat, msg_filter, msg_filter_frags, msg_filter_pickups, cl_countpendingpl, cl_download_mapsrc; +extern cvar_t requiredownloads, mod_precache, cl_standardchat, msg_filter, msg_filter_frags, msg_filter_pickups, cl_countpendingpl, cl_download_mapsrc; int oldparsecountmod; int parsecountmod; double parsecounttime; @@ -1354,6 +1354,9 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) cl.model_precache[i] = NULL; else #endif + if (!cls.timedemo && i!=1 && mod_precache.ival != 1) + cl.model_precache[i] = Mod_FindName (Mod_FixName(cl.model_name[i], cl.model_name[1])); + else cl.model_precache[i] = Mod_ForName (Mod_FixName(cl.model_name[i], cl.model_name[1]), MLV_WARN); S_ExtraUpdate(); @@ -1839,6 +1842,16 @@ void CL_RequestNextDownload (void) } } + + if (mod_precache.ival >= 2) + { + int i; + for (i=1 ; iloadstate == MLS_NOTLOADED) + Mod_LoadModel(cl.model_precache[i], MLV_WARN); + } + } } } diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 33b4e376..f28e15e4 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -1130,7 +1130,7 @@ void CL_PredictMovePNum (int seat) from.frame = i; from.time = backdate->senttime; from.cmd = &backdate->cmd[seat]; - if (to.frame > pv->prop.sequence) + if (cl.inframes[to.frame&UPDATE_MASK].ackframe > pv->prop.sequence) continue; //if we didn't predict to this frame yet, then the waterjump etc state will be invalid, so try to go for an older frame so that it actually propagates properly. if (from.time < simtime && from.frame != to.frame) break; //okay, we found the first frame that is older, no need to continue looking @@ -1184,7 +1184,8 @@ void CL_PredictMovePNum (int seat) } if (i == pe->num_entities && pv->nolocalplayer) { - //return; //no player, nothing makes sense any more. + if (cls.state >= ca_active) + return; //no player, nothing makes sense any more. from.state = &nullstate; nopred = true; } @@ -1243,6 +1244,11 @@ void CL_PredictMovePNum (int seat) { int stopframe; //Con_Printf("Pred %i to %i\n", to.frame+1, min(from.frame+UPDATE_BACKUP, cl.movesequence)); + + //fix up sequence numbers for nq + int validsequence = cl.inframes[cl.validsequence&UPDATE_MASK].ackframe; + from.frame = cl.inframes[from.frame&UPDATE_MASK].ackframe; + to.frame = cl.inframes[to.frame&UPDATE_MASK].ackframe; for (i=to.frame+1, stopframe=min(from.frame+UPDATE_BACKUP, cl.movesequence) ; i < stopframe; i++) { outframe_t *of = &cl.outframes[i & UPDATE_MASK]; @@ -1273,7 +1279,7 @@ void CL_PredictMovePNum (int seat) VectorCopy(pv->prop.gravitydir, from.state->gravitydir); } CL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd); - if (i <= cl.validsequence && simtime >= to.time) + if (i <= validsequence && simtime >= to.time) { //this frame is final keep track of our propagated values. pv->prop.onground = pmove.onground; pv->prop.jump_held = pmove.jump_held; diff --git a/engine/client/image.c b/engine/client/image.c index 79584018..286fc3bd 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -7484,8 +7484,8 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags) { int mip; - if (mips->type != PTI_2D) - return; //blurgh + if (mips->type == PTI_3D) + return; //3d mipmaps are more complicated to compute. if (flags & IF_NOMIPMAP) return; @@ -7493,6 +7493,9 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags) if (sh_config.can_genmips && mips->encoding != PTI_P8) return; + if (mips->mip[0].depth != 1) + return; //blurgh. we can't deal with layers. + switch(mips->encoding) { case TF_TRANS8: @@ -12745,6 +12748,8 @@ struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname mips = Z_Malloc(sizeof(*mips)); mips->type = (flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT; + if (mips->type == PTI_ANY) + mips->type = PTI_2D; //d if (Image_GenMip0(mips, flags, rgbadata, NULL, imgwidth, imgheight, format, true)) { Image_GenerateMips(mips, flags); diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 86850663..04049c2c 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -1694,7 +1694,7 @@ changed: } static qboolean menu_mousedown; -extern menu_t *menu_script; +extern emenu_t *menu_script; static void M_Draw (menu_t *menu) { emenu_t *m = (emenu_t*)menu; diff --git a/engine/client/merged.h b/engine/client/merged.h index 73a2c5af..34573a5e 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -1,3 +1,6 @@ +#ifndef MERGED_H +#define MERGED_H + #ifdef VKQUAKE //we need some types available elsewhere, but don't really want to have to include the entire vulkan api everywhere. //unfortunately, vulkan's handle types are not well defined. @@ -515,4 +518,4 @@ typedef struct rendererinfo_s { texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags); texid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *height); - +#endif //MERGED_H diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 1e0f26cb..e1387dc2 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -815,34 +815,45 @@ void QCBUILTIN PF_CL_is_cached_pic (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_CL_precache_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + extern cvar_t pr_precachepic_slow; const char *str; mpic_t *pic; - float fromwad; + unsigned int flags; +#define PIC_FROMWAD 1 /*obsolete, probably better to just use gfx/foo.lmp instead*/ +//#define PIC_TEMPORARY 2 /*DP, not meaningful here*/ +//#define PIC_NOCLAMP 4 /*DP, not useful here - use a shader instead*/ +//#define PIC_MIPMAP 8 /*DP, not useful here - use a shader instead*/ +#define PRECACHE_PIC_DOWNLOAD 256 /*block until loaded, downloading if missing*/ +#define PRECACHE_PIC_TEST 512 /*block until loaded*/ + G_INT(OFS_RETURN) = G_INT(OFS_PARM0); str = PR_GetStringOfs(prinst, OFS_PARM0); if (prinst->callargc > 1) - fromwad = G_FLOAT(OFS_PARM1); + flags = G_FLOAT(OFS_PARM1); else - fromwad = false; + flags = 0; - if (fromwad) + if (pr_precachepic_slow.ival) + flags |= PRECACHE_PIC_DOWNLOAD|PRECACHE_PIC_TEST; + + if (flags & PIC_FROMWAD) pic = R2D_SafePicFromWad(str); else { pic = R2D_SafeCachePic(str); - if ((!pic || !R_GetShaderSizes(pic, NULL, NULL, true)) && cls.state + if ((flags & PRECACHE_PIC_DOWNLOAD) && cls.state //if we're allowed to download it... + && strchr(str, '.') //only try to download it if it looks as though it contains a path. #ifndef CLIENTONLY - && !sv.active + && !sv.active //not if we're already the server... #endif - && strchr(str, '.')) //only try to download it if it looks as though it contains a path. + && (!pic || !R_GetShaderSizes(pic, NULL, NULL, true))) //and it wasn't loaded... CL_CheckOrEnqueDownloadFile(str, str, 0); } - if (pic && R_GetShaderSizes(pic, NULL, NULL, true)) - G_INT(OFS_RETURN) = G_INT(OFS_PARM0); - else - G_INT(OFS_RETURN) = 0; + if (flags & PRECACHE_PIC_TEST) + if (!pic || !R_GetShaderSizes(pic, NULL, NULL, true)) + G_INT(OFS_RETURN) = 0; } #ifdef CSQC_DAT diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index 5c0c8a13..c4d9f585 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -369,6 +369,8 @@ void COM_AddWork(wgroup_t thread, void(*func)(void *ctx, void *data, size_t a, s void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b); //high priority qboolean COM_HasWork(void); void COM_WorkerFullSync(void); +void COM_WorkerLock(void); //callable on main thread to temporarily suspend workers (in a safe location) +void COM_WorkerUnlock(void); void COM_DestroyWorkerThread(void); void COM_WorkerPartialSync(void *priorityctx, int *address, int value); //aka: while(*address==value)wait(); extern void *com_resourcemutex; //random mutex to simplify resource creation type stuff. @@ -384,6 +386,8 @@ void COM_AssertMainThread(const char *msg); #define COM_InsertWork(t,f,a,b,c,d) (f)((a),(b),(c),(d)) #define COM_WorkerPartialSync(c,a,v) #define COM_WorkerFullSync() +#define COM_WorkerLock() +#define COM_WorkerUnlock() #define COM_HasWork() false #define COM_DoWork(t,l) false #define COM_AssertMainThread(msg) diff --git a/engine/client/render.h b/engine/client/render.h index a54ce4a4..a7f81501 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -598,7 +598,10 @@ qbyte *R_MarkLeaves_Q2 (void); qbyte *R_MarkLeaves_Q3 (void); void R_SetFrustum (float projmat[16], float viewmat[16]); void R_SetRenderer(rendererinfo_t *ri); -void R_RegisterRenderer(rendererinfo_t *ri); +qboolean R_RegisterRenderer(void *module, rendererinfo_t *ri); +struct plugvrfuncs_s; +qboolean R_RegisterVRDriver(void *module, struct plugvrfuncs_s *vrfuncs); +qboolean R_UnRegisterModule(void *module); void R_AnimateLight (void); void R_UpdateHDR(vec3_t org); void R_UpdateLightStyle(unsigned int style, const char *stylestring, float r, float g, float b); @@ -645,7 +648,7 @@ extern cvar_t r_shadow_realtime_dlight, r_shadow_realtime_dlight_shadows; extern cvar_t r_shadow_realtime_dlight_ambient; extern cvar_t r_shadow_realtime_dlight_diffuse; extern cvar_t r_shadow_realtime_dlight_specular; -extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_shadows, r_shadow_realtime_world_lightmaps; +extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_shadows, r_shadow_realtime_world_lightmaps, r_shadow_realtime_world_importlightentitiesfrommap; extern cvar_t r_shadow_shadowmapping; extern cvar_t r_mirroralpha; extern cvar_t r_wateralpha; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index b593229d..70902a8d 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -4,8 +4,13 @@ #include "gl_draw.h" #include "shader.h" #include "glquake.h" +#include "vr.h" #include +#ifdef __GLIBC__ +#include //for malloc_trim +#endif + #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 @@ -1213,74 +1218,93 @@ rendererinfo_t dedicatedrendererinfo = { extern rendererinfo_t headlessrenderer; #endif -rendererinfo_t *rendererinfo[16] = +static struct +{ + void *module; + rendererinfo_t *ri; +} rendererinfo[16] = { #ifdef GLQUAKE #ifdef FTE_RPI - &rpirendererinfo, + {NULL, &rpirendererinfo}, #endif - &openglrendererinfo, + {NULL, &openglrendererinfo}, #ifdef USE_EGL - &eglrendererinfo, + {NULL, &eglrendererinfo}, #endif #endif #ifdef D3D9QUAKE - &d3d9rendererinfo, + {NULL, &d3d9rendererinfo}, #endif #ifdef VKQUAKE - &vkrendererinfo, + {NULL, &vkrendererinfo}, #if defined(_WIN32) && defined(GLQUAKE) && !defined(FTE_SDL) - &nvvkrendererinfo, + {NULL, &nvvkrendererinfo}, #endif #endif #ifdef D3D11QUAKE - &d3d11rendererinfo, + {NULL, &d3d11rendererinfo}, #endif #ifdef SWQUAKE - &swrendererinfo, + {NULL, &swrendererinfo}, #endif #ifdef D3D8QUAKE - &d3d8rendererinfo, + {NULL, &d3d8rendererinfo}, #endif #ifdef WAYLANDQUAKE #ifdef GLQUAKE - &rendererinfo_wayland_gl, + {NULL, &rendererinfo_wayland_gl}, #endif #ifdef VKQUAKE - &rendererinfo_wayland_vk, + {NULL, &rendererinfo_wayland_vk}, #endif #endif #ifdef GLQUAKE - &fbdevrendererinfo, //direct stuff that doesn't interact well with the system should always be low priority + {NULL, &fbdevrendererinfo}, //direct stuff that doesn't interact well with the system should always be low priority #endif #ifndef NPQTV - &dedicatedrendererinfo, + {NULL, &dedicatedrendererinfo}, #endif #ifdef HEADLESSQUAKE - &headlessrenderer, + {NULL, &headlessrenderer}, #ifdef VKQUAKE - //&headlessvkrendererinfo, + //{NULL, &headlessvkrendererinfo}, #endif #endif }; -void R_RegisterRenderer(rendererinfo_t *ri) +qboolean R_RegisterRenderer(void *module, rendererinfo_t *ri) { size_t i; for (i = 0; i < countof(rendererinfo); i++) { //already registered - if (rendererinfo[i] == ri) - return; + if (rendererinfo[i].ri == ri) + return true; } for (i = 0; i < countof(rendererinfo); i++) { //register it in the first empty slot - if (!rendererinfo[i]) + if (!rendererinfo[i].ri) { - rendererinfo[i] = ri; - return; + rendererinfo[i].module = module; + rendererinfo[i].ri = ri; + return true; } } Sys_Printf("unable to register renderer %s\n", ri->description); + return false; +} + +static plugvrfuncs_t *vrfuncs; +qboolean R_RegisterVRDriver(void *module, plugvrfuncs_t *vr) +{ + if (!vrfuncs) + { + vrfuncs = vr; + return true; + } + + Sys_Printf("unable to register renderer %s\n", vr->description); + return false; } void R_SetRenderer(rendererinfo_t *ri) @@ -1365,6 +1389,10 @@ void R_ShutdownRenderer(qboolean devicetoo) if (VID_DeInit && devicetoo) { + if (vid.vr) + vid.vr->Shutdown(); + vid.vr = NULL; + TRACE(("dbg: R_ApplyRenderer: VID_DeInit\n")); VID_DeInit(); } @@ -1430,6 +1458,10 @@ qboolean R_ApplyRenderer (rendererstate_t *newr) R_ShutdownRenderer(true); Con_DPrintf("video shutdown took %f seconds\n", Sys_DoubleTime() - time); +#ifdef __GLIBC__ + malloc_trim(0); +#endif + if (qrenderer == QR_NONE) { if (newr->renderer->rtype == qrenderer && currentrendererstate.renderer) @@ -1951,6 +1983,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) newr->rate = vid_refreshrate.value; newr->stereo = (r_stereo_method.ival == 1); newr->srgb = vid_srgb.ival; + newr->vr = vrfuncs; #if defined(_WIN32) && !defined(FTE_SDL) if (newr->bpp && newr->bpp < 24) @@ -1990,9 +2023,9 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) { //special hack so that android doesn't weird out when not focused. for (i = 0; i < countof(rendererinfo); i++) { - if (rendererinfo[i] && rendererinfo[i]->name[0] && rendererinfo[i]->rtype == QR_HEADLESS) + if (rendererinfo[i].ri && rendererinfo[i].ri->name[0] && rendererinfo[i].ri->rtype == QR_HEADLESS) { - newr->renderer = rendererinfo[i]; + newr->renderer = rendererinfo[i].ri; break; } } @@ -2005,11 +2038,11 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) //I'd like to just qsort the renderers, but that isn't stable and might reorder gl+d3d etc. for (i = 0; i < countof(rendererinfo); i++) { - pri = R_PriorityForRenderer(rendererinfo[i]); + pri = R_PriorityForRenderer(rendererinfo[i].ri); if (pri > bestpri) { bestpri = pri; - newr->renderer = rendererinfo[i]; + newr->renderer = rendererinfo[i].ri; } } } @@ -2018,27 +2051,27 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) int count; for (i = 0, count = 0; i < countof(rendererinfo); i++) { - if (!rendererinfo[i] || !rendererinfo[i]->description) + if (!rendererinfo[i].ri || !rendererinfo[i].ri->description) continue; //not valid in this build. :( - if (rendererinfo[i]->rtype == QR_NONE || //dedicated servers are not useful - rendererinfo[i]->rtype == QR_HEADLESS || //headless appears buggy - rendererinfo[i]->rtype == QR_SOFTWARE ) //software is just TOO buggy/limited for us to care. + if (rendererinfo[i].ri->rtype == QR_NONE || //dedicated servers are not useful + rendererinfo[i].ri->rtype == QR_HEADLESS || //headless appears buggy + rendererinfo[i].ri->rtype == QR_SOFTWARE ) //software is just TOO buggy/limited for us to care. continue; count++; } count = rand()%count; for (i = 0; i < countof(rendererinfo); i++) { - if (!rendererinfo[i] || !rendererinfo[i]->description) + if (!rendererinfo[i].ri || !rendererinfo[i].ri->description) continue; //not valid in this build. :( - if (rendererinfo[i]->rtype == QR_NONE || - rendererinfo[i]->rtype == QR_HEADLESS || - rendererinfo[i]->rtype == QR_SOFTWARE ) + if (rendererinfo[i].ri->rtype == QR_NONE || + rendererinfo[i].ri->rtype == QR_HEADLESS || + rendererinfo[i].ri->rtype == QR_SOFTWARE ) continue; if (!count--) { - newr->renderer = rendererinfo[i]; - Con_Printf("randomly selected renderer: %s\n", rendererinfo[i]->description); + newr->renderer = rendererinfo[i].ri; + Con_Printf("randomly selected renderer: %s\n", rendererinfo[i].ri->description); break; } } @@ -2048,20 +2081,20 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) int bestpri = -2, pri; for (i = 0; i < countof(rendererinfo); i++) { - if (!rendererinfo[i] || !rendererinfo[i]->description) + if (!rendererinfo[i].ri || !rendererinfo[i].ri->description) continue; //not valid in this build. :( for (j = 4-1; j >= 0; j--) { - if (!rendererinfo[i]->name[j]) + if (!rendererinfo[i].ri->name[j]) continue; - if (!stricmp(rendererinfo[i]->name[j], com_token)) + if (!stricmp(rendererinfo[i].ri->name[j], com_token)) { - pri = R_PriorityForRenderer(rendererinfo[i]); + pri = R_PriorityForRenderer(rendererinfo[i].ri); if (pri > bestpri) { bestpri = pri; - newr->renderer = rendererinfo[i]; + newr->renderer = rendererinfo[i].ri; } break; //try the next renderer now. } @@ -2182,6 +2215,12 @@ void R_RestartRenderer (rendererstate_t *newr) rendererinfo_t *skip = newr->renderer; struct sortedrenderers_s sorted[countof(rendererinfo)]; + if (failed && newr->vr) + { + Con_Printf(CON_NOTICE "Trying without vr\n"); + newr->vr = NULL; + failed = !R_ApplyRenderer(newr); + } if (failed && newr->fullscreen == 1) { Con_Printf(CON_NOTICE "Trying fullscreen windowed"CON_DEFAULT"\n"); @@ -2213,7 +2252,7 @@ void R_RestartRenderer (rendererstate_t *newr) for (i = 0; i < countof(sorted); i++) { sorted[i].index = i; - sorted[i].r = rendererinfo[i]; + sorted[i].r = rendererinfo[i].ri; sorted[i].pri = R_PriorityForRenderer(sorted[i].r); } qsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers); @@ -2295,7 +2334,7 @@ void R_SetRenderer_f (void) for (i = 0; i < countof(sorted); i++) { sorted[i].index = i; - sorted[i].r = rendererinfo[i]; + sorted[i].r = rendererinfo[i].ri; sorted[i].pri = R_PriorityForRenderer(sorted[i].r); } qsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers); @@ -2337,7 +2376,7 @@ static void R_UpdateRendererOpts(void) for (i = 0; i < countof(sorted); i++) { sorted[i].index = i; - sorted[i].r = rendererinfo[i]; + sorted[i].r = rendererinfo[i].ri; sorted[i].pri = R_PriorityForRenderer(sorted[i].r); } qsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers); diff --git a/engine/client/vid.h b/engine/client/vid.h index 6b125a90..ed5c9898 100644 --- a/engine/client/vid.h +++ b/engine/client/vid.h @@ -54,6 +54,7 @@ typedef struct { char subrenderer[MAX_QPATH]; //external driver char devicename[MAX_QPATH]; //device name (usually monitor) struct rendererinfo_s *renderer; + struct plugvrfuncs_s *vr; //the vr driver we're trying to use. } rendererstate_t; #ifndef SERVERONLY extern rendererstate_t currentrendererstate; @@ -116,6 +117,8 @@ typedef struct qboolean forcecursor; float forcecursorpos[2]; //in physical pixels + + struct plugvrfuncs_s *vr; //how do deal with VR contexts. } viddef_t; extern viddef_t vid; // global video state diff --git a/engine/client/vr.h b/engine/client/vr.h new file mode 100644 index 00000000..8f804ead --- /dev/null +++ b/engine/client/vr.h @@ -0,0 +1,89 @@ +#include "merged.h" +typedef struct vrsetup_s +{ + //engine-set + size_t structsize; + enum + { + VR_HEADLESS, //not to be confused with decapitation + VR_EGL, + VR_X11_GLX, +// VR_ANDROID_EGL, + VR_WIN_WGL, + VR_VULKAN, //vulkan has no platform variation + VR_D3D11, //d3d11 only works on windows, so no platform variation + } vrplatform; //the type of renderer/args getting inited. abort if unknown. + void *userctx; //for use in callbacks. + qboolean (*createinstance)(struct vrsetup_s *, char *instanceextensions, void *result); //used by vulkan, can be null for other renderers + + //vr-set (by preinit) + struct + { + int major, minor; + } minver, maxver; + unsigned int deviceid[2]; + char *deviceextensions; + + + //engine-set (for full init) + //this stuff is intentionally at the end + union { + struct + { + void *display; + int visualid; + void *glxfbconfig; + unsigned long drawable; //really int32 + void *glxcontext; + } x11_glx; + + struct + { + void *(*getprocaddr)(const char *name); + void *egldisplay; + void *eglconfig; + void *eglcontext; + } egl; + + struct + { + void *hdc; + void *hglrc; + } wgl; + + struct + { + void *device; + } d3d; + + struct + { //sometimes pointers, sometimes ints. nasty datatypes that suck. this is hideous. +#ifndef VulkanAPIRandomness + #if defined(__LP64__) || defined(_WIN64) + #define VulkanAPIRandomness void* + #elif defined(_MSC_VER) && _MSC_VER < 1300 + #define VulkanAPIRandomness __int64 + #else + #define VulkanAPIRandomness long long + #endif +#endif + VulkanAPIRandomness instance; + VulkanAPIRandomness physicaldevice; + VulkanAPIRandomness device; + unsigned int queuefamily; + unsigned int queueindex; + } vk; + }; +} vrsetup_t; + +//interface registered by plugins for VR stuff. +typedef struct plugvrfuncs_s +{ + const char *description; + 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, vec3_t axisorg[4])); + void (*Shutdown) (void); +#define plugvrfuncs_name "VR" +} plugvrfuncs_t; diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index b8273f0c..c781b229 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -853,9 +853,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifdef FTE_TARGET_WEB #define MAX_EDICTS ((1<<15)-1) #else -//#define MAX_EDICTS ((1<<22)-1) // expandable up to 22 bits -#define MAX_EDICTS ((1<<18)-1) // expandable up to 22 bits +#define MAX_EDICTS ((1<<22)-1) // expandable up to 22 bits +//#define MAX_EDICTS ((1<<18)-1) // expandable up to 22 bits #endif + #define MAX_NET_LIGHTSTYLES (INVALID_LIGHTSTYLE+1) // 16bit. the last index MAY be used to signify an invalid lightmap in the bsp, but is still valid for rtlights. #define MAX_STANDARDLIGHTSTYLES 64 #define MAX_PRECACHE_MODELS 4096 // 14bit. diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 27e5dd9a..22ef9f3b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -739,7 +739,7 @@ static void Cmd_Exec_f (void) Con_TPrintf ("couldn't exec %s. check permissions.\n", name); return; } - if (cl_warncmd.ival || developer.ival || cvar_watched) + if (cl_warncmd.ival || developer.ival || cvar_watched || dpcompat_console.ival) { if (loc.search) Con_TPrintf ("execing ^[^7%s\\tip\\from %s/%s^]\n", name, loc.search->logicalpath, name); @@ -3964,7 +3964,7 @@ static void Cmd_WriteConfig_f(void) MasterInfo_WriteServers(); #endif - f = FS_OpenWithFriends(fname, sysname, sizeof(sysname), 4, "quake.rc", "hexen.rc", "*.cfg", "configs/*.cfg"); + f = FS_OpenWithFriends(fname, sysname, sizeof(sysname), 4, "quake.rc", "hexen.rc", "*.cfg", "configs/*.cfg", "dlcache/*.pk3*"); all = cfg_save_all.ival; } diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 50530f18..ef087f80 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1978,6 +1978,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in lerpcutoff = inf->lerpcutoff * r_lerpmuzzlehack.value; if (Sh_StencilShadowsActive() || e->fatness || lerpcutoff) { + memset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2)); mesh->xyz2_array = NULL; mesh->xyz_blendw[0] = 1; mesh->xyz_blendw[1] = 0; diff --git a/engine/common/common.c b/engine/common/common.c index 099d3a01..3697b324 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -5410,6 +5410,8 @@ static int COM_WorkerThread(void *arg) { while(COM_DoWork(group, true)) { + if (thread->request == WR_DIE) + break; if (worker_sleeptime.value) { Sys_UnlockConditional(com_workercondition[group]); @@ -5512,6 +5514,51 @@ void COM_DestroyWorkerThread(void) com_resourcemutex = NULL; } +//Dangerous: stops workers WITHOUT flushing their queue. Be SURE to 'unlock' to start them up again. +void COM_WorkerLock(void) +{ + int i; + if (!com_liveworkers[WG_LOADER]) + return; //nothing to do. + + //add a fake worker and ask workers to die + Sys_LockConditional(com_workercondition[WG_LOADER]); + com_liveworkers[WG_LOADER] += 1; + for (i = 0; i < WORKERTHREADS; i++) + com_worker[i].request = WR_DIE; //flag them all to die + Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up to check their new death values. + Sys_UnlockConditional(com_workercondition[WG_LOADER]); + + //wait for the workers to stop (leaving their work, because of our fake worker) + while(com_liveworkers[WG_LOADER]>1) + { + if (!COM_DoWork(WG_MAIN, false)) //need to check this to know they're done. + COM_DoWork(WG_LOADER, false); //might as well, while we're waiting. + } + + //remove our fake worker now... + Sys_LockConditional(com_workercondition[WG_LOADER]); + com_liveworkers[WG_LOADER] -= 1; + Sys_UnlockConditional(com_workercondition[WG_LOADER]); +} +//called after COM_WorkerLock +void COM_WorkerUnlock(void) +{ + int i; + for (i = 0; i < WORKERTHREADS; i++) + { + if (i >= worker_count.ival) + continue; //worker stays dead + + //lower thread indexes need to be (re)created + if (!com_worker[i].thread) + { + com_worker[i].request = WR_NONE; + com_worker[i].thread = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_worker[i], 0, 256*1024); + } + } +} + //fully flushes ALL pending work. void COM_WorkerFullSync(void) { @@ -5690,7 +5737,7 @@ static void QDECL COM_WorkerCount_Change(cvar_t *var, char *oldvalue) if (!*var->string) { - count = 4; + count = var->ival = 4; } //try to respond to any kill requests now, so we don't get surprised by the cvar changing too often. diff --git a/engine/common/fs.c b/engine/common/fs.c index 4bdc2b74..d436444b 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -298,7 +298,7 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm) static void FS_Manifest_Print(ftemanifest_t *man) { - char buffer[1024]; + char buffer[65536]; int i, j; if (man->updateurl) Con_Printf("updateurl %s\n", COM_QuotedString(man->updateurl, buffer, sizeof(buffer), false)); @@ -319,9 +319,45 @@ static void FS_Manifest_Print(ftemanifest_t *man) if (man->protocolname) Con_Printf("protocolname %s\n", COM_QuotedString(man->protocolname, buffer, sizeof(buffer), false)); if (man->defaultexec) - Con_Printf("defaultexec %s\n", COM_QuotedString(man->defaultexec, buffer, sizeof(buffer), false)); + { + char *s = buffer, *e; + for (s = man->defaultexec; *s; s = e) + { + e = strchr(s, '\n'); + if (e) + { + *e = 0; + Con_Printf("-%s\n", s); + *e++ = '\n'; + } + else + { + Con_Printf("-%s\n", s); + e = s+strlen(s); + } + } + //Con_Printf("defaultexec %s\n", COM_QuotedString(man->defaultexec, buffer, sizeof(buffer), false)); + } if (man->defaultoverrides) - Con_Printf("%s", man->defaultoverrides); + { + char *s = buffer, *e; + for (s = man->defaultoverrides; *s; s = e) + { + e = strchr(s, '\n'); + if (e) + { + *e = 0; + Con_Printf("+%s\n", s); + *e++ = '\n'; + } + else + { + Con_Printf("+%s\n", s); + e = s+strlen(s); + } + } + //Con_Printf("%s", man->defaultoverrides); + } if (man->rtcbroker) Con_Printf("rtcbroker %s\n", COM_QuotedString(man->rtcbroker, buffer, sizeof(buffer), false)); if (man->iconname) @@ -1376,7 +1412,7 @@ static void FS_RebuildFSHash_Update(const char *fname) } - COM_WorkerFullSync(); + COM_WorkerLock(); if (!Sys_LockMutex(fs_thread_mutex)) return; //amg! @@ -1391,6 +1427,7 @@ static void FS_RebuildFSHash_Update(const char *fname) FS_AddFileHash(depth, fname, NULL, filehandle); Sys_UnlockMutex(fs_thread_mutex); + COM_WorkerUnlock(); } void FS_FlushFSHashWritten(const char *fname) @@ -3490,8 +3527,8 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) #define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS /*NetQuake reconfiguration, to make certain people feel more at home...*/ #define NQCFG "//disablehomedir 1\n//mainconfig ftenq\ncfg_save_auto 1\n" QCFG "set sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n" -#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\n" -#define FITZCFG NQCFG "fps_preset builtin_spasm\n" +#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\ncl_sbar 2\nset gl_load24bit 1\n" +#define FITZCFG NQCFG "fps_preset builtin_spasm\ncl_sbar 2\nset gl_load24bit 1\n" #define TENEBRAECFG NQCFG "fps_preset builtin_tenebrae\n" //nehahra has to be weird with its extra cvars, and buggy fullbrights. #define NEHCFG QCFG "set nospr32 0\nset cutscene 1\nalias startmap_sp \"map nehstart\"\nr_fb_bmodels 0\nr_fb_models 0\n" @@ -6197,7 +6234,7 @@ static int QDECL Mods_AddGamedir(const char *fname, qofs_t fsize, time_t mtime, if (strchr(gamedir, ';') || !FS_GamedirIsOkay(gamedir)) { - Z_Free(gamedir); + Z_Free(desc); return true; //don't list it if we can't use it anyway } diff --git a/engine/common/mathlib.c b/engine/common/mathlib.c index dd5d7b8a..3d67a0db 100644 --- a/engine/common/mathlib.c +++ b/engine/common/mathlib.c @@ -1321,6 +1321,50 @@ void Matrix4x4_Identity(float *outm) outm[15] = 1; } +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. + + float fovy = fovu-fovd; + float fovx = fovr-fovl; + + //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[4] = 0; + proj[8] = (xmax + xmin) / (xmax - xmin); + proj[12] = 0; + + proj[1] = 0; + proj[5] = (2*neard) / (ymax - ymin); + proj[9] = (ymax + ymin) / (ymax - ymin); + proj[13] = 0; + + proj[2] = 0; + proj[6] = 0; + proj[10] = (fard*df-neard*dn)/(neard-fard); + proj[14] = ((df-dn)*fard*neard)/(neard-fard); + + proj[3] = 0; + proj[7] = 0; + proj[11] = -1; + proj[15] = 0; +} + void Matrix4x4_CM_Projection_Far(float *proj, float fovx, float fovy, float neard, float fard, qboolean d3d) { double xmin, xmax, ymin, ymax; diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 720af553..09eed62c 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -191,6 +191,7 @@ void QuaternionSlerp(const vec4_t p, vec4_t q, float t, vec4_t qt); //projection matricies of different types... gesh void Matrix4x4_CM_Orthographic (float *proj, float xmin, float xmax, float ymax, float ymin, float znear, float zfar); void Matrix4x4_CM_OrthographicD3D(float *proj, float xmin, float xmax, float ymax, float ymin, float znear, float zfar); +void Matrix4x4_CM_Projection_Offset(float *proj, float fovl, float fovr, float fovu, float fovd, float neard, float fard, qboolean d3d); void Matrix4x4_CM_Projection_Far(float *proj, float fovx, float fovy, float neard, float fard, qboolean d3d); void Matrix4x4_CM_Projection2 (float *proj, float fovx, float fovy, float neard); void Matrix4x4_CM_Projection_Inf(float *proj, float fovx, float fovy, float neard, qboolean d3d); diff --git a/engine/common/net.h b/engine/common/net.h index e21d75bf..7428c3e6 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -155,7 +155,7 @@ enum addressscope_e ASCOPE_LAN=3, ASCOPE_NET=4 }; -enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc); +enum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc); qboolean NET_AddrIsReliable(netadr_t *adr); //hints that the protocol is reliable. if so, we don't need to wait for acks qboolean NET_CompareAdr (netadr_t *a, netadr_t *b); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 0c179aef..c1a5410c 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -128,8 +128,10 @@ cvar_t tls_ignorecertificateerrors = CVARFD("tls_ignorecertificateerrors", "0", #endif #if defined(TCPCONNECT) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV)) #ifdef HAVE_SERVER -cvar_t net_enable_qizmo = CVARD("net_enable_qizmo", "1", "Enables compatibility with qizmo's tcp connections serverside. Frankly, using sv_port_tcp without this is a bit pointless."); -cvar_t net_enable_qtv = CVARD("net_enable_qtv", "1", "Listens for qtv proxies, or clients using the qtvplay command."); +cvar_t net_enable_qizmo = CVARD("net_enable_qizmo", "1", "Enables serverside support for 'connect tcp://foo' or 'connect tls://foo' (with net_enable_tls), as well as qizmo's tcp connections and compatibles."); +#endif +#ifdef MVD_RECORDING +cvar_t net_enable_qtv = CVARD("net_enable_qtv", "2", "Controls whether inbound qtv requests will be honoured (both proxies and clients using qtvplay).\n0: Don't accept qtv connections.\n1: Accept connections.\n2: Accept qtv connections only from host-scopped addresses (read: 127.*.*.*, ::1, or unix sockets)."); #endif #if defined(HAVE_SSL) cvar_t net_enable_tls = CVARD("net_enable_tls", "0", "If enabled, binary data sent to a non-tls tcp port will be interpretted as a tls handshake (enabling https or wss over the same tcp port."); @@ -5304,12 +5306,20 @@ static enum{ //for QTV connections, we just need the method and a blank line. our qtv parser will parse the actual headers. if (!Q_strncasecmp(st->inbuffer, "QTV", 3)) { //FIXME: make sure its removed from epoll and not killed prematurely - int r = net_enable_qtv.ival?SV_MVD_GotQTVRequest(st->clientstream, st->inbuffer, st->inbuffer+st->inlen, &st->qtvstate):-1; + int r = -2; + const char *desc; + if (net_enable_qtv.ival == 2 && NET_ClassifyAddress(&st->remoteaddr, &desc) > ASCOPE_HOST) + ; + else if (net_enable_qtv.ival) + r = SV_MVD_GotQTVRequest(st->clientstream, st->inbuffer, st->inbuffer+st->inlen, &st->qtvstate); i = st->inlen; memmove(st->inbuffer, st->inbuffer+i, st->inlen - (i)); st->inlen -= i; switch(r) { + case -2: + VFS_PUTS(st->clientstream, "QTVSV 1\n" "PERROR: net_enable_qtv is disabled on this server\n\n"); + return FTETCP_KILL; case -1: //error return FTETCP_KILL; case 0: //retry @@ -7720,7 +7730,7 @@ int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_gener return found; } -static enum addressscope_e NET_ClassifyAddressipv4(int ip, char **outdesc) +static enum addressscope_e NET_ClassifyAddressipv4(int ip, const char **outdesc) { int scope = ASCOPE_NET; char *desc = NULL; @@ -7742,10 +7752,10 @@ static enum addressscope_e NET_ClassifyAddressipv4(int ip, char **outdesc) *outdesc = desc; return scope; } -enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc) +enum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc) { int scope = ASCOPE_NET; - char *desc = NULL; + const char *desc = NULL; if (adr->type == NA_LOOPBACK) { @@ -7773,6 +7783,12 @@ enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc) desc = "vp-mapped"; } } +#ifdef UNIXSOCKETS + else if (adr->type == NA_UNIX) + { + scope = ASCOPE_HOST, desc = "unix"; + } +#endif else if (adr->type == NA_IP) scope = NET_ClassifyAddressipv4(*(int*)adr->address.ip, &desc); if (outdesc) @@ -7792,7 +7808,7 @@ void NET_PrintAddresses(ftenet_connections_t *collection) const char *params[sizeof(addr)/sizeof(addr[0])]; qboolean warn = true; static const char *scopes[] = {"process", "local", "link", "lan", "net"}; - char *desc; + const char *desc; if (!collection) return; @@ -8432,6 +8448,8 @@ void NET_Init (void) #if defined(TCPCONNECT) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV)) #ifdef HAVE_SERVER Cvar_Register(&net_enable_qizmo, "networking"); +#endif +#ifdef MVD_RECORDING Cvar_Register(&net_enable_qtv, "networking"); #endif #if defined(HAVE_SSL) diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 9ee47084..646f0c16 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -4,6 +4,7 @@ #include "quakedef.h" #include "fs.h" +#include "vr.h" #define FTEENGINE #include "../plugins/plugin.h" @@ -390,28 +391,21 @@ static qboolean QDECL PlugBI_GetPluginName(int plugnum, char *outname, size_t na static qboolean QDECL PlugBI_ExportInterface(const char *name, void *interfaceptr, size_t structsize) { - if (0) - ; - /* - else if (!strncmp(name, "VID_DisplayDriver")) //a video driver, loaded by name as given by vid_renderer - { - FS_RegisterModuleDriver(, func); - currentplug->blockcloses++; - } - */ #if defined(PLUGINS) && !defined(SERVERONLY) #ifdef HAVE_MEDIA_DECODER - else if (!strcmp(name, "Media_VideoDecoder")) - Media_RegisterDecoder(currentplug, interfaceptr); + if (!strcmp(name, "Media_VideoDecoder")) + return Media_RegisterDecoder(currentplug, interfaceptr); #endif #ifdef HAVE_MEDIA_ENCODER - else if (!strcmp(name, "Media_VideoEncoder")) - Media_RegisterEncoder(currentplug, interfaceptr); + if (!strcmp(name, "Media_VideoEncoder")) + return Media_RegisterEncoder(currentplug, interfaceptr); #endif #endif - else - return 0; - return 1; +#ifdef HAVE_CLIENT + if (!strcmp(name, plugvrfuncs_name)) + return R_RegisterVRDriver(currentplug, interfaceptr); +#endif + return false; } static cvar_t *QDECL Plug_Cvar_GetNVFDG(const char *name, const char *defaultvalue, unsigned int flags, const char *description, const char *groupname) @@ -475,6 +469,11 @@ static qboolean QDECL Plug_Cvar_Update(qhandle_t handle, int *modificationcount, return false; } +static void QDECL Plug_Cmd_TokenizeString(const char *text) +{ + Cmd_TokenizeString(text, false, false); +} + //void Cmd_Args(char *buffer, int buffersize) static void QDECL Plug_Cmd_Args(char *buffer, int maxsize) { @@ -1778,6 +1777,9 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s Plug_Con_Print, Plug_Sys_Error, Plug_Sys_Milliseconds, + Sys_LoadLibrary, + Sys_GetAddressForName, + Sys_CloseLibrary, }; if (structsize == sizeof(funcs)) return &funcs; @@ -1787,7 +1789,7 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s static plugcmdfuncs_t funcs = { Plug_Cmd_AddCommand, - NULL,//Plug_Cmd_TokenizeString, + Plug_Cmd_TokenizeString, Plug_Cmd_Args, Plug_Cmd_Argv, Plug_Cmd_Argc, diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 91c2b24d..49c84f89 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -36,6 +36,9 @@ cvar_t pr_gc_threaded = CVARD("pr_gc_threaded", "0", "Says whether to use a sepa cvar_t pr_sourcedir = CVARD("pr_sourcedir", "src", "Subdirectory where your qc source is located. Used by the internal compiler and qc debugging functionality."); cvar_t pr_enable_uriget = CVARD("pr_enable_uriget", "1", "Allows gamecode to make direct http requests"); cvar_t pr_enable_profiling = CVARD("pr_enable_profiling", "0", "Enables profiling support. Will run more slowly. Change the map and then use the profile_ssqc/profile_csqc commands to see the results."); +#ifdef HAVE_CLIENT +cvar_t pr_precachepic_slow = CVARD("pr_precachepic_slow", "0", "Legacy setting. Should be set to 0 where supported."); +#endif int tokenizeqc(const char *str, qboolean dpfuckage); void PF_buf_shutdown(pubprogfuncs_t *prinst); @@ -98,6 +101,9 @@ void PF_Common_RegisterCvars(void) Cvar_Register (&pr_fixbrokenqccarrays, cvargroup_progs); Cvar_Register (&utf8_enable, cvargroup_progs); Cvar_Register (&pr_autocreatecvars, cvargroup_progs); +#ifdef HAVE_CLIENT + Cvar_Register (&pr_precachepic_slow, cvargroup_progs); +#endif #ifdef RAGDOLL Cmd_AddCommand("skel_info", skel_info_f); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index d202fbaa..d7a00552 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -83,7 +83,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PEXT2_NEWSIZEENCODING 0x00000040 //richer size encoding. #define PEXT2_INFOBLOBS 0x00000080 //serverinfo+userinfo lengths can be MUCH higher (protocol is unbounded, but expect low sanity limits on userinfo), and contain nulls etc. #define PEXT2_STUNAWARE 0x00000100 //changes the netchan to biased-bigendian (so lead two bits are 1 and not stun's 0, so we don't get confused) -//#define PEXT2_NEWINTENTS 0x00000200 //clc_move changes, more buttons etc +//#define PEXT2_NEWINTENTS 0x00000200 //clc_move changes, more buttons etc. vr stuff! #define PEXT2_CLIENTSUPPORT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS|PEXT2_STUNAWARE) //EzQuake/Mvdsv extensions. (use ezquake name, to avoid confusion about .mvd format and its protocol differences) diff --git a/engine/common/sys.h b/engine/common/sys.h index e922ed02..5e52e50a 100644 --- a/engine/common/sys.h +++ b/engine/common/sys.h @@ -53,7 +53,7 @@ void Sys_Quit (void); void Sys_RecentServer(char *command, char *target, char *title, char *desc); qboolean Sys_RunInstaller(void); -typedef struct { +typedef struct dllfunction_s { void **funcptr; char *name; } dllfunction_t; diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 692df29a..5b17e79a 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -6,6 +6,7 @@ #include "shader.h" #include "renderque.h" #include "resource.h" +#include "vr.h" #define FUCKDXGI @@ -981,6 +982,7 @@ static void initD3D11(HWND hWnd, rendererstate_t *info) static IID factiid = {0x770aae78, 0xf26f, 0x4dba, {0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87}}; IDXGIFactory1 *fact = NULL; IDXGIAdapter *adapt = NULL; + vrsetup_t vrsetup = {sizeof(vrsetup)}; dllfunction_t d3d11funcs[] = { {(void**)&fnc, "D3D11CreateDeviceAndSwapChain"}, @@ -1000,6 +1002,7 @@ static void initD3D11(HWND hWnd, rendererstate_t *info) if (!d3d11mod) return; + vrsetup.vrplatform = VR_D3D11; if (pCreateDXGIFactory1) { HRESULT hr; @@ -1008,9 +1011,34 @@ static void initD3D11(HWND hWnd, rendererstate_t *info) Con_Printf("CreateDXGIFactory1 failed: %s\n", D3D_NameForResult(hr)); if (fact) { - IDXGIFactory1_EnumAdapters(fact, 0, &adapt); + if (info->vr) + { + if (!info->vr->Prepare(&vrsetup)) + { + info->vr->Shutdown(); + info->vr = NULL; + } + else + { + int id = 0; + while (S_OK==IDXGIFactory1_EnumAdapters(fact, id++, &adapt)) + { + DXGI_ADAPTER_DESC desc; + IDXGIAdapter_GetDesc(adapt, &desc); + if (desc.AdapterLuid.LowPart == vrsetup.deviceid[0] && desc.AdapterLuid.HighPart == vrsetup.deviceid[1]) + break; + IDXGIAdapter_Release(adapt); + adapt = NULL; + } + } + } + + if (!adapt) + IDXGIFactory1_EnumAdapters(fact, 0, &adapt); } } + else + info->vr = NULL; //o.O initD3D11Device(hWnd, info, fnc, adapt); diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 40559b54..25c43f3d 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -5753,7 +5753,7 @@ int GLBE_BeginRenderBuffer_DepthOnly(texid_t depthtexture) //state->colour is created if usedepth is set and it doesn't previously exist int GLBE_FBO_Update(fbostate_t *state, unsigned int enables, texid_t *destcol, int mrt, texid_t destdepth, int width, int height, int layer) { - GLenum allcolourattachments[] ={GL_COLOR_ATTACHMENT0_EXT,GL_COLOR_ATTACHMENT1_EXT,GL_COLOR_ATTACHMENT2_EXT,GL_COLOR_ATTACHMENT3_EXT, + static GLenum allcolourattachments[] ={GL_COLOR_ATTACHMENT0_EXT,GL_COLOR_ATTACHMENT1_EXT,GL_COLOR_ATTACHMENT2_EXT,GL_COLOR_ATTACHMENT3_EXT, GL_COLOR_ATTACHMENT4_EXT,GL_COLOR_ATTACHMENT5_EXT,GL_COLOR_ATTACHMENT6_EXT,GL_COLOR_ATTACHMENT7_EXT}; int i; int old; diff --git a/engine/gl/gl_bloom.c b/engine/gl/gl_bloom.c index 6b644dd3..2d1f52bb 100644 --- a/engine/gl/gl_bloom.c +++ b/engine/gl/gl_bloom.c @@ -220,7 +220,7 @@ void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h) R2D_Flush(); #if 1 /*filter the screen into a downscaled image*/ - VKBE_RT_Gen(&vk_rt_filter, texwidth[0], texheight[0], false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&vk_rt_filter, NULL, texwidth[0], texheight[0], false, RT_IMAGEFLAGS); VKBE_RT_Begin(&vk_rt_filter); vk.sourcecolour = source; R2D_ScalePic(0, 0, vid.width, vid.height, bloomfilter); @@ -251,7 +251,7 @@ void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h) r_worldentity.glowmod[1] = 0; - VKBE_RT_Gen(&vk_rt_bloom[1][i], texwidth[i], texheight[i], false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&vk_rt_bloom[1][i], NULL, texwidth[i], texheight[i], false, RT_IMAGEFLAGS); VKBE_RT_Begin(&vk_rt_bloom[1][i]); vk.sourcecolour = intex; BE_SelectEntity(&r_worldentity); @@ -261,7 +261,7 @@ void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h) r_worldentity.glowmod[0] = 0; r_worldentity.glowmod[1] = 1.0 / texheight[i]; - VKBE_RT_Gen(&vk_rt_bloom[0][i], texwidth[i], texheight[i], false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&vk_rt_bloom[0][i], NULL, texwidth[i], texheight[i], false, RT_IMAGEFLAGS); VKBE_RT_Begin(&vk_rt_bloom[0][i]); vk.sourcecolour = &vk_rt_bloom[1][i].q_colour; BE_SelectEntity(&r_worldentity); @@ -286,10 +286,10 @@ void VK_R_BloomShutdown(void) int i; for (i = 0; i < MAXLEVELS; i++) { - VKBE_RT_Gen(&vk_rt_bloom[0][i], 0, 0, false, RT_IMAGEFLAGS); - VKBE_RT_Gen(&vk_rt_bloom[1][i], 0, 0, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&vk_rt_bloom[0][i], NULL, 0, 0, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&vk_rt_bloom[1][i], NULL, 0, 0, false, RT_IMAGEFLAGS); } - VKBE_RT_Gen(&vk_rt_filter, 0, 0, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&vk_rt_filter, NULL, 0, 0, false, RT_IMAGEFLAGS); R_InitBloomTextures(); } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 412c3bdd..ced8245a 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -4323,7 +4323,7 @@ qboolean Heightmap_EdictInFatPVS (model_t *mod, const struct pvscache_s *edict, const hmpvs_t *hmpvs = (const hmpvs_t*)pvsdata; const hmpvsent_t *hmed = (const hmpvsent_t*)edict; - if (!hm->culldistance) + if (!hm->culldistance || !hmpvs) return true; //check distance @@ -4940,7 +4940,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g return; case ter_ent_set: { - int idx = G_INT(OFS_PARM1); + unsigned int idx = G_INT(OFS_PARM1); int id; const char *newvals; if (idx >= MAX_EDICTS) //we need some sanity limit... many ents will get removed like lights so this one isn't quite correct, but it'll be in the right sort of ballpark. @@ -7641,20 +7641,33 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) else if (inbrush) { //parse a plane - //Quake: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname soffset toffset rotation sscale tscale - //hexen2:( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname soffset toffset rotation sscale tscale utterlypointless - //Valve: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale - //fte : ( px py pz pd ) texname [sx sy sz sd] [tx ty tz td] 0 1 1 - //q3 : ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname common/caulk rotation sscale tscale detailcontents unused unused - //q3bp : brushDef { ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) ( ( x y o ) ( x y o ) ) texname detailcontents unused unused } //generate tangent+bitangent from the normal to generate base texcoords, then transform by the given 2*3 matrix. I prefer valve's way - it rotates more cleanly. - //doom3: brushDef3 { ( px py pz pd ) ( ( x y z ) ( x y z ) ) texname detailcontents unused unused } + //Quake: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname soffset toffset rotation sscale tscale + //Hexen2: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname soffset toffset rotation sscale tscale surfvalue + //Valve: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale + //FTE : ( px py pz pd ) texname [x y z d] [x y z d] rotation sscale tscale + //Quake2: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname soffset toffset rotation sscale tscale contents surfflags surfvalue + //Quake3: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname soffset toffset rotation sscale tscale contents surfflags surfvalue + //Q3 BP: brushDef { ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) ( ( x y o ) ( x y o ) ) texname contents surfflags surfvalue } //generate tangent+bitangent from the normal to generate base texcoords, then transform by the given 2*3 matrix. I prefer valve's way - it rotates more cleanly. + //Doom3: brushDef3 { ( px py pz pd ) ( ( x y o ) ( x y o ) ) texname contents surfflags surfvalue } + //hexen2's extra surfvalue is completely unused, and should normally be -1 + //q3 ignores all contents except detail, as well surfaceflags and surfacevalue + //220 ignores rotation, provided only for UI info, scale is still used + + //we don't care whether the input is planes or points. + //if we get a [ instead of an soffset then its + brushtex_t *bt; vec3_t d1,d2; vec3_t points[3]; vec4_t texplane[2]; float scale[2], rot; int p; - qboolean hlstyle = false; + enum + { + TEXTYPE_AXIAL, //urgh + TEXTYPE_PLANES, + TEXTYPE_BP, //weird 2d planes + } textype = TEXTYPE_AXIAL; memset(points, 0, sizeof(points)); if (patch_tex) { @@ -7695,6 +7708,40 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) return false; } + if (token[0] == '(') + { + textype = TEXTYPE_BP; + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (token[0] == '(') + { + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][0] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][1] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][3] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (token[0] != ')') + return false; + } + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (token[0] == '(') + { + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][0] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][1] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][3] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (token[0] != ')') + return false; + } + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (token[0] != ')') + return false; + } + bt = Terr_Brush_FindTexture(subhm, token); if (*token == '*') { @@ -7716,53 +7763,57 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) else brushcontents = FTECONTENTS_SOLID; - //halflife/valve220 format has the entire [x y z dist] plane specified. - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - if (*token == '[') + if (textype != TEXTYPE_BP) { - hlstyle = true; + //halflife/valve220 format has the entire [x y z dist] plane specified. + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (*token == '[') + { + textype = TEXTYPE_PLANES; + + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][0] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][1] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][2] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[0][3] = atof(token); + + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + //] + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + //[ + + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][0] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][1] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][2] = atof(token); + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][3] = atof(token); + + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + //] + } + else + { //vanilla quake + VectorClear(texplane[0]); + VectorClear(texplane[1]); + texplane[0][3] = atof(token); //aka soffset + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + texplane[1][3] = atof(token); //aka toffset + } entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[0][0] = atof(token); + rot = atof(token); entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[0][1] = atof(token); + scale[0] = atof(token); entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[0][2] = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[0][3] = atof(token); - - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - //] - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - //[ - - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[1][0] = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[1][1] = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[1][2] = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[1][3] = atof(token); - - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - //] + scale[1] = atof(token); } - else - { //vanilla quake - VectorClear(texplane[0]); - VectorClear(texplane[1]); - texplane[0][3] = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - texplane[1][3] = atof(token); - } - - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - rot = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - scale[0] = atof(token); - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - scale[1] = atof(token); + else rot = 0; //hexen2 has some extra junk that is useless - some 'light' value, but its never used and should normally be -1. //quake2/3 on the other hand has 3 different args. Contents SurfaceFlags SurfaceValue. @@ -7821,9 +7872,31 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) } faces[numplanes].tex = bt; - //quake's .maps use the normal to decide which texture directions to use in some lame axially-aligned way. - if (!hlstyle) + if (textype == TEXTYPE_BP) { + float *norm = planes[numplanes]; + float RotY = -atan2(norm[2], sqrt(norm[1]*norm[1] + norm[0]*norm[0])); + float RotZ = atan2(norm[1], norm[0]); + vec3_t tx = {-sin(RotZ), cos(RotZ), 0}; //tangent + vec3_t ty = {-sin(RotY)*cos(RotZ), -sin(RotY)*sin(RotZ), -cos(RotY)}; //bitangent + vec2_t tms = {texplane[0][0],texplane[0][1]}, tmt = {texplane[1][0],texplane[1][1]}; //bah, locals reuse suck + texplane[0][0] = (tx[0] * tms[0]) + (ty[0] * tms[1]); //multiply out some matricies + texplane[0][1] = (tx[1] * tms[0]) + (ty[1] * tms[1]); + texplane[0][2] = (tx[2] * tms[0]) + (ty[2] * tms[1]); + texplane[1][0] = (tx[0] * tmt[0]) + (ty[0] * tmt[1]); + texplane[1][1] = (tx[1] * tmt[0]) + (ty[1] * tmt[1]); + texplane[1][2] = (tx[2] * tmt[0]) + (ty[2] * tmt[1]); + + //scale is part of the matrix. + scale[0] = 1; + scale[1] = 1; + + //FIXME: these faces should NOT be scaled by the texture's size! + } + else if (textype == TEXTYPE_PLANES) + ;//texture planes were properly loaded above (the scaling below is still needed though). + else if (textype == TEXTYPE_AXIAL) + { //quake's .maps use the normal to decide which texture directions to use in some lame axially-aligned way. float a=fabs(planes[numplanes][0]),b=fabs(planes[numplanes][1]),c=fabs(planes[numplanes][2]); if (a>=b&&a>=c) texplane[0][1] = 1; @@ -7833,22 +7906,22 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) texplane[1][1] = -1; else texplane[1][2] = -1; - } - if (rot) - { - int mas, mat; - float s,t; - float a = rot*(M_PI/180); - float cosa = cos(a), sina=sin(a); - for (mas=0; mas<2&&!texplane[0][mas]; mas++); - for (mat=0; mat<2&&!texplane[1][mat]; mat++); - for (i = 0; i < 2; i++) + if (rot) { - s = cosa*texplane[i][mas] - sina*texplane[i][mat]; - t = sina*texplane[i][mas] + cosa*texplane[i][mat]; - texplane[i][mas] = s; - texplane[i][mat] = t; + int mas, mat; + float s,t; + float a = rot*(M_PI/180); + float cosa = cos(a), sina=sin(a); + for (mas=0; mas<2&&!texplane[0][mas]; mas++); + for (mat=0; mat<2&&!texplane[1][mat]; mat++); + for (i = 0; i < 2; i++) + { + s = cosa*texplane[i][mas] - sina*texplane[i][mat]; + t = sina*texplane[i][mas] + cosa*texplane[i][mat]; + texplane[i][mas] = s; + texplane[i][mat] = t; + } } } diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 1ef5b1ee..2a423c9f 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -1316,13 +1316,9 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) if (mod->loadstate == MLS_NOTLOADED && *mod->name != '*') { mod->loadstate = MLS_LOADING; -// if (verbose == MLV_ERROR) //if its fatal on failure (ie: world), do it on the main thread and block to wait for it. -// Mod_LoadModelWorker(mod, MLV_WARN, 0); -// else -// if (verbose == MLV_ERROR || verbose == MLV_WARNSYNC) -// Mod_LoadModelWorker(mod, NULL, verbose, 0); -// COM_AddWork(WG_MAIN, Mod_LoadModelWorker, mod, NULL, verbose, 0); -// else + if (verbose == MLV_ERROR || verbose == MLV_WARNSYNC) + COM_InsertWork(WG_LOADER, Mod_LoadModelWorker, mod, NULL, verbose, 0); + else COM_AddWork(WG_LOADER, Mod_LoadModelWorker, mod, NULL, verbose, 0); //block until its loaded, if we care. diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 0ac8d989..fbf44832 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -2283,6 +2283,7 @@ void R_EditLights_RegisterCommands(void) Cvar_Register (&r_editlights_import_ambient, "Realtime Light editing/importing"); Cvar_Register (&r_editlights_import_diffuse, "Realtime Light editing/importing"); Cvar_Register (&r_editlights_import_specular, "Realtime Light editing/importing"); + Cvar_Register (&r_shadow_realtime_world_importlightentitiesfrommap, "Realtime Light editing/importing"); Cvar_Register (&r_editlights, "Realtime Light editing/importing"); Cvar_Register (&r_editlights_cursordistance, "Realtime Light editing/importing"); diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index c187dfe5..40e6498b 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "glquake.h" #include "renderque.h" #include "shader.h" +#include "vr.h" void R_RenderBrushPoly (msurface_t *fa); @@ -84,27 +85,28 @@ cvar_t r_xflip = CVAR("leftisright", "0"); extern cvar_t scr_fov; -shader_t *scenepp_rescaled; -shader_t *scenepp_antialias; -shader_t *scenepp_waterwarp; -shader_t *scenepp_gamma; +static shader_t *scenepp_rescaled; +static shader_t *scenepp_antialias; +static shader_t *scenepp_waterwarp; +static shader_t *scenepp_gamma; // post processing stuff -texid_t sceneblur_texture; -texid_t scenepp_texture_warp; -texid_t scenepp_texture_edge; +static texid_t sceneblur_texture; +static texid_t scenepp_texture_warp; +static texid_t scenepp_texture_edge; texid_t scenepp_postproc_cube; -int scenepp_postproc_cube_size; +static int scenepp_postproc_cube_size; -fbostate_t fbo_gameview; -fbostate_t fbo_postproc; -fbostate_t fbo_postproc_cube; +static fbostate_t fbo_vr; +static fbostate_t fbo_gameview; +static fbostate_t fbo_postproc; +static fbostate_t fbo_postproc_cube; // KrimZon - init post processing - called in GL_CheckExtensions, when they're called // I put it here so that only this file need be changed when messing with the post // processing shaders -void GL_InitSceneProcessingShaders_WaterWarp (void) +static void GL_InitSceneProcessingShaders_WaterWarp (void) { scenepp_waterwarp = NULL; if (gl_config.arb_shader_objects) @@ -130,6 +132,7 @@ void GL_InitSceneProcessingShaders_WaterWarp (void) void GL_ShutdownPostProcessing(void) { + GLBE_FBO_Destroy(&fbo_vr); GLBE_FBO_Destroy(&fbo_gameview); GLBE_FBO_Destroy(&fbo_postproc); GLBE_FBO_Destroy(&fbo_postproc_cube); @@ -427,56 +430,71 @@ void R_RotateForEntity (float *m, float *modelview, const entity_t *e, const mod //================================================================================== -qboolean R_GameRectIsFullscreen(void); /* ============= R_SetupGL ============= */ -void R_SetupGL (float stereooffset, int i) +static void R_SetupGL (vec3_t axisorigin[4], vec4_t fovoverrides, texid_t fbo) { int x, x2, y2, y, w, h; vec3_t newa; - float fov_x, fov_y; + float fov_x, fov_y, fov_l, fov_r, fov_d, fov_u; float fovv_x, fovv_y; + TRACE(("dbg: calling R_SetupGL\n")); + if (!r_refdef.recurse) { newa[0] = r_refdef.viewangles[0]; newa[1] = r_refdef.viewangles[1]; newa[2] = r_refdef.viewangles[2] + gl_screenangle.value; - if (r_refdef.stereomethod) - newa[1] += r_stereo_convergence.value * (i?0.5:-0.5); //can we get away with this cheapness? rip 6dof - if (0) + if (axisorigin) { vec3_t paxis[3]; AngleVectors (newa, paxis[0], paxis[1], paxis[2]); //R_ConcatRotations(r_refdef.headaxis, paxis, vpn); - VectorMA(vec3_origin, r_refdef.headaxis[0][0], paxis[0], vpn); - VectorMA(vpn, r_refdef.headaxis[0][1], paxis[1], vpn); - VectorMA(vpn, r_refdef.headaxis[0][2], paxis[2], vpn); + VectorMA(vec3_origin, axisorigin[0][0], paxis[0], vpn); + VectorMA(vpn, axisorigin[0][1], paxis[1], vpn); + VectorMA(vpn, axisorigin[0][2], paxis[2], vpn); - VectorMA(vec3_origin, r_refdef.headaxis[1][0], paxis[0], vright); - VectorMA(vright, r_refdef.headaxis[1][1], paxis[1], vright); - VectorMA(vright, r_refdef.headaxis[1][2], paxis[2], vright); + VectorMA(vec3_origin, axisorigin[1][0], paxis[0], vright); + VectorMA(vright, axisorigin[1][1], paxis[1], vright); + VectorMA(vright, axisorigin[1][2], paxis[2], vright); - VectorMA(vec3_origin, r_refdef.headaxis[2][0], paxis[0], vup); - VectorMA(vup, r_refdef.headaxis[2][1], paxis[1], vup); - VectorMA(vup, r_refdef.headaxis[2][2], paxis[2], vup); + VectorMA(vec3_origin, axisorigin[2][0], paxis[0], vup); + VectorMA(vup, axisorigin[2][1], paxis[1], vup); + VectorMA(vup, axisorigin[2][2], paxis[2], vup); + + + VectorMA(r_refdef.vieworg, axisorigin[3][0], vpn, r_origin); + VectorMA(r_origin, axisorigin[3][1], vright, r_origin); + VectorMA(r_origin, axisorigin[3][2], vup, r_origin); } else + { AngleVectors (newa, vpn, vright, vup); + VectorCopy(r_refdef.vieworg, r_origin); + } - VectorMA(r_refdef.vieworg, stereooffset, vright, r_origin); - VectorAdd(r_origin, r_refdef.eyeoffset, r_origin); + VectorAdd(r_origin, r_refdef.eyeoffset, r_origin); //used for vr screenshots // // set up viewpoint // - if (r_refdef.flags & (RDF_ALLPOSTPROC|RDF_RENDERSCALE)) + if (fbo) + { + //with VR fbo postprocessing, we disable all viewport. + r_refdef.pxrect.x = 0; + r_refdef.pxrect.y = 0; + r_refdef.pxrect.width = fbo->width; + r_refdef.pxrect.height = fbo->height; + r_refdef.pxrect.maxheight = fbo->height; + } + else if (r_refdef.flags & (RDF_ALLPOSTPROC|RDF_RENDERSCALE)) { //with fbo postprocessing, we disable all viewport. r_refdef.pxrect.x = 0; @@ -523,7 +541,7 @@ void R_SetupGL (float stereooffset, int i) w = x2 - x; h = y2 - y; - if (r_refdef.stereomethod == STEREO_CROSSEYED +/* if (r_refdef.stereomethod == STEREO_CROSSEYED #ifdef FTE_TARGET_WEB || r_refdef.stereomethod == STEREO_WEBVR #endif @@ -533,32 +551,54 @@ void R_SetupGL (float stereooffset, int i) if (i) x += vid.fbpwidth/2; } - +*/ r_refdef.pxrect.x = x; r_refdef.pxrect.y = y; r_refdef.pxrect.width = w; r_refdef.pxrect.height = h; r_refdef.pxrect.maxheight = vid.fbpheight; } - fov_x = r_refdef.fov_x; - fov_y = r_refdef.fov_y; - fovv_x = r_refdef.fovv_x; - fovv_y = r_refdef.fovv_y; - if ((*r_refdef.rt_destcolour[0].texname || *r_refdef.rt_depth.texname) && strcmp(r_refdef.rt_destcolour[0].texname, "megascreeny")) + if (fovoverrides) { - r_refdef.pxrect.y = r_refdef.pxrect.maxheight - (r_refdef.pxrect.height+r_refdef.pxrect.y); - fov_y *= -1; - fovv_y *= -1; - r_refdef.flipcull ^= SHADER_CULL_FLIP; + 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 if ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP)) + else { - 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); + fov_x = r_refdef.fov_x; + fov_y = r_refdef.fov_y; + fovv_x = r_refdef.fovv_x; + fovv_y = r_refdef.fovv_y; - 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 ((*r_refdef.rt_destcolour[0].texname || *r_refdef.rt_depth.texname) && strcmp(r_refdef.rt_destcolour[0].texname, "megascreeny")) + { + r_refdef.pxrect.y = r_refdef.pxrect.maxheight - (r_refdef.pxrect.height+r_refdef.pxrect.y); + fov_y *= -1; + fovv_y *= -1; + r_refdef.flipcull ^= SHADER_CULL_FLIP; + } + else 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); + } + fov_l = -fov_x / 2; + fov_r = fov_x / 2; + fov_d = -fov_y / 2; + fov_u = fov_y / 2; } GL_ViewportUpdate(); @@ -591,12 +631,12 @@ void R_SetupGL (float stereooffset, int i) // 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_Far(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist, false); + 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_std, fovv_x, fovv_y, r_refdef.mindist, false); Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_view, fovv_x, fovv_y, r_refdef.mindist, false); } @@ -672,14 +712,70 @@ R_RenderScene r_refdef must be set before the first call ================ */ -void R_RenderScene (void) +static void R_RenderScene_Internal(void) +{ + int tmpvisents = cl_numvisedicts; + TRACE(("dbg: calling R_SetFrustrum\n")); + if (!r_refdef.recurse) + R_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view); + + RQ_BeginFrame(); + + TRACE(("dbg: calling Surf_DrawWorld\n")); + Surf_DrawWorld (); // adds static entities to the list + + S_ExtraUpdate (); // don't let sound get messed up if going slow + +// R_DrawDecals(); + + TRACE(("dbg: calling R_RenderDlights\n")); + R_RenderDlights (); + + if (r_refdef.recurse) + RQ_RenderBatch(); + else + RQ_RenderBatchClear(); + + cl_numvisedicts = tmpvisents; +} +static void R_RenderEyeScene (texid_t rendertarget, vec4_t fovoverride, vec3_t axisorigin[4]) +{ + refdef_t refdef = r_refdef; + int pw = vid.fbpwidth; + int ph = vid.fbpheight; + int r = 0; + + if (rendertarget) + { + r = GLBE_FBO_Update(&fbo_vr, FBO_RB_DEPTH, &rendertarget, 1, r_nulltex, rendertarget->width, rendertarget->height, 0); + GL_ForceDepthWritable(); + qglClear (GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); + vid.fbpwidth = rendertarget->width; + vid.fbpheight = rendertarget->height; + } + + R_SetupGL (axisorigin, fovoverride, rendertarget); + R_RenderScene_Internal(); + + if (rendertarget) + { + GLBE_FBO_Pop(r); + if (gl_finish.ival) + qglFinish(); + } + + r_refdef = refdef; + vid.fbpwidth = pw; + vid.fbpheight = ph; +} +static void R_RenderScene (void) { float stereooffset[2]; int stereoframes = 1; int stereomode; int i; - int tmpvisents = cl_numvisedicts; /*world rendering is allowed to add additional ents, but we don't want to keep them for recursive views*/ int cull = r_refdef.flipcull; + vec3_t axisorg[4], ang; stereomode = r_refdef.stereomethod; if (stereomode == STEREO_QUAD) @@ -706,7 +802,19 @@ void R_RenderScene (void) stereoframes = 2; } - for (i = 0; i < stereoframes; i++) + r_framecount++; + if (vid.vr && !r_refdef.recurse && vid.vr->Render(R_RenderEyeScene)) + ; //we drew something VR-ey + else if (stereomode == STEREO_OFF) + { + GL_ForceDepthWritable(); + qglClear (GL_DEPTH_BUFFER_BIT); + r_framecount++; + + R_SetupGL (NULL, NULL, NULL); + R_RenderScene_Internal(); + } + else for (i = 0; i < stereoframes; i++) { switch (stereomode) { @@ -765,31 +873,15 @@ void R_RenderScene (void) r_framecount++; } - TRACE(("dbg: calling R_SetupGL\n")); - R_SetupGL (stereooffset[i], i); - - TRACE(("dbg: calling R_SetFrustrum\n")); - if (!r_refdef.recurse) - R_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view); - - RQ_BeginFrame(); - - TRACE(("dbg: calling Surf_DrawWorld\n")); - Surf_DrawWorld (); // adds static entities to the list - - S_ExtraUpdate (); // don't let sound get messed up if going slow - - // R_DrawDecals(); - - TRACE(("dbg: calling R_RenderDlights\n")); - R_RenderDlights (); - - if (r_refdef.recurse) - RQ_RenderBatch(); - else - RQ_RenderBatchClear(); - - cl_numvisedicts = tmpvisents; + ang[0] = 0; + ang[1] = r_stereo_convergence.value * (i?0.5:-0.5); + ang[2] = 0;; + AngleVectors(ang, axisorg[0], axisorg[1], axisorg[2]); + axisorg[3][0] = 0; + axisorg[3][1] = stereooffset[i]; + axisorg[3][2] = 0; + R_SetupGL (axisorg, NULL, NULL); + R_RenderScene_Internal (); } switch (stereomode) @@ -1354,11 +1446,6 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], R_Clear ============= */ -qboolean R_GameRectIsFullscreen(void) -{ - return r_refdef.grect.x == 0 && r_refdef.grect.y == 0 && (unsigned)r_refdef.grect.width == vid.fbvwidth && (unsigned)r_refdef.grect.height == vid.fbvheight; -} - int gldepthfunc = GL_LEQUAL; qboolean depthcleared; void R_Clear (qboolean fbo) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 79a29fbc..4876b857 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -6956,7 +6956,7 @@ void Shader_Default2D(parsestate_t *ps, const char *shortname, const void *genar "sort additive\n" "}\n" ); - TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); + TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP|IF_HIGHPRIORITY)); } else { @@ -6973,7 +6973,7 @@ void Shader_Default2D(parsestate_t *ps, const char *shortname, const void *genar "sort additive\n" "}\n" ); - TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); + TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP|IF_HIGHPRIORITY)); } } void Shader_PolygonShader(struct shaderparsestate_s *ps, const char *shortname, const void *args) diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 5d5c3b69..92b99606 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -57,6 +57,7 @@ cvar_t r_shadow_scissor = CVARD("r_shadow_scissor", "1", "constrains stencil sha cvar_t r_shadow_realtime_world = CVARFD ("r_shadow_realtime_world", "0", CVAR_ARCHIVE, "Enables the use of static/world realtime lights."); 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", "1", 0, "Controls default loading of world lightmaps.\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_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"); @@ -3731,7 +3732,7 @@ void Sh_PreGenerateLights(void) if ((r_shadow_realtime_dlight.ival || r_shadow_realtime_world.ival) && rtlights_max == RTL_FIRST) { qboolean okay = false; - if (!okay) + if (!okay && r_shadow_realtime_world_importlightentitiesfrommap.ival <= 1) okay |= R_LoadRTLights(); if (!okay) { @@ -3739,9 +3740,9 @@ void Sh_PreGenerateLights(void) R_StaticEntityToRTLight(i); okay |= rtlights_max != RTL_FIRST; } - if (!okay) + if (!okay && r_shadow_realtime_world_importlightentitiesfrommap.ival >= 1) okay |= R_ImportRTLights(Mod_GetEntitiesString(cl.worldmodel)); - if (!okay && r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value != 1) + if (!okay && r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value < 0.5) { r_shadow_realtime_world_lightmaps.value = 1; Con_Printf(CON_WARNING "No lights detected in map.\n"); diff --git a/engine/gl/gl_videgl.c b/engine/gl/gl_videgl.c index 63ac1015..c2ade6a1 100644 --- a/engine/gl/gl_videgl.c +++ b/engine/gl/gl_videgl.c @@ -1,6 +1,7 @@ #include "quakedef.h" #if defined(GLQUAKE) && defined(USE_EGL) #include "gl_videgl.h" +#include "vr.h" //EGL_KHR_gl_colorspace #ifndef EGL_GL_COLORSPACE_KHR @@ -42,7 +43,7 @@ static EGLBoolean (EGLAPIENTRY *qeglSwapBuffers)(EGLDisplay dpy, EGLSurface surf static EGLBoolean (EGLAPIENTRY *qeglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); static EGLContext (EGLAPIENTRY *qeglCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); static EGLBoolean (EGLAPIENTRY *qeglDestroyContext)(EGLDisplay dpy, EGLContext ctx); -static void * (EGLAPIENTRY *qeglGetProcAddress) (char *name); +static void * (EGLAPIENTRY *qeglGetProcAddress) (const char *name); static EGLBoolean (EGLAPIENTRY *qeglSwapInterval) (EGLDisplay display, EGLint interval); @@ -426,6 +427,25 @@ qboolean EGL_InitWindow (rendererstate_t *info, int eglplat, void *nwindow, EGLN EGL_UpdateSwapInterval(); + + if (info->vr) + { + vrsetup_t vrsetup = {sizeof(vrsetup)}; + vrsetup.vrplatform = VR_EGL; + vrsetup.egl.getprocaddr = qeglGetProcAddress; + vrsetup.egl.egldisplay = egldpy; + vrsetup.egl.eglconfig = cfg; + vrsetup.egl.eglcontext = eglctx; + if (!info->vr->Prepare(&vrsetup) || + !info->vr->Init(&vrsetup, info)) + { + info->vr->Shutdown(); + info->vr = NULL; + } + else + vid.vr = info->vr; + } + return true; } diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index e236d29c..6171a366 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -77,6 +77,7 @@ static qboolean XVK_SetupSurface_XCB(void); #include "gl_videgl.h" #endif #include "glquake.h" +#include "vr.h" #endif #define USE_VMODE @@ -2102,6 +2103,19 @@ static qboolean GLX_Init(rendererstate_t *info, GLXFBConfig fbconfig, XVisualInf // extern cvar_t vid_gl_context_selfreset; extern cvar_t vid_gl_context_noerror; + vrsetup_t setup = {sizeof(setup)}; + setup.vrplatform = VR_X11_GLX; + setup.x11_glx.display = vid_dpy; + setup.x11_glx.visualid = visinfo->visualid; + setup.x11_glx.glxfbconfig = fbconfig; + setup.x11_glx.drawable = vid_window; + + if (info->vr && !info->vr->Prepare(&setup)) + { + info->vr->Shutdown(); + info->vr = NULL; + } + if (fbconfig && glx.CreateContextAttribs) { unsigned int majorver=1, minorver=1; @@ -2123,6 +2137,12 @@ static qboolean GLX_Init(rendererstate_t *info, GLXFBConfig fbconfig, XVisualInf else minorver = 0; + if (majorver < setup.minver.major || (majorver == setup.minver.major && minorver < setup.minver.minor)) + { //if vr stuff requires a minimum version then try and ask for that now + majorver = setup.minver.major; + minorver = setup.minver.minor; + } + //some weirdness for you: //3.0 simply marked stuff as deprecated, without removing it. //3.1 removed it (moved to the optional GL_ARB_compatibility extension, which shouldn't be supported in a forward-compatible context). @@ -2191,6 +2211,15 @@ static qboolean GLX_Init(rendererstate_t *info, GLXFBConfig fbconfig, XVisualInf return false; } + + setup.x11_glx.glxcontext = ctx; + if (info->vr && !info->vr->Init(&setup, info)) + { + info->vr->Shutdown(); + return false; + } + vid.vr = info->vr; + //okay, we have a context, now init OpenGL-proper. return GL_Init(info, &GLX_GetSymbol); } @@ -4323,7 +4352,7 @@ static qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int break; } #endif - Con_Printf(CON_ERROR "Failed to create a vulkan context.\n"); + //Con_Printf(CON_ERROR "Failed to create a vulkan context.\n"); GLVID_Shutdown(); return false; #endif diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index 79fc66ac..55250557 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "winquake.h" #include "resource.h" #include "shader.h" +#include "vr.h" #include #ifdef USE_EGL @@ -1447,6 +1448,8 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette) #endif // HDC hdc; + vrsetup_t setup = {sizeof(setup)}; + TRACE(("dbg: GLVID_SetMode\n")); // so Con_Printfs don't mess us up by forcing vid and snd updates @@ -1465,6 +1468,13 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette) #ifdef VKQUAKE case MODE_NVVULKAN: #endif + setup.vrplatform = VR_WIN_WGL; + if (info->vr && !info->vr->Prepare(&setup)) + { + info->vr->Shutdown(); + info->vr = NULL; + } + // Set either the fullscreen or windowed mode qwglChoosePixelFormatARB = NULL; qwglGetPixelFormatAttribfvARB = NULL; @@ -1515,10 +1525,18 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette) if (modestate == MS_FULLWINDOW) ShowWindow (dibwindow, SW_SHOWMAXIMIZED); else - ShowWindow (dibwindow, SW_SHOWNORMAL); + ShowWindow (dibwindow, SW_SHOWNORMAL); if (!GL_Init(info, getglfunc)) return false; + setup.wgl.hdc = maindc; + setup.wgl.hglrc = baseRC; + if (info->vr && !info->vr->Init(&setup, info)) + { + info->vr->Shutdown(); + return false; + } + vid.vr = info->vr; if (qwglGetPixelFormatAttribfvARB) //just for debugging info. { diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index f1055989..be60eff1 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -13532,7 +13532,7 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar pr_scope->filen = thearray.sym->filen; pr_scope->s_filed = thearray.sym->s_filed; - index = QCC_PR_GetSRef(type_float, "__indexg", pr_scope, true, 0, false); + index = QCC_PR_GetSRef(type_float, "__indexg", pr_scope, true, 0, GDF_PARAMETER); scope->initialized = true; scope->symboldata[0]._int = pr_scope - functions; @@ -13722,8 +13722,8 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar pr_scope->filen = thearray.sym->filen; pr_scope->s_filed = thearray.sym->s_filed; - index = QCC_PR_GetSRef(type_float, "indexs___", pr_scope, true, 0, false); - value = QCC_PR_GetSRef(thearray.cast, "value___", pr_scope, true, 0, false); + index = QCC_PR_GetSRef(type_float, "indexs___", pr_scope, true, 0, GDF_PARAMETER); + value = QCC_PR_GetSRef(thearray.cast, "value___", pr_scope, true, 0, GDF_PARAMETER); scope->initialized = true; scope->symboldata[0]._int = pr_scope - functions; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 4b4129ea..02102268 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1899,7 +1899,7 @@ void PR_SpawnInitialEntities(const char *file) } void SV_RegisterH2CustomTents(void); -void Q_InitProgs(qboolean cinematic) +void Q_InitProgs(enum initprogs_e flags) { int i, i2; func_t f, f2; @@ -1924,189 +1924,237 @@ void Q_InitProgs(qboolean cinematic) svs.numprogs=0; - d1 = FS_FLocateFile("progs.dat", FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|FSLF_IGNOREBASEDEPTH, NULL); - d2 = FS_FLocateFile("qwprogs.dat", FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|FSLF_IGNOREBASEDEPTH, NULL); - //FIXME id1/progs.dat vs qw/qwprogs.dat - these should be considered to have the same priority. - if (d1 < d2) //progs.dat is closer to the gamedir - strcpy(addons, "progs.dat"); - else if (d1 > d2) //qwprogs.dat is closest + if (flags & INITPROGS_EDITOR) { - strcpy(addons, "qwprogs.dat"); - d1 = d2; + oldprnum = AddProgs("sseditor.dat"); + PR_LoadGlabalStruct(true); } - //both are an equal depth - same path. - else if (deathmatch.value && !COM_CheckParm("-game")) //if deathmatch, default to qw + else { - strcpy(addons, "qwprogs.dat"); - d1 = d2; - } - else //single player/coop is better done with nq. - { - strcpy(addons, "progs.dat"); - } - //if progs cvar is left blank and a q2 map is loaded, the server will use the q2 game dll. - //if you do set a value here, q2 dll is not used. - - - //hexen2 - maplist contains a list of maps that we need to use an alternate progs.dat for. - d2 = COM_FDepthFile("maplist.txt", true); - if (d2 <= d1)//Use it if the maplist.txt file is within a more or equal important gamedir. - { - int j, maps; - char *f; - - f = COM_LoadTempFile("maplist.txt", 0, NULL); - f = COM_Parse(f); - maps = atoi(com_token); - for (j = 0; j < maps; j++) + d1 = FS_FLocateFile("progs.dat", FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|FSLF_IGNOREBASEDEPTH, NULL); + d2 = FS_FLocateFile("qwprogs.dat", FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|FSLF_IGNOREBASEDEPTH, NULL); + //FIXME id1/progs.dat vs qw/qwprogs.dat - these should be considered to have the same priority. + if (d1 < d2) //progs.dat is closer to the gamedir + strcpy(addons, "progs.dat"); + else if (d1 > d2) //qwprogs.dat is closest { + strcpy(addons, "qwprogs.dat"); + d1 = d2; + } + //both are an equal depth - same path. + else if (deathmatch.value && !COM_CheckParm("-game")) //if deathmatch, default to qw + { + strcpy(addons, "qwprogs.dat"); + d1 = d2; + } + else //single player/coop is better done with nq. + { + strcpy(addons, "progs.dat"); + } + //if progs cvar is left blank and a q2 map is loaded, the server will use the q2 game dll. + //if you do set a value here, q2 dll is not used. + + + //hexen2 - maplist contains a list of maps that we need to use an alternate progs.dat for. + d2 = COM_FDepthFile("maplist.txt", true); + if (d2 <= d1)//Use it if the maplist.txt file is within a more or equal important gamedir. + { + int j, maps; + char *f; + + f = COM_LoadTempFile("maplist.txt", 0, NULL); f = COM_Parse(f); - if (!Q_strcasecmp(svs.name, com_token)) + maps = atoi(com_token); + for (j = 0; j < maps; j++) { f = COM_Parse(f); - strcpy(addons, com_token); - break; + if (!Q_strcasecmp(svs.name, com_token)) + { + f = COM_Parse(f); + strcpy(addons, com_token); + break; + } + f = strchr(f, '\n'); //skip to the end of the line. } - f = strchr(f, '\n'); //skip to the end of the line. } - } - /*if pr_ssqc_progs cvar is set, override the default*/ - if (*pr_ssqc_progs.string && strlen(pr_ssqc_progs.string)<64 && *pr_ssqc_progs.string != '*') //a * is a special case to not load a q2 dll. - { - Q_strncpyz(addons, pr_ssqc_progs.string, MAX_QPATH); - COM_DefaultExtension(addons, ".dat", sizeof(addons)); - } - oldprnum= AddProgs(addons); - - /*try to load qwprogs.dat if we didn't manage to load one yet*/ - if (oldprnum < 0 && strcmp(addons, "qwprogs.dat")) - { -#ifndef SERVERONLY - if (SCR_UpdateScreen) - SCR_UpdateScreen(); -#endif - oldprnum= AddProgs("qwprogs.dat"); - } - - /*try to load qwprogs.dat if we didn't manage to load one yet*/ - if (oldprnum < 0 && strcmp(addons, "progs.dat")) - { -#ifndef SERVERONLY - if (SCR_UpdateScreen) - SCR_UpdateScreen(); -#endif - oldprnum= AddProgs("progs.dat"); - } - if (oldprnum < 0) - { - PR_LoadGlabalStruct(true); - if (cinematic) //making this fatal, because it sucks to sit through a cinematic only to find the game isn't playable after. - SV_Error("No gamecode available. Try using the downloads menu.\n"); - Con_Printf(CON_ERROR"Running without gamecode\n"); - } - - if (oldprnum >= 0) - f = PR_FindFunction (svprogfuncs, "AddAddonProgs", oldprnum); - else - f = 0; -/* if (num) - { - //restore progs - for (i = 1; i < num; i++) + /*if pr_ssqc_progs cvar is set, override the default*/ + if (*pr_ssqc_progs.string && strlen(pr_ssqc_progs.string)<64 && *pr_ssqc_progs.string != '*') //a * is a special case to not load a q2 dll. { - if (f) + Q_strncpyz(addons, pr_ssqc_progs.string, MAX_QPATH); + COM_DefaultExtension(addons, ".dat", sizeof(addons)); + } + oldprnum= AddProgs(addons); + + /*try to load qwprogs.dat if we didn't manage to load one yet*/ + if (oldprnum < 0 && strcmp(addons, "qwprogs.dat")) + { +#ifndef SERVERONLY + if (SCR_UpdateScreen) + SCR_UpdateScreen(); +#endif + oldprnum= AddProgs("qwprogs.dat"); + } + + /*try to load qwprogs.dat if we didn't manage to load one yet*/ + if (oldprnum < 0 && strcmp(addons, "progs.dat")) + { +#ifndef SERVERONLY + if (SCR_UpdateScreen) + SCR_UpdateScreen(); +#endif + oldprnum= AddProgs("progs.dat"); + } + + if (oldprnum < 0) + { + PR_LoadGlabalStruct(true); + if (flags & INITPROGS_REQUIRE) + SV_Error("No gamecode available. Try using the downloads menu.\n"); + Con_Printf(CON_ERROR"Running without gamecode\n"); + } + + if (oldprnum >= 0) + f = PR_FindFunction (svprogfuncs, "AddAddonProgs", oldprnum); + else + f = 0; + /* if (num) + { + //restore progs + for (i = 1; i < num; i++) { - pr_globals = PR_globals(PR_CURRENT); - G_SETSTRING(OFS_PARM0, svs.progsnames[i]); - PR_ExecuteProgram (f); + if (f) + { + pr_globals = PR_globals(PR_CURRENT); + G_SETSTRING(OFS_PARM0, svs.progsnames[i]); + PR_ExecuteProgram (f); + } + else + { + prnum = AddProgs(svs.progsnames[i]); + f2 = PR_FindFunction ( "init", prnum); + + if (f2) + { + pr_globals = PR_globals(PR_CURRENT); + G_PROG(OFS_PARM0) = oldprnum; + PR_ExecuteProgram(f2); + } + oldprnum=prnum; + } } + } + */ + //additional (always) progs + as = NULL; + a = COM_LoadStackFile("mod.gam", addons, 2048, NULL); + + if (a) + { + if (progstype == PROG_QW) + as = strstr(a, "extraqwprogs="); else + as = strstr(a, "extraprogs="); + if (as) { - prnum = AddProgs(svs.progsnames[i]); - f2 = PR_FindFunction ( "init", prnum); + for (a = as+13; *a; a++) + { + if (*a < ' ') + { + *a = '\0'; + break; + } + } + a = (as+=13); + } + } + if (as) + { + while(*a) + { + if (*a == ';') + { + *a = '\0'; + for (i = 0; i < svs.numprogs; i++) //don't add if already added + { + if (!strcmp(svs.progsnames[i], as)) + break; + } + if (i == svs.numprogs) + { + if (f) + { + pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, as); + PR_ExecuteProgram (svprogfuncs, f); + } + else + { + prnum = AddProgs(as); + if (prnum>=0) + { + f2 = PR_FindFunction (svprogfuncs, "init", prnum); + + if (f2) + { + pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + G_PROG(OFS_PARM0) = oldprnum; + PR_ExecuteProgram(svprogfuncs, f2); + } + oldprnum=prnum; + } + } + } + *a = ';'; + as = a+1; + } + a++; + } + } + + if (COM_FDepthFile("fteadd.dat", true)!=FDEPTH_MISSING) + { + prnum = AddProgs("fteadd.dat"); + if (prnum>=0) + { + f2 = PR_FindFunction (svprogfuncs, "init", prnum); if (f2) { - pr_globals = PR_globals(PR_CURRENT); + pr_globals = PR_globals(svprogfuncs, PR_CURRENT); G_PROG(OFS_PARM0) = oldprnum; - PR_ExecuteProgram(f2); + PR_ExecuteProgram(svprogfuncs, f2); } oldprnum=prnum; } } - } -*/ - //additional (always) progs - as = NULL; - a = COM_LoadStackFile("mod.gam", addons, 2048, NULL); + prnum = 0; - if (a) - { - if (progstype == PROG_QW) - as = strstr(a, "extraqwprogs="); - else - as = strstr(a, "extraprogs="); - if (as) + switch (sv.world.worldmodel->fromgame) //spawn functions for - spawn funcs still come from the first progs found. { - for (a = as+13; *a; a++) - { - if (*a < ' ') - { - *a = '\0'; - break; - } - } - a = (as+=13); - } - } - if (as) - { - while(*a) - { - if (*a == ';') - { - *a = '\0'; - for (i = 0; i < svs.numprogs; i++) //don't add if already added - { - if (!strcmp(svs.progsnames[i], as)) - break; - } - if (i == svs.numprogs) - { - if (f) - { - pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, as); - PR_ExecuteProgram (svprogfuncs, f); - } - else - { - prnum = AddProgs(as); - if (prnum>=0) - { - f2 = PR_FindFunction (svprogfuncs, "init", prnum); + case fg_quake2: + if (COM_FDepthFile("q2bsp.dat", true)!=FDEPTH_MISSING) + prnum = AddProgs("q2bsp.dat"); + break; + case fg_quake3: + if (COM_FDepthFile("q3bsp.dat", true)!=FDEPTH_MISSING) + prnum = AddProgs("q3bsp.dat"); + else if (COM_FDepthFile("q2bsp.dat", true)!=FDEPTH_MISSING) //fallback + prnum = AddProgs("q2bsp.dat"); + break; + case fg_doom: + if (COM_FDepthFile("doombsp.dat", true)!=FDEPTH_MISSING) + prnum = AddProgs("doombsp.dat"); + break; + case fg_halflife: + if (COM_FDepthFile("hlbsp.dat", true)!=FDEPTH_MISSING) + prnum = AddProgs("hlbsp.dat"); + break; - if (f2) - { - pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - G_PROG(OFS_PARM0) = oldprnum; - PR_ExecuteProgram(svprogfuncs, f2); - } - oldprnum=prnum; - } - } - } - *a = ';'; - as = a+1; - } - a++; + default: + break; } - } - if (COM_FDepthFile("fteadd.dat", true)!=FDEPTH_MISSING) - { - prnum = AddProgs("fteadd.dat"); if (prnum>=0) { f2 = PR_FindFunction (svprogfuncs, "init", prnum); @@ -2119,95 +2167,96 @@ void Q_InitProgs(qboolean cinematic) } oldprnum=prnum; } - } - prnum = 0; - switch (sv.world.worldmodel->fromgame) //spawn functions for - spawn funcs still come from the first progs found. - { - case fg_quake2: - if (COM_FDepthFile("q2bsp.dat", true)!=FDEPTH_MISSING) - prnum = AddProgs("q2bsp.dat"); - break; - case fg_quake3: - if (COM_FDepthFile("q3bsp.dat", true)!=FDEPTH_MISSING) - prnum = AddProgs("q3bsp.dat"); - else if (COM_FDepthFile("q2bsp.dat", true)!=FDEPTH_MISSING) //fallback - prnum = AddProgs("q2bsp.dat"); - break; - case fg_doom: - if (COM_FDepthFile("doombsp.dat", true)!=FDEPTH_MISSING) - prnum = AddProgs("doombsp.dat"); - break; - case fg_halflife: - if (COM_FDepthFile("hlbsp.dat", true)!=FDEPTH_MISSING) - prnum = AddProgs("hlbsp.dat"); - break; - - default: - break; - } - - if (prnum>=0) - { - f2 = PR_FindFunction (svprogfuncs, "init", prnum); - - if (f2) + //progs depended on by maps. + a = as = COM_LoadStackFile(va("maps/%s.inf", svs.name), addons, sizeof(addons), NULL); + if (a) { - pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - G_PROG(OFS_PARM0) = oldprnum; - PR_ExecuteProgram(svprogfuncs, f2); - } - oldprnum=prnum; - } - - //progs depended on by maps. - a = as = COM_LoadStackFile(va("maps/%s.inf", svs.name), addons, sizeof(addons), NULL); - if (a) - { - if (progstype == PROG_QW) - as = strstr(a, "qwprogs="); - else - as = strstr(a, "progs="); - if (as) - { - for (a = as+11; *a; a++) - { - if (*a < ' ') + if (progstype == PROG_QW) + as = strstr(a, "qwprogs="); + else + as = strstr(a, "progs="); + if (as) { - *a = '\0'; - break; + for (a = as+11; *a; a++) + { + if (*a < ' ') + { + *a = '\0'; + break; + } + } + a = (as+=11); } } - a = (as+=11); - } - } - if (as) - { - while(*a) + if (as) { - if (*a == ';') + while(*a) + { + if (*a == ';') + { + *a = '\0'; + for (i = 0; i < svs.numprogs; i++) //don't add if already added + { + if (!strcmp(svs.progsnames[i], as)) + break; + } + if (i == svs.numprogs) + { + if (f) + { + pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, as); + PR_ExecuteProgram (svprogfuncs, f); + } + else + { + prnum = AddProgs(as); + if (prnum>=0) + { + f2 = PR_FindFunction (svprogfuncs, "init", prnum); + + if (f2) + { + pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + G_PROG(OFS_PARM0) = oldprnum; + PR_ExecuteProgram(svprogfuncs, f2); + } + oldprnum=prnum; + } + } + } + *a = ';'; + as = a+1; + } + a++; + } + } + + //add any addons specified + for (i2 = 0; i2 < MAXADDONS; i2++) + { + if (*sv_addon[i2].string) { - *a = '\0'; for (i = 0; i < svs.numprogs; i++) //don't add if already added { - if (!strcmp(svs.progsnames[i], as)) + if (!strcmp(svs.progsnames[i], sv_addon[i2].string)) break; } - if (i == svs.numprogs) + if (i == svs.numprogs) //Not added yet. Add it. { if (f) { pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, as); + G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, sv_addon[i2].string); PR_ExecuteProgram (svprogfuncs, f); } else { - prnum = AddProgs(as); - if (prnum>=0) + prnum = AddProgs(sv_addon[i2].string); + if (prnum >= 0) { f2 = PR_FindFunction (svprogfuncs, "init", prnum); - if (f2) { pr_globals = PR_globals(svprogfuncs, PR_CURRENT); @@ -2216,48 +2265,8 @@ void Q_InitProgs(qboolean cinematic) } oldprnum=prnum; } - } - } - *a = ';'; - as = a+1; - } - a++; - } - } - //add any addons specified - for (i2 = 0; i2 < MAXADDONS; i2++) - { - if (*sv_addon[i2].string) - { - for (i = 0; i < svs.numprogs; i++) //don't add if already added - { - if (!strcmp(svs.progsnames[i], sv_addon[i2].string)) - break; - } - if (i == svs.numprogs) //Not added yet. Add it. - { - if (f) - { - pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, sv_addon[i2].string); - PR_ExecuteProgram (svprogfuncs, f); - } - else - { - prnum = AddProgs(sv_addon[i2].string); - if (prnum >= 0) - { - f2 = PR_FindFunction (svprogfuncs, "init", prnum); - if (f2) - { - pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - G_PROG(OFS_PARM0) = oldprnum; - PR_ExecuteProgram(svprogfuncs, f2); - } - oldprnum=prnum; } - } } } @@ -11038,10 +11047,10 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"drawtextfield", PF_Fixme, 0, 0, 0, 0/*314*/, D("float(vector pos, vector size, float alignflags, string text)", "Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. Returns the total number of lines.")},// (EXT_CSQC) {"drawline", PF_Fixme, 0, 0, 0, 315, D("void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag)", "Draws a 2d line between the two 2d points.")},// (EXT_CSQC) {"iscachedpic", PF_Fixme, 0, 0, 0, 316, D("float(string name)", "Checks to see if the image is currently loaded. Engines might lie, or cache between maps.")},// (EXT_CSQC) - {"precache_pic", PF_Fixme, 0, 0, 0, 317, D("string(string name, optional float trywad)", "Forces the engine to load the named image. If trywad is specified, the specified name must lack any path and extension.")},// (EXT_CSQC) + {"precache_pic", PF_Fixme, 0, 0, 0, 317, D("string(string name, optional float flags)", "Forces the engine to load the named image. Flags are a bitmask of the PRECACHE_PIC_* flags.")},// (EXT_CSQC) {"r_uploadimage", PF_Fixme, 0, 0, 0, 0, D("void(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format)", "Updates a texture with the specified rgba data (uploading it to the gpu). Will be created if needed. If datasize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture.")}, {"r_readimage", PF_Fixme, 0, 0, 0, 0, D("int*(string filename, __out int width, __out int height)", "Reads and decodes an image from disk, providing raw R8G8B8A8 pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it.")}, - {"drawgetimagesize",PF_Fixme, 0, 0, 0, 318, D("#define draw_getimagesize drawgetimagesize\nvector(string picname)", "Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution.")},// (EXT_CSQC) + {"drawgetimagesize",PF_Fixme, 0, 0, 0, 318, D("#define draw_getimagesize drawgetimagesize\nvector(string picname)", "Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. WARNING: this function may be slow if used without or directly after its initial precache_pic.")},// (EXT_CSQC) {"freepic", PF_Fixme, 0, 0, 0, 319, D("void(string name)", "Tells the engine that the image is no longer needed. The image will appear to be new the next time its needed.")},// (EXT_CSQC) //320 {"drawcharacter", PF_Fixme, 0, 0, 0, 320, D("float(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag)", "Draw the given quake character at the given position.\nIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\nsize should normally be something like '8 8 0'.\nrgb should normally be '1 1 1'\nalpha normally 1.\nSoftware engines may assume the named defaults.\nNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely.")},// (EXT_CSQC, [EXT_CSQC_???]) @@ -12636,6 +12645,10 @@ void PR_DumpPlatform_f(void) {"STAT_USER", "const float", QW|NQ|CS, D("Custom user stats start here (lower values are reserved for engine use)."), 32}, #endif + {"PRECACHE_PIC_FROMWAD","const float", CS|MENU, D("Attempt to load it from the legacy gfx.wad file (usually its better to just use a gfx/ prefix instead)."), 1}, + {"PRECACHE_PIC_DOWNLOAD","const float", CS|MENU, D("If no image could be loaded then attempt to download one from the server. This flag can cause the function to block until completion. (Slow!)"), 256}, + {"PRECACHE_PIC_TEST", "const float", CS|MENU, D("The precache will block until the image is fully loaded, returning a null string on failure. (Slow!)"), 512}, + {"VF_MIN", "const float", CS|MENU, D("The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins."), VF_MIN}, {"VF_MIN_X", "const float", CS|MENU, NULL, VF_MIN_X}, {"VF_MIN_Y", "const float", CS|MENU, NULL, VF_MIN_Y}, diff --git a/engine/server/progs.h b/engine/server/progs.h index 75de6b2d..7f88da39 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -29,7 +29,13 @@ void SVQ1_CvarChanged(cvar_t *var); void Q_SetProgsParms(qboolean forcompiler); void PR_Deinit(void); //server shutting down void PR_LoadGlabalStruct(qboolean muted); -void Q_InitProgs(qboolean cinematic); +enum initprogs_e +{ + INITPROGS_NORMAL = 0, + INITPROGS_EDITOR = 1<<0, + INITPROGS_REQUIRE = 1<<1, +}; +void Q_InitProgs(enum initprogs_e flags); void PR_SpawnInitialEntities(const char *file); void PR_RegisterFields(void); void PR_Init(void); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 13e54d48..944c8244 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -404,8 +404,9 @@ static int QDECL ShowMapList (const char *name, qofs_t flags, time_t mtime, void "maps/%s.png" }; size_t u; - char stripped[64]; + char stripped[MAX_QPATH]; char completed[256]; + char *ext = parm; if (name[5] == 'b' && name[6] == '_') //skip box models return true; @@ -423,35 +424,29 @@ static int QDECL ShowMapList (const char *name, qofs_t flags, time_t mtime, void } #endif - COM_StripExtension(name+5, stripped, sizeof(stripped)); + name += 5; //skip the maps/ prefix + COM_StripExtension(name, stripped, sizeof(stripped)); for (u = 0; u < countof(levelshots); u++) { const char *ls = va(levelshots[u], stripped); if (COM_FCheckExists(ls)) { - Con_Printf("^[\\map\\%s\\img\\%s\\w\\64\\h\\48^]", stripped, ls); - Con_Printf("^[[%s]%s\\map\\%s\\tipimg\\%s^]\n", stripped, completed, stripped, ls); + Con_Printf("^[\\map\\%s\\img\\%s\\w\\64\\h\\48^]", name, ls); + Con_Printf("^[[%s%s]%s\\map\\%s\\tipimg\\%s^]\n", stripped, ext, completed, name, ls); return true; } } - Con_Printf("^[[%s]%s\\map\\%s^]\n", stripped, completed, stripped); - return true; -} -static int QDECL ShowMapListExt (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath) -{ - if (name[5] == 'b' && name[6] == '_') //skip box models - return true; - Con_Printf("^[[%s]\\map\\%s^]\n", name+5, name+5); + Con_Printf("^[[%s%s]%s\\map\\%s^]\n", stripped, ext, completed, name); return true; } static void SV_MapList_f(void) { - COM_EnumerateFiles("maps/*.bsp", ShowMapList, NULL); - COM_EnumerateFiles("maps/*.bsp.gz", ShowMapListExt, NULL); - COM_EnumerateFiles("maps/*.map", ShowMapListExt, NULL); - COM_EnumerateFiles("maps/*.map.gz", ShowMapListExt, NULL); - COM_EnumerateFiles("maps/*.cm", ShowMapList, NULL); - COM_EnumerateFiles("maps/*.hmp", ShowMapList, NULL); + COM_EnumerateFiles("maps/*.bsp", ShowMapList, ""); + COM_EnumerateFiles("maps/*.bsp.gz", ShowMapList, ".bsp.gz"); + COM_EnumerateFiles("maps/*.map", ShowMapList, ".map"); + COM_EnumerateFiles("maps/*.map.gz", ShowMapList, ".gz"); + COM_EnumerateFiles("maps/*.cm", ShowMapList, ".cm"); + COM_EnumerateFiles("maps/*.hmp", ShowMapList, ".hmp"); } static int QDECL CompleteMapList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath) @@ -547,6 +542,7 @@ void SV_Map_f (void) #endif qboolean waschangelevel = false; + qboolean mapeditor = false; int i; char *startspot; @@ -619,6 +615,7 @@ void SV_Map_f (void) newunit = flushparms || (!strcmp(Cmd_Argv(0), "changelevel") && !startspot); q2savetos0 = !strcmp(Cmd_Argv(0), "gamemap") && !isDedicated; //q2 #endif + mapeditor = !strcmp(Cmd_Argv(0), "mapedit"); sv.mapchangelocked = false; @@ -931,7 +928,7 @@ void SV_Map_f (void) { if (waschangelevel && !startspot) startspot = ""; - SV_SpawnServer (level, startspot, false, cinematic, 0); + SV_SpawnServer (level, startspot, mapeditor, cinematic, 0); } SCR_SetLoadingFile("server spawned"); @@ -3246,6 +3243,7 @@ void SV_InitOperatorCommands (void) Cmd_AddCommand ("killserver", SV_KillServer_f); Cmd_AddCommandD ("precaches", SV_PrecacheList_f, "Displays a list of current server precaches."); Cmd_AddCommandAD ("map", SV_Map_f, SV_Map_c, "Changes map. If a second argument is specified then that is normally the name of the initial start spot."); + Cmd_AddCommandAD ("mapedit", SV_Map_f, SV_Map_c, "Loads the named map without any gamecode active."); #ifdef Q3SERVER Cmd_AddCommandAD ("spmap", SV_Map_f, SV_Map_c, NULL); #endif diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index c3577dfe..8e9c1f21 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -120,8 +120,9 @@ extern int sv_nailmodel, sv_supernailmodel, sv_playermodel; qboolean demonails; #endif -static edict_t *csqcent[MAX_EDICTS]; -static int csqcnuments; +static edict_t **csqcent/*[csqcmaxents]*/; +static size_t csqcmaxents; +static size_t csqcnuments; qboolean SV_AddNailUpdate (edict_t *ent) { @@ -263,6 +264,8 @@ static qboolean SV_AddCSQCUpdate (client_t *client, edict_t *ent) if (!(client->csqcactive)) return false; + if (csqcnuments >= csqcmaxents) + Z_ReallocElements((void**)&csqcent, &csqcmaxents, csqcnuments + 1024, sizeof(*csqcent)); csqcent[csqcnuments++] = ent; return true; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index bd9fed32..b2d9a1f1 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -831,7 +831,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, { extern cvar_t allow_download_refpackages; func_t f; - const char *file; + const char *file, *csprogsname; gametype_e newgametype; @@ -1075,8 +1075,12 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, //do we allow csprogs? #ifdef PEXT_CSQC fsz = 0; - if (*sv_csqc_progname.string) - file = COM_LoadTempFile(sv_csqc_progname.string, 0, &fsz); + if (noents) + csprogsname = "csaddon.dat"; + else + csprogsname = sv_csqc_progname.string; + if (*csprogsname) + file = COM_LoadTempFile(csprogsname, 0, &fsz); else file = NULL; if (file) @@ -1087,8 +1091,8 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, InfoBuf_SetValueForStarKey(&svs.info, "*csprogs", text); sprintf(text, "0x%x", (unsigned int)fsz); InfoBuf_SetValueForStarKey(&svs.info, "*csprogssize", text); - if (strcmp(sv_csqc_progname.string, "csprogs.dat")) - InfoBuf_SetValueForStarKey(&svs.info, "*csprogsname", sv_csqc_progname.string); + if (strcmp(csprogsname, "csprogs.dat")) + InfoBuf_SetValueForStarKey(&svs.info, "*csprogsname", csprogsname); else InfoBuf_SetValueForStarKey(&svs.info, "*csprogsname", ""); } @@ -1130,35 +1134,35 @@ MSV_OpenUserDatabase(); #endif newgametype = svs.gametype; -#ifdef HLSERVER - if (SVHL_InitGame()) - newgametype = GT_HALFLIFE; - else -#endif -#ifdef Q3SERVER - if (SVQ3_InitGame(false)) - newgametype = GT_QUAKE3; - else -#endif -#ifdef Q2SERVER - if ((sv.world.worldmodel->fromgame == fg_quake2 || sv.world.worldmodel->fromgame == fg_quake3) && !*pr_ssqc_progs.string && SVQ2_InitGameProgs()) //these are the rules for running a q2 server - newgametype = GT_QUAKE2; //we loaded the dll - else -#endif -#ifdef VM_LUA - if (PR_LoadLua()) - newgametype = GT_LUA; - else -#endif -#ifdef VM_Q1 - if (PR_LoadQ1QVM()) - newgametype = GT_Q1QVM; - - else -#endif + if (noents) { newgametype = GT_PROGS; //let's just hope this loads. - Q_InitProgs(usecinematic); + Q_InitProgs(INITPROGS_EDITOR); + } +#ifdef HLSERVER + else if (SVHL_InitGame()) + newgametype = GT_HALFLIFE; +#endif +#ifdef Q3SERVER + else if (SVQ3_InitGame(false)) + newgametype = GT_QUAKE3; +#endif +#ifdef Q2SERVER + else if ((sv.world.worldmodel->fromgame == fg_quake2 || sv.world.worldmodel->fromgame == fg_quake3) && !*pr_ssqc_progs.string && SVQ2_InitGameProgs()) //these are the rules for running a q2 server + newgametype = GT_QUAKE2; //we loaded the dll +#endif +#ifdef VM_LUA + else if (PR_LoadLua()) + newgametype = GT_LUA; +#endif +#ifdef VM_Q1 + else if (PR_LoadQ1QVM()) + newgametype = GT_Q1QVM; +#endif + else + { + newgametype = GT_PROGS; //let's just hope this loads. + Q_InitProgs(usecinematic?INITPROGS_REQUIRE:INITPROGS_NORMAL); } // if ((sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) && !*progs.string && SVQ2_InitGameProgs()) //full q2 dll decision in one if statement diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 8754cdf1..705b297b 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -86,7 +86,7 @@ extern cvar_t password; #endif cvar_t spectator_password = CVARF("spectator_password", "", CVAR_NOUNSAFEEXPAND); // password for entering as a sepctator -static cvar_t sv_dlURL = CVARFD(/*ioq3*/"sv_dlURL", "", CVAR_SERVERINFO|CVAR_ARCHIVE, "Provides clients with an external url from which they can obtain pk3s/packages from an external http server instead of having to download over udp."); +cvar_t sv_dlURL = CVARAFD(/*ioq3*/"sv_dlURL", "", /*dp*/"sv_curl_defaulturl", CVAR_SERVERINFO|CVAR_ARCHIVE, "Provides clients with an external url from which they can obtain pk3s/packages from an external http server instead of having to download over udp."); cvar_t allow_download = CVARAD("allow_download", "1", /*q3*/"sv_allowDownload", "If 1, permits downloading. Set to 0 to unconditionally block *ALL* downloads from this server. You may wish to set sv_dlURL if you wish clients to still be able to download content."); cvar_t allow_download_skins = CVARD("allow_download_skins", "1", "0 blocks downloading of any file in the skins/ directory"); cvar_t allow_download_models = CVARD("allow_download_models", "1", "0 blocks downloading of any file in the progs/ or models/ directory"); @@ -167,7 +167,7 @@ cvar_t sv_masterport = CVAR("sv_masterport", "0"); cvar_t sv_reliable_sound = CVARFD("sv_reliable_sound", "0", 0, "Causes all sounds to be sent reliably, so they will not be missed due to packetloss. However, this will cause them to be delayed somewhat, and slightly bursty. This can be overriden using the 'rsnd' userinfo setting (either forced on or forced off). Note: this does not affect sounds attached to particle effects."); cvar_t sv_gamespeed = CVARAF("sv_gamespeed", "1", "slowmo", 0); cvar_t sv_csqcdebug = CVARD("sv_csqcdebug", "0", "Inject packet size information for data directed to csqc."); -cvar_t sv_csqc_progname = CVAR("sv_csqc_progname", "csprogs.dat"); +cvar_t sv_csqc_progname = CVARAF("sv_csqc_progname", "csprogs.dat", /*dp*/"csqc_progname", 0); cvar_t pausable = CVAR("pausable", ""); cvar_t sv_banproxies = CVARD("sv_banproxies", "0", "If enabled, anyone connecting via known proxy software will be refused entry. This should aid with blocking aimbots, but is only reliable for certain public proxies."); cvar_t sv_specprint = CVARD("sv_specprint", "3", "Bitfield that controls which player events spectators see when tracking that player.\n&1: spectators will see centerprints.\n&2: spectators will see sprints (pickup messages etc).\n&4: spectators will receive console commands, this is potentially risky.\nIndividual spectators can use 'setinfo sp foo' to limit this setting."); @@ -3052,11 +3052,6 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info) newcl->fteprotocolextensions &= ~PEXT_SPLITSCREEN; newcl->controller = NULL; -#ifdef PEXT_CSQC - if (sv.csqcchecksum && !(newcl->fteprotocolextensions & PEXT_CSQC) && !ISDPCLIENT(newcl)) - SV_PrintToClient(newcl, PRINT_HIGH, "This server is using CSQC - you are missing out due to your choice of outdated client / protocol!\n"); -#endif - if (!redirect) { Sys_ServerActivity(); diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 516b5675..c4ce2c74 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -712,17 +712,21 @@ void SVNQ_New_f (void) if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7) { - size_t sz; + char buf[1024]; + size_t sz = 0; char *f; + char *csprogsname = InfoBuf_ValueForKey(&svs.info, "*csprogsname"); + if (!*csprogsname && *InfoBuf_ValueForKey(&svs.info, "*csprogs")) + csprogsname = "csprogs.dat"; MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, "cl_serverextension_download 1\n"); - f = COM_LoadTempFile("csprogs.dat", 0, &sz); + f = *csprogsname?COM_LoadTempFile(csprogsname, 0, &sz):NULL; if (f) { MSG_WriteByte (&host_client->netchan.message, svc_stufftext); - MSG_WriteString (&host_client->netchan.message, va("csqc_progname %s\n", "csprogs.dat")); + MSG_WriteString (&host_client->netchan.message, va("csqc_progname %s\n", COM_QuotedString(csprogsname, buf, sizeof(buf), false))); MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("csqc_progsize %u\n", (unsigned int)sz)); MSG_WriteByte (&host_client->netchan.message, svc_stufftext); @@ -731,6 +735,11 @@ void SVNQ_New_f (void) MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, "cmd enablecsqc\n"); } + else + { + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, "csqc_progcrc \"\"\n"); + } } else if (allow_download.value && (protext1||protext2)) { //technically this is a DP extension, but is separate from actual protocols and shouldn't harm anything. @@ -1037,6 +1046,11 @@ void SV_SendClientPrespawnInfo(client_t *client) { char buffer[1024]; +#ifdef PEXT_CSQC + if (sv.csqcchecksum && !(client->fteprotocolextensions & PEXT_CSQC) && !ISDPCLIENT(client)) + SV_PrintToClient(client, PRINT_HIGH, "This server is using CSQC - you are missing out due to your choice of outdated client / protocol!\n"); +#endif + while (client->netchan.message.cursize < maxsize) { if (client->prespawn_idx == 0) @@ -3013,6 +3027,58 @@ void SV_Voice_UnmuteAll_f(void) } #endif +qboolean SV_FindRemotePackage(const char *package, char *url, size_t urlsize) +{ + //FIXME: interrogate the package manager first? it probably doesn't have source urls though. I guess that should be done clientside. + + //basedir/gamedir/curl_urls.txt contains something like: + //maps_*.pk3 https://host/maps/ + //* https://host/gamedir/ + + //or something. + + extern cvar_t sv_dlURL; + vfsfile_t *f; + char line[512]; + + //filter out the gamedir, so different gamedirs can have different sets of urls. + //(useful for eg pakN.pak, if it were not blocked elsewhere) + char *sep = strchr(package, '/'); + if (!sep || sep-package>=strlen(line)) + return false; + memcpy(line, package, sep-package+1); + line[sep-package+1] = 0; + package = sep+1; + + Q_strncatz(line, "curl_urls.txt", sizeof(line)); + f = FS_OpenVFS(line, "rb", FS_ROOT); //this is for server admins to deal with. urls are too unreliable for paks. + if (f) + { + char pattern[256]; + char *e; + while (VFS_GETS(f, line, sizeof(line))) + { + e = COM_ParseOut(line, pattern, sizeof(pattern)); + if (*pattern && wildcmp(pattern, package)) + { + COM_ParseOut(e, url, urlsize); + Q_strncatz(url, package, urlsize); + VFS_CLOSE(f); + return true; + } + } + VFS_CLOSE(f); + } + + if (*sv_dlURL.string) + { //a fallback, though the above mechanism allows for a wildcard for all. + Q_strncatz(sv_dlURL.string, package, urlsize); + Q_strncatz(url, package, urlsize); + return true; + } + return false; +} + //Use of this function is on name only. //Be aware that the maps directory should be restricted based on weather the file was from a pack file //this is to preserve copyright - please do not breach due to a bug. @@ -3204,7 +3270,14 @@ static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacem if (!Q_strncasecmp(name, "package/", 8)) { - vfsfile_t *f = FS_OpenVFS(name+8, "rb", FS_ROOT); + vfsfile_t *f; + if (redirectpaks && SV_FindRemotePackage(name+8, tmpname, sizeof(tmpname))) + { + *replacementname = tmpname; + return DLERR_REDIRECTFILE; + } + + f = FS_OpenVFS(name+8, "rb", FS_ROOT); if (f) { loc->len = VFS_GETLEN(f); @@ -6949,7 +7022,7 @@ size_t playertouchmax; void SV_PreRunCmd(void) { - size_t max = MAX_EDICTS;//(sv.world.num_edicts+7)&~7; + size_t max = (sv.world.num_edicts+512+7)&~7; if (max > playertouchmax) { playertouchmax = max; @@ -7534,7 +7607,7 @@ if (sv_player->v->health > 0 && before && !after ) n = pmove.physents[pmove.touchindex[i]].info; ent = EDICT_NUM_PB(svprogfuncs, n); - if (n >= playertouchmax || playertouch[n>>3]&(1<<(n&7))) + if (n >= playertouchmax || (playertouch[n>>3]&(1<<(n&7)))) continue; playertouch[n>>3] |= 1 << (n&7); diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index 2cfa476e..09771247 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -4708,7 +4708,7 @@ static void VKBE_RT_Purge(void *ptr) VK_DestroyVkTexture(&ctx->mscolour); VK_DestroyVkTexture(&ctx->colour); } -void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qboolean clear, unsigned int flags) +void VKBE_RT_Gen(struct vk_rendertarg *targ, vk_image_t *colour, uint32_t width, uint32_t height, qboolean clear, unsigned int flags) { //sooooo much work... VkImageCreateInfo colour_imginfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; @@ -4717,16 +4717,27 @@ void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qb struct vkbe_rtpurge *purge; static VkClearValue clearvalues[3]; + if (colour) + { //override the width+height if we already have an image to draw to. + width = colour->width; + height = colour->height; + } + targ->restartinfo.clearValueCount = 3; targ->depthcleared = true; //will be once its activated. if (targ->width == width && targ->height == height && targ->q_colour.flags == flags && (!(targ->rpassflags&RP_MULTISAMPLE))==(targ->mscolour.image==VK_NULL_HANDLE)) { - if (clear || targ->firstuse) - targ->restartinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->rpassflags); - else - targ->restartinfo.renderPass = VK_GetRenderPass(RP_DEPTHCLEAR|targ->rpassflags); //don't care - return; //no work to do. + if ((colour && colour->image == targ->colour.image) || (!colour && !targ->externalimage)) + { + if (width == 0 || height == 0) + targ->restartinfo.renderPass = VK_NULL_HANDLE; //illegal combination used for destruction. + else if (clear || targ->firstuse) + targ->restartinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->rpassflags); + else + targ->restartinfo.renderPass = VK_GetRenderPass(RP_DEPTHCLEAR|targ->rpassflags); //don't care + return; //no work to do. + } } if (targ->framebuffer) @@ -4734,6 +4745,8 @@ void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qb purge = VK_AtFrameEnd(VKBE_RT_Purge, NULL, sizeof(*purge)); purge->framebuffer = targ->framebuffer; purge->colour = targ->colour; + if (targ->externalimage) + purge->colour.image = VK_NULL_HANDLE; purge->mscolour = targ->mscolour; purge->depth = targ->depth; memset(&targ->colour, 0, sizeof(targ->colour)); @@ -4742,6 +4755,7 @@ void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qb targ->framebuffer = VK_NULL_HANDLE; } + targ->externalimage = !!colour; targ->q_colour.vkimage = &targ->colour; targ->q_depth.vkimage = &targ->depth; targ->q_colour.status = TEX_LOADED; @@ -4775,7 +4789,10 @@ void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qb colour_imginfo.queueFamilyIndexCount = 0; colour_imginfo.pQueueFamilyIndices = NULL; colour_imginfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkAssert(vkCreateImage(vk.device, &colour_imginfo, vkallocationcb, &targ->colour.image)); + if (targ->externalimage) + targ->colour.image = colour->image; + else + VkAssert(vkCreateImage(vk.device, &colour_imginfo, vkallocationcb, &targ->colour.image)); depth_imginfo = colour_imginfo; depth_imginfo.format = vk.depthformat; @@ -4789,7 +4806,10 @@ void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qb } VkAssert(vkCreateImage(vk.device, &depth_imginfo, vkallocationcb, &targ->depth.image)); - VK_AllocateBindImageMemory(&targ->colour, true); + if (targ->externalimage) //an external image is assumed to already have memory bound. don't allocate it elsewhere. + memset(&targ->colour.mem, 0, sizeof(targ->colour.mem)); + else + VK_AllocateBindImageMemory(&targ->colour, true); VK_AllocateBindImageMemory(&targ->depth, true); { @@ -4907,6 +4927,9 @@ void VKBE_RT_Gen_Cube(struct vk_rendertarg_cube *targ, uint32_t size, qboolean c uint32_t f; static VkClearValue clearvalues[2]; + if (targ->size == size && !clear) + return; //no work to do. + for (f = 0; f < 6; f++) { if (clear) @@ -5195,7 +5218,7 @@ static qboolean BE_GenerateRefraction(batch_t *batch, shader_t *bs) r_refdef.vrect.y = 0; r_refdef.vrect.width = max(1, vid.fbvwidth*bs->portalfboscale); r_refdef.vrect.height = max(1, vid.fbvheight*bs->portalfboscale); - VKBE_RT_Gen(&shaderstate.rt_reflection, r_refdef.vrect.width, r_refdef.vrect.height, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&shaderstate.rt_reflection, NULL, r_refdef.vrect.width, r_refdef.vrect.height, false, RT_IMAGEFLAGS); VKBE_RT_Begin(&shaderstate.rt_reflection); R_DrawPortal(batch, cl.worldmodel->batches, NULL, 1); VKBE_RT_End(&shaderstate.rt_reflection); @@ -5214,7 +5237,7 @@ static qboolean BE_GenerateRefraction(batch_t *batch, shader_t *bs) r_refdef.vrect.y = 0; r_refdef.vrect.width = vid.fbvwidth/2; r_refdef.vrect.height = vid.fbvheight/2; - VKBE_RT_Gen(&shaderstate.rt_refraction, r_refdef.vrect.width, r_refdef.vrect.height, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&shaderstate.rt_refraction, NULL, r_refdef.vrect.width, r_refdef.vrect.height, false, RT_IMAGEFLAGS); VKBE_RT_Begin(&shaderstate.rt_refraction); R_DrawPortal(batch, cl.worldmodel->batches, NULL, ((bs->flags & SHADER_HASREFRACTDEPTH)?3:2)); //fixme VKBE_RT_End(&shaderstate.rt_refraction); diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index d9a87da6..7bba345c 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -5,6 +5,8 @@ #include "shader.h" #include "renderque.h" //is anything still using this? +#include "vr.h" + extern qboolean vid_isfullscreen; cvar_t vk_stagingbuffers = CVARFD ("vk_stagingbuffers", "", CVAR_RENDERERLATCH, "Configures which dynamic buffers are copied into gpu memory for rendering, instead of reading from shared memory. Empty for default settings.\nAccepted chars are u(niform), e(lements), v(ertex), 0(none)."); @@ -2217,7 +2219,7 @@ static void VK_Shutdown_PostProc(void) if (vk.device) { for (i = 0; i < countof(postproc); i++) - VKBE_RT_Gen(&postproc[i], 0, 0, true, RT_IMAGEFLAGS); + VKBE_RT_Gen(&postproc[i], NULL, 0, 0, true, RT_IMAGEFLAGS); VK_R_BloomShutdown(); } @@ -2622,6 +2624,52 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb) return true; } +void VK_R_RenderEye(texid_t image, vec4_t fovoverride, vec3_t axisorg[4]) +{ + struct vk_rendertarg *rt; + + VK_SetupViewPortProjection(false); + + rt = &postproc[postproc_buf++%countof(postproc)]; + VKBE_RT_Gen(rt, image?image->vkimage:NULL, 320, 200, false, RT_IMAGEFLAGS); + VKBE_RT_Begin(rt); + + + if (!vk.rendertarg->depthcleared) + { + VkClearAttachment clr; + VkClearRect rect; + clr.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + clr.clearValue.depthStencil.depth = 1; + clr.clearValue.depthStencil.stencil = 0; + clr.colorAttachment = 1; + rect.rect.offset.x = r_refdef.pxrect.x; + rect.rect.offset.y = r_refdef.pxrect.y; + rect.rect.extent.width = r_refdef.pxrect.width; + rect.rect.extent.height = r_refdef.pxrect.height; + rect.layerCount = 1; + rect.baseArrayLayer = 0; + vkCmdClearAttachments(vk.rendertarg->cbuf, 1, &clr, 1, &rect); + vk.rendertarg->depthcleared = true; + } + + VKBE_SelectEntity(&r_worldentity); + + R_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view); + RQ_BeginFrame(); + if (!(r_refdef.flags & RDF_NOWORLDMODEL)) + { + if (cl.worldmodel) + P_DrawParticles (); + } + Surf_DrawWorld(); + RQ_RenderBatchClear(); + + vk.rendertarg->depthcleared = false; + + VKBE_RT_End(rt); +} + void VK_R_RenderView (void) { extern unsigned int r_viewcontents; @@ -2641,6 +2689,12 @@ void VK_R_RenderView (void) Surf_SetupFrame(); + if (vid.vr && vid.vr->Render(VK_R_RenderEye)) + { + VK_Set2D (); + return; + } + //check if we can do underwater warp if (cls.protocol != CP_QUAKE2) //quake2 tells us directly { @@ -2743,7 +2797,7 @@ void VK_R_RenderView (void) rt->rpassflags |= RP_MULTISAMPLE; if (r_refdef.flags&RDF_SCENEGAMMA) //if we're doing scenegamma here, use an fp16 target for extra precision rt->rpassflags |= RP_FP16; - VKBE_RT_Gen(rt, r_refdef.pxrect.width, r_refdef.pxrect.height, false, (r_renderscale.value < 0)?RT_IMAGEFLAGS-IF_LINEAR+IF_NEAREST:RT_IMAGEFLAGS); + VKBE_RT_Gen(rt, NULL, r_refdef.pxrect.width, r_refdef.pxrect.height, false, (r_renderscale.value < 0)?RT_IMAGEFLAGS-IF_LINEAR+IF_NEAREST:RT_IMAGEFLAGS); } else rt = rtscreen; @@ -2838,7 +2892,7 @@ void VK_R_RenderView (void) { rt = &postproc[postproc_buf++]; rt->rpassflags = 0; - VKBE_RT_Gen(rt, 320, 200, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS); } else rt = rtscreen; @@ -2860,7 +2914,7 @@ void VK_R_RenderView (void) { rt = &postproc[postproc_buf++]; rt->rpassflags = 0; - VKBE_RT_Gen(rt, 320, 200, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS); } else rt = rtscreen; @@ -2879,7 +2933,7 @@ void VK_R_RenderView (void) { rt = &postproc[postproc_buf++]; rt->rpassflags = 0; - VKBE_RT_Gen(rt, 320, 200, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS); } else rt = rtscreen; @@ -2899,7 +2953,7 @@ void VK_R_RenderView (void) { rt = &postproc[postproc_buf++]; rt->rpassflags = 0; - VKBE_RT_Gen(rt, 320, 200, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS); } else rt = rtscreen; @@ -4274,6 +4328,47 @@ void VK_CheckTextureFormats(void) sh_config.hw_astc = 2; //the core vulkan formats refer to the ldr profile. hdr is a separate extension, which is still not properly specified.. } +//creates a vulkan instance with the additional extensions, and hands a copy of the instance to the caller. +qboolean VK_CreateInstance(vrsetup_t *info, char *vrexts, void *result) +{ + VkInstanceCreateInfo inst_info = *(VkInstanceCreateInfo*)info->userctx; + VkResult err; + const char *ext[64]; + unsigned int numext = inst_info.enabledExtensionCount; + memcpy(ext, inst_info.ppEnabledExtensionNames, numext*sizeof(*ext)); + while (vrexts && numext < countof(ext)) + { + ext[numext++] = vrexts; + vrexts = strchr(vrexts, ' '); + if (!vrexts) + break; + *vrexts++ = 0; + } + + err = vkCreateInstance(&inst_info, vkallocationcb, &vk.instance); + switch(err) + { + case VK_ERROR_INCOMPATIBLE_DRIVER: + Con_Printf("VK_ERROR_INCOMPATIBLE_DRIVER: please install an appropriate vulkan driver\n"); + return false; + case VK_ERROR_EXTENSION_NOT_PRESENT: + Con_Printf("VK_ERROR_EXTENSION_NOT_PRESENT: something on a system level is probably misconfigured\n"); + return false; + case VK_ERROR_LAYER_NOT_PRESENT: + Con_Printf("VK_ERROR_LAYER_NOT_PRESENT: requested layer is not known/usable\n"); + return false; + default: + Con_Printf("Unknown vulkan instance creation error: %x\n", err); + return false; + case VK_SUCCESS: + break; + } + + if (result) + *(VkInstance*)result = vk.instance; + return true; +} + //initialise the vulkan instance, context, device, etc. qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe)) { @@ -4285,6 +4380,9 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre const char *extensions[8]; uint32_t extensions_count = 0; + qboolean okay; + vrsetup_t vrsetup = {sizeof(vrsetup)}; + //device extensions that want to enable //initialised in reverse order, so superseeded should name later extensions. struct @@ -4312,9 +4410,6 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre for (e = 0; e < countof(knowndevexts); e++) *knowndevexts[e].flag = false; -#ifdef MULTITHREAD - vk.allowsubmissionthread = true; -#endif vk.neednewswapchain = true; vk.triplebuffer = info->triplebuffer; vk.vsync = info->wait; @@ -4326,7 +4421,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre #ifdef VK_NO_PROTOTYPES if (!vkGetInstanceProcAddr) { - Con_Printf("vkGetInstanceProcAddr is null\n"); + Con_Printf(CON_ERROR"vkGetInstanceProcAddr is null\n"); return false; } #define VKFunc(n) vk##n = (PFN_vk##n)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk"#n); @@ -4395,7 +4490,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre if (sysextnames && (!vk.khr_swapchain || !surfext)) { - Con_Printf("Vulkan instance lacks driver support for %s\n", sysextnames[0]); + Con_Printf(CON_ERROR"Vulkan instance lacks driver support for %s\n", sysextnames[0]); return false; } } @@ -4418,24 +4513,33 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre inst_info.enabledExtensionCount = extensions_count; inst_info.ppEnabledExtensionNames = extensions; - err = vkCreateInstance(&inst_info, vkallocationcb, &vk.instance); - switch(err) + vrsetup.vrplatform = VR_VULKAN; + vrsetup.userctx = &inst_info; + vrsetup.createinstance = VK_CreateInstance; + if (info->vr) { - case VK_ERROR_INCOMPATIBLE_DRIVER: - Con_Printf("VK_ERROR_INCOMPATIBLE_DRIVER: please install an appropriate vulkan driver\n"); - return false; - case VK_ERROR_EXTENSION_NOT_PRESENT: - Con_Printf("VK_ERROR_EXTENSION_NOT_PRESENT: something on a system level is probably misconfigured\n"); - return false; - case VK_ERROR_LAYER_NOT_PRESENT: - Con_Printf("VK_ERROR_LAYER_NOT_PRESENT: requested layer is not known/usable\n"); - return false; - default: - Con_Printf("Unknown vulkan instance creation error: %x\n", err); - return false; - case VK_SUCCESS: - break; + okay = info->vr->Prepare(&vrsetup); + if (!okay) + { + info->vr->Shutdown(); + info->vr = NULL; + } } + else + okay = false; + if (!okay) + okay = vrsetup.createinstance(&vrsetup, NULL, NULL); + if (!okay) + { + if (info->vr) + info->vr->Shutdown(); + return false; + } + vid.vr = info->vr; + +#ifdef MULTITHREAD + vk.allowsubmissionthread = !vid.vr; +#endif //third set of functions... #ifdef VK_NO_PROTOTYPES @@ -4510,7 +4614,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre vkEnumeratePhysicalDevices(vk.instance, &gpucount, NULL); if (!gpucount) { - Con_Printf("vulkan: no devices known!\n"); + Con_Printf(CON_ERROR"vulkan: no devices known!\n"); return false; } devs = malloc(sizeof(VkPhysicalDevice)*gpucount); @@ -4564,7 +4668,12 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre pri = 4; break; } - if (wantdev >= 0) + if (vrsetup.vk.physicaldevice != VK_NULL_HANDLE) + { //if we're using vr, use the gpu our vr context requires. + if (devs[i] == vrsetup.vk.physicaldevice) + pri = 0; + } + else if (wantdev >= 0) { if (wantdev == i) pri = 0; @@ -4586,7 +4695,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre if (!vk.gpu) { - Con_Printf("vulkan: unable to pick a usable device\n"); + Con_Printf(CON_ERROR"vulkan: unable to pick a usable device\n"); return false; } } @@ -4708,7 +4817,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre if (vk.queuefam[0] == ~0u || vk.queuefam[1] == ~0u) { free(queueprops); - Con_Printf("unable to find suitable queues\n"); + Con_Printf(CON_ERROR"vulkan: unable to find suitable queues\n"); return false; } } @@ -4859,7 +4968,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre switch(err) { case VK_ERROR_INCOMPATIBLE_DRIVER: - Con_Printf("VK_ERROR_INCOMPATIBLE_DRIVER: please install an appropriate vulkan driver\n"); + Con_Printf(CON_ERROR"VK_ERROR_INCOMPATIBLE_DRIVER: please install an appropriate vulkan driver\n"); return false; case VK_ERROR_EXTENSION_NOT_PRESENT: case VK_ERROR_FEATURE_NOT_PRESENT: @@ -4867,10 +4976,10 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre case VK_ERROR_DEVICE_LOST: case VK_ERROR_OUT_OF_HOST_MEMORY: case VK_ERROR_OUT_OF_DEVICE_MEMORY: - Con_Printf("%s: something on a system level is probably misconfigured\n", VK_VKErrorToString(err)); + Con_Printf(CON_ERROR"%s: something on a system level is probably misconfigured\n", VK_VKErrorToString(err)); return false; default: - Con_Printf("Unknown vulkan device creation error: %x\n", err); + Con_Printf(CON_ERROR"Unknown vulkan device creation error: %x\n", err); return false; case VK_SUCCESS: break; @@ -4887,6 +4996,19 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre vkGetDeviceQueue(vk.device, vk.queuefam[0], vk.queuenum[0], &vk.queue_render); vkGetDeviceQueue(vk.device, vk.queuefam[1], vk.queuenum[1], &vk.queue_present); + vrsetup.vk.instance = vk.instance; + vrsetup.vk.device = vk.device; + vrsetup.vk.physicaldevice = vk.gpu; + vrsetup.vk.queuefamily = vk.queuefam[1]; + vrsetup.vk.queueindex = vk.queuenum[1]; + if (vid.vr) + { + if (!vid.vr->Init(&vrsetup, info)) + { + vid.vr->Shutdown(); + vid.vr = NULL; + } + } vkGetPhysicalDeviceMemoryProperties(vk.gpu, &vk.memory_properties); @@ -4980,7 +5102,7 @@ void VK_Shutdown(void) VK_DestroySwapChain(); for (i = 0; i < countof(postproc); i++) - VKBE_RT_Gen(&postproc[i], 0, 0, false, RT_IMAGEFLAGS); + VKBE_RT_Gen(&postproc[i], NULL, 0, 0, false, RT_IMAGEFLAGS); VKBE_RT_Gen_Cube(&vk_rt_cubemap, 0, false); VK_R_BloomShutdown(); diff --git a/engine/vk/vkrenderer.h b/engine/vk/vkrenderer.h index 137354f5..3dd88f2a 100644 --- a/engine/vk/vkrenderer.h +++ b/engine/vk/vkrenderer.h @@ -236,7 +236,6 @@ enum dynbuf_e }; struct vk_rendertarg { - VkCommandBuffer cbuf; //cbuf allocated for this render target. VkFramebuffer framebuffer; vk_image_t colour, depth, mscolour; @@ -252,6 +251,7 @@ struct vk_rendertarg VkRenderPassBeginInfo restartinfo; VkSemaphore presentsemaphore; qboolean firstuse; + qboolean externalimage; struct vk_rendertarg *prevtarg; }; @@ -483,7 +483,7 @@ void VKBE_BeginShadowmapFace(void); void VKBE_DoneShadows(void); void VKBE_RT_Gen_Cube(struct vk_rendertarg_cube *targ, uint32_t size, qboolean clear); -void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qboolean clear, unsigned int flags); +void VKBE_RT_Gen(struct vk_rendertarg *targ, vk_image_t *colour, uint32_t width, uint32_t height, qboolean clear, unsigned int flags); void VKBE_RT_Begin(struct vk_rendertarg *targ); void VKBE_RT_End(struct vk_rendertarg *targ); void VKBE_RT_Destroy(struct vk_rendertarg *targ); diff --git a/plugins/Makefile b/plugins/Makefile index d6054cd4..b6b888a7 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -103,6 +103,7 @@ clean: ezscript-clean qi-clean hud-clean irc-clean help: @-echo make a subdirectory +###################################### #small script to download+install avformat for windows cross compiles. #linux users are expected to have the library installed locally already. If your version is too old or missing, run the following command to install it (to /usr/local), then delete the gz and directory. #wget http://ffmpeg.org/releases/ffmpeg-1.2.tar.gz && cd tar xvfz ffmpeg-1.2.tar.gz && cd ffmpeg-1.2/ && ./configure --disable-yasm --enable-shared && make && sudo make install @@ -189,7 +190,9 @@ $(PLUG_PREFIX)ffmpeg$(PLUG_NATIVE_EXT): $(AV_DEP) $(FFMPEG_ZIP) $(PLUG_PREFIX)ffmpeg$(PLUG_NATIVE_EXT): $(AVPLUG_OBJS) $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -s -o $@ -shared $(PLUG_CFLAGS) $(AV_CFLAGS) $(AVPLUG_OBJS) $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(AV_LDFLAGS) +###################################### +###################################### #small script for ode #FIXME: ode fails to compile under cygwin #FIXME: race condition if you try compiling debug+release at the same time, as this makefile is invoked twice by the engine's one @@ -212,7 +215,9 @@ ODE_FILES=../engine/common/com_phys_ode.c ../engine/common/mathlib.c plugin.c $( $(PLUG_PREFIX)ode$(PLUG_NATIVE_EXT): $(ODE_FILES) $(CC) -flto -s $(BASE_CFLAGS) $(CFLAGS) -Os -DFTEPLUGIN -DODE_STATIC -o $@ -shared $(PLUG_CFLAGS) -I$(ODE_BASE)ode-$(ODE_VER)/include $(ODE_FILES) $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -static-libgcc `$(CC) -print-file-name=libstdc++.a` -lpthread #NATIVE_PLUGINS+=ode +###################################### +###################################### ifneq ($(CMAKERULES),) BULLET_CFLAGS+=-static-libstdc++ PLUG_CMAKE+= -DCMAKE_TOOLCHAIN_FILE="$(CMAKERULES)" @@ -255,23 +260,9 @@ $(BULLET_LIB): $(OUT_DIR)/../bullet3-$(BULLET_VER).tar.gz $(CMAKERULES) $(PLUG_PREFIX)bullet$(PLUG_NATIVE_EXT): bullet/bulletplug.cpp plugin.c $(BULLET_LIBS) $(CXX) $(BASE_CXXFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(BULLET_CFLAGS) #NATIVE_PLUGINS+=bullet +###################################### - --include Makefile.private - -$(PLUG_PREFIX)mpq$(PLUG_NATIVE_EXT): mpq/fs_mpq.c mpq/blast.c plugin.c - $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Impq $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS_ZLIB) $(PLUG_LDFLAGS) -NATIVE_PLUGINS+=mpq - -$(PLUG_PREFIX)xmpp$(PLUG_NATIVE_EXT): jabber/jabberclient.c jabber/jingle.c jabber/sift.c jabber/xml.c plugin.c ../engine/common/sha1.c emailnot/md5.c - $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Ijabber $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(LIBRESOLV) -NATIVE_PLUGINS+=xmpp - -$(PLUG_PREFIX)qi$(PLUG_NATIVE_EXT): qi/qi.c jabber/xml.c plugin.c - $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Ijabber $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -NATIVE_PLUGINS+=qi - - +###################################### #Regarding CEF Versions: Cef 2526 is reportedly the most recent _WORKING_ version of libcef. Later versions have screwed webgl etc, and are just generally unstable. #However, that's now impossible to get hold of since the old cefbuilds server went down. New builds are hosted by spotify and they're all randomly broken, so we might as well just use whatever seems fairly recent. @@ -314,18 +305,49 @@ else $(PLUG_PREFIX)cef$(PLUG_NATIVE_EXT): echo cef plugin not supported on this arch endif +###################################### + +#for custom/private plugins... +-include Makefile.private + +#small plugins with simpler build rules... + +$(PLUG_PREFIX)mpq$(PLUG_NATIVE_EXT): mpq/fs_mpq.c mpq/blast.c plugin.c + $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Impq $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS_ZLIB) $(PLUG_LDFLAGS) +NATIVE_PLUGINS+=mpq + +$(PLUG_PREFIX)xmpp$(PLUG_NATIVE_EXT): jabber/jabberclient.c jabber/jingle.c jabber/sift.c jabber/xml.c plugin.c ../engine/common/sha1.c emailnot/md5.c + $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Ijabber $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(LIBRESOLV) +NATIVE_PLUGINS+=xmpp + +$(PLUG_PREFIX)qi$(PLUG_NATIVE_EXT): qi/qi.c jabber/xml.c plugin.c + $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Ijabber $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) +NATIVE_PLUGINS+=qi + + $(PLUG_PREFIX)irc$(PLUG_NATIVE_EXT): irc/ircclient.c plugin.c $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Iirc $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) NATIVE_PLUGINS+=irc +#OpenXR plugin... +$(PLUG_PREFIX)openxr$(PLUG_NATIVE_EXT): openxr.c plugin.c + $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -DXR_NO_PROTOTYPES `pkg-config --cflags openxr` -DGLQUAKE -DVKQUAKE -DD3D11QUAKE +#ifeq ($(shell pkg-config --exists openxr && echo 1),1) +#NATIVE_PLUGINS+=openxr +#endif + #for compat with ezquake $(PLUG_PREFIX)ezhud$(PLUG_NATIVE_EXT): ezhud/ezquakeisms.c ezhud/hud.c ezhud/hud_common.c ezhud/hud_editor.c plugin.c $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Iezhud $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) NATIVE_PLUGINS+=ezhud +#not really relevant now that gltf was made an internal plugin $(PLUG_PREFIX)models$(PLUG_NATIVE_EXT): models/gltf.c models/exportiqm.c models/models.c plugin.c $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Imodels $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -NATIVE_PLUGINS+=models +#NATIVE_PLUGINS+=models + + + all: $(foreach FOO,$(NATIVE_PLUGINS), $(PLUG_PREFIX)$(FOO)$(PLUG_NATIVE_EXT)) diff --git a/plugins/cef/cef.c b/plugins/cef/cef.c index 0fbddaf0..e85e3ddf 100644 --- a/plugins/cef/cef.c +++ b/plugins/cef/cef.c @@ -2,18 +2,41 @@ #include "../plugin.h" #include "../engine.h" +static plugfsfuncs_t *fsfuncs; +static plugsubconsolefuncs_t *confuncs; +static plugclientfuncs_t *clientfuncs; + +#pragma GCC diagnostic ignored "-Wstrict-prototypes" //not my bug. + //libcef defs #include "include/cef_version.h" #include "include/capi/cef_app_capi.h" #include "include/capi/cef_client_capi.h" +#include "include/capi/cef_parser_capi.h" +#include "include/capi/cef_request_context_handler_capi.h" //#include "include/capi/cef_url_capi.h" #include "assert.h" #if defined(_DEBUG) && defined(_MSC_VER) #include #endif +#ifndef _WIN32 +#include +#include +#endif +#define EXPECTED_COMMIT_NUMBER 2179 //last version of libcef we tried building against... +#if EXPECTED_COMMIT_NUMBER != EXPECTED_COMMIT_NUMBER + #warning "libcef version different from expected. expect problems with libcef's unstable API." +#endif + +#define cef_addref(ptr) (ptr)->base.add_ref(&(ptr)->base) +#define cef_release(ptr) (((ptr)->base.release)(&(ptr)->base)) + +#ifndef LIBCEF_STATIC + #define LIBCEF_DYNAMIC +#endif +#ifdef LIBCEF_DYNAMIC //avoid conflicts with cef headers -#define cef_api_hash pcef_api_hash #define cef_version_info pcef_version_info #define cef_initialize pcef_initialize #define cef_do_message_loop_work pcef_do_message_loop_work @@ -44,17 +67,12 @@ #define cef_string_list_size pcef_string_list_size #define cef_string_list_value pcef_string_list_value -#define cef_addref(ptr) (ptr)->base.add_ref(&(ptr)->base) -#define cef_release(ptr) (((ptr)->base.release)(&(ptr)->base)) - - -static const char* (*cef_api_hash)(int entry); static int (*cef_version_info)(int entry); static int (*cef_initialize)(const struct _cef_main_args_t* args, const cef_settings_t* settings, cef_app_t* application, void* windows_sandbox_info); static void (*cef_do_message_loop_work)(void); static void (*cef_shutdown)(void); static int (*cef_execute_process)(const cef_main_args_t* args, cef_app_t* application, void* windows_sandbox_info); -static cef_browser_t* (*cef_browser_host_create_browser_sync)(const cef_window_info_t* windowInfo, cef_client_t* client, const cef_string_t* url, const cef_browser_settings_t* settings, cef_request_context_t* request_context); +static cef_browser_t* (*cef_browser_host_create_browser_sync)(const cef_window_info_t* windowInfo, cef_client_t* client, const cef_string_t* url, const cef_browser_settings_t* settings, cef_dictionary_value_t* extra_info, cef_request_context_t* request_context); static int (*cef_string_utf8_to_utf16)(const char* src, size_t src_len, cef_string_utf16_t* output); static int (*cef_string_utf16_to_utf8)(const char16* src, size_t src_len, cef_string_utf8_t* output); static void (*cef_string_utf16_clear)(cef_string_utf16_t* str); @@ -79,16 +97,6 @@ static void (*cef_string_multimap_free)(cef_string_multimap_t map); static size_t (*cef_string_list_size)(cef_string_list_t list); static int (*cef_string_list_value)(cef_string_list_t list, size_t index, cef_string_t* value); - - - -#ifndef CEF_VERSION //old builds lack this -#define CEF_VERSION "cef"STRINGIFY(CEF_VERSION_MAJOR)"."STRINGIFY(CEF_REVISION)"."STRINGIFY(CHROME_VERSION_BUILD) -#endif -#ifndef CEF_COMMIT_NUMBER -#define CEF_COMMIT_NUMBER CEF_REVISION -#endif - #ifdef _WIN32 //we can't use pSys_LoadLibrary, because plugin builtins do not work unless the engine's plugin system is fully initialised, which doesn't happen in the 'light weight' sub processes, so we'll just roll our own (consistent) version. dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) @@ -169,16 +177,13 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) return (dllhandle_t*)lib; } #endif +#endif - -#define Cvar_Register(v) ((v)->handle = pCvar_Register((v)->name, (v)->string, (v)->flags, (v)->group)) -#define Cvar_Update(v) ((v)->modificationcount = pCvar_Update((v)->handle, &(v)->modificationcount, (v)->string, &(v)->value)) - -vmcvar_t cef_incognito = {"cef_incognito", "0", "browser settings", 0}; -vmcvar_t cef_allowplugins = {"cef_allowplugins", "0", "browser settings", 0}; -vmcvar_t cef_allowcvars = {"cef_allowcvars", "0", "browser settings", 0}; -vmcvar_t cef_devtools = {"cef_devtools", "0", "browser settings", 0}; +static cvar_t *cef_incognito; +static cvar_t *cef_allowplugins; +static cvar_t *cef_allowcvars; +static cvar_t *cef_devtools; static char plugname[MAX_OSPATH]; static char *newconsole; @@ -317,14 +322,10 @@ static int browser_release(browser_t *br) return false; } -#if CEF_COMMIT_NUMBER >= 1658 //not sure what number it is. - #define cef_base_t cef_base_ref_counted_t -#endif - #define browser_subs(sub) \ - static void CEF_CALLBACK browser_##sub##_addref(cef_base_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); browser_addref(br);}; \ - static int CEF_CALLBACK browser_##sub##_release(cef_base_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); return browser_release(br);}; \ - static int CEF_CALLBACK browser_##sub##_hasoneref(cef_base_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); return br->refcount == 1;}; + static void CEF_CALLBACK browser_##sub##_addref(cef_base_ref_counted_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); browser_addref(br);}; \ + static int CEF_CALLBACK browser_##sub##_release(cef_base_ref_counted_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); return browser_release(br);}; \ + static int CEF_CALLBACK browser_##sub##_hasoneref(cef_base_ref_counted_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); return br->refcount == 1;}; browser_subs(client); browser_subs(render_handler); browser_subs(display_handler); @@ -371,8 +372,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe return false; else if (!strncmp(req, "getcvar_", 8)) { - Cvar_Update(&cef_allowcvars); - if (cef_allowcvars.value && pCvar_GetString(req+8, buffer, buffersize)) + if (cef_allowcvars->value && cvarfuncs->GetString(req+8, buffer, buffersize)) return true; } else if (!strncmp(req, "setcvar_", 8)) @@ -383,10 +383,9 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe else eq = req+strlen(req); - Cvar_Update(&cef_allowcvars); - if (cef_allowcvars.value) + if (cef_allowcvars->value) { - pCvar_SetString(req+8, eq); + cvarfuncs->SetString(req+8, eq); *buffer = 0; return true; } @@ -396,7 +395,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe //FIXME: should be more than just a one-off. unsigned int stats[256], i, m; char *e = buffer; - m = pCL_GetStats(0, stats, countof(stats)); + m = clientfuncs->GetStats(0, stats, countof(stats)); if (!m) { m = 0; @@ -423,7 +422,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe char *e = buffer; int players[MAX_SPLITS]; int tracks[MAX_SPLITS]; - int seats = pGetLocalPlayerNumbers(0, MAX_SPLITS, players, tracks); + int seats = clientfuncs->GetLocalPlayerNumbers(0, MAX_SPLITS, players, tracks); *e++ = '['; for (i = 0; i < seats; i++) { @@ -440,7 +439,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe { char serverinfo[4096]; char *e = buffer; - pGetServerInfo(serverinfo, sizeof(serverinfo)); + clientfuncs->GetServerInfo(serverinfo, sizeof(serverinfo)); e = Info_JSONify(serverinfo, e, buffer + buffersize - e-1); if (e == buffer) e++; *buffer = '{'; @@ -458,8 +457,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe *e++ = '['; for (i = 0; ; i++) { - if (!pGetPlayerInfo(i, &info)) - break; + clientfuncs->GetPlayerInfo(i, &info); if (buffer + buffersize - e-1 < 100) break; @@ -468,7 +466,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe *e++ = ','; *e++ = '{'; //splurge the specific info - sprintf(e, "\"frags\":%i,\"ping\":%i,\"pl\":%i,\"start\":%i,\"userid\":%i", info.frags, info.ping, info.pl, info.starttime, info.userid); + sprintf(e, "\"frags\":%i,\"ping\":%i,\"pl\":%i,\"active\":%i,\"userid\":%i", info.frags, info.ping, info.pl, info.activetime, info.userid); e += strlen(e); //splurge the generic info (colours, name, team) e = Info_JSONify(info.userinfo, e, buffer + buffersize - e-1); @@ -483,7 +481,7 @@ static qboolean browser_handle_query(const char *req, char *buffer, size_t buffe return false; } -static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self, cef_browser_t* browser, cef_process_id_t source_process, cef_process_message_t* message) +static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_process_id_t source_process, cef_process_message_t* message) { int handled = false; // browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler)); @@ -523,7 +521,7 @@ static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self, cef_string_utf8_clear(&queryname); if (cmdunusable) cef_string_userfree_free(cmdunusable); - browser->send_process_message(browser, source_process, reply); + frame->send_process_message(frame, source_process, reply); handled = true; } cef_release(message); @@ -536,7 +534,7 @@ static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self, } //render_handler methods -static int CEF_CALLBACK browser_get_view_rect(cef_render_handler_t *self, cef_browser_t *browser, cef_rect_t *rect) +static void CEF_CALLBACK browser_get_view_rect(cef_render_handler_t *self, cef_browser_t *browser, cef_rect_t *rect) { browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, render_handler)); @@ -545,7 +543,6 @@ static int CEF_CALLBACK browser_get_view_rect(cef_render_handler_t *self, cef_br rect->width = br->desiredwidth; rect->height = br->desiredheight; cef_release(browser); - return true; } static void CEF_CALLBACK browser_on_paint(cef_render_handler_t *self, cef_browser_t *browser, cef_paint_element_type_t type, size_t dirtyRectsCount, cef_rect_t const* dirtyRects, const void* buffer, int width, int height) { @@ -639,7 +636,7 @@ static void CEF_CALLBACK browser_on_before_context_menu(struct _cef_context_menu //display_handler methods //redirect console.log messages to quake's console, but only display them if we've got developer set. -static int CEF_CALLBACK browser_on_console_message(cef_display_handler_t* self, cef_browser_t* browser, const cef_string_t* message, const cef_string_t* source, int line) +static int CEF_CALLBACK browser_on_console_message(cef_display_handler_t* self, cef_browser_t* browser, cef_log_severity_t level, const cef_string_t* message, const cef_string_t* source, int line) { cef_string_utf8_t u8_source = {NULL}; cef_string_utf8_t u8_message = {NULL}; @@ -664,7 +661,7 @@ static void CEF_CALLBACK browser_on_title_change(cef_display_handler_t* self, ce cef_string_utf8_copy(br->currenturl.str, br->currenturl.length, &br->currenttitle); if (br->consolename) - pCon_SetConsoleString(br->consolename, "title", br->currenttitle.str?br->currenttitle.str:""); + confuncs->SetConsoleString(br->consolename, "title", br->currenttitle.str?br->currenttitle.str:""); cef_release(browser); } @@ -683,7 +680,7 @@ static void CEF_CALLBACK browser_on_favicon_urlchange(cef_display_handler_t* sel cef_string_clear(&str); if (br->consolename) - pCon_SetConsoleString(br->consolename, "icon", br->currenticon.str?br->currenticon.str:""); + confuncs->SetConsoleString(br->consolename, "icon", br->currenticon.str?br->currenticon.str:""); cef_release(browser); } @@ -693,7 +690,7 @@ static void CEF_CALLBACK browser_on_fullscreenmode_change(cef_display_handler_t* br->fullscreen = fullscreen; if (br->consolename) - pCon_SetConsoleFloat(br->consolename, "fullscreen", br->fullscreen); + confuncs->SetConsoleFloat(br->consolename, "fullscreen", br->fullscreen); cef_release(browser); } @@ -707,7 +704,7 @@ static void CEF_CALLBACK browser_on_address_change(cef_display_handler_t* self, { cef_string_utf8_clear(&br->currenticon); if (br->consolename) - pCon_SetConsoleString(br->consolename, "icon", br->currenticon.str?br->currenticon.str:""); + confuncs->SetConsoleString(br->consolename, "icon", br->currenticon.str?br->currenticon.str:""); } //FIXME: should probably make sure its the root frame @@ -722,7 +719,7 @@ static int CEF_CALLBACK browser_on_tooltip(cef_display_handler_t* self, cef_brow { cef_string_utf8_t u8_text = {NULL}; cef_string_to_utf8(text->str, text->length, &u8_text); - pCon_SetConsoleString(br->consolename, "tooltip", u8_text.str?u8_text.str:""); + confuncs->SetConsoleString(br->consolename, "tooltip", u8_text.str?u8_text.str:""); cef_string_utf8_clear(&u8_text); } cef_release(browser); @@ -736,7 +733,7 @@ static void CEF_CALLBACK browser_on_status_message(cef_display_handler_t* self, cef_string_utf8_t u8_value = {NULL}; if (value) cef_string_to_utf8(value->str, value->length, &u8_value); - pCon_SetConsoleString(br->consolename, "footer", u8_value.str?u8_value.str:""); + confuncs->SetConsoleString(br->consolename, "footer", u8_value.str?u8_value.str:""); cef_string_utf8_clear(&u8_value); } @@ -744,7 +741,9 @@ static void CEF_CALLBACK browser_on_status_message(cef_display_handler_t* self, } //request_handler methods -static int CEF_CALLBACK browser_on_before_browse(cef_request_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_request_t* request, int is_redirect) + + +static int CEF_CALLBACK browser_on_before_browse(cef_request_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_request_t* request, int user_gesture, int is_redirect) { // browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler)); @@ -752,7 +751,7 @@ static int CEF_CALLBACK browser_on_before_browse(cef_request_handler_t* self, ce cef_release(frame); cef_release(request); - return false; + return false; //false = allow navigation, true = block } static void CEF_CALLBACK browser_on_render_process_terminated(cef_request_handler_t* self, cef_browser_t* browser, cef_termination_status_t status) { @@ -795,7 +794,6 @@ browser_subs(context_menu_handler); nb->client.get_drag_handler = NULL; nb->client.get_find_handler = NULL; nb->client.get_focus_handler = NULL; - nb->client.get_geolocation_handler = NULL; nb->client.get_jsdialog_handler = NULL; nb->client.get_keyboard_handler = NULL; nb->client.get_life_span_handler = browser_get_life_span_handler; @@ -828,15 +826,15 @@ browser_subs(context_menu_handler); nb->request_handler.on_before_browse = browser_on_before_browse; nb->request_handler.on_open_urlfrom_tab = NULL; - nb->request_handler.on_before_resource_load = NULL; - nb->request_handler.get_resource_handler = NULL; - nb->request_handler.on_resource_redirect = NULL; - nb->request_handler.on_resource_response = NULL; - nb->request_handler.get_resource_response_filter = NULL; - nb->request_handler.on_resource_load_complete = NULL; +// nb->request_handler.on_before_resource_load = NULL; +// nb->request_handler.get_resource_handler = NULL; +// nb->request_handler.on_resource_redirect = NULL; +// nb->request_handler.on_resource_response = NULL; +// nb->request_handler.get_resource_response_filter = NULL; +// nb->request_handler.on_resource_load_complete = NULL; nb->request_handler.get_auth_credentials = NULL; nb->request_handler.on_quota_request = NULL; - nb->request_handler.on_protocol_execution = NULL; //FIXME: should implement. +// nb->request_handler.on_protocol_execution = NULL; //FIXME: should implement. nb->request_handler.on_certificate_error = NULL; // nb->request_handler.on_select_client_certificate = NULL; //we have no such certs nb->request_handler.on_plugin_crashed = NULL; @@ -877,11 +875,7 @@ browser_subs(context_menu_handler); static cef_request_context_t *request_context; static cef_request_context_handler_t request_context_handler; //request_context_handler methods -static int CEF_CALLBACK request_context_handler_on_before_plugin_load(cef_request_context_handler_t* self, const cef_string_t* mime_type, const cef_string_t* plugin_url, -#if CEF_COMMIT_NUMBER >= 1658 - int is_main_frame, -#endif - const cef_string_t* top_origin_url, cef_web_plugin_info_t* plugin_info, cef_plugin_policy_t* plugin_policy) +static int CEF_CALLBACK request_context_handler_on_before_plugin_load(cef_request_context_handler_t* self, const cef_string_t* mime_type, const cef_string_t* plugin_url, int is_main_frame, const cef_string_t* top_origin_url, cef_web_plugin_info_t* plugin_info, cef_plugin_policy_t* plugin_policy) { // Con_DPrintf("%s (%s), \"%s\" \"%s\" \"%s\" \"%s\"\n", policy_url.ToString().c_str(), url.ToString().c_str(), // info->GetName().ToString().c_str(), info->GetPath().ToString().c_str(), info->GetVersion().ToString().c_str(), info->GetDescription().ToString().c_str()); @@ -890,8 +884,7 @@ static int CEF_CALLBACK request_context_handler_on_before_plugin_load(cef_reques cef_release(plugin_info); - Cvar_Update(&cef_allowplugins); - if (!cef_allowplugins.value) + if (!cef_allowplugins->value) { *plugin_policy = PLUGIN_POLICY_DISABLE; // Con_Printf("Blocking plugin: %s (%s)\n", info->GetName().ToString().c_str(), url.ToString().c_str()); @@ -921,21 +914,16 @@ static cef_render_process_handler_t* CEF_CALLBACK app_get_render_process_handler static void CEF_CALLBACK app_on_register_custom_schemes(struct _cef_app_t* self, cef_scheme_registrar_t* registrar) { cef_string_t fte = makecefstring("fte"); - registrar->add_custom_scheme(registrar, &fte - , /*is_standard*/true - , /*is_local*/false - , /*is_display_isolated*/true -#if CEF_COMMIT_NUMBER >= 1658 //not sure what number it is. - , /*is_secure*/true - , /*is_cors_enabled*/true //display_isolated means that we shouldn't be getting requests from http anyway, so we don't need CORS protection. - , /*is_csp_bypassing*/false -#endif + registrar->add_custom_scheme(registrar, &fte, CEF_SCHEME_OPTION_NONE + |CEF_SCHEME_OPTION_STANDARD + /*|CEF_SCHEME_OPTION_LOCAL*/ + |CEF_SCHEME_OPTION_DISPLAY_ISOLATED + |CEF_SCHEME_OPTION_SECURE + |CEF_SCHEME_OPTION_CORS_ENABLED + /*|CEF_SCHEME_OPTION_CSP_BYPASSING*/ + /*|CEF_SCHEME_OPTION_FETCH_ENABLED*/ ); cef_string_clear(&fte); - -#ifndef cef_base_t - cef_release(registrar); -#endif } static void CEF_CALLBACK browser_process_handler_on_context_initialized(cef_browser_process_handler_t* self) @@ -992,12 +980,12 @@ typedef struct int64 queryid; atomic_uint32_t refcount; } queryresponse_t; -static void CEF_CALLBACK queryresponse_addref(cef_base_t* self) +static void CEF_CALLBACK queryresponse_addref(cef_base_ref_counted_t* self) { queryresponse_t *qr = (queryresponse_t*)((char*)self - offsetof(queryresponse_t, task.base)); atomic_fetch_add(&qr->refcount, 1); } -static int CEF_CALLBACK queryresponse_release(cef_base_t* self) +static int CEF_CALLBACK queryresponse_release(cef_base_ref_counted_t* self) { queryresponse_t *qr = (queryresponse_t*)((char*)self - offsetof(queryresponse_t, task.base)); if (atomic_fetch_sub(&qr->refcount, 1) == 1) @@ -1113,7 +1101,7 @@ static int CEF_CALLBACK fsfunc_execute(cef_v8handler_t* self, const cef_string_t args->set_int(args, 3, (q->queryid>>32) & 0xffffffff); cef_release(args); - browser->send_process_message(browser, PID_BROWSER, msg); + frame->send_process_message(frame, PID_BROWSER, msg); if (setting) cef_string_userfree_free(setting); @@ -1129,7 +1117,7 @@ static int CEF_CALLBACK fsfunc_execute(cef_v8handler_t* self, const cef_string_t return 1; } -static int CEF_CALLBACK render_process_handler_on_process_message_received(cef_render_process_handler_t* self,cef_browser_t* browser, cef_process_id_t source_process,cef_process_message_t* message) +static int CEF_CALLBACK render_process_handler_on_process_message_received(cef_render_process_handler_t* self,cef_browser_t* browser, cef_frame_t* frame, cef_process_id_t source_process, cef_process_message_t* message) { int handled = false; // browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler)); @@ -1174,12 +1162,12 @@ typedef struct unsigned int resultcode; char *responseheaders; } fteresource_t; -static void CEF_CALLBACK resource_handler_addref(cef_base_t* self) +static void CEF_CALLBACK resource_handler_addref(cef_base_ref_counted_t* self) { fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh.base)); atomic_fetch_add(&rh->refcount, 1); } -static int CEF_CALLBACK resource_handler_release(cef_base_t* self) +static int CEF_CALLBACK resource_handler_release(cef_base_ref_counted_t* self) { fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh.base)); if (atomic_fetch_sub(&rh->refcount, 1) == 1) @@ -1279,7 +1267,7 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t* //(also blocks any fancy http:// parsing that an engine might do) if (!strncmp(u8_url.str, "fte://data/", 11)) { - pVFS_Open(u8_url.str+6, &rh->fh, "rb"); + rh->fh = fsfuncs->OpenVFS(u8_url.str+6, "rb", FS_GAME); if (rh->fh) { cef_string_userfree_t mt = cef_get_mime_type(&ext); @@ -1319,14 +1307,12 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t* rh->resultcode = 404; - if (!BUILTINISVALID(PR_GetVMInstance)) - progs = NULL; - else if (!strncmp(u8_url.str, "fte://ssqc/", 11)) - progs = pPR_GetVMInstance(0); //WARNING: goes direct rather than via the server. + if (!strncmp(u8_url.str, "fte://ssqc/", 11)) + progs = plugfuncs->GetEngineInterface("SSQCVM", sizeof(*progs)); //WARNING: goes direct rather than via the server, so basically single-player only. else if (!strncmp(u8_url.str, "fte://csqc/", 11)) - progs = pPR_GetVMInstance(1); + progs = plugfuncs->GetEngineInterface("CSQCVM", sizeof(*progs)); else if (!strncmp(u8_url.str, "fte://menu/", 11)) - progs = pPR_GetVMInstance(2); + progs = plugfuncs->GetEngineInterface("MenuQCVM", sizeof(*progs)); else progs = NULL; @@ -1535,8 +1521,6 @@ static cef_resource_handler_t* CEF_CALLBACK scheme_handler_factory_create(cef_sc rh->rh.process_request = resource_handler_process_request; rh->rh.get_response_headers = resource_handler_get_response_headers; rh->rh.read_response = resource_handler_read_response; - rh->rh.can_get_cookie = NULL; - rh->rh.can_set_cookie = NULL; rh->rh.cancel = resource_handler_cancel; cef_addref(&rh->rh); @@ -1580,8 +1564,7 @@ cef_request_context_t *Cef_GetRequestContext(void) cef_request_context_t *ret = NULL; qboolean incog; - Cvar_Update(&cef_incognito); - incog = cef_incognito.value; + incog = cef_incognito->value; if (!incog) ret = request_context; @@ -1590,7 +1573,7 @@ cef_request_context_t *Cef_GetRequestContext(void) { cef_request_context_settings_t csettings = {sizeof(csettings)}; csettings.persist_user_preferences = !incog; - if (!incog && pFS_NativePath("cefcache", FS_ROOT, utf8, sizeof(utf8))) + if (!incog && fsfuncs->NativePath("cefcache", FS_ROOT, utf8, sizeof(utf8))) cef_string_from_utf8(utf8, strlen(utf8), &csettings.cache_path); //should be empty for incognito. ret = cef_request_context_create_context(&csettings, &request_context_handler); cef_string_clear(&csettings.cache_path); @@ -1644,16 +1627,24 @@ static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks) //const char *ua = "FTEBrowser"; //cef_string_from_utf8(ua, strlen(ua), &settings.user_agent); - if (pFS_NativePath("cefcache", FS_ROOT, utf8, sizeof(utf8))) + if (fsfuncs->NativePath("cefcache", FS_ROOT, utf8, sizeof(utf8))) cef_string_from_utf8(utf8, strlen(utf8), &settings.cache_path); - if (pFS_NativePath("cef_debug.log", FS_ROOT, utf8, sizeof(utf8))) + if (fsfuncs->NativePath("cef_debug.log", FS_ROOT, utf8, sizeof(utf8))) cef_string_from_utf8(utf8, strlen(utf8), &settings.log_file); - if (pFS_NativePath("", FS_BINARYPATH, utf8, sizeof(utf8))) + if (fsfuncs->NativePath("", FS_BINARYPATH, utf8, sizeof(utf8))) cef_string_from_utf8(utf8, strlen(utf8), &settings.resources_dir_path); - if (pFS_NativePath("locales", FS_BINARYPATH, utf8, sizeof(utf8))) + if (fsfuncs->NativePath("locales", FS_BINARYPATH, utf8, sizeof(utf8))) + { + struct stat statbuf; + if (stat(utf8, &statbuf) < 0) + { + Con_Printf("%s not found\n", utf8); + return NULL; + } cef_string_from_utf8(utf8, strlen(utf8), &settings.locales_dir_path); + } #ifdef _WIN32 { @@ -1696,14 +1687,9 @@ static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks) if (!cefwasinitialised) return NULL; - Cvar_Update(&cef_allowplugins); - //tbh, web browser's are so horribly insecure that it seems pointless to even try disabling stuff that might be useful browserSettings.windowless_frame_rate = 60; browserSettings.javascript_close_windows = STATE_DISABLED; -#if CEF_COMMIT_NUMBER < 1658 - browserSettings.javascript_open_windows = STATE_DISABLED; -#endif browserSettings.javascript_access_clipboard = STATE_DISABLED; // browserSettings.universal_access_from_file_urls = STATE_DISABLED; // browserSettings.file_access_from_file_urls = STATE_DISABLED; @@ -1713,9 +1699,6 @@ static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks) window_info.windowless_rendering_enabled = true; memset(&window_info.parent_window, 0, sizeof(window_info.parent_window)); -#if CEF_COMMIT_NUMBER < 1658 - window_info.transparent_painting_enabled = true; -#endif newbrowser = browser_create(); if (!newbrowser) @@ -1726,7 +1709,7 @@ static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks) cef_string_from_utf8(name, strlen(name), &url); cef_addref(&newbrowser->client); - newbrowser->thebrowser = pcef_browser_host_create_browser_sync(&window_info, &newbrowser->client, &url, &browserSettings, Cef_GetRequestContext()); + newbrowser->thebrowser = cef_browser_host_create_browser_sync(&window_info, &newbrowser->client, &url, &browserSettings, NULL, Cef_GetRequestContext()); cef_string_to_utf8(url.str, url.length, &newbrowser->currenturl); cef_string_clear(&url); if (!newbrowser->thebrowser) @@ -1735,8 +1718,7 @@ static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks) return NULL; //cef fucked up. } - Cvar_Update(&cef_devtools); - if (cef_devtools.value) + if (cef_devtools->value) { cef_browser_host_t *host = newbrowser->thebrowser->get_host(newbrowser->thebrowser); browser_t *devtools = browser_create(); @@ -1759,11 +1741,7 @@ static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks) window_info.windowless_rendering_enabled = false; cef_addref(&devtools->client); - host->show_dev_tools(host, &window_info, &devtools->client, &browserSettings -#if CEF_COMMIT_NUMBER >= 1373 //not sure about the actual revision - , NULL -#endif - ); + host->show_dev_tools(host, &window_info, &devtools->client, &browserSettings, NULL); cef_release(host); browser_release(devtools); //cef should continue to hold a reference to it while its visible, but its otherwise out of engine now. @@ -1910,7 +1888,7 @@ static void VARGS Cef_Key (void *ctx, int code, int unicode, int event) if (browser->keystate[code]) kev.native_key_code |= 1<<30; if (event) - kev.native_key_code |= 1<<31; + kev.native_key_code |= 1u<<31; if (event) browser->keystate[code] = 0; @@ -1938,7 +1916,7 @@ static void VARGS Cef_Key (void *ctx, int code, int unicode, int event) if (browser->keystate[code]) kev.native_key_code |= 1<<30; if (event) - kev.native_key_code |= 1<<31; + kev.native_key_code |= 1u<<31; kev.is_system_key = 0; kev.character = unicode; @@ -2024,7 +2002,7 @@ static void VARGS Cef_ChangeStream (void *ctx, const char *streamname) cef_string_clear(&thescript); cef_string_clear(&url); } - else if (!strncmp(streamname, "raw:", 4)) + /*else if (!strncmp(streamname, "raw:", 4)) { cef_string_t thehtml = {NULL}; cef_string_t url = {NULL}; @@ -2034,7 +2012,7 @@ static void VARGS Cef_ChangeStream (void *ctx, const char *streamname) frame->load_string(frame, &thehtml, &url); cef_string_clear(&thehtml); cef_string_clear(&url); - } + }*/ else if (*streamname && strcmp(streamname, "http:") && strcmp(streamname, "https:")) { cef_string_t url = {NULL}; @@ -2139,25 +2117,31 @@ static qintptr_t Cef_Shutdown(qintptr_t *args) } #ifndef _WIN32 -int argc=0; -char *argv[64]; -char commandline[8192]; -static void SetupArgv(void) +static int argc=0; +static char *argv[64]; +static char commandline[8192]; +static void SetupArgv(cef_main_args_t *a) { - if (argc) - return; - int i; - FILE *f = fopen("/proc/self/cmdline", "r"); - char *e = commandline+fread(commandline, 1, sizeof(commandline), f); - fclose(f); - char *s = commandline; - while(s < e) + FILE *f; + if (!argc) { - argv[argc++] = s; - while(*s) - s++; - s++; + f = fopen("/proc/self/cmdline", "r"); + if (f) + { + char *s = commandline; + char *e = commandline+fread(commandline, 1, sizeof(commandline), f); + fclose(f); + while(s < e) + { + argv[argc++] = s; + while(*s) + s++; + s++; + } + } } + a->argc = argc; + a->argv = argv; } #endif @@ -2167,16 +2151,16 @@ static qboolean Cef_Init(qboolean engineprocess) #ifdef _WIN32 cef_main_args_t args = {GetModuleHandle(NULL)}; #else - SetupArgv(); - cef_main_args_t args = {argc, argv}; + cef_main_args_t args; + SetupArgv(&args); #endif { int result; +#ifdef LIBCEF_DYNAMIC dllfunction_t ceffuncs[] = { - {(void **)&cef_api_hash, "cef_api_hash"}, {(void **)&cef_version_info, "cef_version_info"}, {(void **)&cef_initialize, "cef_initialize"}, {(void **)&cef_do_message_loop_work, "cef_do_message_loop_work"}, @@ -2191,7 +2175,6 @@ static qboolean Cef_Init(qboolean engineprocess) {(void **)&cef_string_utf8_set, "cef_string_utf8_set"}, {(void **)&cef_string_userfree_utf16_free, "cef_string_userfree_utf16_free"}, {(void **)&cef_register_scheme_handler_factory, "cef_register_scheme_handler_factory"}, - {(void **)&cef_get_mime_type, "cef_get_mime_type"}, {(void **)&cef_v8value_create_function, "cef_v8value_create_function"}, {(void **)&cef_v8value_create_string, "cef_v8value_create_string"}, {(void **)&cef_process_message_create, "cef_process_message_create"}, @@ -2214,11 +2197,20 @@ static qboolean Cef_Init(qboolean engineprocess) Con_Printf("Unable to load libcef (version "CEF_VERSION")\n"); return false; } +#endif if (engineprocess) { Con_Printf("libcef %i.%i: chrome %i.%i (%i)\n", cef_version_info(0), cef_version_info(1), cef_version_info(2), cef_version_info(3), cef_version_info(4)); - if (strcmp(cef_api_hash(0), CEF_API_HASH_PLATFORM)) + + if (cef_version_info(0) != CEF_VERSION_MAJOR|| + cef_version_info(1) != CEF_VERSION_MINOR|| + cef_version_info(2) != CEF_VERSION_PATCH|| + cef_version_info(3) != CEF_COMMIT_NUMBER|| + cef_version_info(4) != CHROME_VERSION_MAJOR|| + cef_version_info(5) != CHROME_VERSION_MINOR|| + cef_version_info(6) != CHROME_VERSION_BUILD|| + cef_version_info(7) != CHROME_VERSION_PATCH) { //the libcef api hash can be used to see if there's an api change that might break stuff. //refuse to load it if the api changed. Con_Printf("libcef outdated. Please install libcef version "CEF_VERSION"\n"); @@ -2247,10 +2239,10 @@ int NATIVEEXPORT CefSubprocessInit(void) static qintptr_t Cef_ExecuteCommand(qintptr_t *args) { char cmd[256]; - pCmd_Argv(0, cmd, sizeof(cmd)); + cmdfuncs->Argv(0, cmd, sizeof(cmd)); if (!strcmp(cmd, "cef")) { - if (BUILTINISVALID(Con_SetConsoleFloat)) + if (confuncs) { static int sequence; char f[128]; @@ -2258,18 +2250,18 @@ static qintptr_t Cef_ExecuteCommand(qintptr_t *args) Q_snprintf(f, sizeof(f), "libcef:%i", ++sequence); newconsole = f; strcpy(videomap, "cef:"); - pCmd_Argv(1, videomap+4, sizeof(videomap)-4); + cmdfuncs->Argv(1, videomap+4, sizeof(videomap)-4); if (!videomap[4]) strcpy(videomap, "cef:http://fte.triptohell.info"); - pCon_SetConsoleString(f, "title", videomap+4); - pCon_SetConsoleFloat(f, "iswindow", true); - pCon_SetConsoleFloat(f, "forceutf8", true); - pCon_SetConsoleFloat(f, "wnd_w", 640+16); - pCon_SetConsoleFloat(f, "wnd_h", 480+16+8); - pCon_SetConsoleString(f, "backvideomap", videomap); - pCon_SetConsoleFloat(f, "linebuffered", 2); - pCon_SetActive(f); + confuncs->SetConsoleString(f, "title", videomap+4); + confuncs->SetConsoleFloat(f, "iswindow", true); + confuncs->SetConsoleFloat(f, "forceutf8", true); + confuncs->SetConsoleFloat(f, "wnd_w", 640+16); + confuncs->SetConsoleFloat(f, "wnd_h", 480+16+8); + confuncs->SetConsoleString(f, "backvideomap", videomap); + confuncs->SetConsoleFloat(f, "linebuffered", 2); + confuncs->SetActive(f); newconsole = NULL; } @@ -2278,27 +2270,18 @@ static qintptr_t Cef_ExecuteCommand(qintptr_t *args) return false; } -qintptr_t Plug_Init(qintptr_t *args) +qboolean Plug_Init(void) { - CHECKBUILTIN(Plug_GetPluginName); - CHECKBUILTIN(FS_NativePath); - if (!BUILTINISVALID(Plug_GetPluginName) || !BUILTINISVALID(FS_NativePath)) + fsfuncs = (plugfsfuncs_t*)plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs)); //for fte://data/ scheme + confuncs = (plugsubconsolefuncs_t*)plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs)); //for cef command etc. + clientfuncs = (plugclientfuncs_t*)plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs)); //for weird people trying to use xml requests to query game status (for hud stuff) + if (!fsfuncs || !confuncs || !clientfuncs + || !plugfuncs->ExportFunction("Tick", Cef_Tick) + || !plugfuncs->ExportFunction("Shutdown", Cef_Shutdown)) { - Con_Printf("CEF plugin failed: Engine too old\n"); + Con_Printf("CEF plugin failed: Required engine feature missing.\n"); return false; } - pPlug_GetPluginName(-1, plugname, sizeof(plugname)); - if (!Plug_Export("Tick", Cef_Tick)) - { - Con_Printf("CEF plugin failed: Engine doesn't support Tick feature\n"); - return false; - } - if (!Plug_Export("Shutdown", Cef_Shutdown)) - { - Con_Printf("CEF plugin failed: Engine doesn't support Shutdown feature\n"); - return false; - } - decoderfuncs.structsize = sizeof(media_decoder_funcs_t); decoderfuncs.drivername = "cef"; @@ -2313,19 +2296,19 @@ qintptr_t Plug_Init(qintptr_t *args) decoderfuncs.changestream = Cef_ChangeStream; decoderfuncs.getproperty = Cef_GetProperty; - if (!pPlug_ExportNative("Media_VideoDecoder", &decoderfuncs)) + if (!plugfuncs->ExportInterface("Media_VideoDecoder", &decoderfuncs, sizeof(decoderfuncs))) { Con_Printf("CEF plugin failed: Engine doesn't support media decoder plugins\n"); return false; } - if (Plug_Export("ExecuteCommand", Cef_ExecuteCommand)) - pCmd_AddCommand("cef"); + if (plugfuncs->ExportFunction("ExecuteCommand", Cef_ExecuteCommand)) + cmdfuncs->AddCommand("cef"); - Cvar_Register(&cef_incognito); - Cvar_Register(&cef_allowplugins); - Cvar_Register(&cef_allowcvars); - Cvar_Register(&cef_devtools); + cef_incognito = cvarfuncs->GetNVFDG("cef_incognito", "0", 0, NULL, "browser settings"); + cef_allowplugins = cvarfuncs->GetNVFDG("cef_allowplugins", "0", 0, NULL, "browser settings"); + cef_allowcvars = cvarfuncs->GetNVFDG("cef_allowcvars", "0", 0, NULL, "browser settings"); + cef_devtools = cvarfuncs->GetNVFDG("cef_devtools", "0", 0, NULL, "browser settings"); return true; } diff --git a/plugins/openxr.c b/plugins/openxr.c new file mode 100644 index 00000000..ad3e7cac --- /dev/null +++ b/plugins/openxr.c @@ -0,0 +1,1609 @@ +#include "plugin.h" +static plugfsfuncs_t *fsfuncs; + +#include "../engine/client/vr.h" + +//#define XR_NO_PROTOTYPES + +//figure out which platforms(read: windowing apis) we need... +#if defined(_WIN32) + #define XR_USE_PLATFORM_WIN32 +//#elif defined(ANDROID) +// #define XR_USE_PLATFORM_ANDROID +#else + #ifndef NO_X11 + #define XR_USE_PLATFORM_XLIB + #endif + #if defined(GLQUAKE) && defined(USE_EGL) + //wayland, android, and x11-egl can all just use the EGL path... + //at least once the openxr spec gets fixed (the wayland extension is apparently basically unusable) + //note: XR_MND_egl_enable is a vendor extension to work around openxr stupidly trying to ignore it entirely. + #define XR_USE_PLATFORM_EGL + #endif +#endif + +//figure out which graphics apis we need... +#ifdef GLQUAKE + #define XR_USE_GRAPHICS_API_OPENGL +#endif +#ifdef VKQUAKE + #define XR_USE_GRAPHICS_API_VULKAN +#endif +#ifdef D3D11QUAKE + #ifdef _WIN32 + #define XR_USE_GRAPHICS_API_D3D11 + #endif +#endif + +//include any headers we need for things to make sense. +#ifdef XR_USE_GRAPHICS_API_OPENGL + #include "glquake.h" +#endif +#ifdef XR_USE_GRAPHICS_API_VULKAN + #include "../engine/vk/vkrenderer.h" +#endif +#ifdef XR_USE_GRAPHICS_API_D3D11 + #include +#endif +#ifdef XR_USE_PLATFORM_EGL + #include "gl_videgl.h" +#endif +#ifdef XR_USE_PLATFORM_XLIB + #include +#endif + +//and finally include openxr stuff now that its hopefully not going to fail about missing typedefs. +#include + +#ifdef XR_NO_PROTOTYPES +#define XRFUNCS \ + XRFUNC(xrGetInstanceProcAddr) \ + XRFUNC(xrEnumerateInstanceExtensionProperties) \ + XRFUNC(xrCreateInstance) \ + XRFUNC(xrGetInstanceProperties) \ + XRFUNC(xrGetSystem) \ + XRFUNC(xrGetSystemProperties) \ + XRFUNC(xrEnumerateViewConfigurations) \ + XRFUNC(xrEnumerateViewConfigurationViews) \ + XRFUNC(xrCreateSession) \ + XRFUNC(xrCreateReferenceSpace) \ + XRFUNC(xrCreateActionSet) \ + XRFUNC(xrStringToPath) \ + XRFUNC(xrCreateAction) \ + XRFUNC(xrSuggestInteractionProfileBindings) \ + XRFUNC(xrCreateActionSpace) \ + XRFUNC(xrAttachSessionActionSets) \ + XRFUNC(xrSyncActions) \ + XRFUNC(xrGetActionStatePose) \ + XRFUNC(xrLocateSpace) \ + XRFUNC(xrGetActionStateBoolean) \ + XRFUNC(xrGetActionStateFloat) \ + XRFUNC(xrGetActionStateVector2f) \ + XRFUNC(xrGetCurrentInteractionProfile) \ + XRFUNC(xrEnumerateBoundSourcesForAction) \ + XRFUNC(xrGetInputSourceLocalizedName) \ + XRFUNC(xrPathToString) \ + XRFUNC(xrCreateSwapchain) \ + XRFUNC(xrEnumerateSwapchainFormats) \ + XRFUNC(xrEnumerateSwapchainImages) \ + XRFUNC(xrPollEvent) \ + XRFUNC(xrBeginSession) \ + XRFUNC(xrWaitFrame) \ + XRFUNC(xrBeginFrame) \ + XRFUNC(xrLocateViews) \ + XRFUNC(xrAcquireSwapchainImage) \ + XRFUNC(xrWaitSwapchainImage) \ + XRFUNC(xrReleaseSwapchainImage) \ + XRFUNC(xrEndFrame) \ + XRFUNC(xrRequestExitSession) \ + XRFUNC(xrEndSession) \ + XRFUNC(xrDestroySwapchain) \ + XRFUNC(xrDestroySpace) \ + XRFUNC(xrDestroySession) \ + XRFUNC(xrDestroyInstance) +#define XRFUNC(n) static PFN_##n n; +XRFUNCS +#undef XRFUNC +#endif + +#ifdef SVNREVISION + #define APPLICATIONVERSION atoi(STRINGIFY(SVNREVISION)) + #define ENGINEVERSION atoi(STRINGIFY(SVNREVISION)) +#else + #define APPLICATIONVERSION 0 + #define ENGINEVERSION 0 +#endif + +static cvar_t *xr_enable; +static cvar_t *xr_formfactor; +static cvar_t *xr_viewconfig; +static cvar_t *xr_metresize; +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 XR_PoseToTransform(XrPosef *pose, vec3_t axisorg[4]) +{ + float xx, xy, xz, xw, yy, yz, yw, zz, zw; + float x2, y2, z2; + x2 = pose->orientation.x + pose->orientation.x; + y2 = pose->orientation.y + pose->orientation.y; + z2 = pose->orientation.z + pose->orientation.z; + + xx = pose->orientation.x * x2; xy = pose->orientation.x * y2; xz = pose->orientation.x * z2; + yy = pose->orientation.y * y2; yz = pose->orientation.y * z2; zz = pose->orientation.z * z2; + xw = pose->orientation.w * x2; yw = pose->orientation.w * y2; zw = pose->orientation.w * z2; + + axisorg[0][0] = (1.0f - (yy + zz)); + axisorg[0][1] = (xy + zw); + axisorg[0][2] = (xz - yw); + + axisorg[1][0] = (xy - zw); + axisorg[1][1] = (1.0f - (xx + zz)); + axisorg[1][2] = (yz + xw); + + axisorg[2][0] = (xz + yw); + axisorg[2][1] = (yz - xw); + axisorg[2][2] = (1.0f - (xx + yy)); + + axisorg[3][0] = METRES_TO_QUAKE(-pose->position.z); //-z forward + axisorg[3][1] = METRES_TO_QUAKE(pose->position.x); //+x right + axisorg[3][2] = METRES_TO_QUAKE(pose->position.y); //+y up +} +#define VectorAngles VectorAnglesPluginsSuck +void QDECL VectorAngles(const float *forward, const float *up, float *result, qboolean meshpitch) //up may be NULL +{ + float yaw, pitch, roll; + + if (forward[1] == 0 && forward[0] == 0) + { + if (forward[2] > 0) + { + pitch = -M_PI * 0.5; + yaw = up ? atan2(-up[1], -up[0]) : 0; + } + else + { + pitch = M_PI * 0.5; + yaw = up ? atan2(up[1], up[0]) : 0; + } + roll = 0; + } + else + { + yaw = atan2(forward[1], forward[0]); + pitch = -atan2(forward[2], sqrt (forward[0]*forward[0] + forward[1]*forward[1])); + + if (up) + { + vec_t cp = cos(pitch), sp = sin(pitch); + vec_t cy = cos(yaw), sy = sin(yaw); + vec3_t tleft, tup; + tleft[0] = -sy; + tleft[1] = cy; + tleft[2] = 0; + tup[0] = sp*cy; + tup[1] = sp*sy; + tup[2] = cp; + roll = -atan2(DotProduct(up, tleft), DotProduct(up, tup)); + } + else + roll = 0; + } + + pitch *= 180 / M_PI; + yaw *= 180 / M_PI; + roll *= 180 / M_PI; +// if (meshpitch) +// pitch *= r_meshpitch.value; + if (pitch < 0) + pitch += 360; + if (yaw < 0) + yaw += 360; + if (roll < 0) + roll += 360; + + result[0] = pitch; + result[1] = yaw; + result[2] = roll; +} + +static struct +{ +//instance state (in case we want to start up) + XrInstance instance; //loader context + XrSystemId systemid; //device type thingie we're going for +#define MAX_VIEW_COUNT 12 //kinda abusive, but that's VR for you. + unsigned int viewcount; + XrViewConfigurationView *views; + XrViewConfigurationType viewtype; + +//engine context info (for restarting sessions) + int renderer; //rendering api we're using + void *bindinginfo; //appropriate XrGraphicsBinding*KHR struct so we can restart sessions. + +//session state + XrSession session; //driver context + XrSessionState state; + XrSpace space; + struct + { //basically just swapchain state. + XrSwapchain swapchain; + unsigned int numswapimages; + XrSwapchainSubImage subimage; + image_t *swapimages; + } eye[MAX_VIEW_COUNT]; //note that eye is a vauge term. + + XrActiveActionSet actionset; + + qboolean timeknown; + XrTime time; + XrFrameState framestate; + int srgb; //<0 = gamma-only. 0 = no srgb at all, >0 full srgb, including textures and stuff + + unsigned int numactions; + struct + { + XrActionType acttype; + const char *actname; //doubles up as command names for buttons + const char *actdescription; //user-visible string (exposed via openxr runtime somehow) + const char *subactionpath; //somethingblahblah + + XrAction action; //for querying. + XrPath path; //for querying. + XrSpace space; //for poses. + qboolean held; //for buttons. + } actions[256]; +} xr; + +static qboolean QDECL XR_PluginMayUnload(void) +{ + if (xr.instance) + return false; //something is still using us... don't let our code go away. + return true; +} +static void XR_SessionEnded(void) +{ + size_t u; + if (xr.space) + { + xrDestroySpace(xr.space); + xr.space = XR_NULL_HANDLE; + } + + for (u = 0; u < countof(xr.eye); u++) + { + free(xr.eye[u].swapimages); + xr.eye[u].swapimages = NULL; + xr.eye[u].numswapimages = 0; + if (xr.eye[u].swapchain) + { + xrDestroySwapchain(xr.eye[u].swapchain); + xr.eye[u].swapchain = XR_NULL_HANDLE; + } + } + + if (xr.session) + { + xrDestroySession(xr.session); + xr.session = XR_NULL_HANDLE; + } +} +static void XR_Shutdown(void) +{ //called on any kind of failure + XR_SessionEnded(); + + free(xr.bindinginfo); + free(xr.views); + if (xr.instance) + xrDestroyInstance(xr.instance); + + memset(&xr, 0, sizeof(xr)); +} + +static qboolean XR_PreInit(vrsetup_t *qreqs) +{ + XrResult res; + const char *ext; + + XR_Shutdown(); //just in case... + + if (qreqs->structsize != sizeof(*qreqs)) + return false; //nope, get lost. + + switch(qreqs->vrplatform) + { + /*case VR_HEADLESS: + ext = XR_MND_HEADLESS_EXTENSION_NAME; + break;*/ +#ifdef XR_USE_GRAPHICS_API_VULKAN + case VR_VULKAN: + ext = XR_KHR_VULKAN_ENABLE_EXTENSION_NAME; + break; +#endif +#ifdef XR_USE_GRAPHICS_API_OPENGL +#ifdef XR_MND_EGL_ENABLE_EXTENSION_NAME + case VR_EGL: + ext = XR_MND_EGL_ENABLE_EXTENSION_NAME; + break; +#endif +#ifdef XR_USE_PLATFORM_XLIB + case VR_X11_GLX: +#endif +#ifdef XR_USE_PLATFORM_WIN32 + case VR_WIN_WGL: +#endif + ext = XR_KHR_OPENGL_ENABLE_EXTENSION_NAME; + break; +#endif +#ifdef XR_USE_GRAPHICS_API_D3D11 + case VR_D3D11: + ext = XR_KHR_D3D11_ENABLE_EXTENSION_NAME; + break; +#endif + default: + Con_Printf("OpenXR: windowing-api or rendering-api not supported\n"); + return false; + } + +#ifdef XR_NO_PROTOTYPES + { + static dllhandle_t *lib; + static dllfunction_t funcs[] = { + #define XRFUNC(n) {(void*)&n, #n}, + XRFUNCS + #undef XRFUNC + {NULL}}; +#define XR_LOADER_LIBNAME "libopenxr_loader" + if (!lib) + lib = plugfuncs->LoadDLL(XR_LOADER_LIBNAME, funcs); + if (!lib) + { + Con_Printf(CON_ERROR"OpenXR: Unable to load "XR_LOADER_LIBNAME"\n"); + return false; + } + } +#endif + + { + unsigned int exts = 0, u=0; + XrExtensionProperties *extlist; + res = xrEnumerateInstanceExtensionProperties(NULL, 0, &exts, NULL); + 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); + for (u = 0; u < exts; u++) + if (!strcmp(extlist[u].extensionName, ext)) + break; + free(extlist); + } + if (u == exts) + { + Con_Printf("OpenXR: instance driver does not support required %s\n", ext); + return false; + } + } + + xr.instance = NULL; + + //create our instance + { + XrInstanceCreateInfo createinfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + createinfo.createFlags = 0; + Q_strlcpy(createinfo.applicationInfo.applicationName, FULLENGINENAME, sizeof(createinfo.applicationInfo.applicationName)); + createinfo.applicationInfo.applicationVersion = APPLICATIONVERSION; + 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; + res = xrCreateInstance(&createinfo, &xr.instance); + } + if (XR_FAILED(res) || !xr.instance) + return false; + + { + XrInstanceProperties props = {XR_TYPE_INSTANCE_PROPERTIES}; + if (!XR_FAILED(xrGetInstanceProperties(xr.instance, &props))) + Con_Printf("OpenXR Runtime: %s %u.%u.%u\n", props.runtimeName, XR_VERSION_MAJOR(props.runtimeVersion), XR_VERSION_MINOR(props.runtimeVersion), XR_VERSION_PATCH(props.runtimeVersion)); + else + Con_Printf("OpenXR Runtime: Unable to determine runtime version\n"); + } + + { + XrSystemGetInfo systemInfo = { XR_TYPE_SYSTEM_GET_INFO }; + if (!strncasecmp(xr_formfactor->string, "hand", 4)) + systemInfo.formFactor = XR_FORM_FACTOR_HANDHELD_DISPLAY; + else if (!strncasecmp(xr_formfactor->string, "head",4)) + systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + else + { + if (*xr_formfactor->string) + Con_Printf("\"%s\" is not a recognised value for xr_formfactor\n", xr_formfactor->string); + else + Con_Printf("xr_formfactor not set, assuming headmounted\n"); + systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + } + res = xrGetSystem(xr.instance, &systemInfo, &xr.systemid); + if (XR_FAILED(res) || !xr.systemid) + return false; + } + + { + XrSystemProperties props = {XR_TYPE_SYSTEM_PROPERTIES}; + if (XR_SUCCEEDED(xrGetSystemProperties(xr.instance, xr.systemid, &props))) + { + Con_Printf("OpenXR System: %s\n", props.systemName); + } + } + + switch(qreqs->vrplatform) + { + default: + XR_Shutdown(); + return false; + case VR_HEADLESS: + break; + +#ifdef XR_USE_GRAPHICS_API_VULKAN + case VR_VULKAN: + { + XrGraphicsRequirementsVulkanKHR reqs = {XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR}; + VkInstance inst = VK_NULL_HANDLE; + VkPhysicalDevice physdev; + uint32_t extlen; + char *extstr; //space-delimited list, for some reason. writable though. + + PFN_xrGetVulkanGraphicsRequirementsKHR xrGetVulkanGraphicsRequirementsKHR; + PFN_xrGetVulkanInstanceExtensionsKHR xrGetVulkanInstanceExtensionsKHR; + PFN_xrGetVulkanDeviceExtensionsKHR xrGetVulkanDeviceExtensionsKHR; + PFN_xrGetVulkanGraphicsDeviceKHR xrGetVulkanGraphicsDeviceKHR; + if (XR_FAILED(xrGetInstanceProcAddr(xr.instance, "xrGetVulkanGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsRequirementsKHR)) || + XR_FAILED(xrGetInstanceProcAddr(xr.instance, "xrGetVulkanInstanceExtensionsKHR", (PFN_xrVoidFunction*)&xrGetVulkanInstanceExtensionsKHR)) || + XR_FAILED(xrGetInstanceProcAddr(xr.instance, "xrGetVulkanDeviceExtensionsKHR", (PFN_xrVoidFunction*)&xrGetVulkanDeviceExtensionsKHR)) || + XR_FAILED(xrGetInstanceProcAddr(xr.instance, "xrGetVulkanGraphicsDeviceKHR", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsDeviceKHR))) + return false; + xrGetVulkanGraphicsRequirementsKHR(xr.instance, xr.systemid, &reqs); + qreqs->maxver.major = XR_VERSION_MAJOR(reqs.maxApiVersionSupported); + qreqs->maxver.minor = XR_VERSION_MINOR(reqs.maxApiVersionSupported); + qreqs->minver.major = XR_VERSION_MAJOR(reqs.minApiVersionSupported); + qreqs->minver.minor = XR_VERSION_MINOR(reqs.minApiVersionSupported); + + xrGetVulkanInstanceExtensionsKHR(xr.instance, xr.systemid, 0, &extlen, NULL); + extstr = malloc(extlen); + xrGetVulkanInstanceExtensionsKHR(xr.instance, xr.systemid, extlen, &extlen, extstr); + + //create vulkan instance now... + qreqs->createinstance(qreqs, extstr, &inst); + free(extstr); + + xrGetVulkanDeviceExtensionsKHR(xr.instance, xr.systemid, 0, &extlen, NULL); + extstr = malloc(extlen); + xrGetVulkanDeviceExtensionsKHR(xr.instance, xr.systemid, extlen, &extlen, extstr); + + res = xrGetVulkanGraphicsDeviceKHR(xr.instance, xr.systemid, inst, &physdev); + + qreqs->deviceextensions = extstr; + qreqs->vk.physicaldevice = physdev; + } + break; +#endif + +#ifdef XR_USE_GRAPHICS_API_OPENGL + case VR_X11_GLX: + case VR_EGL: + case VR_WIN_WGL: + { + XrGraphicsRequirementsOpenGLKHR reqs = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + PFN_xrGetOpenGLGraphicsRequirementsKHR xrGetOpenGLGraphicsRequirementsKHR; + if (XR_SUCCEEDED(xrGetInstanceProcAddr(xr.instance, "xrGetOpenGLGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetOpenGLGraphicsRequirementsKHR))) + xrGetOpenGLGraphicsRequirementsKHR(xr.instance, xr.systemid, &reqs); + + qreqs->maxver.major = XR_VERSION_MAJOR(reqs.maxApiVersionSupported); + qreqs->maxver.minor = XR_VERSION_MINOR(reqs.maxApiVersionSupported); + qreqs->minver.major = XR_VERSION_MAJOR(reqs.minApiVersionSupported); + qreqs->minver.minor = XR_VERSION_MINOR(reqs.minApiVersionSupported); + //caller must validate when creating its context. + } + break; +#endif + +#ifdef XR_USE_GRAPHICS_API_D3D11 + case VR_D3D11: + { + XrGraphicsRequirementsD3D11KHR reqs = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR}; + PFN_xrGetD3D11GraphicsRequirementsKHR xrGetD3D11GraphicsRequirementsKHR; + if (XR_SUCCEEDED(xrGetInstanceProcAddr(xr.instance, "xrGetD3D11GraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetD3D11GraphicsRequirementsKHR))) + xrGetD3D11GraphicsRequirementsKHR(xr.instance, xr.systemid, &reqs); + + qreqs->minver.major = reqs.minFeatureLevel; + qreqs->deviceid[0] = reqs.adapterLuid.LowPart; + qreqs->deviceid[1] = reqs.adapterLuid.HighPart; + } + break; +#endif + } + + { + XrViewConfigurationType *viewtype; + uint32_t viewtypes, u; + res = xrEnumerateViewConfigurations(xr.instance, xr.systemid, 0, &viewtypes, NULL); + viewtype = alloca(viewtypes*sizeof(viewtype)); + res = xrEnumerateViewConfigurations(xr.instance, xr.systemid, viewtypes, &viewtypes, viewtype); + xr.viewtype = (XrViewConfigurationType)0; + for (u = 0; u < viewtypes; u++) + { + switch(viewtype[u]) + { + case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO: + if (!strcasecmp(xr_viewconfig->string, "mono")) + xr.viewtype = viewtype[u]; + break; + case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO: + if (!strcasecmp(xr_viewconfig->string, "stereo")) + xr.viewtype = viewtype[u]; + break; + case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO: + if (!strcasecmp(xr_viewconfig->string, "quad")) + xr.viewtype = viewtype[u]; + break; + default: + break; + } + } + if (!xr.viewtype) + { + if (viewtypes) + xr.viewtype = viewtype[0]; + + if (*xr_viewconfig->string) + { + Con_Printf("OpenXR: Viewtype %s unavailable, using ", xr_viewconfig->string); + switch(xr.viewtype) + { + case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO: Con_Printf("mono\n"); break; + case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO: Con_Printf("stereo\n"); break; + case XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO: Con_Printf("quad\n"); break; + default: + Con_Printf("unknown (%i)\n", xr.viewtype); + break; + } + } + } + } + + 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); + res = xrEnumerateViewConfigurationViews(xr.instance, xr.systemid, xr.viewtype, xr.viewcount, &xr.viewcount, xr.views); + + //caller now knows what device/contextversion/etc to init with + return true; +} + +static qboolean XR_Init(vrsetup_t *qreqs, rendererstate_t *info) +{ + xr.srgb = info->srgb; + switch(qreqs->vrplatform) + { + case VR_HEADLESS: + break; + default: + return false; //error. not supported in this build. +#ifdef XR_USE_GRAPHICS_API_VULKAN + case VR_VULKAN: + { + XrGraphicsBindingVulkanKHR *vk = xr.bindinginfo = calloc(1, sizeof(*vk)); + vk->type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR; + vk->instance = qreqs->vk.instance; + vk->physicalDevice = qreqs->vk.physicaldevice; + vk->device = qreqs->vk.device; + vk->queueFamilyIndex = qreqs->vk.queuefamily; + vk->queueIndex = qreqs->vk.queueindex; + xr.renderer = QR_VULKAN; + } + break; +#endif +#ifdef XR_USE_GRAPHICS_API_OPENGL +#ifdef XR_MND_EGL_ENABLE_EXTENSION_NAME + case VR_EGL: //x11-egl, wayland, and hopefully android... + { + XrGraphicsBindingEGLMND *egl = xr.bindinginfo = calloc(1, sizeof(*egl)); + egl->type = XR_TYPE_GRAPHICS_BINDING_EGL_MND; + egl->getProcAddress = qreqs->egl.getprocaddr; + egl->display = qreqs->egl.egldisplay; + egl->config = qreqs->egl.eglconfig; + egl->context = qreqs->egl.eglcontext; + xr.renderer = QR_OPENGL; + } + break; +#endif +#ifdef XR_USE_PLATFORM_XLIB + case VR_X11_GLX: + { + XrGraphicsBindingOpenGLXlibKHR *glx = xr.bindinginfo = calloc(1, sizeof(*glx)); + glx->type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; + glx->xDisplay = qreqs->x11_glx.display; + glx->visualid = qreqs->x11_glx.visualid; + glx->glxFBConfig = qreqs->x11_glx.glxfbconfig; + glx->glxDrawable = qreqs->x11_glx.drawable; + glx->glxContext = qreqs->x11_glx.glxcontext; + xr.renderer = QR_OPENGL; + } + break; +#endif +#ifdef XR_USE_PLATFORM_WIN32 + case VR_WIN_WGL: + { + XrGraphicsBindingOpenGLWin32KHR *wgl = xr.bindinginfo = calloc(1, sizeof(*wgl)); + wgl->type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; + wgl->hDC = qreqs->wgl.hdc; + wgl->hGLRC = qreqs->wgl.hglrc; + xr.renderer = QR_OPENGL; + } + break; +#endif +#endif //def XR_USE_GRAPHICS_API_OPENGL +#ifdef XR_USE_GRAPHICS_API_D3D11 + case VR_D3D11: + { + XrGraphicsBindingD3D11KHR *d3d = xr.bindinginfo = calloc(1, sizeof(*d3d)); + d3d->type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR; + d3d->device = qreqs->d3d.device; + xr.renderer = QR_DIRECT3D11; + } + break; +#endif + } + return true; +} + +static XrAction XR_DefineAction(XrActionType type, const char *name, const char *description, const char *root) +{ + XrActionCreateInfo info = {XR_TYPE_ACTION_CREATE_INFO}; + XrResult res; + char *ffs; + + size_t u; + int nconflicts = 0; + int dconflicts = 0; + for (u = 0; u < xr.numactions; u++) + { + if (xr.actions[u].acttype == type && !strcmp(xr.actions[u].actname, name) && !strcmp(xr.actions[u].actdescription, description) && !strcmp(xr.actions[u].subactionpath?xr.actions[u].subactionpath:"", root?root:"")) + { //looks like a dupe... + return xr.actions[u].action; + } + if (!strcasecmp(xr.actions[u].actname, name)) + nconflicts++; //arse balls knob cock + if (!strcasecmp(xr.actions[u].actdescription, description)) + dconflicts++; //arse balls knob cock + } + if (xr.numactions == countof(xr.actions)) + return XR_NULL_HANDLE; //nope, list full. sorry. + + xr.actions[u].acttype = type; + xr.actions[u].actname = strdup(name); + xr.actions[u].actdescription = strdup(description); + xr.actions[u].subactionpath = (root&&*root)?strdup(root):NULL; + xr.numactions++; + + if (xr.actions[u].subactionpath) + xrStringToPath(xr.instance, xr.actions[u].subactionpath, &xr.actions[u].path); + else + xr.actions[u].path = XR_NULL_PATH; + if (xr.actions[u].path == XR_NULL_PATH) + { + info.countSubactionPaths = 0; + info.subactionPaths = NULL; + } + else + { + info.countSubactionPaths = 1; + info.subactionPaths = &xr.actions[u].path; + } + info.actionType = xr.actions[u].acttype; + if (*xr.actions[u].actname == '+') + Q_strlcpy(info.actionName, xr.actions[u].actname+1, sizeof(info.actionName)); + else + Q_strlcpy(info.actionName, xr.actions[u].actname, sizeof(info.actionName)); + while ((ffs=strchr(info.actionName, ' '))) *ffs = '_'; //convert spaces to underscores. + Q_strlcpy(info.localizedActionName, xr.actions[u].actdescription, sizeof(info.localizedActionName)); + res = xrCreateAction(xr.actionset.actionSet, &info, &xr.actions[u].action); + if (XR_FAILED(res)) + Con_Printf("openxr: Unable to create action %s [%s] - %i\n", info.actionName, info.localizedActionName, res); + + return xr.actions[u].action; +} + +static qboolean XR_ReadLine(const char **text, char *buffer, size_t buflen) +{ + char in; + char *out = buffer; + size_t len; + if (buflen <= 1) + return false; + len = buflen-1; + while (len > 0) + { + in = *(*text); + if (!in) + { + if (len == buflen-1) + return false; + *out = 0; + return true; + } + (*text)++; + if (in == '\n') + break; + *out++ = in; + len--; + } + *out = '\0'; + + //if there's a trailing \r, strip it. + if (out > buffer) + if (out[-1] == '\r') + out[-1] = 0; + + return true; +} + +static int XR_BindProfileStr(const char *fname, const char *file) +{ + XrAction act; + XrResult res; + XrPath path; + char line[1024]; + char name[1024]; + char type[256]; + char desc[1024]; + char bind[1024]; + char root[1024]; + unsigned int p; + char prefix[2][1024]; + + //first line is eg: /interaction_profiles/khr/simple_controller + while (XR_ReadLine(&file, line, sizeof(line))) + { + cmdfuncs->TokenizeString(line); + if (cmdfuncs->Argc()) + break; + } + cmdfuncs->Argv(0, name, sizeof(name)); + for (p = 0; p < countof(prefix); p++) + cmdfuncs->Argv(p+1, prefix[p], sizeof(prefix[p])); + if (*name && XR_SUCCEEDED(xrStringToPath(xr.instance, name, &path))) + { //okay, it accepted that path at least... + XrInteractionProfileSuggestedBinding suggestedbindings = {XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + unsigned int acts = 0; + XrActionSuggestedBinding bindings[256]; + + while (XR_ReadLine(&file, line, sizeof(line))) + { + cmdfuncs->TokenizeString(line); + if (cmdfuncs->Argc()) + { + cmdfuncs->Argv(0, name, sizeof(name)); + cmdfuncs->Argv(1, desc, sizeof(desc)); + cmdfuncs->Argv(2, type, sizeof(type)); + cmdfuncs->Argv(3, bind, sizeof(bind)); + cmdfuncs->Argv(4, root, sizeof(root)); + + if (*type) + { + XrActionType xrtype; + if (!strcasecmp(type, "button")) + xrtype = XR_ACTION_TYPE_BOOLEAN_INPUT; + else if (!strcasecmp(type, "float")) + xrtype = XR_ACTION_TYPE_FLOAT_INPUT; + else if (!strcasecmp(type, "vector2f")) + xrtype = XR_ACTION_TYPE_VECTOR2F_INPUT; + else if (!strcasecmp(type, "pose")) + xrtype = XR_ACTION_TYPE_POSE_INPUT; + else if (!strcasecmp(type, "vibration")) + xrtype = XR_ACTION_TYPE_VIBRATION_OUTPUT; + else + continue; + + act = XR_DefineAction(xrtype, name, desc, root); + if (act != XR_NULL_HANDLE && *bind) + { + if (*bind == '/') + { + res = xrStringToPath(xr.instance, bind, &bindings[acts].binding); + if (XR_SUCCEEDED(res)) + bindings[acts++].action = act; + } + else for (p = 0; p < countof(prefix) && *prefix[p]=='/'; p++) + { + res = xrStringToPath(xr.instance, va("%s%s", prefix[p], bind), &bindings[acts].binding); + if (XR_SUCCEEDED(res)) + bindings[acts++].action = act; + } + } + } + } + } + + if (acts) + { + suggestedbindings.interactionProfile = path; + suggestedbindings.countSuggestedBindings = acts; + suggestedbindings.suggestedBindings = bindings; + res = xrSuggestInteractionProfileBindings(xr.instance, &suggestedbindings); + if (XR_FAILED(res)) + Con_Printf("%s: xrSuggestInteractionProfileBindings failed - %i\n", fname, res); + return acts; + } + } + return 0; +} + +static int QDECL XR_BindProfileFile(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package) +{ + vfsfile_t *f = fsfuncs->OpenVFS(fname, "rb", FS_GAME); + if (f) + { + size_t len = VFS_GETLEN(f); + char *buf = malloc(len+1); + VFS_READ(f, buf, len); + buf[len] = 0; + *(unsigned int*)ctx += XR_BindProfileStr(fname, buf); + free(buf); + VFS_CLOSE(f); + } + return false; +} + +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 - %i\n", 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" + "+select \"Select\" button input/select/click\n" + "togglemenu \"Toggle Menu\" button input/menu/click\n" + "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" + //"unbound \"Unused Button\" button input/system/click\n" + //"unbound \"Unused Button\" button input/system/touch\n" + //"unbound \"Unused Button\" button input/a/click\n" + //"unbound \"Unused Button\" button input/a/touch\n" + //"unbound \"Unused Button\" button input/b/click\n" + //"unbound \"Unused Button\" button input/b/touch\n" + //"unbound \"Unused Button\" float input/squeeze/value\n" + //"unbound \"Unused Button\" button input/squeeze/force\n" + //"unbound \"Unused Button\" button input/trigger/click\n" + //"unbound \"Unused Button\" float input/trigger/value\n" + //"unbound \"Unused Button\" button input/trigger/touch\n" + //"unbound \"Unused Button\" float input/thumbstick/x\n" + //"unbound \"Unused Button\" float input/thumbstick/y\n" + //"unbound \"Unused Button\" button input/thumbstick/click\n" + //"unbound \"Unused Button\" button input/thumbstick/touch\n" + //"unbound \"Unused Button\" float input/trackpad/x\n" + //"unbound \"Unused Button\" float input/trackpad/y\n" + //"unbound \"Unused Button\" button input/trackpad/force\n" + //"unbound \"Unused Button\" button input/trackpad/touch\n" + //"unbound \"Unused Button\" pose input/grip/pose\n" + //"unbound \"Unused Button\" pose input/aim/pose\n" + //"unbound \"Unused Button\" vibration output/haptic\n" + ); + + //FIXME: map to quake's keys. + XR_BindProfileStr("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" + //"unbound \"Unused Button\" button input/b/click\n" + //"unbound \"Unused Button\" button input/x/click\n" + //"unbound \"Unused Button\" button input/y/click\n" + "+back \"Move Backwards\" button input/dpad_down/click\n" + "+moveright \"Move Right\" button input/dpad_right/click\n" + "+forward \"Move Forward\" button input/dpad_up/click\n" + "+moveleft \"Move Left\" button input/dpad_left/click\n" + "+jump \"Jump\" button input/shoulder_left/click\n" + "+attack \"Attack\" button input/shoulder_right/click\n" + //"unbound \"Unused Button\" button input/thumbstick_left/click\n" + //"unbound \"Unused Button\" button input/thumbstick_right/click\n" + //"unbound \"Unused Axis\" float input/trigger_left/click\n" + //"unbound \"Unused Axis\" float input/trigger_right/click\n" + //"unbound \"Unused Axis\" float input/thumbstick_left/x\n" + //"unbound \"Unused Axis\" float input/thumbstick_left/y\n" + //"unbound \"Unused Axis\" float input/thumbstick_right/x\n" + //"unbound \"Unused Axis\" float input/thumbstick_right/y\n" + //"unbound \"Unused Vibrator\" vibration output/haptic_left\n" + //"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" + ); + } + +//begin session specific. stuff + + //create action space stuff. + for (h = 0; h < xr.numactions; h++) + { + switch(xr.actions[h].acttype) + { + case XR_ACTION_TYPE_POSE_INPUT: + { + XrActionSpaceCreateInfo info = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + info.action = xr.actions[h].action; + info.subactionPath = xr.actions[h].path; + info.poseInActionSpace.orientation.w = 1; //just fill with identity. + + res = xrCreateActionSpace(xr.session, &info, &xr.actions[h].space); + if (XR_FAILED(res)) + Con_Printf("openxr: xrCreateActionSpace failed - %i\n", res); + } + break; + default: + xr.actions[h].space = XR_NULL_HANDLE; + break; + } + } + + //and attach it. + { + XrSessionActionSetsAttachInfo info = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + info.countActionSets = 1; + info.actionSets = &xr.actionset.actionSet; + res = xrAttachSessionActionSets(xr.session, &info); + if (XR_FAILED(res)) + Con_Printf("openxr: xrAttachSessionActionSets failed - %i\n", res); + } + +#if 1 + if (cvarfuncs->GetFloat("developer")) + { + XrInteractionProfileState profile = {XR_TYPE_INTERACTION_PROFILE_STATE}; + XrPath path; + unsigned int u; + static const char *paths[] = {"/user/hand/left", "/user/hand/right", "/user/head", "/user/gamepad", "/user/treadmill", "/user/"}; + for (u = 0; u < countof(paths); u++) + { + xrStringToPath(xr.instance, paths[u], &path); + res = xrGetCurrentInteractionProfile(xr.session, path, &profile); + if (XR_SUCCEEDED(res)) + { + char buf[256]; + uint32_t len = sizeof(buf); + if (!profile.interactionProfile) + Con_Printf("openxr: %s == no profile/device\n", paths[u]); + else + { + res = xrPathToString(xr.instance, profile.interactionProfile, sizeof(buf), &len, buf); + Con_Printf("openxr: %s == %s\n", paths[u], buf); + } + } + } + + + Con_Printf("Bound actions:\n"); + for (u = 0; u < xr.numactions; u++) + { + XrBoundSourcesForActionEnumerateInfo info = {XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + uint32_t inputs, i, bufsize; + XrPath input[20]; + info.action = xr.actions[u].action; + res = xrEnumerateBoundSourcesForAction(xr.session, &info, countof(input), &inputs, input); + if (XR_SUCCEEDED(res)) + { + Con_Printf("\t%s:\n", xr.actions[u].actname); + for (i = 0; i < inputs; i++) + { + char buffer[8192]; + XrInputSourceLocalizedNameGetInfo info = {XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + info.sourcePath = input[i]; + info.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT| //'left hand' + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT| //'foo controller' + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; //'trigger' + res = xrGetInputSourceLocalizedName(xr.session, &info, sizeof(buffer), &bufsize, buffer); + if (XR_FAILED(res)) + Q_snprintf(buffer, sizeof(buffer), "error %i", res); + Con_Printf("\t\t%s\n", buffer); + } + } + else if (res == XR_ERROR_HANDLE_INVALID) //monado reports this for unimplemented things. + Con_Printf("\t%s: error XR_ERROR_HANDLE_INVALID (not implemented?)\n", xr.actions[u].actname); + else + Con_Printf("\t%s: error %i\n", xr.actions[u].actname, res); + } + } +#endif +} +static void XR_UpdateInputs(XrTime time) +{ + XrResult res; + unsigned int h; + + { + XrActionsSyncInfo syncinfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncinfo.countActiveActionSets = 1; + syncinfo.activeActionSets = &xr.actionset; + res = xrSyncActions(xr.session, &syncinfo); + if (res == XR_SESSION_NOT_FOCUSED) + ; //handle it anyway, giving us a chance to disable various inputs. + else if (XR_FAILED(res)) + return; + } + + for (h = 0; h < xr.numactions; h++) + { + if (xr.actions[h].action == XR_NULL_HANDLE) //failed to init + continue; + switch(xr.actions[h].acttype) + { + case XR_ACTION_TYPE_POSE_INPUT: + { + XrActionStatePose pose = {XR_TYPE_ACTION_STATE_POSE}; + XrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = xr.actions[h].action; + info.subactionPath = xr.actions[h].path; + + xrGetActionStatePose(xr.session, &info, &pose); + if (pose.isActive) + { //its mapped to something, woo. + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + vec3_t transform[4]; + xrLocateSpace(xr.actions[h].space, xr.space, time, &loc); + //if (loc.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) + //if (loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) + //if (loc.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) + //if (loc.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) + XR_PoseToTransform(&loc.pose, transform); + + { + vec3_t angles; + char cmd[256]; + VectorAngles(transform[0], transform[2], angles, false); + Q_snprintf(cmd, sizeof(cmd), "echo %s %g %g %g %g %g %g\n", xr.actions[h].actname, angles[0], angles[1], angles[2], transform[3][0], transform[3][1], transform[3][2]); + cmdfuncs->AddText(cmd, false); + } + } + } + break; + case XR_ACTION_TYPE_BOOLEAN_INPUT: + { + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + XrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = xr.actions[h].action; + info.subactionPath = xr.actions[h].path; + xrGetActionStateBoolean(xr.session, &info, &state); + if (!state.isActive) state.currentState = XR_FALSE; + if ((!!state.currentState) != xr.actions[h].held) + { + xr.actions[h].held = !!state.currentState; + if (xr.actions[h].held || *xr.actions[h].actname == '+') + { + char cmd[256]; + Q_strlcpy(cmd, xr.actions[h].actname, sizeof(cmd)); + Q_strlcat(cmd, "\n", sizeof(cmd)); + if (!xr.actions[h].held) + *cmd = '-'; //release events. + cmdfuncs->AddText(cmd, false); + } + } + } + break; + case XR_ACTION_TYPE_FLOAT_INPUT: + { + XrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT}; + XrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = xr.actions[h].action; + info.subactionPath = xr.actions[h].path; + xrGetActionStateFloat(xr.session, &info, &state); + + if (!state.isActive) state.currentState = 0.0f; + { + char cmd[256]; + Q_snprintf(cmd, sizeof(cmd), "echo %s %g\n", xr.actions[h].actname, state.currentState); + cmdfuncs->AddText(cmd, false); + } + } + break; + case XR_ACTION_TYPE_VECTOR2F_INPUT: + { + XrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F}; + XrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = xr.actions[h].action; + info.subactionPath = xr.actions[h].path; + xrGetActionStateVector2f(xr.session, &info, &state); + + if (!state.isActive) state.currentState.x = state.currentState.y = 0.0f; + { + char cmd[256]; + Q_snprintf(cmd, sizeof(cmd), "echo %s %g %g\n", xr.actions[h].actname, state.currentState.x, state.currentState.y); + cmdfuncs->AddText(cmd, false); + } + } + break; + case XR_ACTION_TYPE_VIBRATION_OUTPUT: + default: + break; + } + } +} + +static qboolean XR_Begin(void) +{ + uint32_t u; + XrResult res; + uint32_t swapfmts; + int64_t *fmts, fmttouse=0; + { + XrSessionCreateInfo sessioninfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessioninfo.next = xr.bindinginfo; + sessioninfo.createFlags = 0; + sessioninfo.systemId = xr.systemid; + res = xrCreateSession(xr.instance, &sessioninfo, &xr.session); + } + if (XR_FAILED(res)) + return false; + + { + XrReferenceSpaceCreateInfo info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + info.poseInReferenceSpace.orientation.w = 1; + res = xrCreateReferenceSpace(xr.session, &info, &xr.space); + if (XR_FAILED(res)) + return false; + } + + xrEnumerateSwapchainFormats(xr.session, 0, &swapfmts, NULL); + fmts = alloca(sizeof(*fmts)*swapfmts); + xrEnumerateSwapchainFormats(xr.session, swapfmts, &swapfmts, fmts); + if (!swapfmts) + Con_Printf("OpenXR: No swapchain formats to use\n"); +#ifdef XR_USE_GRAPHICS_API_OPENGL + else if (xr.renderer == QR_OPENGL) + { + fmttouse = fmts[0]; //favour the first... its probably a bad choice though... + for (u = 0; u < swapfmts; u++) switch(fmts[u]) + { + case GL_RGBA16F: Con_DPrintf("OpenXr fmt%u: %s\n", u, "GL_RGBA16F"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case GL_RGBA8: Con_DPrintf("OpenXr fmt%u: %s\n", u, "GL_RGBA8"); if (!xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case GL_SRGB8_ALPHA8_EXT: Con_DPrintf("OpenXr fmt%u: %s\n", u, "GL_SRGB8_ALPHA8");if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + default: + Con_DPrintf("OpenXr fmt%u: %x\n", u, (unsigned)fmts[u]); + break; + } + } +#endif +#ifdef XR_USE_GRAPHICS_API_VULKAN + else if (xr.renderer == QR_VULKAN) + { + fmttouse = fmts[0]; //favour the first... its probably a bad choice though... + for (u = 0; u < swapfmts; u++) switch(fmts[u]) + { + case VK_FORMAT_R16G16B16A16_SFLOAT: Con_DPrintf("OpenXr fmt%u: %s\n", u, "VK_FORMAT_R16G16B16A16_SFLOAT"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case VK_FORMAT_B8G8R8A8_UNORM: Con_DPrintf("OpenXr fmt%u: %s\n", u, "VK_FORMAT_B8G8R8A8_UNORM"); if (!xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case VK_FORMAT_R8G8B8A8_UNORM: Con_DPrintf("OpenXr fmt%u: %s\n", u, "VK_FORMAT_R8G8B8A8_UNORM"); if (!xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case VK_FORMAT_B8G8R8A8_SRGB: Con_DPrintf("OpenXr fmt%u: %s\n", u, "VK_FORMAT_B8G8R8A8_SRGB"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case VK_FORMAT_R8G8B8A8_SRGB: Con_DPrintf("OpenXr fmt%u: %s\n", u, "VK_FORMAT_R8G8B8A8_SRGB"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + default: + Con_DPrintf("OpenXr fmt%u: %u\n", u, (unsigned)fmts[u]); + break; + } + } +#endif +#ifdef XR_USE_GRAPHICS_API_D3D11 + else if (xr.renderer == QR_DIRECT3D11) + { + fmttouse = fmts[0]; //favour the first... its probably a bad choice though... + for (u = 0; u < swapfmts; u++) switch(fmts[u]) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_R16G16B16A16_FLOAT"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case DXGI_FORMAT_B8G8R8A8_UNORM: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_B8G8R8A8_UNORM"); if (!xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case DXGI_FORMAT_B8G8R8X8_UNORM: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_B8G8R8X8_UNORM"); if (!xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case DXGI_FORMAT_R8G8B8A8_UNORM: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_R8G8B8A8_UNORM"); if (!xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: Con_DPrintf("OpenXr fmt%u: %s\n", u, "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB"); if (xr.srgb) fmttouse = fmts[u],u=swapfmts; break; + default: + Con_DPrintf("OpenXr fmt%u: %x\n", u, (unsigned)fmts[u]); + break; + } + } +#endif + else + { + fmttouse = fmts[0]; + for (u = 0; u < swapfmts; u++) + Con_Printf("fmt%u: %u / %x\n", u, (unsigned)fmts[u], (unsigned)fmts[u]); + } + + for (u = 0; u < xr.viewcount; u++) + { + XrSwapchainCreateInfo swapinfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapinfo.createFlags = 0; + swapinfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapinfo.format = fmttouse; + swapinfo.sampleCount = 1;//xr.views->recommendedSwapchainSampleCount; + swapinfo.width = xr.views->recommendedImageRectWidth; + swapinfo.height = xr.views->recommendedImageRectHeight; + swapinfo.faceCount = 1; //2d, not a cube + swapinfo.arraySize = 1; //1 for 2d, a 1d array isn't allowed + swapinfo.mipCount = 1; + res = xrCreateSwapchain(xr.session, &swapinfo, &xr.eye[u].swapchain); + if (XR_FAILED(res)) + return false; + res = xrEnumerateSwapchainImages(xr.eye[u].swapchain, 0, &xr.eye[u].numswapimages, NULL); + if (XR_FAILED(res)) + return false; + + //using a separate swapchain for each eye, so just depend upon npot here and use the whole image. + xr.eye[u].subimage.imageRect.offset.x = 0; + xr.eye[u].subimage.imageRect.offset.y = 0; + xr.eye[u].subimage.imageRect.extent.width = swapinfo.width; + xr.eye[u].subimage.imageRect.extent.height = swapinfo.height; + xr.eye[u].subimage.swapchain = xr.eye[u].swapchain; + xr.eye[u].subimage.imageArrayIndex = 0; + + //okay, this is annoying. the returned array size has different strides for different apis, etc. + //translate it into something the relevant backend should understand. + switch(xr.renderer) + { + default: + return false; //erk? +#ifdef XR_USE_GRAPHICS_API_D3D11 + case QR_DIRECT3D11: + { + uint32_t i; + XrSwapchainImageD3D11KHR *xrimg = calloc(xr.eye[u].numswapimages, sizeof(*xrimg)); + for (i = 0; i < xr.eye[u].numswapimages; i++) + xrimg[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR; + res = xrEnumerateSwapchainImages(xr.eye[u].swapchain, xr.eye[u].numswapimages, &xr.eye[u].numswapimages, (XrSwapchainImageBaseHeader*)xrimg); + if (XR_FAILED(res)) + xr.eye[u].numswapimages = 0; + + 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].ptr = xrimg[i].texture; + xr.eye[u].swapimages[i].ptr2 = NULL; //view + xr.eye[u].swapimages[i].width = swapinfo.width; + xr.eye[u].swapimages[i].height = swapinfo.height; + xr.eye[u].swapimages[i].depth = 1; + xr.eye[u].swapimages[i].status = TEX_LOADED; + } + } + break; +#endif +#ifdef XR_USE_GRAPHICS_API_VULKAN + case QR_VULKAN: + { + uint32_t i; + XrSwapchainImageVulkanKHR *xrimg = calloc(xr.eye[u].numswapimages, sizeof(*xrimg)); + struct vk_image_s *vkimg; + for (i = 0; i < xr.eye[u].numswapimages; i++) + xrimg[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR; + res = xrEnumerateSwapchainImages(xr.eye[u].swapchain, xr.eye[u].numswapimages, &xr.eye[u].numswapimages, (XrSwapchainImageBaseHeader*)xrimg); + if (XR_FAILED(res)) + xr.eye[u].numswapimages = 0; + + xr.eye[u].swapimages = calloc(xr.eye[u].numswapimages, sizeof(*xr.eye[u].swapimages)+sizeof(struct vk_image_s)); + vkimg = (struct vk_image_s*)&xr.eye[u].swapimages[xr.eye[u].numswapimages]; + for (i = 0; i < xr.eye[u].numswapimages; i++) + { + xr.eye[u].swapimages[i].vkimage = &vkimg[i]; + vkimg[i].image = xrimg[i].image; + //vkimg[i].mem.* = 0; + vkimg[i].view = VK_NULL_HANDLE; + vkimg[i].sampler = VK_NULL_HANDLE; + vkimg[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + vkimg[i].width = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + xr.eye[u].swapimages[i].width = vkimg[i].width = swapinfo.width; + xr.eye[u].swapimages[i].height = vkimg[i].height = swapinfo.height; + xr.eye[u].swapimages[i].depth = vkimg[i].layers = 1; + vkimg[i].mipcount = swapinfo.mipCount; + vkimg[i].encoding = PTI_INVALID; //blurgh, is this needed? + vkimg[i].type = PTI_2D; + xr.eye[u].swapimages[i].status = TEX_LOADED; + } + } + break; +#endif +#ifdef XR_USE_GRAPHICS_API_OPENGL + case QR_OPENGL: + { + uint32_t i; + XrSwapchainImageOpenGLKHR *xrimg = calloc(xr.eye[u].numswapimages, sizeof(*xrimg)); + for (i = 0; i < xr.eye[u].numswapimages; i++) + xrimg[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; + res = xrEnumerateSwapchainImages(xr.eye[u].swapchain, xr.eye[u].numswapimages, &xr.eye[u].numswapimages, (XrSwapchainImageBaseHeader*)xrimg); + if (XR_FAILED(res)) + xr.eye[u].numswapimages = 0; + + 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].num = xrimg[i].image; + xr.eye[u].swapimages[i].width = swapinfo.width; + xr.eye[u].swapimages[i].height = swapinfo.height; + xr.eye[u].swapimages[i].depth = 1; + xr.eye[u].swapimages[i].status = TEX_LOADED; + } + } + break; +#endif + } + } + if (XR_FAILED(res)) + return false; + + XR_SetupInputs(); + + return true; +} + +static void XR_ProcessEvents(void) +{ + XrEventDataBuffer ev; + XrResult res; + for (;;) + { + res = xrPollEvent(xr.instance, &ev); + if (res == XR_EVENT_UNAVAILABLE || XR_FAILED(res)) + return; //nothing interesting here folks + + switch(ev.type) + { + default: //no idea wtf that is + Con_Printf("openxr event %u\n", ev.type); + 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: %i\n", res); + } + break; + case XR_SESSION_STATE_STOPPING: + res = xrEndSession(xr.session); + if (XR_FAILED(res)) + Con_Printf("Unable to end session: %i\n", res); + break; + } + xr.state = s->state; + } + break; + } + } +} +static qboolean XR_SyncFrame(double *frametime) +{ + XrResult res; + + if (!xr.instance) + return false; + + memset(&xr.framestate, 0, sizeof(xr.framestate)); + xr.framestate.type = XR_TYPE_FRAME_STATE; + switch(xr.state) + { + case XR_SESSION_STATE_FOCUSED: + case XR_SESSION_STATE_SYNCHRONIZED: + case XR_SESSION_STATE_VISIBLE: + xr.framestate.shouldRender = !!xr.session; + break; + default: + break; + } + + if (xr.framestate.shouldRender) + { + XrTime time; + res = xrWaitFrame(xr.session, NULL, &xr.framestate); + if (XR_FAILED(res)) + { + Con_Printf("xrWaitFrame: %i\n", res); + return false; + } + time = xr.framestate.predictedDisplayTime; + if (xr.timeknown) + { + if (time < xr.time) //make sure time doesn't go backward... + time = xr.time; + *frametime = (time-xr.time)/1000000000.0; + } + xr.time = time; + xr.timeknown = true; + } + + XR_ProcessEvents(); + if (xr.session) + XR_UpdateInputs(xr.framestate.predictedDisplayTime); + + return true; +} +static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3_t axisorg[4])) +{ + XrFrameEndInfo endframeinfo = {XR_TYPE_FRAME_END_INFO}; + unsigned int u; + XrResult res; + + XrCompositionLayerProjection proj = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + const XrCompositionLayerBaseHeader *projlist[] = {(XrCompositionLayerBaseHeader*)&proj}; + XrCompositionLayerProjectionView projviews[MAX_VIEW_COUNT]; + + if (!xr.instance) + return false; //err... noooes! + + if (!xr.session) + { + if (!xr_enable->ival) + return false; + if (!XR_Begin()) + { //something catasrophic went wrong. don't spam begins. + XR_Shutdown(); + } + return false; + } + + if (!xr_enable->ival) + { + res = xrRequestExitSession(xr.session); + if (XR_FAILED(res)) + Con_Printf("openxr: Unable to request session end: %i\n", res); + + XR_ProcessEvents(); + } + + switch(xr.state) + { + 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: + break; + default: + return false; //not ready. + } + + res = xrBeginFrame(xr.session, NULL); + if (XR_FAILED(res)) + Con_Printf("xrBeginFrame: %i\n", res); + if (xr.framestate.shouldRender) + { + uint32_t eyecount; + XrViewState viewstate = {XR_TYPE_VIEW_STATE}; + XrViewLocateInfo locateinfo = {XR_TYPE_VIEW_LOCATE_INFO}; + XrView eyeview[MAX_VIEW_COUNT]={}; + vec3_t transform[4]; + for (u = 0; u < MAX_VIEW_COUNT; u++) + eyeview[u].type = XR_TYPE_VIEW; + + locateinfo.displayTime = xr.framestate.predictedDisplayTime; + locateinfo.space = xr.space; + res = xrLocateViews(xr.session, &locateinfo, &viewstate, xr.viewcount, &eyecount, eyeview); + if (XR_FAILED(res)) + Con_Printf("xrLocateViews: %i\n", res); + + proj.layerFlags = 0; + proj.space = xr.space; + proj.views = projviews; + endframeinfo.layerCount = 1; + + for (u = 0; u < xr.viewcount && u < eyecount; u++) + { + vec4_t fovoverride; + XrSwapchainImageWaitInfo waitinfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + unsigned int imgidx = 0; + res = xrAcquireSwapchainImage(xr.eye[u].swapchain, NULL, &imgidx); + if (XR_FAILED(res)) + Con_Printf("xrAcquireSwapchainImage: %i\n", res); + + memset(&projviews[u], 0, sizeof(projviews[u])); + projviews[u].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + projviews[u].pose = eyeview[u].pose; + projviews[u].fov = eyeview[u].fov; + projviews[u].subImage = xr.eye[u].subimage; + + XR_PoseToTransform(&eyeview[u].pose, transform); + fovoverride[0] = eyeview[u].fov.angleLeft * (180/M_PI); + fovoverride[1] = eyeview[u].fov.angleRight * (180/M_PI); + fovoverride[2] = eyeview[u].fov.angleDown * (180/M_PI); + fovoverride[3] = eyeview[u].fov.angleUp * (180/M_PI); + + waitinfo.timeout = 100000; + res = xrWaitSwapchainImage(xr.eye[u].swapchain, &waitinfo); + if (XR_FAILED(res)) + Con_Printf("xrWaitSwapchainImage: %i\n", res); + rendereye(&xr.eye[u].swapimages[imgidx], fovoverride, transform); + //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. + // I take this to mean that the openxr runtime is responsible for barriers (as it'll need to transition it to general or shader-read anyway). + res = xrReleaseSwapchainImage(xr.eye[u].swapchain, NULL); + if (XR_FAILED(res)) + Con_Printf("xrReleaseSwapchainImage: %i\n", res); + } + proj.viewCount = u; + } + + endframeinfo.layers = projlist; + endframeinfo.displayTime = xr.framestate.predictedDisplayTime; + endframeinfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; //we don't do the alpha channel very well. + res = xrEndFrame(xr.session, &endframeinfo); + if (XR_FAILED(res)) + { + Con_Printf("xrEndFrame: %i\n", res); + if (res == XR_ERROR_SESSION_LOST || res == XR_ERROR_SESSION_NOT_RUNNING || res == XR_ERROR_SWAPCHAIN_RECT_INVALID) + XR_SessionEnded(); //something sessiony + else //if (res == XR_ERROR_INSTANCE_LOST) + XR_Shutdown(); //don't really know what it was, just kill everything + } + + return xr_skipregularview->ival; +} + +static plugvrfuncs_t openxr = +{ + "OpenXR", + XR_PreInit, + XR_Init, + XR_SyncFrame, + XR_Render, + XR_Shutdown, +}; + +qboolean Plug_Init(void) +{ + fsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs)); + plugfuncs->ExportFunction("MayUnload", XR_PluginMayUnload); + if (plugfuncs->ExportInterface(plugvrfuncs_name, &openxr, sizeof(openxr))) + { + xr_enable = cvarfuncs->GetNVFDG("xr_enable", "1", 0, "Controls whether to use openxr rendering or not.", "OpenXR configuration"); + 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"); + return true; + } + return false; +} \ No newline at end of file diff --git a/plugins/plugin.h b/plugins/plugin.h index f926bcc9..8cf56c3e 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -182,7 +182,8 @@ struct wstats_s; #define F(t, n, args) t (QDECL *n) args - +typedef void dllhandle_t; +struct dllfunction_s; typedef struct //core stuff { //Basic builtins: @@ -193,6 +194,9 @@ typedef struct //core stuff F(void, Print, (const char *message)); //print on (main) console. F(void, Error, (const char *message)); //abort the entire engine. F(quintptr_t,GetMilliseconds, (void)); + F(dllhandle_t*,LoadDLL, (const char *modulename, struct dllfunction_s *funcs)); + F(void*, GetDLLSymbol, (dllhandle_t *handle, const char *symbolname)); + F(void, CloseDLL, (dllhandle_t *handle)); //not guarenteed to actually do anything, of course. #define plugcorefuncs_name "Core" } plugcorefuncs_t; diff --git a/quakec/menusys/menu/servers.qc b/quakec/menusys/menu/servers.qc index c2560c78..9b1ed23e 100644 --- a/quakec/menusys/menu/servers.qc +++ b/quakec/menusys/menu/servers.qc @@ -111,7 +111,7 @@ class mitem_servers : mitem maxsv = slider.val + maxy; float sort = gethostcachevalue(SLIST_SORTFIELD); - string colkey; + string colkey = __NULL__; #define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x && ui.mousepos[1] < pos_y+8) colkey = #sortname; pos_x += width+8;} COLUMNS #undef COLUMN diff --git a/quakec/menusys/menu/updates.qc b/quakec/menusys/menu/updates.qc index 72837e6c..4c258081 100644 --- a/quakec/menusys/menu/updates.qc +++ b/quakec/menusys/menu/updates.qc @@ -138,7 +138,7 @@ class menu_updates : mitem_exmenu { if (key == "info") { - string text, tmp; + string text=__NULL__, tmp; tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_VERSION); text = strcat(text, "^aVersion:^a ", tmp?:"Unspecified"); tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_AUTHOR); text = strcat(text, "\n^aAuthor:^a ", tmp?:"Unknown"); tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_LICENSE); text = strcat(text, "\n^aLicense:^a ", tmp?:"Unknown"); diff --git a/quakec/menusys/menusys/mitem_frame.qc b/quakec/menusys/menusys/mitem_frame.qc index b8de1801..8e812f77 100644 --- a/quakec/menusys/menusys/mitem_frame.qc +++ b/quakec/menusys/menusys/mitem_frame.qc @@ -623,7 +623,7 @@ void() mitem_frame::item_resized = void(vector pos) mitem_frame::item_draw = { local vector omin = ui.drawrectmin, omax = ui.drawrectmax; - local mitem ch; + local mitem ch = __NULL__; local vector clientpos; local vector clientsize;