diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 95c2d023..e4e2f046 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -4195,7 +4195,7 @@ void CL_LinkPacketEntities (void) Q_snprintfz(name, sizeof(name), "textures/bmodels/simple_%s_%i.tga", basename, ent->skinnum); else Q_snprintfz(name, sizeof(name), "textures/models/simple_%s_%i.tga", basename, ent->skinnum); - model->simpleskin[ent->skinnum] = R_RegisterShader(name, 0, va("{\nnomipmaps\nprogram defaultsprite\nsurfaceparm noshadows\nsurfaceparm nodlight\nsort seethrough\n{\nmap \"%s\"\nalphafunc ge128\n}\n}\n", name)); + model->simpleskin[ent->skinnum] = R_RegisterShader(name, 0, va("{\nnomipmaps\nprogram defaultsprite#MASK=0.5\nsurfaceparm noshadows\nsurfaceparm nodlight\nsort seethrough\n{\nmap \"%s\"\nalphafunc ge128\n}\n}\n", name)); } VectorCopy(le->angles, angles); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 3959d588..ec9dfba9 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3493,9 +3493,14 @@ client_connect: //fixme: make function if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) cls.netchan.qportsize = 1; } - cls.netchan.fragmentsize = connectinfo.mtu; + cls.netchan.pext_fragmentation = connectinfo.mtu?true:false; if (connectinfo.mtu >= 64) + { + cls.netchan.mtu = connectinfo.mtu; cls.netchan.message.maxsize = sizeof(cls.netchan.message_buf); + } + else + cls.netchan.mtu = MAX_QWMSGLEN; #ifdef HUFFNETWORK cls.netchan.compresstable = Huff_CompressionCRC(connectinfo.compresscrc); #else @@ -6396,7 +6401,12 @@ void Host_FinishLoading(void) FS_ChangeGame(NULL, true, true); if (waitingformanifest) + { +#ifdef MULTITHREAD + Sys_Sleep(0.1); +#endif return; + } Con_History_Load(); @@ -6437,7 +6447,12 @@ void Host_FinishLoading(void) } if (PM_IsApplying(true)) + { +#ifdef MULTITHREAD + Sys_Sleep(0.1); +#endif return; + } //android may find that it has no renderer at various points. if (r_forceheadless) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 683d44c0..2f7a6940 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -1390,6 +1390,15 @@ static void PM_MarkPackage(package_t *package, unsigned int markflag) return; //looks like its already picked. marking it again will do no harm. } +#ifndef WEBCLIENT + //can't mark for download if we cannot download. + if (!(package->flags & DPF_PRESENT)) + { //though we can at least unmark it for deletion... + package->flags &= ~DPF_PURGE; + return; + } +#endif + //any file-conflicts prevent the package from being installable. //this is mostly for pak1.pak for (dep = package->deps; dep; dep = dep->next) @@ -1881,7 +1890,7 @@ static void PM_WriteInstalledPackages(void) if (*p->title && strcmp(p->title, p->name)) { Q_strncatz(buf, " ", sizeof(buf)); - COM_QuotedConcat(va("title=%s", p->version), buf, sizeof(buf)); + COM_QuotedConcat(va("title=%s", p->title), buf, sizeof(buf)); } if (*p->version) { @@ -2745,7 +2754,6 @@ qboolean PM_CanInstall(const char *packagename) void PM_Command_f(void) { - size_t i; package_t *p; const char *act = Cmd_Argv(1); const char *key; @@ -2878,6 +2886,7 @@ void PM_Command_f(void) } Con_Printf("\n"); } +#ifdef WEBCLIENT else if (!strcmp(act, "sources") || !strcmp(act, "addsource")) { if (Cmd_Argc() == 2) @@ -2890,6 +2899,7 @@ void PM_Command_f(void) else PM_AddSubList(Cmd_Argv(2), "", true, true); } +#endif else if (!strcmp(act, "remsource")) PM_RemSubList(Cmd_Argv(2)); else if (!strcmp(act, "apply")) @@ -2908,8 +2918,10 @@ void PM_Command_f(void) { PM_RevertChanges(); } +#ifdef WEBCLIENT else if (!strcmp(act, "update")) { //flush package cache, make a new request. + int i; for (i = 0; i < numdownloadablelists; i++) downloadablelist[i].received = 0; } @@ -2924,6 +2936,7 @@ void PM_Command_f(void) else Con_Printf("Already using latest versions of all packages\n"); } +#endif else if (!strcmp(act, "add") || !strcmp(act, "get") || !strcmp(act, "install") || !strcmp(act, "enable")) { //FIXME: make sure this updates. int arg = 2; @@ -2938,6 +2951,7 @@ void PM_Command_f(void) } PM_PrintChanges(); } +#ifdef WEBCLIENT else if (!strcmp(act, "reinstall")) { //fixme: favour the current verson. int arg = 2; @@ -2955,6 +2969,7 @@ void PM_Command_f(void) } PM_PrintChanges(); } +#endif else if (!strcmp(act, "disable") || !strcmp(act, "rem") || !strcmp(act, "remove")) { int arg = 2; @@ -3234,9 +3249,11 @@ static qboolean MD_Key (struct menucustom_s *c, struct menu_s *m, int key, unsig case DPF_USERMARKED: case DPF_MARKED: p->flags |= DPF_PURGE; +#ifdef WEBCLIENT //now: re-get despite already having it. if ((p->flags & DPF_CORRUPT) || ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p))) break; //only makes sense if we already have a cached copy that we're not going to use. +#endif //fallthrough case DPF_USERMARKED|DPF_PURGE: case DPF_AUTOMARKED|DPF_PURGE: @@ -3444,10 +3461,13 @@ static void MD_Download_UpdateStatus(struct menu_s *m) dlmenu_t *info = m->data; int i, y; package_t *p; - unsigned int totalpackages=0, selectedpackages=0, addpackages=0, rempackages=0, downloads=0; + unsigned int totalpackages=0, selectedpackages=0, addpackages=0, rempackages=0; menuoption_t *si; menubutton_t *b, *d; +#ifdef WEBCLIENT + unsigned int downloads=0; menucustom_t *c; +#endif if (info->downloadablessequence != downloadablessequence || !info->populated) { diff --git a/engine/client/m_options.c b/engine/client/m_options.c index e6bd7104..4a3e6004 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -1062,6 +1062,7 @@ void M_Menu_Preset_f (void) item = 6; //fast else item = 7; //simple + item++; //the autosave option item -= bias; while (item --> 0) menu->selecteditem = menu->selecteditem->common.next; diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index b226c5ac..3f6be24f 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -306,15 +306,15 @@ static void S_Info(void); static void S_Shutdown_f(void); */ -static cvar_t s_al_debug = CVAR("s_al_debug", "0"); -static cvar_t s_al_use_reverb = CVAR("s_al_use_reverb", "1"); +static cvar_t s_al_debug = CVARD("s_al_debug", "0", "Enables periodic checks for OpenAL errors."); +static cvar_t s_al_use_reverb = CVARD("s_al_use_reverb", "1", "Controls whether reverb effects will be used. Set to 0 to block them. Reverb requires gamecode to configure the reverb properties, other than underwater."); static cvar_t s_al_max_distance = CVARFC("s_al_max_distance", "1000",0,OnChangeALSettings); -static cvar_t s_al_speedofsound = CVARFC("s_al_speedofsound", "343.3",0,OnChangeALSettings); -static cvar_t s_al_dopplerfactor = CVARFC("s_al_dopplerfactor", "1.0",0,OnChangeALSettings); -static cvar_t s_al_distancemodel = CVARFC("s_al_distancemodel", "2",0,OnChangeALSettings); -static cvar_t s_al_rolloff_factor = CVAR("s_al_rolloff_factor", "1"); -static cvar_t s_al_reference_distance = CVAR("s_al_reference_distance", "120"); -static cvar_t s_al_velocityscale = CVAR("s_al_velocityscale", "1"); +static cvar_t s_al_speedofsound = CVARFCD("s_al_speedofsound", "343.3",0,OnChangeALSettings, "Configures the speed of sound, in game units per second. This affects doppler."); +static cvar_t s_al_dopplerfactor = CVARFCD("s_al_dopplerfactor", "1.0",0,OnChangeALSettings, "Multiplies the strength of doppler effects."); +static cvar_t s_al_distancemodel = CVARFCD("s_al_distancemodel", legacyval("2","0"),0,OnChangeALSettings, "Controls how sounds fade with distance.\n0: Inverse (most realistic)\n1: Inverse Clamped\n2: Linear (Quake-like)\n3: Linear Clamped\n4: Exponential\n5: Exponential Clamped\n6: None"); +//static cvar_t s_al_rolloff_factor = CVAR("s_al_rolloff_factor", "1"); +static cvar_t s_al_reference_distance = CVARD("s_al_reference_distance", "120", "This is the distance at which the sound is audiable with standard volume in the inverse distance models. Nearer sounds will be louder than the original sample."); +static cvar_t s_al_velocityscale = CVARD("s_al_velocityscale", "1", "Rescales velocity values, before doppler can be calculated."); static cvar_t s_al_static_listener = CVAR("s_al_static_listener", "0"); //cheat extern cvar_t snd_doppler; @@ -503,7 +503,7 @@ static void QDECL OpenAL_CvarInit(void) Cvar_Register(&s_al_dopplerfactor, SOUNDVARS); Cvar_Register(&s_al_distancemodel, SOUNDVARS); Cvar_Register(&s_al_reference_distance, SOUNDVARS); - Cvar_Register(&s_al_rolloff_factor, SOUNDVARS); +// Cvar_Register(&s_al_rolloff_factor, SOUNDVARS); Cvar_Register(&s_al_velocityscale, SOUNDVARS); Cvar_Register(&s_al_static_listener, SOUNDVARS); Cvar_Register(&s_al_speedofsound, SOUNDVARS); diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 5855828c..15ffe9d3 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -802,8 +802,7 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) #if defined(__i386__) //x86 signals don't leave the stack in a clean state, so replace the signal handler with the real crash address, and hide this function - ucontext_t *uc = vcontext; - array[1] = (void*)uc->uc_mcontext.gregs[REG_EIP]; + array[1] = (void*)((ucontext_t*)vcontext)->uc_mcontext.gregs[REG_EIP]; firstframe = 1; #elif defined(__amd64__) //amd64 is sane enough, but this function and the libc signal handler are on the stack, and should be ignored. @@ -995,7 +994,6 @@ int main (int c, const char **v) #endif TL_InitLanguages(parms.binarydir); - if (!isatty(STDIN_FILENO)) noconinput = !isPlugin; //don't read the stdin if its probably screwed (running in qtcreator seems to pipe stdout to stdin in an attempt to screw everything up). else diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index dbea5d85..0d8401be 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -3362,10 +3362,15 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod galias->numanimations++; intervals = (daliasinterval_t *)(ingroup+1); - sinter = LittleFloat(intervals->interval); - if (sinter <= 0) - sinter = 0.1; - frame->rate = 1/sinter; + if (frame->numposes == 0) + frame->rate = 10; + else + { + sinter = LittleFloat(intervals->interval); + if (sinter <= 0) + sinter = 0.1; + frame->rate = 1/sinter; + } pinframe = (dtrivertx_t *)(intervals+frame->numposes); for (k = 0; k < frame->numposes; k++) diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 246251d1..15b07cd8 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -52,7 +52,8 @@ typedef struct #endif qboolean loop; int numposes; - float rate; + //float *poseendtime; //first starts at 0, anim duration is poseendtime[numposes-1] + float rate; //average framerate of animation. #ifdef NONSKELETALMODELS galiaspose_t *poseofs; #endif @@ -219,7 +220,7 @@ typedef struct modplugfuncs_s void (QDECL *UnRegisterModelFormat)(int idx); void (QDECL *UnRegisterAllModelFormats)(void); - void *(QDECL *ZG_Malloc)(zonegroup_t *ctx, int size); //ctx=&mod->memgroup and the data will be freed when the model is freed. + void *(QDECL *ZG_Malloc)(zonegroup_t *ctx, size_t size); //ctx=&mod->memgroup and the data will be freed when the model is freed. void (QDECL *ConcatTransforms) (const float in1[3][4], const float in2[3][4], float out[3][4]); void (QDECL *M3x4_Invert) (const float *in1, float *out); diff --git a/engine/common/common.h b/engine/common/common.h index ae01b4b8..8c339fa1 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -131,6 +131,13 @@ typedef enum {false, true} qboolean; #define MAX_SERVERINFO_STRING 1024 //standard quake has 512 here. #define MAX_LOCALINFO_STRING 32768 + +#ifdef HAVE_LEGACY +#define legacyval(_legval,_newval) _legval +#else +#define legacyval(_legval,_newval) _newval +#endif + #ifdef HAVE_CLIENT #define cls_state cls.state #else @@ -448,7 +455,7 @@ struct cache_user_s; extern char com_gamepath[MAX_OSPATH]; extern char com_homepath[MAX_OSPATH]; -extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in +//extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in //extern char *com_basedir; //qofs_Make is used to 'construct' a variable of qofs_t type. this is so the code can merge two 32bit ints on old systems and use a long long type internally without generating warnings about bit shifts when qofs_t is only 32bit instead. diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index c8d0e616..9248f9bb 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -137,6 +137,7 @@ #define HAVE_WINSSPI //on windows #define FTPSERVER //sv_ftp cvar. #define WEBCLIENT //uri_get+any internal downloads etc +#define HAVE_HTTPSV //net_enable_http/websocket #define TCPCONNECT //support for playing over tcp sockets, instead of just udp. compatible with qizmo. //#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea. #define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections. diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 9e344f43..0dba5eaa 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -144,7 +144,7 @@ typedef struct cvar_group_s #define CVAR_WATCHED (1<<22) //report any attempts to change this cvar. #define CVAR_VIDEOLATCH (1<<23) -#define CVAR_LASTFLAG CVAR_SHADERSYSTEM +#define CVAR_LASTFLAG CVAR_VIDEOLATCH #define CVAR_LATCHMASK (CVAR_LATCH|CVAR_RENDERERLATCH|CVAR_SERVEROVERRIDE|CVAR_CHEAT|CVAR_SEMICHEAT) //you're only allowed one of these. #define CVAR_NEEDDEFAULT CVAR_CHEAT diff --git a/engine/common/fs.c b/engine/common/fs.c index b96cc34f..f264f780 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -172,14 +172,15 @@ char gamedirfile[MAX_OSPATH]; char pubgamedirfile[MAX_OSPATH]; //like gamedirfile, but not set to the fte-only paths - +searchpath_t *gameonly_homedir; +searchpath_t *gameonly_gamedir; char com_gamepath[MAX_OSPATH]; //c:\games\quake char com_homepath[MAX_OSPATH]; //c:\users\foo\my docs\fte\quake qboolean com_homepathenabled; qboolean com_homepathusable; //com_homepath is safe, even if not enabled. -char com_configdir[MAX_OSPATH]; //homedir/fte/configs +//char com_configdir[MAX_OSPATH]; //homedir/fte/configs int fs_hash_dups; int fs_hash_files; @@ -737,7 +738,7 @@ static void COM_Path_f (void) Con_Printf("pubgamedirfile: \"%s\"\n", pubgamedirfile); Con_Printf("com_gamepath: \"%s\"\n", com_gamepath); Con_Printf("com_homepath: \"%s\" (enabled: %s, usable: %s)\n", com_homepath, com_homepathenabled?"yes":"no", com_homepathusable?"yes":"no"); - Con_Printf("com_configdir: \"%s\"\n", com_configdir); +// Con_Printf("com_configdir: \"%s\"\n", com_configdir); if (fs_manifest) FS_Manifest_Print(fs_manifest); return; @@ -1915,6 +1916,9 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela fs_accessed_time = realtime; + if (fs_readonly && *mode == 'w') + return NULL; + if (relativeto == FS_SYSTEM) return VFSOS_Open(filename, mode); @@ -1937,23 +1941,48 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela //if there can only be one file (eg: write access) find out where it is. switch (relativeto) { - case FS_GAMEONLY: //OS access only, no paks + case FS_GAMEONLY: //OS access only, no paks. Used for (re)writing files. vfs = NULL; + //FIXME: go via a searchpath, because then the fscache can be selectively updated if (com_homepathenabled) { - if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homepath, gamedirfile, filename)) - return NULL; - if (*mode == 'w') - COM_CreatePath(fullname); - vfs = VFSOS_Open(fullname, mode); + if (gameonly_homedir) + { + if ((*mode == 'w') + ? gameonly_homedir->handle->CreateFile(gameonly_homedir->handle, &loc, filename) + : gameonly_homedir->handle->FindFile (gameonly_homedir->handle, &loc, filename, NULL)) + vfs = gameonly_homedir->handle->OpenVFS (gameonly_homedir->handle, &loc, mode); + else + vfs = NULL; + } + else + { + if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homepath, gamedirfile, filename)) + return NULL; + if (*mode == 'w') + COM_CreatePath(fullname); + vfs = VFSOS_Open(fullname, mode); + } } if (!vfs && *gamedirfile) { - if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_gamepath, gamedirfile, filename)) - return NULL; - if (*mode == 'w') - COM_CreatePath(fullname); - vfs = VFSOS_Open(fullname, mode); + if (gameonly_gamedir) + { + if ((*mode == 'w') + ? gameonly_gamedir->handle->CreateFile(gameonly_gamedir->handle, &loc, filename) + : gameonly_gamedir->handle->FindFile (gameonly_gamedir->handle, &loc, filename, NULL)) + vfs = gameonly_gamedir->handle->OpenVFS (gameonly_gamedir->handle, &loc, mode); + else + vfs = NULL; + } + else + { + if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_gamepath, gamedirfile, filename)) + return NULL; + if (*mode == 'w') + COM_CreatePath(fullname); + vfs = VFSOS_Open(fullname, mode); + } } if (vfs || !(*mode == 'w' || *mode == 'a')) return vfs; @@ -2793,7 +2822,7 @@ Sets com_gamedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ -static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff, unsigned int flags) +static searchpath_t *FS_AddSingleGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff, unsigned int flags) { unsigned int keptflags; searchpath_t *search; @@ -2808,7 +2837,7 @@ static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, c if (!Q_strcasecmp(search->logicalpath, dir)) { search->flags |= flags & SPF_WRITABLE; - return; //already loaded (base paths?) + return search; //already loaded (base paths?) } } @@ -2831,7 +2860,20 @@ static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, c if (!handle) handle = VFSOS_OpenPath(NULL, NULL, dir, dir, ""); - FS_AddPathHandle(oldpaths, puredir, dir, handle, "", flags|keptflags, loadstuff); + return FS_AddPathHandle(oldpaths, puredir, dir, handle, "", flags|keptflags, loadstuff); +} +static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, unsigned int loadstuff, unsigned int flags) +{ + char syspath[MAX_OSPATH]; + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, puredir); + gameonly_gamedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags&~(com_homepathenabled?SPF_WRITABLE:0u)); + if (com_homepathenabled) + { + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, puredir); + gameonly_homedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags); + } + else + gameonly_homedir = NULL; } //if syspath, something like c:\quake\baseq2 @@ -3698,7 +3740,6 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) searchpath_t *next; int i; int orderkey; - char syspath[MAX_OSPATH]; COM_AssertMainThread("FS_ReloadPackFilesFlags"); COM_WorkerFullSync(); @@ -3715,6 +3756,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) com_searchpaths = NULL; com_purepaths = NULL; com_base_searchpaths = NULL; + gameonly_gamedir = gameonly_homedir = NULL; i = COM_CheckParm ("-basepack"); while (i && i < com_argc-1) @@ -3777,23 +3819,11 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) else if (*dir == '*') { //paths with a leading * are private, and not announced to clients that ask what the current gamedir is. - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, dir+1); - FS_AddGameDirectory(&oldpaths, dir+1, syspath, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|(com_homepathenabled?0:SPF_WRITABLE)); - if (com_homepathenabled) - { - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, dir+1); - FS_AddGameDirectory(&oldpaths, dir+1, syspath, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|SPF_WRITABLE); - } + FS_AddGameDirectory(&oldpaths, dir+1, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|SPF_WRITABLE); } else { - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, dir); - FS_AddGameDirectory(&oldpaths, dir, syspath, reloadflags, SPF_EXPLICIT|(com_homepathenabled?0:SPF_WRITABLE)); - if (com_homepathenabled) - { - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, dir); - FS_AddGameDirectory(&oldpaths, dir, syspath, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); - } + FS_AddGameDirectory(&oldpaths, dir, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); } } } @@ -3823,9 +3853,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) } else { - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_gamepath, dir), reloadflags, SPF_EXPLICIT|(com_homepathenabled?0:SPF_WRITABLE)); - if (com_homepathenabled) - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homepath, dir), reloadflags, SPF_EXPLICIT|SPF_WRITABLE); + FS_AddGameDirectory(&oldpaths, dir, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); } } } @@ -6330,7 +6358,6 @@ static void COM_InitHomedir(ftemanifest_t *man) //but if it doesn't exist then we use $XDG_DATA_HOME/.fte instead //we used to use $HOME/.#HOMESUBDIR/ but this is now only used if it actually exists AND the new path doesn't. //new installs use $XDG_DATA_HOME/#HOMESUBDIR/ instead - char *ev = getenv("FTEHOME"); if (ev && *ev) { diff --git a/engine/common/fs.h b/engine/common/fs.h index 70c4a335..83d93364 100644 --- a/engine/common/fs.h +++ b/engine/common/fs.h @@ -49,9 +49,9 @@ struct searchpathfuncs_s qboolean (QDECL *PollChanges)(searchpathfuncs_t *handle); //returns true if there were changes qboolean (QDECL *FileStat)(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime); - qboolean (QDECL *RenameFile)(searchpathfuncs_t *handle, const char *oldname, const char *newname); //returns true on success, false if source doesn't exist, or if dest does. + qboolean (QDECL *CreateFile)(searchpathfuncs_t *handle, flocation_t *loc, const char *filename); //like FindFile, but returns a usable loc even if the file does not exist yet (may also create requisite directories too) + qboolean (QDECL *RenameFile)(searchpathfuncs_t *handle, const char *oldname, const char *newname); //returns true on success, false if source doesn't exist, or if dest does (cached locs may refer to either new or old name). qboolean (QDECL *RemoveFile)(searchpathfuncs_t *handle, const char *filename); //returns true on success, false if it wasn't found or is readonly. - qboolean (QDECL *MkDir)(searchpathfuncs_t *handle, const char *filename); //is this really needed? }; //searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc); //returns a handle to a new pak/path @@ -87,7 +87,7 @@ int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), #define SPF_TEMPORARY 4 //a map-specific path, purged at map change. #define SPF_EXPLICIT 8 //a root gamedir (bumps depth on gamedir depth checks). #define SPF_UNTRUSTED 16 //has been downloaded from somewhere. configs inside it should never be execed with local access rights. -#define SPF_PRIVATE 32 //private to the client. ie: the fte dir. +#define SPF_PRIVATE 32 //private to the client. ie: the fte dir. name is not networked. #define SPF_WRITABLE 64 //safe to write here. lots of weird rules etc. #define SPF_BASEPATH 128 //part of the basegames, and not the mod gamedir(s). qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags); diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index a9be3e13..22a20fb4 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -271,6 +271,30 @@ static void QDECL FSSTDIO_BuildHash(searchpathfuncs_t *handle, int depth, void ( sp->AddFileHash = AddFileHash; Sys_EnumerateFiles(sp->rootpath, "*", FSSTDIO_RebuildFSHash, AddFileHash, handle); } + +static unsigned int QDECL FSSTDIO_CreateLoc(searchpathfuncs_t *handle, flocation_t *loc, const char *filename) +{ + stdiopath_t *sp = (void*)handle; + char *ofs; + + loc->len = 0; + loc->offset = 0; + loc->fhandle = handle; + if ((unsigned int)snprintf(loc->rawname, sizeof(loc->rawname), "%s/%s", sp->rootpath, filename) > sizeof(loc->rawname)-1) + return FF_NOTFOUND; //too long... + + for (ofs = loc->rawname+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (loc->rawname); + *ofs = '/'; + } + } + + return FF_FOUND; +} static unsigned int QDECL FSSTDIO_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult) { stdiopath_t *sp = (void*)handle; @@ -289,7 +313,8 @@ static unsigned int QDECL FSSTDIO_FLocate(searchpathfuncs_t *handle, flocation_t */ // check a file in the directory tree - snprintf (netpath, sizeof(netpath)-1, "%s/%s", sp->rootpath, filename); + if ((unsigned int)snprintf (netpath, sizeof(netpath), "%s/%s", sp->rootpath, filename) > sizeof(netpath)-1) + return FF_NOTFOUND; #ifdef ANDROID { @@ -378,6 +403,7 @@ searchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, searchpathfuncs np->pub.OpenVFS = FSSTDIO_OpenVFS; np->pub.PollChanges = FSSTDIO_PollChanges; np->pub.FileStat = FSSTDIO_FileStat; + np->pub.CreateFile = FSSTDIO_CreateLoc; return &np->pub; } diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index 34a2a42f..1b4a8e30 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -522,6 +522,30 @@ static void QDECL VFSW32_BuildHash(searchpathfuncs_t *handle, int hashdepth, voi wp->hashdepth = hashdepth; Sys_EnumerateFiles(wp->rootpath, "*", VFSW32_RebuildFSHash, AddFileHash, handle); } +static unsigned int QDECL VFSW32_CreateLoc(searchpathfuncs_t *handle, flocation_t *loc, const char *filename) +{ + vfsw32path_t *wp = (void*)handle; + char *ofs; + + loc->len = 0; + loc->offset = 0; + loc->fhandle = handle; + loc->rawname[sizeof(loc->rawname)-1] = 0; + if ((unsigned int)snprintf (loc->rawname, sizeof(loc->rawname), "%s/%s", wp->rootpath, filename) > sizeof(loc->rawname)-1) + return FF_NOTFOUND; + for (ofs = loc->rawname+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (loc->rawname); + *ofs = '/'; + } + } + + return FF_FOUND; +} + #include static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult) { @@ -545,7 +569,8 @@ static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t */ // check a file in the directory tree - snprintf (netpath, sizeof(netpath)-1, "%s/%s", wp->rootpath, filename); + if ((unsigned int)snprintf (netpath, sizeof(netpath), "%s/%s", wp->rootpath, filename) > sizeof(loc->rawname)-1) + return FF_NOTFOUND; if (!WinNT) { @@ -634,16 +659,6 @@ static qboolean QDECL VFSW32_RemoveFile(searchpathfuncs_t *handle, const char *f snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename); return Sys_remove(syspath); } -static qboolean QDECL VFSW32_MkDir(searchpathfuncs_t *handle, const char *filename) -{ - vfsw32path_t *wp = (vfsw32path_t*)handle; - char syspath[MAX_OSPATH]; - if (fs_readonly) - return false; - snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename); - Sys_mkdir(syspath); - return true; -} searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix) { @@ -675,9 +690,10 @@ searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_ np->pub.FileStat = VFSW32_FileStat; +#undef CreateFile //stoopid windows.h + np->pub.CreateFile = VFSW32_CreateLoc; np->pub.RenameFile = VFSW32_RenameFile; np->pub.RemoveFile = VFSW32_RemoveFile; - np->pub.MkDir = VFSW32_MkDir; return &np->pub; } diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 04886554..2b6d09ca 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -313,7 +313,8 @@ typedef struct { struct { - int cp[2]; + unsigned short cp[2]; + unsigned short fixedres[2]; } patch; struct { @@ -527,7 +528,7 @@ static int Patch_FlatnessTest( float maxflat2, const float *point0, const float Patch_GetFlatness =============== */ -static void Patch_GetFlatness( float maxflat, const float *points, int comp, const int *patch_cp, int *flat ) +static void Patch_GetFlatness( float maxflat, const float *points, int comp, const unsigned short *patch_cp, int *flat ) { int i, p, u, v; float maxflat2 = maxflat * maxflat; @@ -579,7 +580,7 @@ static void Patch_Evaluate_QuadricBezier( float t, const vec_t *point0, const ve Patch_Evaluate =============== */ -static void Patch_Evaluate( const vec_t *p, const int *numcp, const int *tess, vec_t *dest, int comp ) +static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const int *tess, vec_t *dest, int comp ) { int num_patches[2], num_tess[2]; int index[3], dstpitch, i, u, v, x, y; @@ -850,7 +851,7 @@ static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numver /* * CM_CreatePatch */ -static void CM_CreatePatch(model_t *loadmodel, q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const int *patch_cp ) +static void CM_CreatePatch(model_t *loadmodel, q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const unsigned short *patch_cp, const unsigned short *patch_subdiv) { int step[2], size[2], flat[2]; int i, j, k ,u, v; @@ -1088,7 +1089,7 @@ static qboolean CM_CreatePatchForFace (model_t *loadmodel, cminfo_t *prv, mleaf_ checkout[facenum] = prv->numpatches++; //gcc warns without this cast - CM_CreatePatch (loadmodel, patch, surf, (const vec_t *)(prv->verts + face->firstvert), face->patch.cp ); + CM_CreatePatch (loadmodel, patch, surf, (const vec_t *)(prv->verts + face->firstvert), face->patch.cp, face->patch.fixedres ); } leaf->contents |= patch->surface->c.value; leaf->numleafpatches++; @@ -2554,8 +2555,12 @@ static qboolean CModRBSP_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l) if (out->facetype == MST_PATCH) { - out->patch.cp[0] = LittleLong ( in->patchwidth ); - out->patch.cp[1] = LittleLong ( in->patchheight ); + unsigned int w = LittleLong ( in->patchwidth ); + unsigned int h = LittleLong ( in->patchheight ); + out->patch.cp[0] = w&0xffff; + out->patch.cp[1] = h&0xffff; + out->patch.fixedres[0]=w>>16; + out->patch.fixedres[1]=h>>16; } else { @@ -2658,11 +2663,12 @@ static index_t tempIndexesArray[MAX_ARRAY_VERTS*6]; static void GL_SizePatch(mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert, cminfo_t *prv) { - int patch_cp[2], step[2], size[2], flat[2]; + unsigned short patch_cp[2]; + int step[2], size[2], flat[2]; float subdivlevel; - patch_cp[0] = patchwidth; - patch_cp[1] = patchheight; + patch_cp[0] = patchwidth&0xffff; + patch_cp[1] = patchheight&0xffff; if (patch_cp[0] <= 0 || patch_cp[1] <= 0 ) { @@ -2692,13 +2698,14 @@ static void GL_SizePatch(mesh_t *mesh, int patchwidth, int patchheight, int numv static void GL_CreateMeshForPatch (model_t *mod, mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert) { cminfo_t *prv = (cminfo_t*)mod->meshinfo; - int numindexes, patch_cp[2], step[2], size[2], flat[2], i, u, v, p; + int numindexes, step[2], size[2], flat[2], i, u, v, p; + unsigned short patch_cp[2]; index_t *indexes; float subdivlevel; int sty; - patch_cp[0] = patchwidth; - patch_cp[1] = patchheight; + patch_cp[0] = patchwidth&0xffff; + patch_cp[1] = patchheight&0xffff; if (patch_cp[0] <= 0 || patch_cp[1] <= 0 ) { diff --git a/engine/common/net.h b/engine/common/net.h index e51960cc..0ed4269d 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -209,9 +209,10 @@ typedef struct float nqreliable_resendtime;//force nqreliable_allowed, thereby forcing a resend of anything n qbyte nqunreliableonly; //nq can't cope with certain reliables some times. if 2, we have a reliable that result in a block (that should be sent). if 1, we are blocking. if 0, we can send reliables freely. if 3, then we just want to ignore clc_moves #endif + qboolean pext_fragmentation; //fte's packet fragmentation extension, to avoid issues with low mtus. struct netprim_s netprim; - int fragmentsize; - int dupe; + int mtu; //the path mtu, if known + int dupe; //how many times to dupe packets float last_received; // for timeouts diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 70a3e91b..1887a7df 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -692,7 +692,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) if (chan->message.overflowed) { chan->fatal_error = true; - Con_TPrintf ("%s:Outgoing message overflow\n" + Con_TPrintf ("%s: Outgoing message overflow\n" , NET_AdrToString (remote_adr, sizeof(remote_adr), &chan->remote_address)); return 0; } @@ -716,7 +716,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) // write the packet header send.data = send_buf; - send.maxsize = MAX_QWMSGLEN + PACKET_HEADER; + send.maxsize = (chan->mtu?chan->mtu:MAX_QWMSGLEN) + PACKET_HEADER; send.cursize = 0; w1 = chan->outgoing_sequence | (send_reliable<<31); @@ -738,9 +738,9 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) } #endif - if (chan->fragmentsize) + if (chan->pext_fragmentation) { - //allow the max size to be bigger + //allow the max size to be bigger, sending everything available send.maxsize = MAX_OVERALLMSGLEN + PACKET_HEADER; MSG_WriteShort(&send, 0); } @@ -748,6 +748,17 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) // copy the reliable message to the packet first if (send_reliable) { + if (send.maxsize - send.cursize < chan->reliable_length) + { + if (!chan->fatal_error) + { + chan->fatal_error = true; + Con_TPrintf ("%s: Path MTU is lower than %u\n" + , NET_AdrToString (remote_adr, sizeof(remote_adr), &chan->remote_address), chan->reliable_length); + } + chan->outgoing_sequence--; + return 0; + } SZ_Write (&send, chan->reliable_buf, chan->reliable_length); chan->last_reliable_sequence = chan->outgoing_sequence; } @@ -765,7 +776,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) if (chan->compresstable) { //int oldsize = send.cursize; - Huff_CompressPacket(chan->compresstable, &send, 8 + ((chan->sock == NS_CLIENT)?2:0) + (chan->fragmentsize?2:0)); + Huff_CompressPacket(chan->compresstable, &send, 8 + ((chan->sock == NS_CLIENT)?2:0) + (chan->pext_fragmentation?2:0)); // Con_Printf("%i becomes %i\n", oldsize, send.cursize); // Huff_DecompressPacket(&send, (chan->sock == NS_CLIENT)?10:8); } @@ -779,20 +790,23 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) { int hsz = 10 + ((chan->sock == NS_CLIENT)?chan->qportsize:0); /*header size, if fragmentation is in use*/ - if ((!chan->fragmentsize))// || send.cursize < ((chan->fragmentsize - hsz)&~7)) - { + if ((!chan->pext_fragmentation))// || send.cursize < ((chan->mtu - hsz)&~7)) + { //vanilla sends for (i = -1; i < chan->dupe && e == NETERR_SENT; i++) e = NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address); send.cursize += send.cursize * i; - if (e == NETERR_MTU && chan->fragmentsize > 560) + //ipv4 'guarentees' mtu sizes of at least 560ish. + //our reliable/backbuf messages are limited to 1024 bytes. + //this means that large reliables may be unsendable. + if (e == NETERR_MTU && chan->mtu > 560) { - Con_Printf("Reducing MSS to %i\n", chan->fragmentsize); - chan->fragmentsize -= 10; + Con_Printf("Reducing MSS to %i\n", chan->mtu); + chan->mtu -= 10; } } else - { + { //fte's fragmentaton protocol int offset = 0, no; qboolean more; @@ -801,7 +815,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) /*send the additional parts, adding new headers within the previous packet*/ do { - no = offset + chan->fragmentsize - hsz; + no = offset + chan->mtu - hsz; if (no < send.cursize-hsz) { no &= ~7; @@ -831,10 +845,10 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) for (i = -1; i < chan->dupe && e == NETERR_SENT; i++) { e = NET_SendPacket (chan->sock, (no - offset) + hsz, send.data + offset, &chan->remote_address); - if (e == NETERR_MTU && !offset && chan->fragmentsize > 560) + if (e == NETERR_MTU && !offset && chan->mtu > 560) { - chan->fragmentsize -= 16; - Con_Printf("Reducing MSS to %i\n", chan->fragmentsize); + chan->mtu -= 16; + Con_Printf("Reducing MSS to %i\n", chan->mtu); no = offset; more = true; break; @@ -917,7 +931,7 @@ qboolean Netchan_Process (netchan_t *chan) MSG_ReadShort (); #endif - if (chan->fragmentsize) + if (chan->pext_fragmentation) offset = (unsigned short)MSG_ReadShort(); else offset = 0; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index bb8ac2ec..afe46985 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -101,9 +101,9 @@ FTE_ALIGN(4) qbyte net_message_buffer[MAX_OVERALLMSGLEN]; #define HAVE_NATPMP #endif -#if defined(HAVE_SERVER) || defined(MASTERONLY) - #define HAVE_HTTPSV -#endif +//#if !defined(HAVE_SERVER) && !defined(MASTERONLY) +// #undef HAVE_HTTPSV +//#endif void NET_GetLocalAddress (int socket, netadr_t *out); //int TCP_OpenListenSocket (const char *localip, int port); @@ -127,6 +127,7 @@ cvar_t net_enable_qtv = CVARD("net_enable_qtv", "1", "Listens for qtv proxie #if defined(HAVE_SSL) cvar_t net_enable_tls = CVARD("net_enable_tls", "1", "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."); #endif +#ifdef HAVE_HTTPSV #ifdef SV_MASTER cvar_t net_enable_http = CVARD("net_enable_http", "1", "If enabled, tcp ports will accept http clients, potentially serving large files which could distrupt gameplay."); #else @@ -135,6 +136,7 @@ cvar_t net_enable_http = CVARD("net_enable_http", "0", "If enabled, tcp port cvar_t net_enable_websockets = CVARD("net_enable_websockets", "1", "If enabled, tcp ports will accept websocket game clients."); cvar_t net_enable_webrtcbroker = CVARD("net_enable_webrtcbroker", "0", "If 1, tcp ports will accept websocket connections from clients trying to broker direct webrtc connections. This should be low traffic, but might involve a lot of mostly-idle connections."); #endif +#endif #if defined(HAVE_DTLS) && defined(HAVE_SERVER) static void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue) { @@ -3693,10 +3695,10 @@ typedef struct ftenet_tcpconnect_stream_s { { TCPC_UNKNOWN, //waiting to see what they send us. TCPC_QIZMO, //'qizmo\n' handshake, followed by packets prefixed with a 16bit packet length. +#ifdef HAVE_HTTPSV TCPC_WEBSOCKETU, //utf-8 encoded data. TCPC_WEBSOCKETB, //binary encoded data (subprotocol = 'binary') TCPC_WEBSOCKETNQ, //raw nq msg buffers with no encapsulation or handshake -#ifdef HAVE_HTTPSV TCPC_HTTPCLIENT, //we're sending a file to this victim. TCPC_WEBRTC_CLIENT, //for brokering webrtc connections, doesn't carry any actual game data itself. TCPC_WEBRTC_HOST //for brokering webrtc connections, doesn't carry any actual game data itself. @@ -4718,8 +4720,8 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcpconnect_connection_t *con, ftenet return FTENET_TCPConnect_HTTPResponse(st, arg, acceptsgzip); } } - -#ifdef HAVE_SSL +#endif +#if defined(HAVE_SSL) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV)) static int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytestoread) { if (bytestoread > net_message.cursize) @@ -4730,7 +4732,6 @@ static int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytes return bytestoread; } #endif -#endif void FTENET_TCPConnect_PrintStatus(ftenet_generic_connection_t *gcon) { ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; @@ -4749,12 +4750,12 @@ void FTENET_TCPConnect_PrintStatus(ftenet_generic_connection_t *gcon) case TCPC_QIZMO: Con_Printf("qizmo %s\n", adr); break; +#ifdef HAVE_HTTPSV case TCPC_WEBSOCKETU: case TCPC_WEBSOCKETB: case TCPC_WEBSOCKETNQ: Con_Printf("websocket %s\n", adr); break; -#ifdef HAVE_HTTPSV case TCPC_HTTPCLIENT: Con_Printf("http %s\n", adr); break; @@ -5011,13 +5012,12 @@ closesvstream: net_from = st->remoteaddr; return true; +#ifdef HAVE_HTTPSV case TCPC_WEBSOCKETU: case TCPC_WEBSOCKETB: case TCPC_WEBSOCKETNQ: -#ifdef HAVE_HTTPSV case TCPC_WEBRTC_HOST: case TCPC_WEBRTC_CLIENT: -#endif while (st->inlen >= 2) { unsigned short ctrl = ((unsigned char*)st->inbuffer)[0]<<8 | ((unsigned char*)st->inbuffer)[1]; @@ -5164,7 +5164,6 @@ closesvstream: Con_TPrintf ("Warning: Oversize packet from %s\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); goto closesvstream; } -#ifdef HAVE_HTTPSV #ifdef SUPPORT_RTC_ICE if (st->clienttype == TCPC_WEBRTC_CLIENT && !*st->webrtc.resource) { //this is a client that's corrected directly to us via webrtc. @@ -5203,7 +5202,6 @@ closesvstream: net_message.cursize = 0; } else -#endif #ifdef NQPROT if (st->clienttype == TCPC_WEBSOCKETNQ) { //hack in an 8-byte header @@ -5248,6 +5246,7 @@ closesvstream: } } break; +#endif } } @@ -5298,7 +5297,6 @@ closesvstream: neterr_t FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to) { - neterr_t e; ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; ftenet_tcpconnect_stream_t *st; @@ -5333,6 +5331,7 @@ neterr_t FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len } } break; +#ifdef HAVE_HTTPSV case TCPC_WEBSOCKETNQ: if (length < 8 || ((char*)data)[0] & 0x80) break; @@ -5344,10 +5343,13 @@ neterr_t FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len //fallthrough case TCPC_WEBSOCKETU: case TCPC_WEBSOCKETB: - e = FTENET_TCPConnect_WebSocket_Splurge(st, (st->clienttype==TCPC_WEBSOCKETU)?1:2, data, length); - if (e != NETERR_SENT) - return e; + { + neterr_t e = FTENET_TCPConnect_WebSocket_Splurge(st, (st->clienttype==TCPC_WEBSOCKETU)?1:2, data, length); + if (e != NETERR_SENT) + return e; + } break; +#endif default: break; } @@ -7877,10 +7879,12 @@ void NET_Init (void) #if defined(HAVE_SSL) Cvar_Register(&net_enable_tls, "networking"); #endif +#ifdef HAVE_HTTPSV Cvar_Register(&net_enable_http, "networking"); Cvar_Register(&net_enable_websockets, "networking"); Cvar_Register(&net_enable_webrtcbroker, "networking"); #endif +#endif diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 8a8e7392..cb75d8eb 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -125,7 +125,7 @@ void Plug_RegisterBuiltin(char *name, Plug_Builtin_t bi, int flags) } //got an empty number. - Con_DPrintf("%s: %i\n", name, newnum); + Con_DLPrintf(2, "%s: %i\n", name, newnum); plugbuiltins[newnum].name = name; plugbuiltins[newnum].func = bi; plugbuiltins[newnum].flags = flags; diff --git a/engine/common/sys_linux_threads.c b/engine/common/sys_linux_threads.c index b0d9b9f6..931a768b 100644 --- a/engine/common/sys_linux_threads.c +++ b/engine/common/sys_linux_threads.c @@ -364,7 +364,9 @@ pubsubserver_t *Sys_ForkServer(void) //make sure we're fully synced, so that workers can't mess up Cvar_Set(Cvar_FindVar("worker_count"), "0"); COM_WorkerFullSync(); +#ifdef WEBCLIENT DL_DeThread(); +#endif #ifdef SQL SQL_KillServers(); //FIXME: this is bad... #endif diff --git a/engine/common/zone.c b/engine/common/zone.c index 5275e278..c9331b1d 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -52,11 +52,11 @@ qbyte sentinalkey; #define TAGLESS 1 -int zmemtotal; -int zmemdelta; +size_t zmemtotal; +size_t zmemdelta; typedef struct memheader_s { - int size; + size_t size; int tag; } memheader_t; @@ -108,13 +108,13 @@ static void Z_DumpTree(void) } #endif -void *VARGS Z_TagMalloc(int size, int tag) +void *Z_TagMalloc(size_t size, int tag) { zone_t *zone; zone = (zone_t *)malloc(size + sizeof(zone_t)); if (!zone) - Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + Sys_Error("Z_Malloc: Failed on allocation of %"PRIuSIZE" bytes", size); Q_memset(zone, 0, size + sizeof(zone_t)); zone->mh.tag = tag; zone->mh.size = size; @@ -184,7 +184,7 @@ void *Z_MallocNamed(int size, char *file, int line) return mem; } #else -void *ZF_Malloc(int size) +void *ZF_Malloc(size_t size) { #ifdef ANDROID void *ret = NULL; @@ -202,11 +202,11 @@ void *ZF_Malloc(int size) return calloc(size, 1); #endif } -void *Z_Malloc(int size) +void *Z_Malloc(size_t size) { void *mem = ZF_Malloc(size); if (!mem) - Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + Sys_Error("Z_Malloc: Failed on allocation of %"PRIuSIZE" bytes", size); return mem; } @@ -423,7 +423,7 @@ void *BZF_MallocNamed(int size, const char *file, int line) //BZ_MallocNamed but return mem; } #else -void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc. +void *BZF_Malloc(size_t size) //BZ_Malloc but allowed to fail - like straight malloc. { void *mem; mem = malloc(size); @@ -446,11 +446,11 @@ void *BZ_MallocNamed(int size, const char *file, int line) //BZ_MallocNamed but return mem; } #else -void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensitive data structures. +void *BZ_Malloc(size_t size) //Doesn't clear. The expectation is a large file, rather than sensitive data structures. { void *mem = BZF_Malloc(size); if (!mem) - Sys_Error("BZ_Malloc: Failed on allocation of %i bytes", size); + Sys_Error("BZ_Malloc: Failed on allocation of %"PRIuSIZE" bytes", size); return mem; } @@ -472,17 +472,17 @@ void *BZ_ReallocNamed(void *data, int newsize, const char *file, int line) return mem; } #else -void *BZF_Realloc(void *data, int newsize) +void *BZF_Realloc(void *data, size_t newsize) { return realloc(data, newsize); } -void *BZ_Realloc(void *data, int newsize) +void *BZ_Realloc(void *data, size_t newsize) { void *mem = BZF_Realloc(data, newsize); if (!mem) - Sys_Error("BZ_Realloc: Failed on reallocation of %i bytes", newsize); + Sys_Error("BZ_Realloc: Failed on reallocation of %"PRIuSIZE" bytes", newsize); return mem; } @@ -508,7 +508,7 @@ typedef struct zonegroupblock_s void *QDECL ZG_Malloc(zonegroup_t *ctx, int size){return ZG_MallocNamed(ctx, size, "ZG_Malloc", size);} void *ZG_MallocNamed(zonegroup_t *ctx, int size, char *file, int line) #else -void *QDECL ZG_Malloc(zonegroup_t *ctx, int size) +void *QDECL ZG_Malloc(zonegroup_t *ctx, size_t size) #endif { zonegroupblock_t *newm; @@ -590,7 +590,7 @@ void Hunk_TempFree(void) //allocates without clearing previous temp. //safer than my hack that fuh moaned about... -void *Hunk_TempAllocMore (int size) +void *Hunk_TempAllocMore (size_t size) { void *buf; @@ -624,7 +624,7 @@ void *Hunk_TempAllocMore (int size) } -void *Hunk_TempAlloc (int size) +void *Hunk_TempAlloc (size_t size) { Hunk_TempFree(); @@ -657,8 +657,8 @@ void Cache_Flush(void) static void Hunk_Print_f (void) { - Con_Printf("Z Delta: %iKB\n", zmemdelta/1024); zmemdelta = 0; - Con_Printf("Z Total: %iKB\n", zmemtotal/1024); + Con_Printf("Z Delta: %"PRIuSIZE"KB\n", zmemdelta/1024); zmemdelta = 0; + Con_Printf("Z Total: %"PRIuSIZE"KB\n", zmemtotal/1024); //note: Zone memory isn't tracked reliably. we don't track the mem that is freed, so it'll just climb and climb //we don't track reallocs either. diff --git a/engine/common/zone.h b/engine/common/zone.h index f8868869..4c8da9aa 100644 --- a/engine/common/zone.h +++ b/engine/common/zone.h @@ -87,12 +87,12 @@ void Memory_Init (void); void Memory_DeInit(void); void VARGS Z_Free (void *ptr); -void *Z_Malloc (int size); // returns 0 filled memory -void *ZF_Malloc (int size); // allowed to fail -void *Z_MallocNamed (int size, char *file, int line); // returns 0 filled memory -void *ZF_MallocNamed (int size, char *file, int line); // allowed to fail +void *Z_Malloc (size_t size); // returns 0 filled memory +void *ZF_Malloc (size_t size); // allowed to fail +void *Z_MallocNamed (size_t size, char *file, int line); // returns 0 filled memory +void *ZF_MallocNamed (size_t size, char *file, int line); // allowed to fail //#define Z_Malloc(x) Z_MallocNamed2(x, __FILE__, __LINE__ ) -void *VARGS Z_TagMalloc (int size, int tag); +void *Z_TagMalloc (size_t size, int tag); void VARGS Z_TagFree(void *ptr); void VARGS Z_FreeTags(int tag); qboolean ZF_ReallocElements(void **ptr, size_t *elements, size_t newelements, size_t elementsize); //returns false on error @@ -101,14 +101,14 @@ qboolean ZF_ReallocElementsNamed(void **ptr, size_t *elements, size_t newelement //Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensitive data structures. //(this is a nicer name for malloc) -void *BZ_Malloc(int size); -void *BZF_Malloc(int size); -void *BZ_MallocNamed (int size, const char *file, int line); // returns 0 filled memory -void *BZF_MallocNamed (int size, const char *file, int line); // allowed to fail -void *BZ_Realloc(void *ptr, int size); -void *BZ_ReallocNamed(void *data, int newsize, const char *file, int line); -void *BZF_Realloc(void *data, int newsize); -void *BZF_ReallocNamed(void *data, int newsize, const char *file, int line); +void *BZ_Malloc(size_t size); +void *BZF_Malloc(size_t size); +void *BZ_MallocNamed (size_t size, const char *file, int line); // returns 0 filled memory +void *BZF_MallocNamed (size_t size, const char *file, int line); // allowed to fail +void *BZ_Realloc(void *ptr, size_t size); +void *BZ_ReallocNamed(void *data, size_t newsize, const char *file, int line); +void *BZF_Realloc(void *data, size_t newsize); +void *BZF_ReallocNamed(void *data, size_t newsize, const char *file, int line); void BZ_Free(void *ptr); //ctx should start off as void*ctx=NULL @@ -117,8 +117,8 @@ typedef struct zonegroup_s void *first; int bytes; } zonegroup_t; -void *QDECL ZG_Malloc(zonegroup_t *ctx, int size); -void *ZG_MallocNamed(zonegroup_t *ctx, int size, char *file, int line); +void *QDECL ZG_Malloc(zonegroup_t *ctx, size_t size); +void *ZG_MallocNamed(zonegroup_t *ctx, size_t size, char *file, int line); void ZG_FreeGroup(zonegroup_t *ctx); #ifdef USE_MSVCRT_DEBUG @@ -141,8 +141,8 @@ void *Hunk_Alloc (int size); // returns 0 filled memory void *Hunk_AllocName (int size, char *name); */ -void *Hunk_TempAlloc (int size); -void *Hunk_TempAllocMore (int size); //Don't clear old temp +void *Hunk_TempAlloc (size_t size); +void *Hunk_TempAllocMore (size_t size); //Don't clear old temp /* typedef struct cache_user_s diff --git a/engine/droid/fte.cfg b/engine/droid/fte.cfg index ca98a586..8346f4e3 100644 --- a/engine/droid/fte.cfg +++ b/engine/droid/fte.cfg @@ -15,4 +15,5 @@ vid_conwidth "0" //make something up based upon aspect ratio vid_conheight "300" //not using autoscale as it can make the menu unusable. vid_conautoscale "0" // Text/Menu size. 2 is the default. 4 is bigger +scr_consize 0.4 //android's onscreen keyboard can take up over half the screen (and we don't know exactly where it is). exec touch.cfg diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 27227d9d..6b58810f 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -4725,6 +4725,20 @@ static void DrawMeshes(void) p = &shaderstate.curshader->passes[passno]; passno += p->numMergedPasses; + if (p->prog) + { + shaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo; + shaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr; + shaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT; + + shaderstate.pendingtexcoordparts[0] = 2; + shaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo; + shaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr; + + BE_RenderMeshProgram(shaderstate.curshader, p, p->prog); + continue; + } + emumode = 0; emumode = (p->shaderbits & SBITS_ATEST_BITS) >> SBITS_ATEST_SHIFT; diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index b386f4ce..2b4a1abe 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -135,11 +135,11 @@ void GL_SetupFormats(void) if (gl_config_gles) { //pre-3 gles doesn't support sized formats, and only a limited number of them too - glfmtc(PTI_RGB8, GL_RGB, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tc_rgb); - glfmtc(PTI_RGBA8, GL_RGBA, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, tc_rgba8); - glfmt(PTI_L8A8, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE); - glfmt(PTI_L8, GL_LUMINANCE, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE); -// glfmt(PTI_RGBA8, GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE); + glfmtc(PTI_RGB8, 0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tc_rgb); + glfmtc(PTI_RGBA8, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, tc_rgba8); + glfmt(PTI_L8A8, 0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE); + glfmt(PTI_L8, 0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE); +// glfmt(PTI_A8, 0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE); if (!gl_config.webgl_ie) { //these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit. @@ -754,7 +754,6 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips) { qglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, 0); qglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, nummips-1); - qglTexParameteri(targ, GL_TEXTURE_LOD_BIAS, 0); } } } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index b664381e..b6737c28 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -24,6 +24,10 @@ struct patchvert_s cvar_t mod_terrain_networked = CVARD("mod_terrain_networked", "0", "Terrain edits are networked. Clients will download sections on demand, and servers will notify clients of changes."); cvar_t mod_terrain_defaulttexture = CVARD("mod_terrain_defaulttexture", "", "Newly created terrain tiles will use this texture. This should generally be updated by the terrain editor."); cvar_t mod_terrain_savever = CVARD("mod_terrain_savever", "", "Which terrain section version to write if terrain was edited."); +cvar_t mod_terrain_sundir = CVARD("mod_terrain_sundir", "0.4 0.7 2", "The direction of the sun (vector will be normalised)."); +cvar_t mod_terrain_ambient = CVARD("mod_terrain_ambient", "0.5", "Proportion of ambient light."); +cvar_t mod_terrain_shadows = CVARD("mod_terrain_shadows", "0", "Cast rays to determine whether parts of the terrain should be in shadow."); +cvar_t mod_terrain_shadow_dist = CVARD("mod_terrain_shadow_dist", "2048", "How far rays should be cast in order to look for occlusing geometry."); enum { @@ -81,7 +85,7 @@ void validatelinks2(link_t *firstnode, link_t *panic) #ifndef SERVERONLY -static void ted_dorelight(heightmap_t *hm); +static void ted_dorelight(model_t *m, heightmap_t *hm); static void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, size_t b); static qboolean Terr_Collect(heightmap_t *hm); #endif @@ -1933,6 +1937,18 @@ void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusa if (!s || s->loadstate < TSLS_LOADING2) return; + { + int cx = s->sx/MAXSECTIONS; + int cy = s->sy/MAXSECTIONS; + hmcluster_t *c = hm->cluster[cx + cy*MAXCLUSTERS]; + int sx = s->sx & (MAXSECTIONS-1); + int sy = s->sy & (MAXSECTIONS-1); + + if (c->section[sx+sy*MAXSECTIONS] != s) + Sys_Error("Section %i,%i already destroyed...\n", s->sx, s->sy); + c->section[sx+sy*MAXSECTIONS] = NULL; + } + validatelinks(&hm->recycle); RemoveLink(&s->recycle); @@ -1966,10 +1982,16 @@ void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusa { if (qglDeleteBuffersARB) { - qglDeleteBuffersARB(1, &s->vbo.coord.gl.vbo); - s->vbo.coord.gl.vbo = 0; - qglDeleteBuffersARB(1, &s->vbo.indicies.gl.vbo); - s->vbo.indicies.gl.vbo = 0; + if (s->vbo.coord.gl.vbo) + { + qglDeleteBuffersARB(1, &s->vbo.coord.gl.vbo); + s->vbo.coord.gl.vbo = 0; + } + if (s->vbo.indicies.gl.vbo) + { + qglDeleteBuffersARB(1, &s->vbo.indicies.gl.vbo); + s->vbo.indicies.gl.vbo = 0; + } } } else @@ -2123,8 +2145,6 @@ validatelinks(&hm->recycle); } else { - c->section[sx+sy*MAXSECTIONS] = NULL; - validatelinks(&hm->recycle); Terr_DestroySection(hm, s, lightmapreusable); validatelinks(&hm->recycle); @@ -2999,7 +3019,7 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) // hm->beinglazy = false; if (hm->relight) - ted_dorelight(hm); + ted_dorelight(m, hm); if (e->model == cl.worldmodel && hm->skyshader) { @@ -3310,9 +3330,10 @@ unsigned int Heightmap_NativeBoxContents(model_t *model, int hulloverride, frame return Heightmap_PointContentsHM(hm, mins[2], org); } -void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) +float Heightmap_Normal(heightmap_t *hm, vec2_t org, vec3_t norm) //returns the z { #if 0 + float z = 0; norm[0] = 0; norm[1] = 0; norm[2] = 1; @@ -3322,6 +3343,7 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) vec3_t d1, d2; const float wbias = CHUNKBIAS * hm->sectionsize; hmsection_t *s; + float z; norm[0] = 0; norm[1] = 0; @@ -3330,12 +3352,12 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) sx = (org[0]+wbias)/hm->sectionsize; sy = (org[1]+wbias)/hm->sectionsize; if (sx < hm->firstsegx || sy < hm->firstsegy) - return; + return hm->defaultgroundheight; if (sx >= hm->maxsegx || sy >= hm->maxsegy) - return; + return hm->defaultgroundheight; s = Terr_GetSection(hm, sx, sy, TGS_TRYLOAD); if (!s) - return; + return hm->defaultgroundheight; x = (org[0]+wbias - (sx*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize; y = (org[1]+wbias - (sy*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize; @@ -3354,6 +3376,10 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) d2[0] = 0; d2[1] = (hm->sectionsize / SECTHEIGHTSIZE); d2[2] = (s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]); + + z = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*(1-y) + + s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE]*(x+y-1) + + s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*(1-x)); } else { //the 0,0 triangle @@ -3366,13 +3392,19 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) d2[0] = 0; d2[1] = (hm->sectionsize / SECTHEIGHTSIZE); d2[2] = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]); + + z = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*(y) + + s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*(x) + + s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]*(1-y-x)); } + VectorNormalize(d1); VectorNormalize(d2); CrossProduct(d1, d2, norm); VectorNormalize(norm); #endif + return z; } typedef struct { @@ -4244,11 +4276,11 @@ qboolean Heightmap_Trace_Test(struct model_s *model, int hulloverride, framestat qboolean ret = Heightmap_Trace(model, hulloverride, framestate, mataxis, start, end, mins, maxs, capsule, against, trace); if (!trace->startsolid) - { + { //FIXME: this code should not be needed. trace_t testtrace; Heightmap_Trace(model, hulloverride, framestate, mataxis, trace->endpos, trace->endpos, mins, maxs, capsule, against, &testtrace); if (testtrace.startsolid) - { + { //yup, we're bugged. Con_DPrintf("Trace became solid\n"); trace->fraction = 0; VectorCopy(start, trace->endpos); @@ -4370,32 +4402,37 @@ static unsigned char *QDECL Terr_GetLightmap(hmsection_t *s, int idx, qboolean e } return lightmap[s->lightmap]->lightmaps + ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap[s->lightmap]->pixbytes; } -static void ted_dorelight(heightmap_t *hm) +static void ted_dorelight(model_t *m, heightmap_t *hm) { unsigned char *lm = Terr_GetLightmap(hm->relight, 0, true); - int x, y; + int x, y, k; #define EXPAND 2 vec3_t surfnorms[(SECTTEXSIZE+EXPAND*2)*(SECTTEXSIZE+EXPAND*2)]; + vec3_t surfpoint[(SECTTEXSIZE+EXPAND*2)*(SECTTEXSIZE+EXPAND*2)]; // float scaletab[EXPAND*2*EXPAND*2]; - vec3_t ldir = {0.4, 0.7, 2}; + vec3_t ldir; hmsection_t *s = hm->relight; + float ambient, diffuse; + trace_t trace; s->flags &= ~TSF_RELIGHT; hm->relight = NULL; if (s->lightmap < 0) return; + ambient = 255*mod_terrain_ambient.value; + diffuse = 255-ambient; + for (y = -EXPAND; y < SECTTEXSIZE+EXPAND; y++) for (x = -EXPAND; x < SECTTEXSIZE+EXPAND; x++) { - vec3_t pos; - pos[0] = hm->relightmin[0] + (x*hm->sectionsize/(SECTTEXSIZE-1)); - pos[1] = hm->relightmin[1] + (y*hm->sectionsize/(SECTTEXSIZE-1)); - pos[2] = 0; - Heightmap_Normal(s->hmmod, pos, surfnorms[x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2)]); + k = x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2); + surfpoint[k][0] = hm->relightmin[0] + (x*hm->sectionsize/(SECTTEXSIZE-1)); + surfpoint[k][1] = hm->relightmin[1] + (y*hm->sectionsize/(SECTTEXSIZE-1)); + surfpoint[k][2] = Heightmap_Normal(s->hmmod, surfpoint[k], surfnorms[k])+0.1; } - VectorNormalize(ldir); + VectorNormalize2(mod_terrain_sundir.vec4, ldir); for (y = 0; y < SECTTEXSIZE; y++, lm += (HMLMSTRIDE-SECTTEXSIZE)*4) for (x = 0; x < SECTTEXSIZE; x++, lm += 4) @@ -4415,10 +4452,18 @@ static void ted_dorelight(heightmap_t *hm) d = DotProduct(ldir, norm); if (d < 0) d = 0; + else if (mod_terrain_shadows.ival) + { + float *point = surfpoint[x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2)]; + vec3_t sun; + VectorMA(point, mod_terrain_shadow_dist.value, ldir, sun); + if (m->funcs.NativeTrace(m, 0, NULL, NULL, point, sun, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID|FTECONTENTS_BODY, &trace)) + d = 0; + } // lm[0] = norm[0]*127 + 128; // lm[1] = norm[1]*127 + 128; // lm[2] = norm[2]*127 + 128; - lm[3] = 127 + d*128; + lm[3] = ambient + d*diffuse; } lightmap[s->lightmap]->modified = true; @@ -7102,11 +7147,11 @@ void Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br) } hasrgba = (y < br->patch->ypoints*br->patch->xpoints); - VFS_PRINTF(file, "\n\tpatchDef%s\n\t{\n\t\t\"%s\"\n\t\t( %.9g %.9g %.9g %.9g %.9g )\n\t\t(\n", + VFS_PRINTF(file, "\n\tpatchDef%s\n\t{\n\t\t\"%s\"\n\t\t( %u %u %.9g %.9g %.9g )\n\t\t(\n", hasrgba?"WS":"2", br->patch->tex?br->patch->tex->shadername:"", - 0.0/*xoffset*/, - 0.0/*yoffset*/, + br->patch->xpoints/*width*/, + br->patch->ypoints/*height*/, 0.0/*rotation*/, 1.0/*xscale*/, 1.0/*yscale*/); @@ -7480,9 +7525,10 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) continue; } } - else if (inbrush && (!strcmp(token, "patchDef2") || !strcmp(token, "patchDefWS"))) + else if (inbrush && (!strcmp(token, "patchDef2") || !strcmp(token, "patchDef3") || !strcmp(token, "patchDefWS"))) { int x, y; + qboolean patchdef3 = !strcmp(token, "patchDef3"); //fancy alternative with rgba colours per control point qboolean parsergba = !strcmp(token, "patchDefWS"); //fancy alternative with rgba colours per control point if (numplanes || patch_tex) { @@ -7498,10 +7544,17 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (strcmp(token, "(")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;} entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - /*xoffset = atof(token);*/ + /*patch_w = atof(token);*/ entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - /*yoffset = atof(token);*/ + /*patch_h = atof(token);*/ entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (patchdef3) + { + /*xsubdiv = atof(token);*/ + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + /*ysubdiv = atof(token);*/ + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + } /*rotation = atof(token);*/ entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); /*xscale = atof(token);*/ @@ -7532,7 +7585,12 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) patch_v[y][x].tc[1] = atof(token); if (parsergba) - { + { //the following four lines are stupid. + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (strcmp(token, ")")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;} + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (strcmp(token, "(")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;} + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); patch_v[y][x].rgba[0] = atof(token); entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); @@ -7981,31 +8039,33 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force) } #ifndef SERVERONLY +#if 0 //not yet ready struct ted_import_s { - int x, y; - int width; - int height; + size_t x, y; + size_t width; + size_t height; unsigned short *data; }; //static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float radius, float strength, int steps, -void ted_import_heights(void *vctx, hmsection_t *s, int idx, float wx, float wy, float strength) +static void ted_import_heights_r16(void *vctx, hmsection_t *s, int idx, float wx, float wy, float strength) { struct ted_import_s *ctx = vctx; unsigned int y = idx/SECTHEIGHTSIZE; unsigned int x = idx%SECTHEIGHTSIZE; x += s->sx*(SECTHEIGHTSIZE-1) - ctx->x; y += s->sy*(SECTHEIGHTSIZE-1) - ctx->y; - if (x < 0 || x >= ctx->width || y < 0 || y >= ctx->height) + if (x >= ctx->width || y >= ctx->height) return; s->flags |= TSF_NOTIFY|TSF_EDITED|TSF_DIRTY|TSF_RELIGHT; s->heights[idx] = ctx->data[x + y*ctx->width] * (8192.0/(1<<16)); } -void Mod_Terrain_Import_f(void) +static void Mod_Terrain_Import_f(void) { model_t *mod; struct ted_import_s ctx; const char *mapname = Cmd_Argv(1); + const char *filename; size_t fsize; heightmap_t *hm; vec3_t pos = {0}; @@ -8025,16 +8085,142 @@ void Mod_Terrain_Import_f(void) return; fsize = 0; - ctx.data = (void*)FS_LoadMallocFile("quake8km/height8km.r16", &fsize); + filename = va("maps/%s.r16", mapname); + ctx.data = (void*)FS_LoadMallocFile(filename, &fsize); + if (!ctx.data) + { + Con_Printf("Unable to read %s\n", filename); + return; + } ctx.width = ctx.height = sqrt(fsize/2); ctx.x = 0; ctx.y = 0; pos[0] += hm->sectionsize * CHUNKBIAS; pos[1] += hm->sectionsize * CHUNKBIAS; if (fsize == ctx.width*ctx.height*2) - ted_itterate(hm, tid_flat, pos, max(ctx.width, ctx.height), 1, SECTHEIGHTSIZE, ted_import_heights, &ctx); + ted_itterate(hm, tid_flat, pos, max(ctx.width, ctx.height), 1, SECTHEIGHTSIZE, ted_import_heights_r16, &ctx); FS_FreeFile(ctx.data); } +static void Mod_Terrain_Export_f(void) +{ + model_t *mod; + struct ted_import_s ctx; + char mapname[MAX_QPATH]; + const char *filename; + heightmap_t *hm; + size_t w, h; + size_t tx, ty; + size_t sx, sy; + unsigned int outtilex=0,outtiley=0; + qboolean populated; + if (Cmd_IsInsecure()) + { + Con_Printf("Please use this command via the console\n"); + return; + } + if (*Cmd_Argv(1)) + mod = NULL;//Mod_FindName(va("maps/%s", mapname)); + else + mod = cl.worldmodel; + if (!mod || mod->type == mod_dummy) + return; + hm = mod->terrain; + if (!hm) + return; + + COM_StripExtension(mod->name, mapname, sizeof(mapname)); + + ctx.x = hm->firstsegx * (SECTHEIGHTSIZE-1); + w = (hm->maxsegx-hm->firstsegx) * (SECTHEIGHTSIZE-1) + 1; + while(w) + { + ctx.width = w; + if (ctx.width > 2048+1) + ctx.width = 2048; + + outtiley = 0; + ctx.y = hm->firstsegy * (SECTHEIGHTSIZE-1); + h = (hm->maxsegy-hm->firstsegy) * (SECTHEIGHTSIZE-1) + 1; + while(h) + { + ctx.height = h; + if (ctx.height > 2048+1) + ctx.height = 2048; + + populated = false; + ctx.data = Z_Malloc(ctx.width*ctx.height*2); + for (sy = ctx.y/(SECTHEIGHTSIZE-1); sy < (ctx.y+ctx.height + SECTHEIGHTSIZE-3)/(SECTHEIGHTSIZE-1); sy++) + for (sx = ctx.x/(SECTHEIGHTSIZE-1); sx < (ctx.x+ctx.width + SECTHEIGHTSIZE-3)/(SECTHEIGHTSIZE-1); sx++) + { + hmsection_t *s = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_ANYSTATE); + if (s->loadstate == TSLS_FAILED) + { //we're doing this weirdly so we can destroy sections as we go. + Terr_DestroySection(hm, s, true); + s = NULL; + } + if (s) + { + populated = true; + for (ty = 0; ty < SECTHEIGHTSIZE; ty++) + { + size_t y = sy*(SECTHEIGHTSIZE-1)+ty - ctx.y; + if (y >= ctx.height) + continue; + for (tx = 0; tx < SECTHEIGHTSIZE; tx++) + { + size_t x = sx*(SECTHEIGHTSIZE-1)+tx - ctx.x; + if (x >= ctx.width) + continue; + ctx.data[x + y*ctx.width] = s->heights[tx+y*SECTHEIGHTSIZE] / (8192.0/(1<<16)); + } + } + if (!(s->flags & TSF_EDITED)) + Terr_DestroySection(hm, s, true); + } + else + { + for (ty = 0; ty < SECTHEIGHTSIZE; ty++) + { + size_t y = sy*(SECTHEIGHTSIZE-1)+ty - ctx.y; + if (y >= ctx.height) + continue; + for (tx = 0; tx < SECTHEIGHTSIZE; tx++) + { + size_t x = sx*(SECTHEIGHTSIZE-1)+tx - ctx.x; + if (x >= ctx.width) + continue; + ctx.data[x + y*ctx.width] = hm->defaultgroundheight / (8192.0/(1<<16)); + } + } + } + } + + filename = va("%s/x%u_y%u.r16", mapname, outtilex, outtiley); + if (populated) + { + if (FS_WriteFile(filename, ctx.data, ctx.width*ctx.height*2, FS_GAMEONLY)) + { + char sysname[1024]; + FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); + Con_Printf("Wrote %s\n", sysname); + } + else + Con_Printf("Unable to write %s\n", filename); + } + else + Con_Printf("Skipping unpopulated %s\n", filename); + Z_Free(ctx.data); + + outtiley++; + ctx.y += ctx.height; + h -= ctx.height; + } + outtilex++; + ctx.x += ctx.width; + w -= ctx.width; + } +} +#endif void Mod_Terrain_Create_f(void) { @@ -8279,9 +8465,15 @@ void Terr_Init(void) Cmd_AddCommand("mod_terrain_save", Mod_Terrain_Save_f); Cmd_AddCommand("mod_terrain_reload", Mod_Terrain_Reload_f); #ifndef SERVERONLY - Cmd_AddCommandD("mod_terrain_import", Mod_Terrain_Import_f, "Import a raw heightmap"); +// Cmd_AddCommandD("mod_terrain_export", Mod_Terrain_Export_f, "Export a raw heightmap"); +// Cmd_AddCommandD("mod_terrain_import", Mod_Terrain_Import_f, "Import a raw heightmap"); Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f); Cmd_AddCommandD("mod_terrain_convert", Mod_Terrain_Convert_f, "mod_terrain_convert [mapname] [texkill]\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation."); + + Cvar_Register(&mod_terrain_sundir, "Terrain"); + Cvar_Register(&mod_terrain_ambient, "Terrain"); + Cvar_Register(&mod_terrain_shadows, "Terrain"); + Cvar_Register(&mod_terrain_shadow_dist, "Terrain"); #endif Mod_RegisterModelFormatText(NULL, "FTE Heightmap Map (hmp)", "terrain", Terr_LoadTerrainModel); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 3b87a509..8e293388 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -1256,6 +1256,7 @@ struct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p) size_t n, pn = 0; char defines[8192]; size_t offset; + qboolean fail = false; extern cvar_t gl_specular, gl_specular_power; @@ -1294,14 +1295,14 @@ struct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p) permutationdefines[pn++] = NULL; if (!sh_config.pCreateProgram(prog, pp, prog->shaderver, permutationdefines, prog->shadertext, prog->tess?prog->shadertext:NULL, prog->tess?prog->shadertext:NULL, prog->geom?prog->shadertext:NULL, prog->shadertext, prog->warned, NULL)) - prog->warned = true; + prog->warned = fail = true; //extra loop to validate the programs actually linked properly. //delaying it like this gives certain threaded drivers a chance to compile them all while we're messing around with other junk - if (sh_config.pValidateProgram && !sh_config.pValidateProgram(prog, pp, prog->warned, NULL)) - prog->warned = true; + if (!fail && sh_config.pValidateProgram && !sh_config.pValidateProgram(prog, pp, prog->warned, NULL)) + prog->warned = fail = true; - if (sh_config.pProgAutoFields) + if (!fail && sh_config.pProgAutoFields) { cvar_t *cvarrefs[64]; char *cvarnames[64+1]; @@ -1320,9 +1321,52 @@ struct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p) cvarnames[i] = NULL; //no more sh_config.pProgAutoFields(prog, pp, cvarrefs, cvarnames, cvartypes); } + if (fail) + { + Z_Free(pp); + return NULL; + } return pp; } +qboolean Shader_PermutationEnabled(unsigned int bit) +{ + if (bit == PERMUTATION_REFLECTCUBEMASK) + return gl_load24bit.ival; + if (bit == PERMUTATION_BUMPMAP) + return r_loadbumpmapping; + return true; +} +qboolean Com_PermuOrFloatArgument(const char *shadername, char *arg, size_t arglen, float def) +{ + extern cvar_t gl_specular; + size_t p; + //load-time-only permutations... + if (arglen == 8 && !strncmp("SPECULAR", arg, arglen) && gl_specular.value) + return true; + if ((arglen==5||arglen==6) && !strncmp("DELUXE", arg, arglen) && r_deluxemapping && Shader_PermutationEnabled(PERMUTATION_BUMPMAP)) + return true; + if (arglen == 13 && !strncmp("OFFSETMAPPING", arg, arglen) && r_glsl_offsetmapping.ival) + return true; + if (arglen == 13 && !strncmp("RELIEFMAPPING", arg, arglen) && r_glsl_offsetmapping.ival && r_glsl_offsetmapping_reliefmapping.ival) + return true; + + //real permutations + if (arglen == 5 && (!strncmp("UPPER", arg, arglen)||!strncmp("LOWER", arg, arglen)) && Shader_PermutationEnabled(PERMUTATION_BIT_UPPERLOWER)) + return true; + for (p = 0; p < countof(permutations); p++) + { + if (arglen == strlen(permutations[p].name) && !strncmp(permutations[p].name, arg, arglen)) + { + if (Shader_PermutationEnabled(permutations[p].bitmask)) + return true; + break; + } + } + + return Com_FloatArgument(shadername, arg, arglen, def) != 0; +} + static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *script, int qrtype, int ver, char *blobfilename) { #if defined(GLQUAKE) || defined(D3DQUAKE) @@ -1412,15 +1456,16 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip if (*token == '=' || *token == '!') { len = strlen(token); - if (*token == (Com_FloatArgument(name, token+1, len-1, 0)?'!':'=')) + if (*token == (Com_PermuOrFloatArgument(name, token+1, len-1, 0)?'!':'=')) ignore = true; continue; } else if (ignore) continue; -#ifdef HAVE_LEGACY +#if 1//def HAVE_LEGACY else if (!strncmp(token, "deluxmap", 8)) { //FIXME: remove this some time. + Con_DPrintf("Outdated texture name \"%s\" in program \"%s\"\n", token, name); token = va("deluxemap%s",token+8); } #endif @@ -1470,7 +1515,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip prog->numsamplers = i; } else - Con_Printf("Unknown texture name in %s\n", name); + Con_Printf("Unknown texture name \"%s\" in program \"%s\"\n", token, name); } } } @@ -1667,7 +1712,8 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip { if (!strncmp(permutations[p].name, script, end - script) && permutations[p].name[end-script] == '\0') { - nopermutation &= ~permutations[p].bitmask; + if (Shader_PermutationEnabled(permutations[p].bitmask)) + nopermutation &= ~permutations[p].bitmask; break; } } @@ -1738,10 +1784,10 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip nopermutation |= PERMUTATION_SKELETAL; //multiple lightmaps is kinda hacky. if any are set, all must be. -#define ALTLIGHTMAPSAMP 13 +#define ALTLIGHTMAPSAMP 14 if (prog->defaulttextures & ((1u<<(ALTLIGHTMAPSAMP+0)) | (1u<<(ALTLIGHTMAPSAMP+1)) | (1u<<(ALTLIGHTMAPSAMP+2)))) prog->defaulttextures |=((1u<<(ALTLIGHTMAPSAMP+0)) | (1u<<(ALTLIGHTMAPSAMP+1)) | (1u<<(ALTLIGHTMAPSAMP+2))); -#define ALTDELUXMAPSAMP 16 +#define ALTDELUXMAPSAMP 17 if (prog->defaulttextures & ((1u<<(ALTDELUXMAPSAMP+0)) | (1u<<(ALTDELUXMAPSAMP+1)) | (1u<<(ALTDELUXMAPSAMP+2)))) prog->defaulttextures |=((1u<<(ALTDELUXMAPSAMP+0)) | (1u<<(ALTDELUXMAPSAMP+1)) | (1u<<(ALTDELUXMAPSAMP+2))); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 93e66883..e613cb67 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -420,7 +420,7 @@ qboolean GL_CheckExtension(char *extname) for (i = 0; i < gl_num_extensions; i++) if (!strcmp(qglGetStringi(GL_EXTENSIONS, i), extname)) { - Con_DPrintf("Detected GL extension %s\n", extname); + Con_DPrintf("GL: Found %s\n", extname); return true; } } @@ -2280,6 +2280,9 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int "#ifndef USE_ARB_SHADOW\n" //fall back on regular samplers if we must "#define sampler2DShadow sampler2D\n" "#elif defined(GL_ES)\n" + "#if __VERSION__ < 300\n" + "#extension GL_EXT_shadow_samplers : require\n" + "#endif\n" "precision lowp sampler2DShadow;\n" //gah "#endif\n" #endif diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index a53d981d..f3b49fcb 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -2975,7 +2975,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND //must support skeletal and 2-way vertex blending or Bad Things Will Happen. //the vertex shader is responsible for calculating lighting values. -"#if gl_affinemodels==1 && __VERSION__ >= 130\n" +"#if gl_affinemodels==1 && __VERSION__ >= 130 && !defined(GL_ES)\n" "#define affine noperspective\n" "#else\n" "#define affine\n" @@ -5599,11 +5599,17 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!permu REFLECTCUBEMASK\n" "!!cvarf r_glsl_offsetmapping_scale\n" "!!cvardf r_tessellation_level=5\n" -"!!samps !EIGHTBIT diffuse specular normalmap fullbright reflectmask reflectcube\n" +"!!samps diffuse\n" +"!!samps !EIGHTBIT =FULLBRIGHT fullbright\n" +"!!samps !EIGHTBIT =BUMP normalmap\n" +"!!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube\n" //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse. -"!!samps =EIGHTBIT paletted 1 specular diffuse\n" -"!!samps lightmap deluxemap\n" -"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3\n" +"!!samps =EIGHTBIT paletted 1\n" +"!!samps =SPECULAR specular\n" +"!!samps lightmap\n" +"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n" +"!!samps =DELUXE deluxmap\n" +"!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\n" "#if defined(ORM) || defined(SG)\n" "#define PBR\n" @@ -5960,7 +5966,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#else\n" //now we have our diffuse+specular terms, modulate by lightmap values. "col.rgb *= lightmaps.rgb;\n" - //add on the fullbright "#ifdef FULLBRIGHT\n" "col.rgb += texture2D(s_fullbright, tc).rgb;\n" @@ -10603,10 +10608,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND #endif #ifdef GLQUAKE {QR_OPENGL, 110, "terrain", +"!!ver 100 300\n" "!!permu FOG\n" -//t0-t3 are the diffusemaps, t4 is the blend factors -"!!samps 4\n" -"!!samps mix=4\n" +//RTLIGHT (+PCF,CUBE,SPOT,etc) +"!!samps tr=0 tg=1 tb=2 tx=3 //the four texturemaps\n" +"!!samps mix=4 //how the ground is blended\n" "!!samps =PCF shadowmap\n" "!!samps =CUBE projectionmap\n" @@ -10703,10 +10709,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec4 r;\n" "vec4 m = texture2D(s_mix, lm);\n" -"r = texture2D(s_t0, tc)*m.r;\n" -"r += texture2D(s_t1, tc)*m.g;\n" -"r += texture2D(s_t2, tc)*m.b;\n" -"r += texture2D(s_t3, tc)*(1.0 - (m.r + m.g + m.b));\n" +"r = texture2D(s_tr, tc)*m.r;\n" +"r += texture2D(s_tg, tc)*m.g;\n" +"r += texture2D(s_tb, tc)*m.b;\n" +"r += texture2D(s_tx, tc)*(1.0 - (m.r + m.g + m.b));\n" + +"r.rgb *= 1.0/r.a; //fancy maths, so low alpha values give other textures a greater focus\n" //vertex colours provide a scaler that applies even through rtlights. "r *= vc;\n" diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 5e50168c..1884da3f 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -23,7 +23,7 @@ void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, const char *name) return ((char *)mem)+sizeof(prmemb_t); } -void *PDECL QC_HunkAlloc(pubprogfuncs_t *ppf, int ammount, char *name) +static void *PDECL QC_HunkAlloc(pubprogfuncs_t *ppf, int ammount, char *name) { return PRHunkAlloc((progfuncs_t*)ppf, ammount, name); } @@ -48,7 +48,7 @@ void PRHunkFree(progfuncs_t *progfuncs, int mark) } /*if we ran out of memory, the vm can allocate a new block, but doing so requires fixing up all sorts of pointers*/ -void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen) +static void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen) { unsigned int i; edictrun_t *e; @@ -583,7 +583,7 @@ static void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, in -struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) +static struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; if (pnum < 0) @@ -598,7 +598,7 @@ struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) return (struct globalvars_s *)pr_progstate[pnum].globals; } -struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) +static struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) { // progfuncs_t *progfuncs = (progfuncs_t*)ppf; if (((edictrun_t *)ed)->ereftype != ER_ENTITY) @@ -607,7 +607,7 @@ struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) return (struct entvars_s *)edvars(ed); } -pbool PDECL PR_GetFunctionInfo(pubprogfuncs_t *ppf, func_t func, int *args, int *builtinnum, char *funcname, size_t funcnamesize) +static pbool PDECL PR_GetFunctionInfo(pubprogfuncs_t *ppf, func_t func, int *args, int *builtinnum, char *funcname, size_t funcnamesize) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; @@ -697,7 +697,7 @@ func_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t p return 0; } -void PDECL QC_FindPrefixedGlobals(pubprogfuncs_t *ppf, int pnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx) +static void PDECL QC_FindPrefixedGlobals(pubprogfuncs_t *ppf, int pnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int i; @@ -795,7 +795,7 @@ eval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_ return NULL; } -char *PDECL PR_VarString (pubprogfuncs_t *ppf, int first) +static char *PDECL PR_VarString (pubprogfuncs_t *ppf, int first) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int i; @@ -816,7 +816,7 @@ char *PDECL PR_VarString (pubprogfuncs_t *ppf, int first) return out; } -int PDECL PR_QueryField (pubprogfuncs_t *ppf, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache) +static int PDECL PR_QueryField (pubprogfuncs_t *ppf, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; fdef_t *var; @@ -868,7 +868,7 @@ eval_t *PDECL QC_GetEdictFieldValue(pubprogfuncs_t *ppf, struct edict_s *ed, con return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[cache->ofs32->ofs]); } -struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) +static struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; if ((unsigned)progs >= (unsigned)prinst.maxedicts) @@ -883,7 +883,7 @@ struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) } return (struct edict_s *)PROG_TO_EDICT_PB(progfuncs.inst, progs); } -int PDECL EdictToProgs (pubprogfuncs_t *ppf, struct edict_s *ed) +static int PDECL EdictToProgs (pubprogfuncs_t *ppf, struct edict_s *ed) { // progfuncs_t *progfuncs = (progfuncs_t*)ppf; return EDICT_TO_PROG(progfuncs, ed); @@ -935,7 +935,7 @@ string_t PDECL PR_StringToProgs (pubprogfuncs_t *ppf, const char *str) return (string_t)((unsigned int)i | STRING_STATIC); } //if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated. -void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) +static void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) { if (!str) *fld = 0; @@ -951,7 +951,7 @@ void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, stri } } -char *PDECL PR_RemoveProgsString (pubprogfuncs_t *ppf, string_t str) +static char *PDECL PR_RemoveProgsString (pubprogfuncs_t *ppf, string_t str) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; char *ret; @@ -1110,7 +1110,7 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals) } -string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) +static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; tempstr_t **ntable; @@ -1319,7 +1319,7 @@ static void PR_FreeAllTemps (progfuncs_t *progfuncs) prinst.numtempstrings = 0; prinst.nexttempstring = 0; } -pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) +static pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; struct progstate_s *ps; @@ -1391,7 +1391,7 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf); static void PDECL RegisterBuiltin(pubprogfuncs_t *progfncs, const char *name, builtin_t func); -pubprogfuncs_t deffuncs = { +static pubprogfuncs_t deffuncs = { PROGSTRUCT_VERSION, PR_CloseProgs, PR_Configure, @@ -1510,11 +1510,11 @@ static void PDECL qclib_free(void *ptr) #endif //defs incase following structure is not passed. -struct edict_s *safesv_edicts; -int safesv_num_edicts; -double safetime=0; +static struct edict_s *safesv_edicts; +static int safesv_num_edicts; +static double safetime=0; -progexterns_t defexterns = { +static progexterns_t defexterns = { PROGSTRUCT_VERSION, NULL, //char *(*ReadFile) (char *fname, void *buffer, int len); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 20ab343c..0e1b4deb 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10451,9 +10451,14 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"bprint", PF_bprint, 0, 23, 0, 0, D("void(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "QW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, {"sprint", PF_sprint, 24, 0, 24, 0, D("void(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "NQ: Concatenates all string arguments, and prints the messsage on the named client's console")}, {"sprint", PF_sprint, 0, 24, 0, 0, D("void(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6)", "QW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, +#ifdef HAVE_LEGACY //these have subtly different behaviour, and are implemented using different internal builtins, which is a bit weird in the extensions file. documentation is documentation. {"dprint", PF_dprint, 25, 0, 25, 0, D("void(string s, ...)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, {"dprint", PF_print, 0, 25, 0, 0, D("void(string s, ...)", "QW: Unconditionally prints the given message on the server's console. Arguments will be concatenated into a single message.")}, +#else + //going forward, we have print and dprint + {"dprint", PF_dprint, 25, 25, 25, 0, D("void(string s, ...)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, +#endif {"ftos", PF_ftos, 26, 26, 26, 0, D("string(float val)", "Returns a tempstring containing a representation of the given float. Precision depends upon engine.")}, {"vtos", PF_vtos, 27, 27, 27, 0, D("string(vector val)", "Returns a tempstring containing a representation of the given vector. Precision depends upon engine.")}, {"coredump", PF_coredump, 28, 28, 28, 0, D("void()", "Writes out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging.")}, diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 776183e2..dfc8f238 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -565,7 +565,10 @@ void SV_Map_f (void) #ifdef Q3SERVER q3singleplayer = !strcmp(Cmd_Argv(0), "spmap"); #endif - flushparms = !strcmp(Cmd_Argv(0), "map") || !strcmp(Cmd_Argv(0), "spmap"); + if ((svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) && progstype == PROG_QW) + flushparms = !strcmp(Cmd_Argv(0), "spmap"); //quakeworld's map command preserves spawnparms. + else + flushparms = !strcmp(Cmd_Argv(0), "map") || !strcmp(Cmd_Argv(0), "spmap"); //[sp]map flushes in nq+h2+q2+etc #ifdef SAVEDGAMES newunit = flushparms || (!strcmp(Cmd_Argv(0), "changelevel") && !startspot); q2savetos0 = !strcmp(Cmd_Argv(0), "gamemap") && !isDedicated; //q2 @@ -1910,7 +1913,10 @@ static void SV_Status_f (void) #if defined(HAVE_SSL) extern cvar_t net_enable_tls; #endif - extern cvar_t net_enable_http, net_enable_webrtcbroker, net_enable_websockets, net_enable_qizmo, net_enable_qtv; + #ifdef HAVE_HTTPSV + extern cvar_t net_enable_http, net_enable_webrtcbroker, net_enable_websockets; + #endif + extern cvar_t net_enable_qizmo, net_enable_qtv; #endif #ifdef NQPROT extern cvar_t sv_listen_nq, sv_listen_dp; @@ -1990,12 +1996,14 @@ static void SV_Status_f (void) if (net_enable_tls.ival) Con_Printf(" TLS"); #endif +#ifdef HAVE_HTTPSV if (net_enable_http.ival) Con_Printf(" HTTP"); if (net_enable_webrtcbroker.ival) Con_Printf(" WebRTC"); if (net_enable_websockets.ival) Con_Printf(" WS"); +#endif if (net_enable_qizmo.ival) Con_Printf(" QZ"); if (net_enable_qtv.ival) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 80b8e6b8..051a3962 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -3140,13 +3140,27 @@ client_t *SVC_DirectConnect(void) else #endif newcl->netchan.compresstable = NULL; + newcl->netchan.pext_fragmentation = mtu?true:false; + //this is the upper bound of the mtu, if its too high we'll get EMSGSIZE and we'll reduce it. + //however, if it drops below newcl->netchan.message.maxsize then we'll start to see undeliverable reliables, which means dropped clients. + newcl->netchan.mtu = MAX_DATAGRAM; //vanilla qw clients are assumed to have an mtu of this size. if (mtu >= 64) { //if we support application fragmenting, then we can send massive reliables without too much issue - newcl->netchan.fragmentsize = mtu; + newcl->netchan.mtu = mtu; newcl->netchan.message.maxsize = sizeof(newcl->netchan.message_buf); } else //otherwise we can't fragment the packets, and the only way to honour the mtu is to send less data. yay for more round-trips. - newcl->netchan.message.maxsize = min(newcl->netchan.message.maxsize, max(net_mtu.ival, 512)); + { + mtu = atoi(Info_ValueForKey (userinfo[0], "mtu")); + if (mtu) + newcl->netchan.mtu = mtu; //locked mtu size, because not everyone has a working connection (we need icmp would-fragment responses for mtu detection) + else //if its not set then use some 'safe' fallback. + mtu = MAX_BACKBUFLEN; //MAX_BACKBUFLEN of 1200 is < ipv6 required segment size so should always work for reliables. + //enforce some boundaries + mtu = bound(512-8, mtu, sizeof(newcl->netchan.message_buf)); + newcl->netchan.message.maxsize = mtu; + } + Con_DLPrintf(2, "MTU size: %i - %i\n", newcl->netchan.message.maxsize, newcl->netchan.mtu); newcl->protocol = protocol; #ifdef NQPROT diff --git a/engine/server/sv_nchan.c b/engine/server/sv_nchan.c index 74a1ef93..84c2e3c2 100644 --- a/engine/server/sv_nchan.c +++ b/engine/server/sv_nchan.c @@ -35,7 +35,7 @@ void ClientReliableCheckBlock(client_t *cl, int maxsize) memset(&cl->backbuf, 0, sizeof(cl->backbuf)); cl->backbuf.allowoverflow = true; cl->backbuf.data = cl->backbuf_data[0]; - cl->backbuf.maxsize = sizeof(cl->backbuf_data[0]); + cl->backbuf.maxsize = min(cl->netchan.message.maxsize, sizeof(cl->backbuf_data[0])); cl->backbuf_size[0] = 0; cl->num_backbuf++; } @@ -54,7 +54,7 @@ void ClientReliableCheckBlock(client_t *cl, int maxsize) memset(&cl->backbuf, 0, sizeof(cl->backbuf)); cl->backbuf.allowoverflow = true; cl->backbuf.data = cl->backbuf_data[cl->num_backbuf]; - cl->backbuf.maxsize = sizeof(cl->backbuf_data[cl->num_backbuf]); + cl->backbuf.maxsize = min(cl->netchan.message.maxsize, sizeof(cl->backbuf_data[cl->num_backbuf])); cl->backbuf_size[cl->num_backbuf] = 0; cl->num_backbuf++; } diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 89f4e8a5..98b038b6 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -2573,13 +2573,15 @@ qboolean SV_SendClientDatagram (client_t *client) client->edict->v->goalentity = 0; } - if (client->netchan.fragmentsize) + if (client->netchan.pext_fragmentation) { if (client->netchan.remote_address.type == NA_LOOPBACK) clientlimit = countof(buf); //biiiig... else - clientlimit = client->netchan.fragmentsize; //try not to overflow + clientlimit = client->netchan.mtu; //try not to overflow } + else if (client->netchan.mtu) + clientlimit = client->netchan.mtu; else if (client->protocol == SCP_NETQUAKE) clientlimit = MAX_NQDATAGRAM; //vanilla client is limited. else @@ -3411,7 +3413,7 @@ void SV_SendClientMessages (void) memset(&c->backbuf, 0, sizeof(c->backbuf)); c->backbuf.data = c->backbuf_data[c->num_backbuf - 1]; c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1]; - c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]); + c->backbuf.maxsize = min(c->netchan.message.maxsize, sizeof(c->backbuf_data[c->num_backbuf-1])); } } } @@ -3526,6 +3528,9 @@ void SV_SendClientMessages (void) c->datagram.cursize = 0; } c->lastoutgoingphysicstime = sv.world.physicstime; + + if (c->netchan.fatal_error) + c->drop = true; } #ifdef MVD_RECORDING if (sv.mvdrecording) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index ebd6f802..fa967228 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -987,12 +987,12 @@ void SV_SendClientPrespawnInfo(client_t *client) return; } - //just because we CAN generate huge messages doesn't meant that we should. + //just because we CAN generate huge messages doesn't mean that we should. //try to keep packets within reasonable sizes so that we don't trigger insane burst+packetloss on map changes. maxsize = client->netchan.message.maxsize/2; - if (client->netchan.fragmentsize && maxsize > client->netchan.fragmentsize-200) + if (client->netchan.mtu && maxsize > client->netchan.mtu-200) { - maxsize = client->netchan.fragmentsize-200; + maxsize = client->netchan.mtu-200; if (maxsize < 500) maxsize = 500; } @@ -1933,7 +1933,6 @@ void SVQW_Spawn_f (void) // when that is completed, a begin command will be issued ClientReliableWrite_Begin (host_client, svc_stufftext, 8); ClientReliableWrite_String (host_client, "skins\n" ); - } /* @@ -7733,6 +7732,8 @@ void SV_ReadQCRequest(void) done: args[i] = 0; rname = MSG_ReadString(); + //We used to use Cmd_foo_args, but that conflicts with a zquake extension and would cause [e]zquake mods that use it to be remotely exploitable (mostly crashes from uninitialised args though). + //Instead, we've switched to some more weird prefix that's much less likly to conflict. if (i) fname = va("CSEv_%s_%s", rname, args); else if (strchr(rname, '_')) //this is awkward, as not forcing an underscore would allow people to mis-call things with lingering data (the alternative is to block underscores entirely). @@ -7749,7 +7750,10 @@ done: rname = va("Cmd_%s", rname); f = PR_FindFunction(svprogfuncs, rname, PR_ANY); if (f) - SV_ClientPrintf(host_client, PRINT_HIGH, "the name \"%s\" is deprecated\n", rname); + { + SV_ClientPrintf(host_client, PRINT_HIGH, "\"%s\" is no longer supported.\n", rname); + f = 0; + } } #endif if (host_client->drop) diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index c8724984..9f04109b 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -809,6 +809,10 @@ static void QDECL PFQ2_SetAreaPortalState(unsigned int p, qboolean s) CMQ2_SetAreaPortalState(sv.world.worldmodel, p, s); } +static void *VARGS ZQ2_TagMalloc(int size, int tag) +{ + return Z_TagMalloc(size, tag); +} qboolean SVQ2_InitGameProgs(void) { extern cvar_t maxclients; @@ -862,7 +866,7 @@ qboolean SVQ2_InitGameProgs(void) import.WriteDir = PFQ2_WriteDir; import.WriteAngle = PFQ2_WriteAngle; - import.TagMalloc = Z_TagMalloc; + import.TagMalloc = ZQ2_TagMalloc; import.TagFree = Z_TagFree; import.FreeTags = Z_FreeTags; diff --git a/engine/shaders/glsl/defaultskin.glsl b/engine/shaders/glsl/defaultskin.glsl index b671c299..8a35d193 100644 --- a/engine/shaders/glsl/defaultskin.glsl +++ b/engine/shaders/glsl/defaultskin.glsl @@ -30,7 +30,7 @@ //must support skeletal and 2-way vertex blending or Bad Things Will Happen. //the vertex shader is responsible for calculating lighting values. -#if gl_affinemodels==1 && __VERSION__ >= 130 +#if gl_affinemodels==1 && __VERSION__ >= 130 && !defined(GL_ES) #define affine noperspective #else #define affine diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index f39135f0..104a5e6e 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -9,11 +9,17 @@ !!permu REFLECTCUBEMASK !!cvarf r_glsl_offsetmapping_scale !!cvardf r_tessellation_level=5 -!!samps !EIGHTBIT diffuse specular normalmap fullbright reflectmask reflectcube +!!samps diffuse +!!samps !EIGHTBIT =FULLBRIGHT fullbright +!!samps !EIGHTBIT =BUMP normalmap +!!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse. -!!samps =EIGHTBIT paletted 1 specular diffuse -!!samps lightmap deluxemap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3 +!!samps =EIGHTBIT paletted 1 +!!samps =SPECULAR specular +!!samps lightmap +!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 +!!samps =DELUXE deluxmap +!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 #if defined(ORM) || defined(SG) #define PBR @@ -299,7 +305,7 @@ void main () vec3 deluxe = (texture2D(s_deluxemap, lm0).rgb-0.5); #ifdef BUMPMODELSPACE deluxe = normalize(deluxe*invsurface); -#else + #else deluxe = normalize(deluxe); lightmaps *= 2.0 / max(0.25, deluxe.z); //counter the darkening from deluxemaps #endif @@ -370,7 +376,6 @@ void main () #else //now we have our diffuse+specular terms, modulate by lightmap values. col.rgb *= lightmaps.rgb; - //add on the fullbright #ifdef FULLBRIGHT col.rgb += texture2D(s_fullbright, tc).rgb; diff --git a/engine/shaders/glsl/terrain.glsl b/engine/shaders/glsl/terrain.glsl index a79f88ee..9b8bed58 100644 --- a/engine/shaders/glsl/terrain.glsl +++ b/engine/shaders/glsl/terrain.glsl @@ -1,7 +1,8 @@ +!!ver 100 300 !!permu FOG -//t0-t3 are the diffusemaps, t4 is the blend factors -!!samps 4 -!!samps mix=4 +//RTLIGHT (+PCF,CUBE,SPOT,etc) +!!samps tr=0 tg=1 tb=2 tx=3 //the four texturemaps +!!samps mix=4 //how the ground is blended !!samps =PCF shadowmap !!samps =CUBE projectionmap @@ -98,10 +99,12 @@ void main (void) vec4 r; vec4 m = texture2D(s_mix, lm); - r = texture2D(s_t0, tc)*m.r; - r += texture2D(s_t1, tc)*m.g; - r += texture2D(s_t2, tc)*m.b; - r += texture2D(s_t3, tc)*(1.0 - (m.r + m.g + m.b)); + r = texture2D(s_tr, tc)*m.r; + r += texture2D(s_tg, tc)*m.g; + r += texture2D(s_tb, tc)*m.b; + r += texture2D(s_tx, tc)*(1.0 - (m.r + m.g + m.b)); + + r.rgb *= 1.0/r.a; //fancy maths, so low alpha values give other textures a greater focus //vertex colours provide a scaler that applies even through rtlights. r *= vc;