From 5e7688a5901efbb6098a76f192b416a23bd2b534 Mon Sep 17 00:00:00 2001 From: Spoike Date: Tue, 12 Mar 2019 05:04:27 +0000 Subject: [PATCH] first public attempt at pbr. update infoblobs to be slightly more self-contained (still not finalised). q3ui can now change audio volumes. linearise 16bit srgb textures as required. code can now potentially support >256 bones. disabled until the stack overflows are fixed... remap bone indexes where required, for a 10-fold speedup on models with otherwise-too-high bone counts gltf loader updates, primarily shader changes, for better conformance. shaders can now specify whether a texture should be treated as srgb or not. implement serverside download queue for ezquake/legacy clients downloading multiple demos. fte clients should never need to use this (would break total download size display). some work towards threading shader loading. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5430 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 2 +- engine/client/cl_input.c | 2 +- engine/client/cl_parse.c | 91 ++- engine/client/cl_plugin.inc | 4 +- engine/client/cl_ui.c | 58 +- engine/client/console.c | 55 +- engine/client/image.c | 22 +- engine/client/m_options.c | 22 +- engine/client/pr_csqc.c | 28 +- engine/client/quakedef.h | 1 + engine/client/r_2d.c | 1 - engine/client/r_part.c | 8 +- engine/client/render.h | 65 +- engine/client/snd_dma.c | 2 +- engine/client/textedit.c | 6 +- engine/common/com_mesh.c | 146 ++++- engine/common/com_mesh.h | 14 +- engine/common/common.c | 100 ++- engine/common/common.h | 2 +- engine/common/protocol.h | 1 + engine/common/zone.c | 2 +- engine/common/zone.h | 2 +- engine/gl/gl_backend.c | 9 +- engine/gl/gl_model.h | 19 +- engine/gl/gl_shader.c | 934 ++++++++++++++++----------- engine/gl/gl_vidcommon.c | 73 +++ engine/gl/gl_vidlinuxglx.c | 6 + engine/gl/r_bishaders.h | 273 ++++++-- engine/gl/shader.h | 32 +- engine/qclib/pr_exec.c | 10 +- engine/server/pr_cmds.c | 6 +- engine/server/server.h | 7 + engine/server/sv_main.c | 24 +- engine/server/sv_send.c | 86 ++- engine/server/sv_user.c | 86 ++- engine/shaders/glsl/defaultskin.glsl | 142 +++- engine/shaders/glsl/defaultwall.glsl | 94 ++- engine/shaders/glsl/rtlight.glsl | 80 ++- engine/vk/vk_init.c | 2 +- plugins/models/gltf.c | 263 +++++--- specs/multiprogs.txt | 4 +- 41 files changed, 1902 insertions(+), 882 deletions(-) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 0b3b82b0a..e4ee067dd 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -3612,7 +3612,7 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp VectorCopy(snew->angles, le->newangle); //fixme: should be oldservertime - le->orglerpdeltatime = servertime-le->orglerpstarttime; + le->orglerpdeltatime = bound(0.001, servertime-le->orglerpstarttime, cl_lerp_maxinterval.value); le->orglerpstarttime = servertime; } diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index bbd47bc3c..e4d9a1b7c 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1922,7 +1922,7 @@ static void CL_SendUserinfoUpdate(void) size_t bloboffset = cls.userinfosync.keys[0].syncpos; unsigned int seat = info - cls.userinfo; size_t blobsize; - const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize); + const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize, NULL); size_t sendsize = blobsize - bloboffset; const char *s; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 716ea35d6..b5ed4694f 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -5272,6 +5272,41 @@ static void CL_UpdateUserinfo (void) } } +static void CL_ParseSetInfoBlob (void) +{ + qbyte slot = MSG_ReadByte(); + char *key = MSG_ReadString(); + size_t keysize; + unsigned int offset = MSG_ReadLong(); + qboolean final = !!(offset & 0x80000000); + unsigned short valsize = MSG_ReadShort(); + char *val = BZ_Malloc(valsize); + MSG_ReadData(val, valsize); + offset &= ~0x80000000; + key = InfoBuf_DecodeString(key, key+strlen(key), &keysize); + + if (slot-- == 0) + InfoBuf_SyncReceive(&cl.serverinfo, key, keysize, val, valsize, offset, final); + else if (slot >= MAX_CLIENTS) + Con_Printf("INVALID SETINFO %i: %s=%s\n", slot, key, val); + else + { + player_info_t *player = &cl.players[slot]; + if (offset) + Con_DLPrintf(2,"SETINFO %s: %s+=%s\n", player->name, key, val); + else + Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val); + + InfoBuf_SyncReceive(&player->userinfo, key, keysize, val, valsize, offset, final); + player->userinfovalid = true; + + if (final) + CL_ProcessUserInfo (slot, player); + } + + Z_Free(key); + Z_Free(val); +} /* ============== CL_SetInfo @@ -5281,67 +5316,27 @@ static void CL_ParseSetInfo (void) { int slot; player_info_t *player; - char *temp; - char *key; char *val; - unsigned int offset; - qboolean final; - size_t keysize; - size_t valsize; + char key[512]; slot = MSG_ReadByte (); - if (slot == 255 && (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS)) - { - slot = MSG_ReadByte(); - offset = MSG_ReadLong(); - final = !!(offset & 0x80000000); - offset &= ~0x80000000; - } - else - { - final = true; - offset = 0; - } + MSG_ReadStringBuffer(key, sizeof(key)); + val = MSG_ReadString(); - temp = MSG_ReadString(); - if (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS) - key = InfoBuf_DecodeString(temp, temp+strlen(temp), &keysize); - else - { - keysize = strlen(temp); - key = Z_StrDup(temp); - } - - temp = MSG_ReadString(); - if (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS) - val = InfoBuf_DecodeString(temp, temp+strlen(temp), &valsize); - else - { - valsize = strlen(temp); - val = Z_StrDup(temp); - } - - if (slot == 255) - InfoBuf_SyncReceive(&cl.serverinfo, key, keysize, val, valsize, offset, final); - else if (slot >= MAX_CLIENTS) + if (slot >= MAX_CLIENTS) Con_Printf("INVALID SETINFO %i: %s=%s\n", slot, key, val); else { player = &cl.players[slot]; - if (offset) - Con_DLPrintf(2,"SETINFO %s: %s+=%s\n", player->name, key, val); - else - Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val); + Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val); - InfoBuf_SyncReceive(&player->userinfo, key, keysize, val, valsize, offset, final); + InfoBuf_SetStarKey(&player->userinfo, key, val); player->userinfovalid = true; CL_ProcessUserInfo (slot, player); } - Z_Free(key); - Z_Free(val); } /* @@ -7160,10 +7155,12 @@ void CLQW_ParseServerMessage (void) case svc_setinfo: CL_ParseSetInfo (); break; - case svc_serverinfo: CL_ServerInfo (); break; + case svcfte_setinfoblob: + CL_ParseSetInfoBlob(); + break; case svc_download: CL_ParseDownload (false); diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index f99961ae7..7eb6b9df6 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -1253,7 +1253,9 @@ static qintptr_t VARGS Plug_Mod_GetPluginModelFuncs(void *offset, quintptr_t mas #endif NULL, Image_GetTexture, - FS_OpenVFS + FS_OpenVFS, + Mod_AccumulateTextureVectors, + Mod_NormaliseTextureVectors, }; if (VM_LONG(arg[0]) >= sizeof(funcs)) return (qintptr_t)&funcs; diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 7513bd7ec..706cf6a1d 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -696,7 +696,53 @@ void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font) } } - +static cvar_t *Cvar_Q3FindVar (const char *var_name) +{ + struct { + const char *q3; + const char *fte; + } cvarremaps[] = + { + {"s_musicvolume", "bgmvolume"}, + {"r_gamma", "gamma"}, + {"s_sdlSpeed", "s_khz"}, + {"r_fullscreen", "vid_fullscreen"}, + {"r_picmip", "gl_picmip"}, + {"r_textureMode", "gl_texturemode"}, + {"r_lodBias", "d_lodbias"}, + {"r_colorbits", "vid_bpp"}, + {"r_dynamiclight", "r_dynamic"}, + {"r_finish", "gl_finish"}, +// {"r_glDriver", NULL}, +// {"r_depthbits", NULL}, +// {"r_stencilbits", NULL}, +// {"s_compression", NULL}, +// {"r_texturebits", NULL}, +// {"r_allowExtensions",NULL}, +// {"s_useOpenAL", NULL}, +// {"sv_running", NULL}, +// {"sv_killserver", NULL}, +// {"color1", NULL}, +// {"in_joystick", NULL}, +// {"joy_threshold", NULL}, +// {"cl_freelook", NULL}, +// {"color1", NULL}, +// {"r_availableModes",NULL}, +// {"r_mode", NULL}, + }; + cvar_t *v; + size_t i; + v = Cvar_FindVar(var_name); + if (v) + return v; + for (i = 0; i < countof(cvarremaps); i++) + { + if (!strcmp(cvarremaps[i].q3, var_name)) + return Cvar_FindVar(cvarremaps[i].fte); + } +// Con_Printf("Q3 Cvar %s is not known\n", var_name); + return NULL; +} #define VALIDATEPOINTER(o,l) if ((quintptr_t)o + l >= mask || VM_POINTER(o) < offset) Host_EndGame("Call to ui trap %i passes invalid pointer\n", (int)fn); //out of bounds. @@ -752,7 +798,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con } else { - var = Cvar_FindVar(vname); + var = Cvar_Q3FindVar(vname); if (var) Cvar_Set(var, vval); //set it else @@ -764,7 +810,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con { cvar_t *var; char *vname = VM_POINTER(arg[0]); - var = Cvar_FindVar(vname); + var = Cvar_Q3FindVar(vname); if (var) VM_FLOAT(ret) = var->value; else @@ -775,7 +821,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con { cvar_t *var; char *vname = VM_POINTER(arg[0]); - var = Cvar_FindVar(vname); + var = Cvar_Q3FindVar(vname); if (!VM_LONG(arg[2])) VM_LONG(ret) = 0; else if (!var) @@ -794,14 +840,14 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; case UI_CVAR_SETVALUE: - Cvar_SetValue(Cvar_FindVar(VM_POINTER(arg[0])), VM_FLOAT(arg[1])); + Cvar_SetValue(Cvar_Q3FindVar(VM_POINTER(arg[0])), VM_FLOAT(arg[1])); break; case UI_CVAR_RESET: //cvar reset { cvar_t *var; char *vname = VM_POINTER(arg[0]); - var = Cvar_FindVar(vname); + var = Cvar_Q3FindVar(vname); if (var) Cvar_Set(var, var->defaultstr); } diff --git a/engine/client/console.c b/engine/client/console.c index f4a725c49..8b94d86be 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2975,7 +2975,9 @@ void Con_DrawConsole (int lines, qboolean noback) playerview_t pv; entity_t ent; vec3_t fwd, rgt, up; - vec3_t lightpos = {0, 1, 0}; + vec3_t lightpos = {1, 1, 0}; + float transforms[12]; + float scale; if (R2D_Flush) R2D_Flush(); @@ -3003,15 +3005,14 @@ void Con_DrawConsole (int lines, qboolean noback) VectorClear(r_refdef.viewangles); r_refdef.viewangles[0] = 20; - r_refdef.viewangles[1] = realtime * 90; + // r_refdef.viewangles[1] = realtime * 90; AngleVectors(r_refdef.viewangles, fwd, rgt, up); VectorScale(fwd, -64, r_refdef.vieworg); memset(&ent, 0, sizeof(ent)); ent.scale = 1; - // ent.angles[1] = realtime*45;//mods->yaw; + ent.angles[1] = realtime*90;//mods->yaw; // ent.angles[0] = realtime*23.4;//mods->pitch; - ent.angles[0]*=r_meshpitch.value; AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]); ent.angles[0]*=r_meshpitch.value; @@ -3020,8 +3021,16 @@ void Con_DrawConsole (int lines, qboolean noback) ent.model = model; if (!ent.model) return; //panic! + //ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2]; + + ent.scale = 1; + scale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2])); + scale = scale?64.0/scale:1; ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2]; Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1); + VectorScale(ent.axis[0], scale, ent.axis[0]); + VectorScale(ent.axis[1], scale, ent.axis[1]); + VectorScale(ent.axis[2], scale, ent.axis[2]); /*if (strstr(model->name, "player")) { ent.bottomcolour = genhsv(realtime*0.1 + 0, 1, 1); @@ -3042,11 +3051,49 @@ void Con_DrawConsole (int lines, qboolean noback) ent.framestate.g[FS_REG].endbone = 0x7fffffff; // ent.customskin = Mod_RegisterSkinFile(va("%s_0.skin", mods->modelname)); + VectorSet(ent.glowmod, 1,1,1); ent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66; ent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33; V_ApplyRefdef(); + if (ent.model->camerabone>0 && Mod_GetTag(ent.model, ent.model->camerabone, &ent.framestate, transforms)) + { + VectorClear(ent.origin); + ent.angles[0]*=r_meshpitch.value; + AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]); + ent.angles[0]*=r_meshpitch.value; + VectorInverse(ent.axis[1]); + scale = 1; + { + vec3_t fwd, up; + float camera[12], et[12] = { + ent.axis[0][0], ent.axis[1][0], ent.axis[2][0], ent.origin[0], + ent.axis[0][1], ent.axis[1][1], ent.axis[2][1], ent.origin[1], + ent.axis[0][2], ent.axis[1][2], ent.axis[2][2], ent.origin[2], + }; + + R_ConcatTransforms((void*)et, (void*)transforms, (void*)camera); + VectorSet(fwd, camera[2], camera[6], camera[10]); + VectorNegate(fwd, fwd); + VectorSet(up, camera[1], camera[5], camera[9]); + VectorSet(r_refdef.vieworg, camera[3], camera[7], camera[11]); + VectorAngles(fwd, up, r_refdef.viewangles, false); + } + } + else + { + ent.angles[1] = realtime*90;//mods->yaw; + ent.angles[0]*=r_meshpitch.value; + AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]); + ent.angles[0]*=r_meshpitch.value; + VectorScale(ent.axis[0], scale, ent.axis[0]); + VectorScale(ent.axis[1], -scale, ent.axis[1]); + VectorScale(ent.axis[2], scale, ent.axis[2]); + } + + ent.scale = scale; + VectorNormalize(lightpos); ent.light_dir[0] = DotProduct(lightpos, ent.axis[0]); ent.light_dir[1] = DotProduct(lightpos, ent.axis[1]); diff --git a/engine/client/image.c b/engine/client/image.c index e53b4b37f..eace66d83 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -7602,6 +7602,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag switch(nf) { + case PTI_R8: case PTI_L8: for (i = 0; i < m; i++) ((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255)); @@ -7611,6 +7612,19 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag for (i = 0; i < m; i+=2) ((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255)); break; + case PTI_R16: + for (i = 0; i < m; i+=4) + ((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff)); + break; + case PTI_RGBA16: + m*=4; + for (i = 0; i < m; i+=4) + { + ((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff)); + ((unsigned short*)rgbadata)[i+1] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+1] * (1.0/0xffff)); + ((unsigned short*)rgbadata)[i+2] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+2] * (1.0/0xffff)); + } + break; case PTI_RGBA8: case PTI_RGBX8: case PTI_BGRA8: @@ -7627,7 +7641,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_BC1_RGBA: case PTI_BC2_RGBA: case PTI_BC3_RGBA: - //FIXME: bc1/2/3 has two leading 16bit values per block. + //FIXME: bc1/2/3 has two leading 16bit 565 values per block. default: //these formats are weird. we can't just fiddle with the rgbdata //FIXME: etc2 has all sorts of weird encoding tables... @@ -8656,7 +8670,7 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns qboolean dontposttoworker = (flags & (IF_NOWORKER | IF_LOADNOW)); qboolean lowpri = (flags & IF_LOWPRIORITY); -// qboolean highpri = (flags & IF_HIGHPRIORITY); + qboolean highpri = (flags & IF_HIGHPRIORITY); flags &= ~(IF_LOADNOW | IF_LOWPRIORITY | IF_HIGHPRIORITY); #ifdef LOADERTHREAD @@ -8782,7 +8796,9 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns } else #endif - if (lowpri) + if (highpri) + COM_InsertWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); + else if (lowpri) COM_AddWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); else COM_AddWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 3fc07338d..6c3ae42d9 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -3254,7 +3254,6 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ VectorScale(fwd, -mods->dist, r_refdef.vieworg); memset(&ent, 0, sizeof(ent)); - ent.scale = 1; // ent.angles[1] = realtime*45;//mods->yaw; // ent.angles[0] = realtime*23.4;//mods->pitch; @@ -3266,8 +3265,18 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ ent.model = Mod_ForName(mods->modelname, MLV_WARN); if (!ent.model) return; //panic! - ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2]; + ent.scale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2])); + ent.scale = ent.scale?64.0/ent.scale:1; + ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5;// + ent.model->mins[2]; + ent.origin[2] *= ent.scale; Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1); + VectorSet(ent.glowmod, 1, 1, 1); + +// VectorScale(ent.axis[0], ent.scale, ent.axis[0]); +// VectorScale(ent.axis[1], ent.scale, ent.axis[1]); +// VectorScale(ent.axis[2], ent.scale, ent.axis[2]); +// ent.scale = 1; + if (strstr(mods->modelname, "player")) { ent.bottomcolour = genhsv(realtime*0.1 + 0, 1, 1); @@ -3311,6 +3320,9 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ VectorCopy(tr.endpos, lightpos); } */ + lightpos[0] = sin(realtime*0.1); + lightpos[1] = cos(realtime*0.1); + lightpos[2] = 0; VectorNormalize(lightpos); ent.light_dir[0] = DotProduct(lightpos, ent.axis[0]); @@ -3619,12 +3631,14 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ "body: %i\n" "geomset: %i %i%s\n" "numverts: %i\nnumtris: %i\n" + "numbones: %i\n" , ent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2], contents, inf->csurface.flags, inf->surfaceid, inf->geomset>=MAX_GEOMSETS?-1:inf->geomset, inf->geomid, inf->geomset>=MAX_GEOMSETS?" (always)":"", - inf->numverts, inf->numindexes/3 + inf->numverts, inf->numindexes/3, + inf->numbones ) , CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); } @@ -3709,7 +3723,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ shader->defaulttextures->base = skin->upperoverlay; //diffuse texture for the upper body(shirt colour). no alpha channel. added to base.rgb break; case 4: - t = "LopwerMap"; + t = "LowerMap"; shader->defaulttextures->base = skin->loweroverlay; //diffuse texture for the lower body(trouser colour). no alpha channel. added to base.rgb break; case 5: diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 2f6332252..7895578be 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1544,30 +1544,6 @@ static void CSQC_PolyFlush(void) csqc_poly_shader = NULL; } -static void Shader_PolygonShader(const char *shortname, shader_t *s, const void *args) -{ - Shader_DefaultScript(shortname, s, - "{\n" - "if $lpp\n" - "program lpp_skin\n" - "else\n" - "program defaultskin#NONORMALS\n" - "endif\n" - "{\n" - "map $diffuse\n" - "rgbgen vertex\n" - "alphagen vertex\n" - "}\n" - "{\n" - "map $fullbright\n" - "blendfunc add\n" - "}\n" - "}\n" - ); - - if (!s->defaulttextures->base && (s->flags & SHADER_HASDIFFUSE)) - R_BuildDefaultTexnums(NULL, s, 0); -} static shader_t *PR_R_PolygonShader(const char *shadername, qboolean twod) { extern shader_t *shader_draw_fill_trans; @@ -3961,7 +3937,7 @@ static void QCBUILTIN PF_cs_serverkeyblob (pubprogfuncs_t *prinst, struct global } ptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr); - blob = InfoBuf_BlobForKey(&cl.serverinfo, keyname, &blobsize); + blob = InfoBuf_BlobForKey(&cl.serverinfo, keyname, &blobsize, NULL); if (qcptr) { @@ -4054,7 +4030,7 @@ static void QCBUILTIN PF_cs_getplayerkeyblob (pubprogfuncs_t *prinst, struct glo else { size_t blobsize = 0; - const char *blob = InfoBuf_BlobForKey(&cl.players[pnum].userinfo, keyname, &blobsize); + const char *blob = InfoBuf_BlobForKey(&cl.players[pnum].userinfo, keyname, &blobsize, NULL); if (qcptr) { diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index a10da7bb2..c3fe78ba9 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -355,6 +355,7 @@ typedef enum WG_COUNT = 2 //main and loaders } wgroup_t; void COM_AddWork(wgroup_t thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b); +void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b); qboolean COM_HasWork(void); void COM_WorkerFullSync(void); void COM_DestroyWorkerThread(void); diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 8217b623e..3f8f031d6 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -436,7 +436,6 @@ mpic_t *R2D_SafeCachePic (const char *path) mpic_t *R2D_SafePicFromWad (const char *name) { - void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs); shader_t *s; if (!qrenderer) return NULL; diff --git a/engine/client/r_part.c b/engine/client/r_part.c index 297de68ce..471d78330 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -892,6 +892,7 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int int i; vec3_t ts, te; physent_t *pe; + model_t *mod; int result=0; vec3_t axis[3]; @@ -906,7 +907,8 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int pe = &pmove.physents[i]; if (pe->nonsolid) continue; - if (pe->model && pe->model->loadstate == MLS_LOADED) + mod = pe->model; + if (mod && mod->loadstate == MLS_LOADED && mod->funcs.NativeTrace) { VectorSubtract(start, pe->origin, ts); VectorSubtract(end, pe->origin, te); @@ -914,10 +916,10 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int { AngleVectors(pe->angles, axis[0], axis[1], axis[2]); VectorNegate(axis[1], axis[1]); - pe->model->funcs.NativeTrace(pe->model, 0, PE_FRAMESTATE, axis, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace); + mod->funcs.NativeTrace(mod, 0, PE_FRAMESTATE, axis, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace); } else - pe->model->funcs.NativeTrace(pe->model, 0, PE_FRAMESTATE, NULL, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace); + mod->funcs.NativeTrace(mod, 0, PE_FRAMESTATE, NULL, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace); if (trace.fraction<1) { if (bestfrac > trace.fraction) diff --git a/engine/client/render.h b/engine/client/render.h index ac641d4eb..f8227118e 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -402,40 +402,45 @@ void R_RenderDlights (void); enum imageflags { /*warning: many of these flags only apply the first time it is requested*/ - IF_CLAMP = 1<<0, //disable texture coord wrapping. - IF_NOMIPMAP = 1<<1, //disable mipmaps. - IF_NEAREST = 1<<2, //force nearest - IF_LINEAR = 1<<3, //force linear - IF_UIPIC = 1<<4, //subject to texturemode2d - //IF_DEPTHCMD=1<<5, //Reserved for d3d11 - IF_SRGB = 1<<6, //texture data is srgb + IF_CLAMP = 1<<0, //disable texture coord wrapping. + IF_NOMIPMAP = 1<<1, //disable mipmaps. + IF_NEAREST = 1<<2, //force nearest + IF_LINEAR = 1<<3, //force linear + IF_UIPIC = 1<<4, //subject to texturemode2d + //IF_DEPTHCMD = 1<<5, //Reserved for d3d11 + IF_SRGB = 1<<6, //texture data is srgb (read-as-linear) /*WARNING: If the above are changed, be sure to change shader pass flags*/ - IF_NOPICMIP = 1<<7, - IF_NOALPHA = 1<<8, /*hint rather than requirement*/ - IF_NOGAMMA = 1<<9, - IF_3DMAP = 1<<10, /*waning - don't test directly*/ - IF_CUBEMAP = 1<<11, /*waning - don't test directly*/ - IF_TEXTYPE = (1<<10) | (1<<11), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/ - IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2-7=cubeface*/ - IF_MIPCAP = 1<<12, - IF_PREMULTIPLYALPHA = 1<<13, //rgb *= alpha + IF_NOPICMIP = 1<<7, + IF_NOALPHA = 1<<8, /*hint rather than requirement*/ + IF_NOGAMMA = 1<<9, /*do not apply texture-based gamma*/ + IF_3DMAP = 1<<10, /*waning - don't test directly*/ + IF_CUBEMAP = 1<<11, /*waning - don't test directly*/ + IF_TEXTYPE = (1<<10) | (1<<11), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/ + IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2-7=cubeface*/ + IF_MIPCAP = 1<<12, //allow the use of d_mipcap + IF_PREMULTIPLYALPHA = 1<<13, //rgb *= alpha - IF_WORLDTEX = 1<<18, //gl_picmip_world - IF_SPRITETEX = 1<<19, //gl_picmip_sprites - IF_NOSRGB = 1<<20, //ignore srgb when loading. this is guarenteed to be linear, for normalmaps etc. + IF_UNUSED14 = 1<<14, // + IF_UNUSED15 = 1<<15, // + IF_UNUSED16 = 1<<16, // + IF_UNUSED17 = 1<<17, // - IF_PALETTIZE = 1<<21, - IF_NOPURGE = 1<<22, - IF_HIGHPRIORITY = 1<<23, - IF_LOWPRIORITY = 1<<24, - IF_LOADNOW = 1<<25, /*hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance*/ - IF_NOPCX = 1<<26, /*block pcx format. meaning qw skins can use team colours and cropping*/ - IF_TRYBUMP = 1<<27, /*attempt to load _bump if the specified _norm texture wasn't found*/ - IF_RENDERTARGET = 1<<28, /*never loaded from disk, loading can't fail*/ - IF_EXACTEXTENSION = 1<<29, /*don't mangle extensions, use what is specified and ONLY that*/ - IF_NOREPLACE = 1<<30, /*don't load a replacement, for some reason*/ - IF_NOWORKER = 1u<<31 /*don't pass the work to a loader thread. this gives fully synchronous loading. only valid from the main thread.*/ + IF_WORLDTEX = 1<<18, //gl_picmip_world + IF_SPRITETEX = 1<<19, //gl_picmip_sprites + IF_NOSRGB = 1<<20, //ignore srgb when loading. this is guarenteed to be linear, for normalmaps etc. + + IF_PALETTIZE = 1<<21, //convert+load it as an RTI_P8 texture for the current palette/colourmap + IF_NOPURGE = 1<<22, //texture is not flushed when no more shaders refer to it (for C code that holds a permanant reference to it - still purged on vid_reloads though) + IF_HIGHPRIORITY = 1<<23, //pushed to start of worker queue instead of end... + IF_LOWPRIORITY = 1<<24, // + IF_LOADNOW = 1<<25, /*hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance*/ + IF_NOPCX = 1<<26, /*block pcx format. meaning qw skins can use team colours and cropping*/ + IF_TRYBUMP = 1<<27, /*attempt to load _bump if the specified _norm texture wasn't found*/ + IF_RENDERTARGET = 1<<28, /*never loaded from disk, loading can't fail*/ + IF_EXACTEXTENSION = 1<<29, /*don't mangle extensions, use what is specified and ONLY that*/ + IF_NOREPLACE = 1<<30, /*don't load a replacement, for some reason*/ + IF_NOWORKER = 1u<<31 /*don't pass the work to a loader thread. this gives fully synchronous loading. only valid from the main thread.*/ }; #define R_LoadTexture8(id,w,h,d,f,t) Image_GetTexture(id, NULL, f, d, NULL, w, h, t?TF_TRANS8:TF_SOLID8) diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 2508fb71f..2372627af 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -74,7 +74,7 @@ int sound_started=0; cvar_t bgmvolume = CVARAFD( "musicvolume", "0.3", "bgmvolume", CVAR_ARCHIVE, "Volume level for background music."); -cvar_t volume = CVARFD( "volume", "0.7", CVAR_ARCHIVE, +cvar_t volume = CVARAFD( "volume", "0.7", /*q3*/"s_volume",CVAR_ARCHIVE, "Main volume level for all engine sound."); cvar_t nosound = CVARFD( "nosound", "0", CVAR_ARCHIVE, diff --git a/engine/client/textedit.c b/engine/client/textedit.c index 514fa8a25..5d5303657 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -1011,9 +1011,13 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st return DEBUG_TRACE_OFF; //whoops } + if (reason) + Con_Printf("QC Exception: %s\n", reason); + if (!pr_debugger.ival) { - Con_Printf("Set %s to trace\n", pr_debugger.name); + if (!stepasm && *filename) + Con_Printf("Set %s to trace\n", pr_debugger.name); if (fatal) return DEBUG_TRACE_ABORT; return DEBUG_TRACE_OFF; //get lost diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index e1e214fb6..1f7dbcf23 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -140,7 +140,7 @@ static clampedmodel_t clampedmodel[] = { -void Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms) +void QDECL Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms) { int i; const float *v0, *v1, *v2; @@ -215,7 +215,7 @@ void Mod_AccumulateMeshTextureVectors(mesh_t *m) Mod_AccumulateTextureVectors(m->xyz_array, m->st_array, m->normals_array, m->snormals_array, m->tnormals_array, m->indexes, m->numindexes, false); } -void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms) +void QDECL Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms) { int i; float f; @@ -352,7 +352,7 @@ static void PSKGenMatrix(float x, float y, float z, float qx, float qy, float qz #endif /*transforms some skeletal vecV_t values*/ -static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, qbyte *bidx, float *weights, float *xyzin, float *fte_restrict xyzout) +static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, boneidx_t *bidx, float *weights, float *xyzin, float *fte_restrict xyzout) { #if 1 int i, j; @@ -428,7 +428,7 @@ static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, qby } /*transforms some skeletal vecV_t values*/ -static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, const qbyte *bidx, float *weights, +static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, const boneidx_t *bidx, float *weights, const float *xyzin, float *fte_restrict xyzout, const float *normin, float *fte_restrict normout) { @@ -472,7 +472,7 @@ static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, co } /*transforms some skeletal vecV_t values*/ -static void Alias_TransformVerticies_VNST(const float *bonepose, int vertcount, const qbyte *bidx, const float *weights, +static void Alias_TransformVerticies_VNST(const float *bonepose, int vertcount, const boneidx_t *bidx, const float *weights, const float *xyzin, float *fte_restrict xyzout, const float *normin, float *fte_restrict normout, const float *sdirin, float *fte_restrict sdirout, @@ -708,6 +708,8 @@ struct entity_t *ent; #ifdef SKELETALMODELS + boneidx_t *bonemap; //force the renderer to forget the current entity when this changes + float gpubones[MAX_BONES*12]; //temp storage for multi-surface models with too many bones. float boneposebuffer1[MAX_BONES*12]; float boneposebuffer2[MAX_BONES*12]; skeltype_t bonecachetype; @@ -1325,7 +1327,7 @@ static const float *Alias_GetBoneInformation(galiasinfo_t *inf, framestate_t *fr static void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, galiasinfo_t *inf) { - qbyte *fte_restrict bidx = inf->ofs_skel_idx[0]; + boneidx_t *fte_restrict bidx = inf->ofs_skel_idx[0]; float *fte_restrict weight = inf->ofs_skel_weight[0]; if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE) @@ -1350,7 +1352,7 @@ static void Alias_BuildSkeletalVerts(float *xyzout, framestate_t *framestate, ga { float buffer[MAX_BONES*12]; float bufferalt[MAX_BONES*12]; - qbyte *fte_restrict bidx = inf->ofs_skel_idx[0]; + boneidx_t *fte_restrict bidx = inf->ofs_skel_idx[0]; float *fte_restrict weight = inf->ofs_skel_weight[0]; const float *bonepose = Alias_GetBoneInformation(inf, framestate, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES); @@ -1654,11 +1656,11 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in usebones = false; else if (inf->ofs_skel_xyz && !inf->ofs_skel_weight) usebones = false; - else if (e->fatness || !inf->ofs_skel_idx || inf->numbones > sh_config.max_gpu_bones) + else if (e->fatness || !inf->ofs_skel_idx || (!inf->mappedbones && inf->numbones > sh_config.max_gpu_bones)) #endif usebones = false; - if (0)//meshcache.ent == e) + if (meshcache.ent == e) { if (meshcache.vertgroup == inf->shares_verts && meshcache.ent == e && usebones == meshcache.usebones) { @@ -1683,7 +1685,22 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in mesh->boneweights = inf->ofs_skel_weight; mesh->bones = meshcache.usebonepose; mesh->numbones = inf->numbones; + } +#ifndef SERVERONLY + if (meshcache.bonemap != inf->bonemap) + { + meshcache.bonemap = inf->bonemap; + BE_SelectEntity(e); } + if (inf->mappedbones) + { + int i; + for (i = 0; i < inf->mappedbones; i++) + memcpy(meshcache.gpubones + i*12, meshcache.usebonepose + inf->bonemap[i]*12, sizeof(float)*12); + meshcache.vbo.numbones = inf->mappedbones; + meshcache.vbo.bones = meshcache.gpubones; + } +#endif return false; //don't generate the new vertex positions. We still have them all. } if (meshcache.bonegroup != inf->shares_bones) @@ -2002,6 +2019,21 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in mesh->boneweights = inf->ofs_skel_weight; mesh->bones = meshcache.usebonepose; mesh->numbones = inf->numbones; +#ifndef SERVERONLY + if (meshcache.bonemap != inf->bonemap) + { + meshcache.bonemap = inf->bonemap; + BE_SelectEntity(e); + } + if (inf->mappedbones) + { + int i; + for (i = 0; i < inf->mappedbones; i++) + memcpy(meshcache.gpubones + i*12, meshcache.usebonepose + inf->bonemap[i]*12, sizeof(float)*12); + meshcache.vbo.numbones = inf->mappedbones; + meshcache.vbo.bones = meshcache.gpubones; + } +#endif } #endif @@ -2779,7 +2811,7 @@ void Mod_DestroyMesh(galiasinfo_t *galias) } #ifndef SERVERONLY -static void Mod_GenerateMeshVBO(galiasinfo_t *galias) +static void Mod_GenerateMeshVBO(model_t *mod, galiasinfo_t *galias) //vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts) { #ifdef NONSKELETALMODELS @@ -2838,10 +2870,64 @@ static void Mod_GenerateMeshVBO(galiasinfo_t *galias) BE_VBO_Data(&vboctx, galias->ofs_skel_svect, sizeof(*galias->ofs_skel_svect) * galias->numverts, &galias->vbo_skel_svector); if (galias->ofs_skel_tvect) BE_VBO_Data(&vboctx, galias->ofs_skel_tvect, sizeof(*galias->ofs_skel_tvect) * galias->numverts, &galias->vbo_skel_tvector); - if (galias->ofs_skel_idx) - BE_VBO_Data(&vboctx, galias->ofs_skel_idx, sizeof(*galias->ofs_skel_idx) * galias->numverts, &galias->vbo_skel_bonenum); - if (galias->ofs_skel_weight) - BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight); + if (!galias->mappedbones /*&& galias->numbones > sh_config.max_gpu_bones*/ && galias->ofs_skel_idx) + { //if we're using gpu bones, then its possible that we're trying to load a model with more bones than the gpu supports + //to work around this (and get performance back), each surface has a gpu->cpu table so that bones not used on a mesh don't cause it to need to use a software fallback + qboolean *seen = alloca(sizeof(*seen) * galias->numbones); + int j, k; + memset(seen, 0, sizeof(*seen) * galias->numbones); + for (j = 0; j < galias->numverts; j++) + for (k = 0; k < 4; k++) + { + if (galias->ofs_skel_weight[j][k]) + seen[galias->ofs_skel_idx[j][k]] = true; + } + + for (j = 0, k = 0; j < galias->numbones; j++) + { + if (seen[j]) + k++; + } + if (k < sh_config.max_gpu_bones) + { //okay, we can hardware accelerate that. + galias->bonemap = ZG_Malloc(&mod->memgroup, sizeof(*galias->bonemap)*sh_config.max_gpu_bones); + galias->mappedbones = 0; + for (j = 0; j < galias->numbones; j++) + { + if (seen[j]) + galias->bonemap[galias->mappedbones++] = j; + } + } + } + if (galias->mappedbones) + { + boneidx_t *remaps = alloca(sizeof(*remaps) * galias->numbones); + bone_vec4_t *bones = alloca(sizeof(*bones) * galias->numverts); + int j, k; + + //our remap table is gpu->cpu, but we need cpu->gpu here + for (j = 0; j < galias->numbones; j++) + remaps[j] = 0; //errors. + for (j = 0; j < galias->mappedbones; j++) + remaps[galias->bonemap[j]] = j; + //now remap them all + for (j = 0; j < galias->numverts; j++) + for (k = 0; k < 4; k++) + bones[j][k] = remaps[galias->ofs_skel_idx[j][k]]; + + //and we can upload + if (galias->ofs_skel_idx) + BE_VBO_Data(&vboctx, bones, sizeof(*bones) * galias->numverts, &galias->vbo_skel_bonenum); + if (galias->ofs_skel_weight) + BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight); + } + else + { + if (galias->ofs_skel_idx) + BE_VBO_Data(&vboctx, galias->ofs_skel_idx, sizeof(*galias->ofs_skel_idx) * galias->numverts, &galias->vbo_skel_bonenum); + if (galias->ofs_skel_weight) + BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight); + } #endif #ifdef NONSKELETALMODELS for (i = 0; i < galias->numanimations; i++) @@ -2989,7 +3075,7 @@ void Mod_LoadAliasShaders(model_t *mod) { if (numskins < ai->numskins) numskins = ai->numskins; - Mod_GenerateMeshVBO(ai); //FIXME: shares verts + Mod_GenerateMeshVBO(mod, ai); //FIXME: shares verts } for (i = 0; i < numskins; i++) { @@ -5842,7 +5928,7 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) vecV_t *skel_xyz; vec3_t *skel_norm, *skel_svect, *skel_tvect; - byte_vec4_t *skel_idx; + bone_vec4_t *skel_idx; vec4_t *skel_weights; /*load the psk*/ @@ -6127,10 +6213,11 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) skel_tvect = ZG_Malloc(&mod->memgroup, sizeof(*skel_tvect) * num_vtxw); skel_idx = ZG_Malloc(&mod->memgroup, sizeof(*skel_idx) * num_vtxw); skel_weights = ZG_Malloc(&mod->memgroup, sizeof(*skel_weights) * num_vtxw); + for (j = 0; j < 4; j++) + skel_idx[i][j] = ~0; for (i = 0; i < num_vtxw; i++) { float t; - *(unsigned int*)skel_idx[i] = ~0; for (j = 0; j < num_rawweights; j++) { if (rawweights[j].pntsindex == vtxw[i].pntsindex) @@ -6945,7 +7032,7 @@ galisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, f } */ -static qboolean IQM_ImportArray4B(const qbyte *fte_restrict base, const struct iqmvertexarray *fte_restrict src, byte_vec4_t *fte_restrict out, size_t count, unsigned int maxval) +static qboolean IQM_ImportArray4Bone(const qbyte *fte_restrict base, const struct iqmvertexarray *fte_restrict src, bone_vec4_t *fte_restrict out, size_t count, unsigned int maxval) { size_t i; unsigned int j; @@ -6953,7 +7040,7 @@ static qboolean IQM_ImportArray4B(const qbyte *fte_restrict base, const struct i unsigned int fmt = LittleLong(src->format); unsigned int offset = LittleLong(src->offset); qboolean invalid = false; - maxval = min(256,maxval); //output is bytes. + maxval = min(MAX_BONES,maxval); //output is bytes. if (!offset) { sz = 0; @@ -7195,7 +7282,7 @@ static const void *IQM_FindExtension(const char *buffer, size_t buffersize, cons return NULL; } -static void Mod_CleanWeights(const char *modelname, size_t numverts, vec4_t *oweight, byte_vec4_t *oindex) +static void Mod_CleanWeights(const char *modelname, size_t numverts, vec4_t *oweight, bone_vec4_t *oindex) { //some IQMs lack weight values, apparently. int j, v; qboolean problemfound = false; @@ -7258,7 +7345,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz vecV_t *opos=NULL; vec3_t *onorm1=NULL, *onorm2=NULL, *onorm3=NULL; vec4_t *oweight=NULL; - byte_vec4_t *oindex=NULL; + bone_vec4_t *oindex=NULL; float *opose=NULL,*oposebase=NULL; vec2_t *otcoords = NULL; vec4_t *orgbaf = NULL; @@ -7273,7 +7360,6 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz galiasanimation_t *fgroup=NULL; galiasbone_t *bones = NULL; index_t *idx; - float basepose[12 * MAX_BONES]; qboolean noweights; frameinfo_t *framegroups; int numgroups; @@ -7481,10 +7567,10 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz GenMatrixPosQuat3Scale(ijoint[i].translate, ijoint[i].rotate, ijoint[i].scale, mat); if (ijoint[i].parent >= 0) - Matrix3x4_Multiply(mat, &basepose[ijoint[i].parent*12], &basepose[i*12]); + Matrix3x4_Multiply(mat, &oposebase[ijoint[i].parent*12], &oposebase[i*12]); else - memcpy(&basepose[i*12], mat, sizeof(mat)); - Matrix3x4_Invert_Simple(&basepose[i*12], bones[i].inverse); + memcpy(&oposebase[i*12], mat, sizeof(mat)); + Matrix3x4_Invert_Simple(&oposebase[i*12], bones[i].inverse); } //pose info (anim) @@ -7526,10 +7612,10 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz GenMatrixPosQuat4Scale(ijoint[i].translate, ijoint[i].rotate, ijoint[i].scale, mat); if (ijoint[i].parent >= 0) - Matrix3x4_Multiply(mat, &basepose[ijoint[i].parent*12], &basepose[i*12]); + Matrix3x4_Multiply(mat, &oposebase[ijoint[i].parent*12], &oposebase[i*12]); else - memcpy(&basepose[i*12], mat, sizeof(mat)); - Matrix3x4_Invert_Simple(&basepose[i*12], bones[i].inverse); + memcpy(&oposebase[i*12], mat, sizeof(mat)); + Matrix3x4_Invert_Simple(&oposebase[i*12], bones[i].inverse); } //pose info (anim) @@ -7552,8 +7638,6 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz } } } - //basepose - memcpy(oposebase, basepose, sizeof(float)*12 * h->num_joints); //now generate the animations. for (i = 0; i < numgroups; i++) @@ -7724,7 +7808,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz gai[i-1].nextsurf = NULL; if (!noweights) { - if (!IQM_ImportArray4B(buffer, &vbone, oindex, h->num_vertexes, h->num_joints)) + if (!IQM_ImportArray4Bone(buffer, &vbone, oindex, h->num_vertexes, h->num_joints)) Con_DPrintf(CON_WARNING "Invalid bone indexes detected inside %s\n", mod->name); IQM_ImportArrayF(buffer, &vweight, (float*)oweight, 4, h->num_vertexes, defaultweight); Mod_CleanWeights(mod->name, h->num_vertexes, oweight, oindex); diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index adb89b22e..246251d1c 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -170,8 +170,8 @@ typedef struct galiasinfo_s struct galiasinfo_s *nextsurf; #ifdef SKELETALMODELS -// int *bonemap; //some models are horribly complicated, this provides a gpubone->cpubone table, reducing the number of gpu bones needed on a per-mesh basis. -// int mappedbones; + boneidx_t *bonemap; //filled in automatically if our mesh has more gpu bones than we can support + unsigned int mappedbones; float *baseframeofs; /*non-heirachical*/ int numbones; @@ -181,7 +181,7 @@ typedef struct galiasinfo_s vec3_t *ofs_skel_norm; vec3_t *ofs_skel_svect; vec3_t *ofs_skel_tvect; - byte_vec4_t *ofs_skel_idx; + bone_vec4_t *ofs_skel_idx; vec4_t *ofs_skel_weight; vboarray_t vbo_skel_verts; @@ -234,8 +234,8 @@ typedef struct modplugfuncs_s void *reserved2; image_t *(QDECL *GetTexture)(const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt); vfsfile_t *(QDECL *OpenVFS)(const char *filename, const char *mode, enum fs_relative relativeto); - void *unused3; - void *unused4; + void (QDECL *AccumulateTextureVectors)(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms); + void (QDECL *NormaliseTextureVectors)(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms); void *unused5; void *unused6; void *unused7; @@ -263,9 +263,9 @@ qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **nam void Mod_DoCRC(model_t *mod, char *buffer, int buffersize); -void Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms); +void QDECL Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms); void Mod_AccumulateMeshTextureVectors(mesh_t *mesh); -void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms); +void QDECL Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms); void R_Generate_Mesh_ST_Vectors(mesh_t *mesh); #ifdef __cplusplus diff --git a/engine/common/common.c b/engine/common/common.c index ccc1932fe..196b8b4c0 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -5209,6 +5209,40 @@ qboolean COM_HasWork(void) } return false; } +void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b) +{ + struct com_work_s *work; + + if (tg >= WG_COUNT) + return; + + //no worker there, just do it immediately on this thread instead of pushing it to the worker. + if (!com_liveworkers[tg] || (tg!=WG_MAIN && com_workererror)) + { + func(ctx, data, a, b); + return; + } + + //build the work + work = Z_Malloc(sizeof(*work)); + work->func = func; + work->ctx = ctx; + work->data = data; + work->a = a; + work->b = b; + + //queue it (fifo) + Sys_LockConditional(com_workercondition[tg]); + work->next = com_work_head[tg]; + if (!com_work_tail[tg]) + com_work_tail[tg] = work; + com_work_head[tg] = work; + +// Sys_Printf("%x: Queued work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); + + Sys_ConditionSignal(com_workercondition[tg]); + Sys_UnlockConditional(com_workercondition[tg]); +} void COM_AddWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b) { struct com_work_s *work; @@ -6068,6 +6102,26 @@ void InfoSync_Add(infosync_t *sync, void *context, const char *name) sync->keys[k].syncpos = 0; } +static qboolean InfoBuf_NeedsEncoding(const char *str, size_t size) +{ + const char *c, *e = str+size; + for (c = str; c < e; c++) + { + switch((unsigned char)*c) + { + case 255: //invalid for vanilla qw, and also used for special encoding + case '\\': //abiguity with end-of-token + case '\"': //parsing often sends these enclosed in quotes + case '\n': //REALLY screws up parsing + case '\r': //generally bad form + case 0: //are we really doing this? + case '$': //a number of engines like expanding things inside quotes. make sure that cannot ever happen. + case ';': //in case someone manages to break out of quotes + return true; + } + } + return false; +} qboolean InfoBuf_FindKey (infobuf_t *info, const char *key, size_t *idx) { size_t k; @@ -6106,14 +6160,18 @@ char *InfoBuf_ValueForKey (infobuf_t *info, const char *key) //not to be used wi valueindex = (valueindex+1)&3; return InfoBuf_ReadKey(info, key, value[valueindex], sizeof(value[valueindex])); } -const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize) //obtains a direct pointer to temp memory +const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize, qboolean *large) //obtains a direct pointer to temp memory { size_t k; if (InfoBuf_FindKey(info, key, &k) && !info->keys[k].partial) { + if (large) + *large = info->keys[k].large; *blobsize = info->keys[k].size; return info->keys[k].value; } + if (large) + *large = InfoBuf_NeedsEncoding(key, sizeof(key)); *blobsize = 0; return NULL; } @@ -6169,26 +6227,25 @@ char *InfoBuf_DecodeString(const char *instart, const char *inend, size_t *sz) } return ret; } + static qboolean InfoBuf_IsLarge(struct infokey_s *key) { + size_t namesize; if (key->partial) return true; - //detect invalid keys/values - //\\ makes parsing really really messy and isn't supported by most clients (although we could do it anyway) - //\" requires string escapes, again compat issues. - //0xff bugs out vanilla. - //nulls are bad, too... - if (strchr(key->name, '\\') || strchr(key->name, '\"') || strchr(key->name, 0xff)) - return true; - if (strchr(key->value, '\\') || strchr(key->value, '\"') || strchr(key->value, 0xff) || strlen(key->value) != key->size) - return true; if (key->size >= 64) - return true; //key length limits is a thing in vanilla qw. - if (strlen(key->name) >= 64) return true; //value length limits is a thing in vanilla qw. //note that qw reads values up to 512, but only sets them up to 64 bytes... //probably just so that people don't spot buffer overflows so easily. + namesize = strlen(key->name); + if (namesize >= 64) + return true; //key length limits is a thing in vanilla qw. + + if (InfoBuf_NeedsEncoding(key->name, namesize)) + return true; + if (InfoBuf_NeedsEncoding(key->value, key->size)) + return true; return false; } //like InfoBuf_SetStarBlobKey, but understands partials. @@ -6423,24 +6480,7 @@ static qboolean InfoBuf_EncodeString_Internal(const char *n, size_t s, char *out { size_t r = 0; const char *c; - for (c = n; c < n+s; c++) - { - if (*c == (char)255 && c == n) - break; - if (*c == '\\') //abiguity with end-of-token - break; - if (*c == '\"') //parsing often sends these enclosed in quotes - break; - if (*c == '\n' || *c == '\r') //generally bad form - break; - if (*c == 0) //are we really doing this? - break; - if (*c == '$') //a number of engines like expanding things inside quotes. make sure that cannot ever happen. - break; - if (*c == ';') //in case someone manages to break out of quotes - break; - } - if (c != n+s) + if (InfoBuf_NeedsEncoding(n, s)) { unsigned int base64_cur = 0; unsigned int base64_bits = 0; diff --git a/engine/common/common.h b/engine/common/common.h index 65c581ae9..4acd5a12c 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -785,7 +785,7 @@ extern const char *basicuserinfos[]; //note: has a leading * extern const char *privateuserinfos[]; //key names that are not broadcast from the server qboolean InfoBuf_FindKey (infobuf_t *info, const char *key, size_t *idx); const char *InfoBuf_KeyForNumber (infobuf_t *info, int num); -const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize); +const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize, qboolean *large); char *InfoBuf_ReadKey (infobuf_t *info, const char *key, char *outbuf, size_t outsize); char *InfoBuf_ValueForKey (infobuf_t *info, const char *key); qboolean InfoBuf_RemoveKey (infobuf_t *info, const char *key); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index c822e442d..cd0916bf5 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -314,6 +314,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define svcfte_updateentities 86 #define svcfte_brushedit 87 // networked brush editing, paired with clcfte_brushedit. #define svcfte_updateseats 88 // byte count, byte playernum[count] +#define svcfte_setinfoblob 89 // [8] 1-based index [string] key [32] isfinal<<31|offset [16] chunksize [chunksize] data //fitz svcs diff --git a/engine/common/zone.c b/engine/common/zone.c index f4f2ed272..5275e2781 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -212,7 +212,7 @@ void *Z_Malloc(int size) } #endif -void Z_StrCat(char **ptr, char *append) +void Z_StrCat(char **ptr, const char *append) { size_t oldlen = *ptr?strlen(*ptr):0; size_t newlen = strlen(append); diff --git a/engine/common/zone.h b/engine/common/zone.h index 76fceb56d..756233b9b 100644 --- a/engine/common/zone.h +++ b/engine/common/zone.h @@ -133,7 +133,7 @@ void ZG_FreeGroup(zonegroup_t *ctx); #endif #define Z_StrDup(s) strcpy(Z_Malloc(strlen(s)+1), s) -void Z_StrCat(char **ptr, char *append); +void Z_StrCat(char **ptr, const char *append); /* void *Hunk_Alloc (int size); // returns 0 filled memory diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 641b57bcc..498d9cacf 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -769,7 +769,7 @@ static void BE_ApplyAttributes(unsigned int bitstochange, unsigned int bitstoend continue; } GL_SelectVBO(shaderstate.sourcevbo->bonenums.gl.vbo); - qglVertexAttribPointer(VATTR_BONENUMS, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, shaderstate.sourcevbo->bonenums.gl.addr); + qglVertexAttribPointer(VATTR_BONENUMS, 4, GL_BONE_INDEX_TYPE, GL_FALSE, 0, shaderstate.sourcevbo->bonenums.gl.addr); break; case VATTR_BONEWEIGHTS: if (!shaderstate.sourcevbo->boneweights.gl.vbo && !shaderstate.sourcevbo->boneweights.gl.addr) @@ -1418,12 +1418,12 @@ static float *FTableForFunc ( unsigned int func ) } } -void Shader_LightPass(const char *shortname, shader_t *s, const void *args) +void Shader_LightPass(struct shaderparsestate_s *ps, const char *shortname, const void *args) { char shadertext[8192*2]; extern cvar_t r_drawflat; sprintf(shadertext, LIGHTPASS_SHADER, (r_lightmap.ival||r_drawflat.ival)?"#FLAT=1.0":""); - Shader_DefaultScript(shortname, s, shadertext); + Shader_DefaultScript(ps, shortname, shadertext); } void GenerateFogTexture(texid_t *tex, float density, float zscale) @@ -3404,6 +3404,9 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu unsigned int ph; const shaderprogparm_t *p; + if (perm->factorsuniform != -1) + qglUniform4fvARB(perm->factorsuniform, countof(shaderstate.curshader->factors), shaderstate.curshader->factors[0]); + /*don't bother setting it if the ent properties are unchanged (but do if the mesh changed)*/ if (entunchanged) return; diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 827645536..46a7aa900 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -54,7 +54,23 @@ typedef enum { SHADER_SORT_COUNT } shadersort_t; +#ifdef FTE_TARGET_WEB #define MAX_BONES 256 +#else +#define MAX_BONES 256 //Note: there's lots of bone data allocated on the stack, so don't bump recklessly. +#endif +#if MAX_BONES>65536 +#define GL_BONE_INDEX_TYPE GL_UNSIGNED_INT +typedef unsigned int boneidx_t; +#elif MAX_BONES>256 +#define GL_BONE_INDEX_TYPE GL_UNSIGNED_SHORT +typedef unsigned short boneidx_t; +#else +#define GL_BONE_INDEX_TYPE GL_UNSIGNED_BYTE +typedef unsigned char boneidx_t; +#endif +typedef boneidx_t bone_vec4_t[4]; + struct doll_s; void rag_uninstanciateall(void); void rag_flushdolls(qboolean force); @@ -102,7 +118,7 @@ typedef struct mesh_s qboolean istrifan; /*if its a fan/poly/single quad (permits optimisations)*/ const float *bones; int numbones; - byte_vec4_t *bonenums; + bone_vec4_t *bonenums; vec4_t *boneweights; } mesh_t; @@ -1040,6 +1056,7 @@ typedef struct model_s // void *meshinfo; //data allocated within the memgroup allocations, will be nulled out when the model is flushed zonegroup_t memgroup; + int camerabone; //the 1-based bone index that the camera should be attached to (for gltf rather than anything else) } model_t; #define MDLF_EMITREPLACE 0x0001 // particle effect engulphs model (don't draw) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 318dbd7e6..af7cdea09 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -29,13 +29,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "hash.h" + #include extern texid_t missing_texture; texid_t r_whiteimage, r_blackimage; qboolean shader_reload_needed; static qboolean shader_rescan_needed; -static char **saveshaderbody; sh_config_t sh_config; @@ -221,7 +221,7 @@ static float Com_FloatArgument(const char *shadername, char *arg, size_t arglen) #define SPF_PROGRAMIFY (1u<<0) /*quake3/fte internal*/ #define SPF_DOOM3 (1u<<1) /*any commands, args, etc, should be interpretted according to doom3's norms*/ -typedef struct +typedef struct shaderparsestate_s { shader_t *s; //the shader we're parsing shaderpass_t *pass; //the pass we're currently parsing @@ -241,6 +241,8 @@ typedef struct vec3_t reflectcolour; float wateralpha; + char **saveshaderbody; + //FIXME: rtlights can't respond to these int offsetmappingmode; float offsetmappingscale; @@ -248,12 +250,11 @@ typedef struct float specularexpscale; //*32 ish float specularvalscale; //*1 ish } parsestate_t; -static parsestate_t parsestate; //FIXME typedef struct shaderkey_s { char *keyword; - void (*func)( shader_t *shader, shaderpass_t *pass, char **ptr ); + void (*func)( parsestate_t *ps, char **ptr ); char *prefix; } shaderkey_t; typedef struct shadercachefile_s { @@ -288,8 +289,8 @@ static qboolean Shader_Parsetok(parsestate_t *ps, shaderkey_t *keys, char *token static void Shader_ParseFunc(shader_t *shader, char **args, shaderfunc_t *func); static void Shader_MakeCache(const char *path, unsigned int parseflags); static qboolean Shader_LocateSource(char *name, char **buf, size_t *bufsize, size_t *offset, shadercachefile_t **sourcefile); -static void Shader_ReadShader(shader_t *s, char *shadersource, shadercachefile_t *sourcefile); -static qboolean Shader_ParseShader(char *parsename, shader_t *s); +static void Shader_ReadShader(parsestate_t *ps, char *shadersource, shadercachefile_t *sourcefile); +static qboolean Shader_ParseShader(parsestate_t *ps, char *parsename); //=========================================================================== @@ -691,11 +692,9 @@ static void Shader_ParseFunc (shader_t *shader, char **ptr, shaderfunc_t *func) //=========================================================================== -static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **name) +static int Shader_SetImageFlags(parsestate_t *parsestate, shaderpass_t *pass, char **name, int flags) { //fixme: pass flags should be handled elsewhere. - int flags = 0; - for(;name;) { if (!Q_strnicmp(*name, "$rt:", 4)) @@ -703,11 +702,7 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam *name += 4; flags |= IF_NOMIPMAP|IF_CLAMP|IF_RENDERTARGET; if (!(flags & (IF_NEAREST|IF_LINEAR))) - { flags |= IF_LINEAR; - if (pass) - pass->flags |= SHADER_PASS_LINEAR; - } } else if (!Q_strnicmp(*name, "$clamp:", 7)) { @@ -724,27 +719,29 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam *name+=6; flags = (flags&~IF_TEXTYPE) | IF_CUBEMAP; } + else if (!Q_strnicmp(*name, "$srgb:", 6)) + { + *name+=6; + flags &= ~IF_NOSRGB; + flags |= IF_SRGB; + } + else if (!Q_strnicmp(*name, "$nosrgb:", 8)) + { + *name+=8; + flags &= ~IF_SRGB; + flags |= IF_NOSRGB; + } else if (!Q_strnicmp(*name, "$nearest:", 9)) { *name+=9; flags &= ~IF_LINEAR; flags |= IF_NEAREST; - if (pass) - { - pass->flags &= ~SHADER_PASS_LINEAR; - pass->flags |= SHADER_PASS_NEAREST; - } } else if (!Q_strnicmp(*name, "$linear:", 8)) { *name+=8; flags &= ~IF_NEAREST; flags |= IF_LINEAR; - if (pass) - { - pass->flags &= ~SHADER_PASS_NEAREST; - pass->flags |= SHADER_PASS_LINEAR; - } } else break; @@ -752,12 +749,28 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam // if (shader->flags & SHADER_SKY) // flags |= IF_SKY; - if (shader->flags & SHADER_NOMIPMAPS) + if (parsestate->s->flags & SHADER_NOMIPMAPS) flags |= IF_NOMIPMAP; - if (shader->flags & SHADER_NOPICMIP) + if (parsestate->s->flags & SHADER_NOPICMIP) flags |= IF_NOPICMIP; flags |= IF_MIPCAP; + if (pass) + { + if (flags & IF_CLAMP) + pass->flags |= SHADER_PASS_CLAMP; + if (flags & IF_LINEAR) + { + pass->flags &= ~SHADER_PASS_NEAREST; + pass->flags |= SHADER_PASS_LINEAR; + } + else if (flags & IF_NEAREST) + { + pass->flags &= ~SHADER_PASS_LINEAR; + pass->flags |= SHADER_PASS_NEAREST; + } + } + return flags; } @@ -804,10 +817,10 @@ texid_t R_LoadColourmapImage(void) return R_LoadTexture("$colourmap", w, h, TF_RGBA32, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA|IF_CLAMP); } -static texid_t Shader_FindImage ( char *name, int flags ) +static texid_t Shader_FindImage (parsestate_t *parsestate, char *name, int flags ) { extern texid_t missing_texture_normal; - if (parsestate.parseflags & SPF_DOOM3) + if (parsestate->parseflags & SPF_DOOM3) { if (!Q_stricmp (name, "_default")) return r_whiteimage; /*fixme*/ @@ -841,8 +854,9 @@ static texid_t Shader_FindImage ( char *name, int flags ) /****************** shader keyword functions ************************/ -static void Shader_Cull ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_Cull (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; char *token; shader->flags &= ~(SHADER_CULL_FRONT|SHADER_CULL_BACK); @@ -858,24 +872,28 @@ static void Shader_Cull ( shader_t *shader, shaderpass_t *pass, char **ptr ) } } -static void Shader_NoMipMaps ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_NoMipMaps (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->flags |= (SHADER_NOMIPMAPS|SHADER_NOPICMIP); } -static void Shader_Affine ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_Affine (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->flags |= SBITS_AFFINE; } -static void Shader_NoPicMip ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_NoPicMip (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->flags |= SHADER_NOPICMIP; } -static void Shader_DeformVertexes ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_DeformVertexes (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; char *token; deformv_t *deformv; @@ -933,8 +951,9 @@ static void Shader_DeformVertexes ( shader_t *shader, shaderpass_t *pass, char * shader->numdeforms++; } -static void Shader_ClutterParms(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_ClutterParms(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; struct shader_clutter_s *clut; char *modelname; @@ -952,8 +971,9 @@ static void Shader_ClutterParms(shader_t *shader, shaderpass_t *pass, char **ptr shader->clutter = clut; } -static void Shader_SkyParms(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_SkyParms(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; skydome_t *skydome; // float skyheight; char *boxname; @@ -978,8 +998,9 @@ static void Shader_SkyParms(shader_t *shader, shaderpass_t *pass, char **ptr) shader->sort = SHADER_SORT_SKY; } -static void Shader_FogParms ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_FogParms (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; float div; vec3_t color, fcolor; @@ -1006,8 +1027,9 @@ static void Shader_FogParms ( shader_t *shader, shaderpass_t *pass, char **ptr ) shader->flags |= SHADER_NODLIGHT|SHADER_NOSHADOWS; } -static void Shader_SurfaceParm ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_SurfaceParm (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; char *token; token = Shader_ParseString ( ptr ); @@ -1045,8 +1067,9 @@ static void Shader_SurfaceParm ( shader_t *shader, shaderpass_t *pass, char **pt shader->flags |= SHADER_HASTOPBOTTOM; } -static void Shader_Sort ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_Sort (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; char *token; token = Shader_ParseString ( ptr ); @@ -1083,26 +1106,30 @@ static void Shader_Sort ( shader_t *shader, shaderpass_t *pass, char **ptr ) } } -static void Shader_Deferredlight ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_Deferredlight (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->sort = SHADER_SORT_DEFERREDLIGHT; } -static void Shader_Portal ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_Portal (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->sort = SHADER_SORT_PORTAL; } -static void Shader_PolygonOffset ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_PolygonOffset (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; /*the q3 defaults*/ shader->polyoffset.factor = -0.05; shader->polyoffset.unit = -25; shader->flags |= SHADER_POLYGONOFFSET; //some backends might be lazy and only allow simple values. } -static void Shader_EntityMergable ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shader_EntityMergable (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->flags |= SHADER_ENTITY_MERGABLE; } @@ -2110,109 +2137,117 @@ static void Shader_SLProgramName (shader_t *shader, shaderpass_t *pass, char **p } } -static void Shader_GLSLProgramName (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_GLSLProgramName (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; Shader_SLProgramName(shader,pass,ptr,QR_OPENGL); } -static void Shader_ProgramName (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_ProgramName (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; Shader_SLProgramName(shader,pass,ptr,qrenderer); } -static void Shader_HLSL9ProgramName (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_HLSL9ProgramName (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; Shader_SLProgramName(shader,pass,ptr,QR_DIRECT3D9); } -static void Shader_HLSL11ProgramName (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_HLSL11ProgramName (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; Shader_SLProgramName(shader,pass,ptr,QR_DIRECT3D11); } -static void Shader_ReflectCube(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_ReflectCube(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->reflectcube = Shader_FindImage(token, flags|IF_CUBEMAP); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_CUBEMAP); + ps->s->defaulttextures->reflectcube = Shader_FindImage(ps, token, flags); } -static void Shader_ReflectMask(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_ReflectMask(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->reflectmask = Shader_FindImage(token, flags); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); + ps->s->defaulttextures->reflectmask = Shader_FindImage(ps, token, flags); } -static void Shader_DiffuseMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DiffuseMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->base = Shader_FindImage(token, flags); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); + ps->s->defaulttextures->base = Shader_FindImage(ps, token, flags); - Q_strncpyz(shader->defaulttextures->mapname, token, sizeof(shader->defaulttextures->mapname)); + Q_strncpyz(ps->s->defaulttextures->mapname, token, sizeof(ps->s->defaulttextures->mapname)); } -static void Shader_SpecularMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_SpecularMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->specular = Shader_FindImage(token, flags); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); + ps->s->defaulttextures->specular = Shader_FindImage(ps, token, flags); } -static void Shader_NormalMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_NormalMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->bump = Shader_FindImage(token, flags|IF_TRYBUMP|IF_NOSRGB); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_TRYBUMP|IF_NOSRGB); + ps->s->defaulttextures->bump = Shader_FindImage(ps, token, flags); } -static void Shader_FullbrightMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_FullbrightMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->fullbright = Shader_FindImage(token, flags); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); + ps->s->defaulttextures->fullbright = Shader_FindImage(ps, token, flags); } -static void Shader_UpperMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_UpperMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->upperoverlay = Shader_FindImage(token, flags); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); + ps->s->defaulttextures->upperoverlay = Shader_FindImage(ps, token, flags); } -static void Shader_LowerMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_LowerMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->loweroverlay = Shader_FindImage(token, flags); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); + ps->s->defaulttextures->loweroverlay = Shader_FindImage(ps, token, flags); } -static void Shader_DisplacementMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DisplacementMap(parsestate_t *ps, char **ptr) { char *token = Shader_ParseString(ptr); - unsigned int flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->displacement = Shader_FindImage(token, flags|IF_NOSRGB); + unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB); + ps->s->defaulttextures->displacement = Shader_FindImage(ps, token, flags); } -static void Shaderpass_QF_Material(shader_t *shader, shaderpass_t *pass, char **ptr) -{ +static void Shaderpass_QF_Material(parsestate_t *ps, char **ptr) +{ //qf_material BASETEXTURE NORMALMAP SPECULARMAP unsigned int flags; char *progname = "defaultwall"; char *token; - char *hash = strchr(shader->name, '#'); + char *hash = strchr(ps->s->name, '#'); if (hash) { //pass the # postfixes from the shader name onto the generic glsl to use char newname[512]; Q_snprintfz(newname, sizeof(newname), "%s%s", progname, hash); - pass->prog = Shader_FindGeneric(newname, qrenderer); + ps->pass->prog = Shader_FindGeneric(newname, qrenderer); } else - pass->prog = Shader_FindGeneric(progname, qrenderer); + ps->pass->prog = Shader_FindGeneric(progname, qrenderer); token = Shader_ParseString(ptr); if (*token && strcmp(token, "-")) { - flags = Shader_SetImageFlags (shader, NULL, &token); + flags = Shader_SetImageFlags (ps, ps->pass, &token, 0); if (*token) - shader->defaulttextures->base = Shader_FindImage(token, flags); + ps->s->defaulttextures->base = Shader_FindImage(ps, token, flags); else { - token = shader->name; + token = ps->s->name; if (hash) *hash = 0; - shader->defaulttextures->base = Shader_FindImage(token, flags); + ps->s->defaulttextures->base = Shader_FindImage(ps, token, flags); if (hash) *hash = '#'; } @@ -2222,103 +2257,132 @@ static void Shaderpass_QF_Material(shader_t *shader, shaderpass_t *pass, char ** token = Shader_ParseString(ptr); if (*token && strcmp(token, "-")) { - flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->bump = Shader_FindImage(token, flags); + flags = Shader_SetImageFlags (ps, NULL, &token, IF_TRYBUMP|IF_NOSRGB); + ps->s->defaulttextures->bump = Shader_FindImage(ps, token, flags); } if (*token) token = Shader_ParseString(ptr); if (*token && strcmp(token, "-")) { - flags = Shader_SetImageFlags (shader, NULL, &token); - shader->defaulttextures->specular = Shader_FindImage(token, flags); + flags = Shader_SetImageFlags (ps, NULL, &token, 0); + ps->s->defaulttextures->specular = Shader_FindImage(ps, token, flags); } } -static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *tname); +static qboolean Shaderpass_MapGen (parsestate_t *ps, shaderpass_t *pass, char *tname); -static void Shader_Translucent(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_Translucent(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->flags |= SHADER_BLEND; } -static void Shader_PortalFBOScale(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_PortalFBOScale(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->portalfboscale = Shader_ParseFloat(shader, ptr, 0); shader->portalfboscale = max(shader->portalfboscale, 0); } -static void Shader_DP_Camera(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_Camera(parsestate_t *ps, char **ptr) { - shader->sort = SHADER_SORT_PORTAL; - parsestate.dpwatertype |= 4; + ps->s->sort = SHADER_SORT_PORTAL; + ps->dpwatertype |= 4; } -static void Shader_DP_Water(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_Water(parsestate_t *ps, char **ptr) { - parsestate.parseflags |= SPF_PROGRAMIFY; + shader_t *shader = ps->s; + ps->parseflags |= SPF_PROGRAMIFY; - parsestate.dpwatertype |= 3; - parsestate.reflectmin = Shader_ParseFloat(shader, ptr, 0); - parsestate.reflectmax = Shader_ParseFloat(shader, ptr, 0); - parsestate.refractfactor = Shader_ParseFloat(shader, ptr, 0); - parsestate.reflectfactor = Shader_ParseFloat(shader, ptr, 0); - Shader_ParseVector(shader, ptr, parsestate.refractcolour); - Shader_ParseVector(shader, ptr, parsestate.reflectcolour); - parsestate.wateralpha = Shader_ParseFloat(shader, ptr, 0); + ps->dpwatertype |= 3; + ps->reflectmin = Shader_ParseFloat(shader, ptr, 0); + ps->reflectmax = Shader_ParseFloat(shader, ptr, 0); + ps->refractfactor = Shader_ParseFloat(shader, ptr, 0); + ps->reflectfactor = Shader_ParseFloat(shader, ptr, 0); + Shader_ParseVector(shader, ptr, ps->refractcolour); + Shader_ParseVector(shader, ptr, ps->reflectcolour); + ps->wateralpha = Shader_ParseFloat(shader, ptr, 0); } -static void Shader_DP_Reflect(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_Reflect(parsestate_t *ps, char **ptr) { - parsestate.parseflags |= SPF_PROGRAMIFY; + ps->parseflags |= SPF_PROGRAMIFY; - parsestate.dpwatertype |= 1; - parsestate.reflectmin = 1; - parsestate.reflectmax = 1; - parsestate.reflectfactor = Shader_ParseFloat(shader, ptr, 0); - Shader_ParseVector(shader, ptr, parsestate.reflectcolour); + ps->dpwatertype |= 1; + ps->reflectmin = 1; + ps->reflectmax = 1; + ps->reflectfactor = Shader_ParseFloat(ps->s, ptr, 0); + Shader_ParseVector(ps->s, ptr, ps->reflectcolour); } -static void Shader_DP_Refract(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_Refract(parsestate_t *ps, char **ptr) { - parsestate.parseflags |= SPF_PROGRAMIFY; + ps->parseflags |= SPF_PROGRAMIFY; - parsestate.dpwatertype |= 2; - parsestate.refractfactor = Shader_ParseFloat(shader, ptr, 0); - Shader_ParseVector(shader, ptr, parsestate.refractcolour); + ps->dpwatertype |= 2; + ps->refractfactor = Shader_ParseFloat(ps->s, ptr, 0); + Shader_ParseVector(ps->s, ptr, ps->refractcolour); } -static void Shader_DP_OffsetMapping(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_OffsetMapping(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; char *token = Shader_ParseString(ptr); if (!strcmp(token, "disable") || !strcmp(token, "none") || !strcmp(token, "off")) - parsestate.offsetmappingmode = 0; + ps->offsetmappingmode = 0; else if (!strcmp(token, "default") || !strcmp(token, "normal")) - parsestate.offsetmappingmode = -1; + ps->offsetmappingmode = -1; else if (!strcmp(token, "linear")) - parsestate.offsetmappingmode = 1; + ps->offsetmappingmode = 1; else if (!strcmp(token, "relief")) - parsestate.offsetmappingmode = 2; - parsestate.offsetmappingscale = Shader_ParseFloat(shader, ptr, 1); + ps->offsetmappingmode = 2; + ps->offsetmappingscale = Shader_ParseFloat(shader, ptr, 1); token = Shader_ParseString(ptr); if (!strcmp(token, "bias")) - parsestate.offsetmappingbias = Shader_ParseFloat(shader, ptr, 0.5); + ps->offsetmappingbias = Shader_ParseFloat(shader, ptr, 0.5); else if (!strcmp(token, "match")) - parsestate.offsetmappingbias = 1.0 - Shader_ParseFloat(shader, ptr, 0.5); + ps->offsetmappingbias = 1.0 - Shader_ParseFloat(shader, ptr, 0.5); else if (!strcmp(token, "match8")) - parsestate.offsetmappingbias = 1.0 - (1.0/255) * Shader_ParseFloat(shader, ptr, 128); + ps->offsetmappingbias = 1.0 - (1.0/255) * Shader_ParseFloat(shader, ptr, 128); else if (!strcmp(token, "match16")) - parsestate.offsetmappingbias = 1.0 - (1.0/65535) * Shader_ParseFloat(shader, ptr, 32768); + ps->offsetmappingbias = 1.0 - (1.0/65535) * Shader_ParseFloat(shader, ptr, 32768); } -static void Shader_DP_GlossScale(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_GlossScale(parsestate_t *ps, char **ptr) { - parsestate.specularvalscale = Shader_ParseFloat(shader, ptr, 0); + ps->specularvalscale = Shader_ParseFloat(ps->s, ptr, 0); } -static void Shader_DP_GlossExponent(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_DP_GlossExponent(parsestate_t *ps, char **ptr) { - parsestate.specularexpscale = Shader_ParseFloat(shader, ptr, 0); + ps->specularexpscale = Shader_ParseFloat(ps->s, ptr, 0); } - -static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shader_FactorBase(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shader->factors[MATERIAL_FACTOR_BASE][0] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_BASE][1] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_BASE][2] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_BASE][3] = Shader_ParseFloat(shader, ptr, 1); +} +static void Shader_FactorSpec(parsestate_t *ps, char **ptr) +{ + shader_t *shader = ps->s; + shader->factors[MATERIAL_FACTOR_SPEC][0] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_SPEC][1] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_SPEC][2] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_SPEC][3] = Shader_ParseFloat(shader, ptr, 1); +} +static void Shader_FactorEmit(parsestate_t *ps, char **ptr) +{ + shader_t *shader = ps->s; + shader->factors[MATERIAL_FACTOR_EMIT][0] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_EMIT][1] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_EMIT][2] = Shader_ParseFloat(shader, ptr, 1); + shader->factors[MATERIAL_FACTOR_EMIT][3] = Shader_ParseFloat(shader, ptr, 1); +} + +static void Shader_BEMode(parsestate_t *ps, char **ptr) +{ + shader_t *shader = ps->s; char subname[1024]; int mode; char tokencopy[1024]; @@ -2440,9 +2504,9 @@ static shaderkey_t shaderkeys[] = {"displacementmap", Shader_DisplacementMap, "fte"}, {"portalfboscale", Shader_PortalFBOScale, "fte"}, //portal/mirror/refraction/reflection FBOs are resized by this scale - {"basefactor", NULL, "fte"}, //material scalers for glsl - {"specularfactor", NULL, "fte"}, //material scalers for glsl - {"fullbrightfactor", NULL, "fte"}, //material scalers for glsl + {"basefactor", Shader_FactorBase, "fte"}, //material scalers for glsl + {"specularfactor", Shader_FactorSpec, "fte"}, //material scalers for glsl + {"fullbrightfactor", Shader_FactorEmit, "fte"}, //material scalers for glsl //TODO: PBR textures... // {"albedomap", Shader_DiffuseMap, "fte"}, //rgb(a) @@ -2530,8 +2594,9 @@ static struct // =============================================================== -static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *tname) +static qboolean Shaderpass_MapGen (parsestate_t *ps, shaderpass_t *pass, char *tname) { + shader_t *shader = ps->s; int tcgen = TC_GEN_BASE; if (!Q_stricmp (tname, "$lightmap")) { @@ -2660,7 +2725,7 @@ static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *t return true; } -shaderpass_t *Shaderpass_DefineMap(shader_t *shader, shaderpass_t *pass) +shaderpass_t *Shaderpass_DefineMap(parsestate_t *ps, shaderpass_t *pass) { //'map foo' works a bit differently when there's a program in the same pass. //instead of corrupting the previous one, it collects multiple maps so that {prog foo;map t0;map t1; map t2; blendfunc add} can work as expected @@ -2670,19 +2735,19 @@ shaderpass_t *Shaderpass_DefineMap(shader_t *shader, shaderpass_t *pass) pass->numMergedPasses++; else { //FIXME: bounds check! - if (shader->numpasses == SHADER_PASS_MAX || shader->numpasses == SHADER_TMU_MAX) + if (ps->s->numpasses == SHADER_PASS_MAX || ps->s->numpasses == SHADER_TMU_MAX) { - Con_DPrintf (CON_WARNING "Shader %s has too many texture passes.\n", shader->name); - parsestate.droppass = true; + Con_DPrintf (CON_WARNING "Shader %s has too many texture passes.\n", ps->s->name); + ps->droppass = true; } // else if (shader->numpasses == be_maxpasses) -// parsestate.droppass = true; +// ps->droppass = true; else { pass->numMergedPasses++; - shader->numpasses++; + ps->s->numpasses++; } - pass = shader->passes+shader->numpasses-1; + pass = ps->s->passes+ps->s->numpasses-1; memset(pass, 0, sizeof(*pass)); } } @@ -2691,19 +2756,21 @@ shaderpass_t *Shaderpass_DefineMap(shader_t *shader, shaderpass_t *pass) return pass; } -static void Shaderpass_Map (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_Map (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; int flags; char *token; - pass = Shaderpass_DefineMap(shader, pass); + pass = Shaderpass_DefineMap(ps, pass); pass->anim_frames[0] = r_nulltex; token = Shader_ParseString (ptr); - flags = Shader_SetImageFlags (shader, pass, &token); - if (!Shaderpass_MapGen(shader, pass, token)) + flags = Shader_SetImageFlags (ps, pass, &token, 0); + if (!Shaderpass_MapGen(ps, pass, token)) { switch((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) { @@ -2722,17 +2789,19 @@ static void Shaderpass_Map (shader_t *shader, shaderpass_t *pass, char **ptr) pass->tcgen = TC_GEN_BASE; if (!*shader->defaulttextures->mapname && *token != '$' && pass->tcgen == TC_GEN_BASE) Q_strncpyz(shader->defaulttextures->mapname, token, sizeof(shader->defaulttextures->mapname)); - pass->anim_frames[0] = Shader_FindImage (token, flags); + pass->anim_frames[0] = Shader_FindImage (ps, token, flags); } } -static void Shaderpass_AnimMap_Flag (shader_t *shader, shaderpass_t *pass, char **ptr, unsigned int flags) +static void Shaderpass_AnimMap_Flag (parsestate_t *ps, char **ptr, unsigned int flags) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; char *token; texid_t image; qboolean isdiffuse = false; - flags |= Shader_SetImageFlags (shader, pass, NULL); + flags |= Shader_SetImageFlags (ps, ps->pass, NULL, 0); if (pass->tcgen == TC_GEN_UNSPECIFIED) pass->tcgen = TC_GEN_BASE; @@ -2757,7 +2826,7 @@ static void Shaderpass_AnimMap_Flag (shader_t *shader, shaderpass_t *pass, char if (pass->anim_numframes < SHADER_MAX_ANIMFRAMES) { - image = Shader_FindImage (token, flags); + image = Shader_FindImage (ps, token, flags); if (isdiffuse) { @@ -2782,28 +2851,29 @@ static void Shaderpass_AnimMap_Flag (shader_t *shader, shaderpass_t *pass, char } } } -static void Shaderpass_AnimMap (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_AnimMap (parsestate_t *ps, char **ptr) { - Shaderpass_AnimMap_Flag(shader, pass, ptr, 0); + Shaderpass_AnimMap_Flag(ps, ptr, 0); } -static void Shaderpass_QF_AnimClampMap (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_QF_AnimClampMap (parsestate_t *ps, char **ptr) { - Shaderpass_AnimMap_Flag(shader, pass, ptr, IF_CLAMP); + Shaderpass_AnimMap_Flag(ps, ptr, IF_CLAMP); } -static void Shaderpass_ClampMap (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_ClampMap (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; int flags; char *token; token = Shader_ParseString (ptr); - flags = Shader_SetImageFlags (shader, pass, &token); - if (!Shaderpass_MapGen(shader, pass, token)) + flags = Shader_SetImageFlags (ps, pass, &token, IF_CLAMP); + if (!Shaderpass_MapGen(ps, pass, token)) { if (pass->tcgen == TC_GEN_UNSPECIFIED) pass->tcgen = TC_GEN_BASE; - pass->anim_frames[0] = Shader_FindImage (token, flags | IF_CLAMP); + pass->anim_frames[0] = Shader_FindImage (ps, token, flags); switch((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) { @@ -2824,14 +2894,15 @@ static void Shaderpass_ClampMap (shader_t *shader, shaderpass_t *pass, char **pt pass->anim_frames[0] = r_nulltex; else pass->anim_frames[0] = missing_texture; - Con_DPrintf (CON_WARNING "Shader %s has a stage with no image: %s.\n", shader->name, token); + Con_DPrintf (CON_WARNING "Shader %s has a stage with no image: %s.\n", ps->s->name, token); } } - pass->flags |= SHADER_PASS_CLAMP; } -static void Shaderpass_VideoMap (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_VideoMap (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; char *token = Shader_ParseSensString (ptr); #ifndef HAVE_MEDIA_DECODER @@ -2862,35 +2933,35 @@ static void Shaderpass_VideoMap (shader_t *shader, shaderpass_t *pass, char **pt #endif } -static void Shaderpass_RTCW_Map_16bit (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RTCW_Map_16bit (parsestate_t *ps, char **ptr) { if (!gl_load24bit.ival) //urm, not sure if suitable choice of cvar - Shaderpass_Map(shader, pass, ptr); + Shaderpass_Map(ps, ptr); } -static void Shaderpass_RTCW_Map_32bit (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RTCW_Map_32bit (parsestate_t *ps, char **ptr) { if (gl_load24bit.ival) - Shaderpass_Map(shader, pass, ptr); + Shaderpass_Map(ps, ptr); } -static void Shaderpass_RTCW_Map_s3tc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RTCW_Map_s3tc (parsestate_t *ps, char **ptr) { if (sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival) - Shaderpass_Map(shader, pass, ptr); + Shaderpass_Map(ps, ptr); } -static void Shaderpass_RTCW_Map_nos3tc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RTCW_Map_nos3tc (parsestate_t *ps, char **ptr) { if (!(sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival)) - Shaderpass_Map(shader, pass, ptr); + Shaderpass_Map(ps, ptr); } -static void Shaderpass_RTCW_AnimMap_s3tc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RTCW_AnimMap_s3tc (parsestate_t *ps, char **ptr) { if ((sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival)) - Shaderpass_AnimMap(shader, pass, ptr); + Shaderpass_AnimMap(ps, ptr); } -static void Shaderpass_RTCW_AnimMap_nos3tc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RTCW_AnimMap_nos3tc (parsestate_t *ps, char **ptr) { if (!(sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival)) - Shaderpass_AnimMap(shader, pass, ptr); + Shaderpass_AnimMap(ps, ptr); } static void Shaderpass_SLProgramName (shader_t *shader, shaderpass_t *pass, char **ptr, int qrtype) @@ -2935,13 +3006,17 @@ static void Shaderpass_SLProgramName (shader_t *shader, shaderpass_t *pass, char else pass->prog = Shader_FindGeneric(Shader_ParseExactString(ptr), qrtype); } -static void Shaderpass_ProgramName (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_ProgramName (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; Shaderpass_SLProgramName(shader,pass,ptr,qrenderer); } -static void Shaderpass_RGBGen (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_RGBGen (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; char *token; token = Shader_ParseString (ptr); @@ -2992,8 +3067,10 @@ static void Shaderpass_RGBGen (shader_t *shader, shaderpass_t *pass, char **ptr) pass->rgbgen = RGB_GEN_BOTTOMCOLOR; } -static void Shaderpass_AlphaGen (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_AlphaGen (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; char *token; token = Shader_ParseString(ptr); @@ -3030,8 +3107,10 @@ static void Shaderpass_AlphaGen (shader_t *shader, shaderpass_t *pass, char **pt pass->alphagen_func.args[0] = fabs(Shader_ParseFloat(shader, ptr, 0)); } } -static void Shaderpass_AlphaShift (shader_t *shader, shaderpass_t *pass, char **ptr) //for alienarena +static void Shaderpass_AlphaShift (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; float speed; float min, max; pass->alphagen = ALPHA_GEN_WAVE; @@ -3113,8 +3192,9 @@ static int Shader_BlendFactor(char *name, qboolean dstnotsrc) return factor; } -static void Shaderpass_BlendFunc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_BlendFunc (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; char *token; //reset to defaults @@ -3149,8 +3229,9 @@ static void Shaderpass_BlendFunc (shader_t *shader, shaderpass_t *pass, char **p } } -static void Shaderpass_AlphaFunc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_AlphaFunc (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; char *token; pass->shaderbits &= ~SBITS_ATEST_BITS; @@ -3170,8 +3251,9 @@ static void Shaderpass_AlphaFunc (shader_t *shader, shaderpass_t *pass, char **p } } -static void Shaderpass_DepthFunc (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_DepthFunc (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; char *token; pass->shaderbits &= ~(SBITS_DEPTHFUNC_BITS); @@ -3197,25 +3279,32 @@ static void Shaderpass_DepthFunc (shader_t *shader, shaderpass_t *pass, char **p Con_DPrintf("Invalid depth func %s\n", token); } -static void Shaderpass_DepthWrite (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_DepthWrite (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; shader->flags |= SHADER_DEPTHWRITE; pass->shaderbits |= SBITS_MISC_DEPTHWRITE; } -static void Shaderpass_NoDepthTest (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_NoDepthTest (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; shader->flags |= SHADER_DEPTHWRITE; pass->shaderbits |= SBITS_MISC_NODEPTHTEST; } -static void Shaderpass_NoDepth (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_NoDepth (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; shader->flags |= SHADER_DEPTHWRITE; } -static void Shaderpass_TcMod (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_TcMod (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; int i; tcmod_t *tcmod; char *token; @@ -3290,8 +3379,10 @@ static void Shaderpass_TcMod (shader_t *shader, shaderpass_t *pass, char **ptr) pass->numtcmods++; } -static void Shaderpass_Scale ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shaderpass_Scale (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; //seperate x and y char *token; tcmod_t *tcmod; @@ -3328,8 +3419,10 @@ static void Shaderpass_Scale ( shader_t *shader, shaderpass_t *pass, char **ptr pass->numtcmods++; } -static void Shaderpass_Scroll (shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_Scroll (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; //seperate x and y char *token; tcmod_t *tcmod; @@ -3364,8 +3457,10 @@ static void Shaderpass_Scroll (shader_t *shader, shaderpass_t *pass, char **ptr) } -static void Shaderpass_TcGen ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shaderpass_TcGen (parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; char *token; token = Shader_ParseString ( ptr ); @@ -3392,76 +3487,96 @@ static void Shaderpass_TcGen ( shader_t *shader, shaderpass_t *pass, char **ptr pass->tcgen = TC_GEN_SKYBOX; } } -static void Shaderpass_EnvMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) //for alienarena +static void Shaderpass_EnvMap (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->tcgen = TC_GEN_ENVIRONMENT; } -static void Shaderpass_Detail ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shaderpass_Detail (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->flags |= SHADER_PASS_DETAIL; } -static void Shaderpass_AlphaMask ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shaderpass_AlphaMask (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->shaderbits &= ~SBITS_ATEST_BITS; pass->shaderbits |= SBITS_ATEST_GE128; } -static void Shaderpass_NoLightMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) +static void Shaderpass_NoLightMap (parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->rgbgen = RGB_GEN_IDENTITY; } -static void Shaderpass_Red(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_Red(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; pass->rgbgen = RGB_GEN_CONST; pass->rgbgen_func.args[0] = Shader_ParseFloat(shader, ptr, 0); } -static void Shaderpass_Green(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_Green(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; pass->rgbgen = RGB_GEN_CONST; pass->rgbgen_func.args[1] = Shader_ParseFloat(shader, ptr, 0); } -static void Shaderpass_Blue(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_Blue(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; pass->rgbgen = RGB_GEN_CONST; pass->rgbgen_func.args[2] = Shader_ParseFloat(shader, ptr, 0); } -static void Shaderpass_Alpha(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_Alpha(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; pass->alphagen = ALPHA_GEN_CONST; pass->alphagen_func.args[0] = Shader_ParseFloat(shader, ptr, 0); } -static void Shaderpass_MaskColor(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_MaskColor(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->shaderbits |= SBITS_MASK_RED|SBITS_MASK_GREEN|SBITS_MASK_BLUE; } -static void Shaderpass_MaskRed(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_MaskRed(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->shaderbits |= SBITS_MASK_RED; } -static void Shaderpass_MaskGreen(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_MaskGreen(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->shaderbits |= SBITS_MASK_GREEN; } -static void Shaderpass_MaskBlue(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_MaskBlue(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->shaderbits |= SBITS_MASK_BLUE; } -static void Shaderpass_MaskAlpha(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_MaskAlpha(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; pass->shaderbits |= SBITS_MASK_ALPHA; } -static void Shaderpass_AlphaTest(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_AlphaTest(parsestate_t *ps, char **ptr) { + shader_t *shader = ps->s; + shaderpass_t *pass = ps->pass; if (Shader_ParseFloat(shader, ptr, 0) == 0.5) pass->shaderbits |= SBITS_ATEST_GE128; else Con_Printf("unsupported alphatest value\n"); } -static void Shaderpass_TexGen(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_TexGen(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; char *token = Shader_ParseString(ptr); if (!strcmp(token, "normal")) pass->tcgen = TC_GEN_NORMAL; @@ -3481,14 +3596,15 @@ static void Shaderpass_TexGen(shader_t *shader, shaderpass_t *pass, char **ptr) Con_Printf("texgen token not understood\n"); } } -static void Shaderpass_CubeMap(shader_t *shader, shaderpass_t *pass, char **ptr) +static void Shaderpass_CubeMap(parsestate_t *ps, char **ptr) { + shaderpass_t *pass = ps->pass; char *token = Shader_ParseString(ptr); if (pass->tcgen == TC_GEN_UNSPECIFIED) pass->tcgen = TC_GEN_SKYBOX; pass->texgen = T_GEN_CUBEMAP; - pass->anim_frames[0] = Shader_FindImage(token, IF_CUBEMAP); + pass->anim_frames[0] = Shader_FindImage(ps, token, IF_CUBEMAP); if (!TEXVALID(pass->anim_frames[0])) { @@ -4045,8 +4161,9 @@ void Shader_SetBlendmode (shaderpass_t *pass, shaderpass_t *lastpass) pass->blendmode = lightmapoverbright?PBM_OVERBRIGHT:PBM_MODULATE; } -void Shader_FixupProgPasses(shader_t *shader, shaderpass_t *pass) +void Shader_FixupProgPasses(parsestate_t *ps, shaderpass_t *pass) { + shader_t *shader = ps->s; int i; int maxpasses = SHADER_PASS_MAX - (pass-shader->passes); struct @@ -4101,7 +4218,7 @@ void Shader_FixupProgPasses(shader_t *shader, shaderpass_t *pass) { if (pass->numMergedPasses >= maxpasses) { //panic... - parsestate.droppass = true; + ps->droppass = true; break; } pass[pass->numMergedPasses].flags |= SHADER_PASS_NOCOLORARRAY; @@ -4251,14 +4368,14 @@ void Shader_Readpass (parsestate_t *ps) if ( shader->numpasses >= SHADER_PASS_MAX ) { - parsestate.droppass = true; + ps->droppass = true; shader = &dummy; shader->numpasses = 1; pass = shader->passes; } else { - parsestate.droppass = false; + ps->droppass = false; pass = &shader->passes[shader->numpasses++]; } @@ -4309,7 +4426,7 @@ void Shader_Readpass (parsestate_t *ps) if (pass->tcgen == TC_GEN_UNSPECIFIED) pass->tcgen = TC_GEN_BASE; - if (!parsestate.droppass) + if (!ps->droppass) { switch(pass->stagetype) { @@ -4322,19 +4439,19 @@ void Shader_Readpass (parsestate_t *ps) case ST_BUMPMAP: if (pass->texgen == T_GEN_SINGLEMAP) shader->defaulttextures->bump = pass->anim_frames[0]; - parsestate.droppass = true; //fixme: scrolling etc may be important. but we're not doom3. + ps->droppass = true; //fixme: scrolling etc may be important. but we're not doom3. break; case ST_SPECULARMAP: if (pass->texgen == T_GEN_SINGLEMAP) shader->defaulttextures->specular = pass->anim_frames[0]; - parsestate.droppass = true; //fixme: scrolling etc may be important. but we're not doom3. + ps->droppass = true; //fixme: scrolling etc may be important. but we're not doom3. break; } } // check some things - if (!parsestate.droppass) + if (!ps->droppass) if ((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO)) { pass->shaderbits |= SBITS_MISC_DEPTHWRITE; @@ -4378,10 +4495,10 @@ void Shader_Readpass (parsestate_t *ps) */ //if this pass specified a program, make sure it has all the textures that the program requires - if (!parsestate.droppass && pass->prog) - Shader_FixupProgPasses(shader, pass); + if (!ps->droppass && pass->prog) + Shader_FixupProgPasses(ps, pass); - if (parsestate.droppass) + if (ps->droppass) { while (pass->numMergedPasses > 0) { @@ -4424,7 +4541,7 @@ static qboolean Shader_Parsetok(parsestate_t *ps, shaderkey_t *keys, char *token if (!prefix || (prefix && key->prefix && !Q_strncasecmp(prefix, key->prefix, strlen(key->prefix)))) { if (key->func) - key->func ( ps->s, ps->pass, &ps->ptr ); + key->func ( ps, &ps->ptr ); return (ps->ptr && *ps->ptr == '}' ); } @@ -4563,8 +4680,9 @@ const char *Shader_AlphaMaskProgArgs(shader_t *s) return ""; } -void Shader_Programify (shader_t *s) +void Shader_Programify (parsestate_t *ps) { + shader_t *s = ps->s; unsigned int reflectrefract = 0; const char *prog = NULL; const char *mask; @@ -4610,9 +4728,9 @@ void Shader_Programify (shader_t *s) return;*/ } - if (parsestate.forcedshader) - prog = parsestate.forcedshader; - else if (parsestate.dpwatertype) + if (ps->forcedshader) + prog = ps->forcedshader; + else if (ps->dpwatertype) { prog = va("altwater%s#USEMODS#FRESNEL_EXP=2.0" //variable parts @@ -4622,22 +4740,22 @@ void Shader_Programify (shader_t *s) "#FRESNEL_MIN=%g#FRESNEL_RANGE=%g" "#ALPHA=%g", //those args - (parsestate.dpwatertype&1)?"#REFLECT":"", - parsestate.refractfactor*0.01, parsestate.reflectfactor*0.01, - parsestate.refractcolour[0],parsestate.refractcolour[1],parsestate.refractcolour[2], - parsestate.reflectcolour[0],parsestate.reflectcolour[1],parsestate.reflectcolour[2], - parsestate.reflectmin, parsestate.reflectmax-parsestate.reflectmin, - parsestate.wateralpha + (ps->dpwatertype&1)?"#REFLECT":"", + ps->refractfactor*0.01, ps->reflectfactor*0.01, + ps->refractcolour[0],ps->refractcolour[1],ps->refractcolour[2], + ps->reflectcolour[0],ps->reflectcolour[1],ps->reflectcolour[2], + ps->reflectmin, ps->reflectmax-ps->reflectmin, + ps->wateralpha ); //clear out blending and force regular depth. s->passes[0].shaderbits &= ~(SBITS_BLEND_BITS|SBITS_MISC_NODEPTHTEST|SBITS_DEPTHFUNC_BITS); s->passes[0].shaderbits |= SBITS_MISC_DEPTHWRITE; - if (parsestate.dpwatertype & 1) + if (ps->dpwatertype & 1) reflectrefract |= SHADER_HASREFLECT; - if (parsestate.dpwatertype & 2) + if (ps->dpwatertype & 2) reflectrefract |= SHADER_HASREFRACT; - if (parsestate.dpwatertype & 4) + if (ps->dpwatertype & 4) { reflectrefract |= SHADER_HASREFRACT|SHADER_HASPORTAL; //doubles up as a 'camera' if (s->sort == SHADER_SORT_PORTAL) @@ -4679,26 +4797,26 @@ void Shader_Programify (shader_t *s) } args[0] = 0; - if (parsestate.specularvalscale != 1) - Q_strncatz(args, va("#specmul=%g", parsestate.specularvalscale), sizeof(args)); - if (parsestate.specularexpscale != 1) - Q_strncatz(args, va("#specexp=%g", parsestate.specularexpscale), sizeof(args)); -/* switch(parsestate.offsetmappingmode) + if (ps->specularvalscale != 1) + Q_strncatz(args, va("#specmul=%g", ps->specularvalscale), sizeof(args)); + if (ps->specularexpscale != 1) + Q_strncatz(args, va("#specexp=%g", ps->specularexpscale), sizeof(args)); +/* switch(ps->offsetmappingmode) { case 0: //force off. - Q_strncatz(args, va("#NOOFFSETMAPPING", parsestate.specularexpscale), sizeof(args)); + Q_strncatz(args, va("#NOOFFSETMAPPING", ps->specularexpscale), sizeof(args)); break; case 1: //force linear - Q_strncatz(args, va("#NORELIEFMAPPING", parsestate.specularexpscale), sizeof(args)); + Q_strncatz(args, va("#NORELIEFMAPPING", ps->specularexpscale), sizeof(args)); break; case 2: //force relief - Q_strncatz(args, va("#RELIEFMAPPING", parsestate.specularexpscale), sizeof(args)); + Q_strncatz(args, va("#RELIEFMAPPING", ps->specularexpscale), sizeof(args)); break; } - if (parsestate.offsetmappingscale != 1) - Q_strncatz(args, va("#OFFSETMAPPING_SCALE=%g", parsestate.offsetmappingscale), sizeof(args)); - if (parsestate.offsetmappingbias != 0) - Q_strncatz(args, va("#OFFSETMAPPING_BIAS=%g", parsestate.offsetmappingbias), sizeof(args)); + if (ps->offsetmappingscale != 1) + Q_strncatz(args, va("#OFFSETMAPPING_SCALE=%g", ps->offsetmappingscale), sizeof(args)); + if (ps->offsetmappingbias != 0) + Q_strncatz(args, va("#OFFSETMAPPING_BIAS=%g", ps->offsetmappingbias), sizeof(args)); */ mask = strchr(s->name, '#'); if (mask) @@ -4737,8 +4855,9 @@ void Shader_Programify (shader_t *s) } } -void Shader_Finish (shader_t *s) +void Shader_Finish (parsestate_t *ps) { + shader_t *s = ps->s; int i; shaderpass_t *pass; @@ -5148,7 +5267,7 @@ done:; } } - if (!s->prog && sh_config.progs_supported && (r_forceprogramify.ival || (parsestate.parseflags & SPF_PROGRAMIFY))) + if (!s->prog && sh_config.progs_supported && (r_forceprogramify.ival || (ps->parseflags & SPF_PROGRAMIFY))) { if (r_forceprogramify.ival >= 2) { @@ -5158,7 +5277,7 @@ done:; s->passes[0].shaderbits = (s->passes[0].shaderbits & ~SBITS_ATEST_BITS) | SBITS_ATEST_GE128; s->passes[0].shaderbits &= ~SBITS_DEPTHFUNC_BITS; //DP ignores this too. } - Shader_Programify(s); + Shader_Programify(ps); } if (s->prog) @@ -5345,7 +5464,6 @@ void Shader_UpdateRegistration (void) s->defaulttextures.loweroverlay = Shader_FindImage (va("%s_pants.tga", shader_diffusemapname), 0); //stupid yanks... } */ -void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args); void QDECL R_BuildDefaultTexnums(texnums_t *src, shader_t *shader, unsigned int imageflags) { char *h; @@ -5791,7 +5909,7 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons } } -void Shader_DefaultScript(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultScript(parsestate_t *ps, const char *shortname, const void *args) { const char *f = args; if (!args) @@ -5801,14 +5919,15 @@ void Shader_DefaultScript(const char *shortname, shader_t *s, const void *args) if (*f == '{') { f++; - Shader_ReadShader(s, (void*)f, NULL); + Shader_ReadShader(ps, (void*)f, NULL); } } -void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPLM(parsestate_t *ps, const char *shortname, const void *args) { + shader_t *s = ps->s; char *builtin = NULL; - if (Shader_ParseShader("defaultwall", s)) + if (Shader_ParseShader(ps, "defaultwall")) return; if (!builtin && r_softwarebanding && (qrenderer == QR_OPENGL || qrenderer == QR_VULKAN) && sh_config.progs_supported) @@ -5919,15 +6038,15 @@ void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args) "}\n" ); - Shader_DefaultScript(shortname, s, builtin); + Shader_DefaultScript(ps, shortname, builtin); if (r_lightprepass) s->flags |= SHADER_HASNORMALMAP; } -void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultCinematic(parsestate_t *ps, const char *shortname, const void *args) { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, va( "{\n" "program default2d\n" @@ -5941,10 +6060,11 @@ void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *arg } /*shortname should begin with 'skybox_'*/ -void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultSkybox(parsestate_t *ps, const char *shortname, const void *args) { + shader_t *s = ps->s; int i; - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, va( "{\n" "sort sky\n" @@ -5966,8 +6086,9 @@ void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args) } } -char *Shader_DefaultBSPWater(shader_t *s, const char *shortname, char *buffer, size_t buffersize) +char *Shader_DefaultBSPWater(parsestate_t *ps, const char *shortname, char *buffer, size_t buffersize) { + shader_t *s = ps->s; int wstyle; int type; float alpha; @@ -6156,30 +6277,30 @@ char *Shader_DefaultBSPWater(shader_t *s, const char *shortname, char *buffer, s } } -void Shader_DefaultWaterShader(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultWaterShader(parsestate_t *ps, const char *shortname, const void *args) { char tmpbuffer[2048]; - Shader_DefaultScript(shortname, s, Shader_DefaultBSPWater(s, shortname, tmpbuffer, sizeof(tmpbuffer))); + Shader_DefaultScript(ps, shortname, Shader_DefaultBSPWater(ps, shortname, tmpbuffer, sizeof(tmpbuffer))); } -void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPQ2(parsestate_t *ps, const char *shortname, const void *args) { if (!strncmp(shortname, "sky/", 4)) { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "surfaceparm nodlight\n" "skyparms - - -\n" "}\n" ); } - else if (Shader_FloatArgument(s, "WARP"))//!strncmp(shortname, "warp/", 5) || !strncmp(shortname, "warp33/", 7) || !strncmp(shortname, "warp66/", 7)) + else if (Shader_FloatArgument(ps->s, "WARP"))//!strncmp(shortname, "warp/", 5) || !strncmp(shortname, "warp33/", 7) || !strncmp(shortname, "warp66/", 7)) { char tmpbuffer[2048]; - Shader_DefaultScript(shortname, s, Shader_DefaultBSPWater(s, shortname, tmpbuffer, sizeof(tmpbuffer))); + Shader_DefaultScript(ps, shortname, Shader_DefaultBSPWater(ps, shortname, tmpbuffer, sizeof(tmpbuffer))); } - else if (Shader_FloatArgument(s, "ALPHA"))// !strncmp(shortname, "trans/", 6)) + else if (Shader_FloatArgument(ps->s, "ALPHA"))// !strncmp(shortname, "trans/", 6)) { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "{\n" "map $diffuse\n" @@ -6190,10 +6311,10 @@ void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args) ); } else - Shader_DefaultBSPLM(shortname, s, args); + Shader_DefaultBSPLM(ps, shortname, args); } -void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPQ1(parsestate_t *ps, const char *shortname, const void *args) { char *builtin = NULL; char tmpbuffer[2048]; @@ -6236,7 +6357,7 @@ void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) if (!builtin && (*shortname == '*' || *shortname == '!')) { - builtin = Shader_DefaultBSPWater(s, shortname, tmpbuffer, sizeof(tmpbuffer)); + builtin = Shader_DefaultBSPWater(ps, shortname, tmpbuffer, sizeof(tmpbuffer)); } if (!builtin && !strncmp(shortname, "sky", 3)) { @@ -6355,16 +6476,16 @@ void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) } if (builtin) - Shader_DefaultScript(shortname, s, builtin); + Shader_DefaultScript(ps, shortname, builtin); else - Shader_DefaultBSPLM(shortname, s, args); + Shader_DefaultBSPLM(ps, shortname, args); } -void Shader_DefaultBSPVertex(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPVertex(parsestate_t *ps, const char *shortname, const void *args) { char *builtin = NULL; - if (Shader_ParseShader("defaultvertexlit", s)) + if (Shader_ParseShader(ps, "defaultvertexlit")) return; if (!builtin) @@ -6381,12 +6502,13 @@ void Shader_DefaultBSPVertex(const char *shortname, shader_t *s, const void *arg ); } - Shader_DefaultScript(shortname, s, builtin); + Shader_DefaultScript(ps, shortname, builtin); } -void Shader_DefaultBSPFlare(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPFlare(parsestate_t *ps, const char *shortname, const void *args) { + shader_t *s = ps->s; shaderpass_t *pass; - if (Shader_ParseShader("defaultflare", s)) + if (Shader_ParseShader(ps, "defaultflare")) return; pass = &s->passes[0]; @@ -6413,14 +6535,14 @@ void Shader_DefaultBSPFlare(const char *shortname, shader_t *s, const void *args s->flags |= SHADER_NODRAW; } -void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultSkin(parsestate_t *ps, const char *shortname, const void *args) { - if (Shader_ParseShader("defaultskin", s)) + if (Shader_ParseShader(ps, "defaultskin")) return; if (r_softwarebanding && qrenderer == QR_OPENGL && sh_config.progs_supported) { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "program defaultskin#EIGHTBIT\n" "affine\n" @@ -6433,7 +6555,7 @@ void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args) } if (r_lightprepass) { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "{\n" "fte_program lpp_wall\n" @@ -6456,7 +6578,7 @@ void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args) if (r_tessellation.ival && sh_config.progs_supported) { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "program defaultskin#TESS\n" "}\n" @@ -6464,7 +6586,7 @@ void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args) return; } - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "if $lpp\n" "program lpp_skin\n" @@ -6495,12 +6617,12 @@ void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args) "}\n" ); } -void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *args) +void Shader_DefaultSkinShell(parsestate_t *ps, const char *shortname, const void *args) { - if (Shader_ParseShader("defaultskinshell", s)) + if (Shader_ParseShader(ps, "defaultskinshell")) return; - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "sort seethrough\n" //before blend, but after other stuff. should fix most issues with shotgun etc effects obscuring it. // "deformvertexes normal 1 1\n" @@ -6520,15 +6642,16 @@ void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *arg "}\n" ); } -void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs) +void Shader_Default2D(parsestate_t *ps, const char *shortname, const void *genargs) { - if (Shader_ParseShader("default2d", s)) + shader_t *s = ps->s; + if (Shader_ParseShader(ps, "default2d")) return; if (sh_config.progs_supported && qrenderer != QR_DIRECT3D9 && !dpcompat_nopremulpics.ival) { //hexen2 needs premultiplied alpha to avoid looking ugly //but that results in problems where things are drawn with alpha not 0, so scale vertex colour by alpha in the fragment program - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "affine\n" "nomipmaps\n" @@ -6544,7 +6667,7 @@ void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs) } else { - Shader_DefaultScript(shortname, s, + Shader_DefaultScript(ps, shortname, "{\n" "affine\n" "nomipmaps\n" @@ -6560,6 +6683,31 @@ void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs) TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); } } +void Shader_PolygonShader(struct shaderparsestate_s *ps, const char *shortname, const void *args) +{ + shader_t *s = ps->s; + Shader_DefaultScript(ps, shortname, + "{\n" + "if $lpp\n" + "program lpp_skin\n" + "else\n" + "program defaultskin#NONORMALS\n" + "endif\n" + "{\n" + "map $diffuse\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "{\n" + "map $fullbright\n" + "blendfunc add\n" + "}\n" + "}\n" + ); + + if (!s->defaulttextures->base && (s->flags & SHADER_HASDIFFUSE)) + R_BuildDefaultTexnums(NULL, s, 0); +} static qboolean Shader_ReadShaderTerms(parsestate_t *ps, struct scondinfo_s *cond) { @@ -6615,22 +6763,24 @@ static qboolean Shader_ReadShaderTerms(parsestate_t *ps, struct scondinfo_s *con } //loads a shader string into an existing shader object, and finalises it and stuff -static void Shader_ReadShader(shader_t *s, char *shadersource, shadercachefile_t *sourcefile) +static void Shader_ReadShader(parsestate_t *ps, char *shadersource, shadercachefile_t *sourcefile) { struct scondinfo_s cond = {0}; + char **savebody = ps->saveshaderbody; + shader_t *s = ps->s; - memset(&parsestate, 0, sizeof(parsestate)); + memset(ps, 0, sizeof(*ps)); if (sourcefile) { - parsestate.forcedshader = *sourcefile->forcedshadername?sourcefile->forcedshadername:NULL; - parsestate.parseflags = sourcefile->parseflags; + ps->forcedshader = *sourcefile->forcedshadername?sourcefile->forcedshadername:NULL; + ps->parseflags = sourcefile->parseflags; } else - parsestate.parseflags = 0; - parsestate.specularexpscale = 1; - parsestate.specularvalscale = 1; - parsestate.ptr = shadersource; - parsestate.s = s; + ps->parseflags = 0; + ps->specularexpscale = 1; + ps->specularvalscale = 1; + ps->ptr = shadersource; + ps->s = s; if (!s->defaulttextures) { @@ -6641,7 +6791,7 @@ static void Shader_ReadShader(shader_t *s, char *shadersource, shadercachefile_t // set defaults s->flags = SHADER_CULL_FRONT; - while (Shader_ReadShaderTerms(&parsestate, &cond)) + while (Shader_ReadShaderTerms(ps, &cond)) { } @@ -6650,21 +6800,20 @@ static void Shader_ReadShader(shader_t *s, char *shadersource, shadercachefile_t Con_Printf("if statements without endif in shader %s\n", s->name); } - Shader_Finish ( s ); + Shader_Finish (ps); //querying the shader body often requires generating the shader, which then gets parsed. - if (saveshaderbody) + if (savebody) { - size_t l = parsestate.ptr?parsestate.ptr - shadersource:0; - Z_Free(*saveshaderbody); - *saveshaderbody = BZ_Malloc(l+1); - (*saveshaderbody)[l] = 0; - memcpy(*saveshaderbody, shadersource, l); - saveshaderbody = NULL; + size_t l = ps->ptr?ps->ptr - shadersource:0; + Z_Free(*savebody); + *savebody = BZ_Malloc(l+1); + (*savebody)[l] = 0; + memcpy(*savebody, shadersource, l); } } -static qboolean Shader_ParseShader(char *parsename, shader_t *s) +static qboolean Shader_ParseShader(parsestate_t *ps, char *parsename) { size_t offset = 0, length; char *buf = NULL; @@ -6688,13 +6837,13 @@ static qboolean Shader_ParseShader(char *parsename, shader_t *s) file = COM_LoadTempMoreFile(parsename, &length); if (file) { - Shader_Reset(s); + Shader_Reset(ps->s); token = COM_ParseExt (&file, true, false); //we need to skip over the leading {. if (*token != '{') token = COM_ParseExt (&file, true, false); //try again, in case we found some legacy name. if (*token == '{') { - Shader_ReadShader(s, file, NULL); + Shader_ReadShader(ps, file, NULL); return true; } else @@ -6716,9 +6865,9 @@ static qboolean Shader_ParseShader(char *parsename, shader_t *s) return false; } - Shader_Reset(s); + Shader_Reset(ps->s); - Shader_ReadShader(s, file, sourcefile); + Shader_ReadShader(ps, file, sourcefile); return true; } @@ -6742,8 +6891,6 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader { int i, f = -1; char cleanname[MAX_QPATH]; - char shortname[MAX_QPATH]; - char *argsstart; shader_t *s; if (!*name) @@ -6787,7 +6934,12 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader if (f == -1) { shader_t **n; - int nm; + int nm; + if (strlen(cleanname) >= sizeof(s->name)) + { + Sys_Error( "R_LoadShader: Shader name too long."); + return NULL; + } f = r_numshaders; if (f == r_maxshaders) { @@ -6806,78 +6958,80 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader r_maxshaders = nm; } } - if (strlen(cleanname) >= sizeof(s->name)) + { - Sys_Error( "R_LoadShader: Shader name too long."); - return NULL; - } - - s = r_shaders[f]; - if (!s) - { - s = r_shaders[f] = Z_Malloc(sizeof(*s)); - } - s->id = f; - if (r_numshaders < f+1) - r_numshaders = f+1; - - if (!s->defaulttextures) - s->defaulttextures = Z_Malloc(sizeof(*s->defaulttextures)); - else - memset(s->defaulttextures, 0, sizeof(*s->defaulttextures)); - s->numdefaulttextures = 0; - Q_strncpyz(s->name, cleanname, sizeof(s->name)); - s->usageflags = usageflags; - s->generator = defaultgen; - s->width = 0; - s->height = 0; - s->uses = 1; - if (genargs) - s->genargs = strdup(genargs); - else - s->genargs = NULL; - - //now determine the 'short name'. ie: the shader that is loaded off disk (no args, no extension) - argsstart = strchr(cleanname, '#'); - if (argsstart) - *argsstart = 0; - COM_StripExtension (cleanname, shortname, sizeof(shortname)); - - if (ruleset_allow_shaders.ival && !(usageflags & SUR_FORCEFALLBACK)) - { - if (sh_config.shadernamefmt) + parsestate_t ps; + char shortname[MAX_QPATH]; + char *argsstart; + s = r_shaders[f]; + if (!s) { - char drivername[MAX_QPATH]; - Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); - if (Shader_ParseShader(drivername, s)) - return s; + s = r_shaders[f] = Z_Malloc(sizeof(*s)); } - if (Shader_ParseShader(cleanname, s)) - return s; - if (strcmp(cleanname, shortname)) - if (Shader_ParseShader(shortname, s)) - return s; - } + ps.saveshaderbody = NULL; + ps.s = s; + s->id = f; + if (r_numshaders < f+1) + r_numshaders = f+1; - // make a default shader - - if (s->generator) - { - Shader_Reset(s); - - if (!strcmp(shortname, "textures/common/clip")) - Shader_DefaultScript(cleanname, s, - "{\n" - "surfaceparm nodraw\n" - "surfaceparm nodlight\n" - "}\n"); + if (!s->defaulttextures) + s->defaulttextures = Z_Malloc(sizeof(*s->defaulttextures)); else - s->generator(cleanname, s, s->genargs); - return s; - } - else - { - Shader_Free(s); + memset(s->defaulttextures, 0, sizeof(*s->defaulttextures)); + s->numdefaulttextures = 0; + Q_strncpyz(s->name, cleanname, sizeof(s->name)); + s->usageflags = usageflags; + s->generator = defaultgen; + s->width = 0; + s->height = 0; + s->uses = 1; + if (genargs) + s->genargs = strdup(genargs); + else + s->genargs = NULL; + + //now determine the 'short name'. ie: the shader that is loaded off disk (no args, no extension) + argsstart = strchr(cleanname, '#'); + if (argsstart) + *argsstart = 0; + COM_StripExtension (cleanname, shortname, sizeof(shortname)); + + if (ruleset_allow_shaders.ival && !(usageflags & SUR_FORCEFALLBACK)) + { + if (sh_config.shadernamefmt) + { + char drivername[MAX_QPATH]; + Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); + if (Shader_ParseShader(&ps, drivername)) + return s; + } + if (Shader_ParseShader(&ps, cleanname)) + return s; + if (strcmp(cleanname, shortname)) + if (Shader_ParseShader(&ps, shortname)) + return s; + } + + // make a default shader + + if (s->generator) + { + Shader_Reset(s); + + if (!strcmp(shortname, "textures/common/clip")) + Shader_DefaultScript(&ps, cleanname, + "{\n" + "surfaceparm nodraw\n" + "surfaceparm nodlight\n" + "}\n"); + else + s->generator(&ps, cleanname, s->genargs); + return s; + } + else + { + Shader_Free(s); + } } return NULL; } @@ -7187,6 +7341,7 @@ char *Shader_Decompose(shader_t *s) char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize) { + parsestate_t ps; char *adr, *parsename=NULL, *argsstart; char cleanname[MAX_QPATH]; char shortname[MAX_QPATH]; @@ -7196,9 +7351,10 @@ char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize) if (!s || !s->uses) return NULL; + ps.s = s; adr = Z_StrDup("UNKNOWN BODY"); - saveshaderbody = &adr; + ps.saveshaderbody = &adr; strcpy(cleanname, s->name); argsstart = strchr(cleanname, '#'); @@ -7210,12 +7366,12 @@ char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize) if (sh_config.shadernamefmt) { Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); - if (!parsename && Shader_ParseShader(drivername, s)) + if (!parsename && Shader_ParseShader(&ps, drivername)) parsename = drivername; } - if (!parsename && Shader_ParseShader(cleanname, s)) + if (!parsename && Shader_ParseShader(&ps, cleanname)) parsename = cleanname; - if (!parsename && Shader_ParseShader(shortname, s)) + if (!parsename && Shader_ParseShader(&ps, shortname)) parsename = shortname; } if (!parsename && s->generator) @@ -7223,7 +7379,7 @@ char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize) oldsort = s->sort; Shader_Reset(s); - s->generator(shortname, s, s->genargs); + s->generator(&ps, shortname, s->genargs); if (s->sort != oldsort) resort = true; @@ -7297,7 +7453,6 @@ char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize) } #endif - saveshaderbody = NULL; return adr; } @@ -7372,6 +7527,7 @@ void Shader_DoReload(void) char cleanname[MAX_QPATH], *argsstart; int oldsort; qboolean resort = false; + parsestate_t ps; //don't spam shader reloads while we're connecting, as that's just wasteful. if (cls.state && cls.state < ca_active) @@ -7411,6 +7567,8 @@ void Shader_DoReload(void) s = r_shaders[i]; if (!s || !s->uses) continue; + ps.s = s; + ps.saveshaderbody = NULL; strcpy(cleanname, s->name); argsstart = strchr(cleanname, '#'); @@ -7423,13 +7581,13 @@ void Shader_DoReload(void) { char drivername[MAX_QPATH]; Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); - if (Shader_ParseShader(drivername, s)) + if (Shader_ParseShader(&ps, drivername)) continue; } - if (Shader_ParseShader(cleanname, s)) + if (Shader_ParseShader(&ps, cleanname)) continue; if (strcmp(cleanname, shortname)) - if (Shader_ParseShader(shortname, s)) + if (Shader_ParseShader(&ps, shortname)) continue; } if (s->generator) @@ -7437,7 +7595,7 @@ void Shader_DoReload(void) oldsort = s->sort; Shader_Reset(s); - s->generator(shortname, s, s->genargs); + s->generator(&ps, shortname, s->genargs); if (s->sort != oldsort) resort = true; diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 7f27b4ba9..ccd79637c 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1404,6 +1404,16 @@ static const char *glsl_hdrs[] = // "#define s_deluxmap3 s_deluxemap3\n" #endif #endif + "#if defined(ORM) || defined(SG)\n" + "uniform vec4 factors[3];\n" + "#define factor_base factors[0]\n" + "#define factor_spec factors[1]\n" + "#define factor_emit factors[2]\n" + "#else\n" + "#define factor_base vec4(1.0)\n" + "#define factor_spec vec4(1.0)\n" + "#define factor_emit vec4(1.0)\n" + "#endif\n" "#ifdef USEUBOS\n" "layout(std140) uniform u_lightinfo\n" "{" @@ -1819,6 +1829,67 @@ static const char *glsl_hdrs[] = "#endif\n" "}\n" , + + "sys/pbr.h", + //ripped from the gltf webgl demo. + //https://github.com/KhronosGroup/glTF-WebGL-PBR/blob/master/shaders/pbr-frag.glsl + //because most of this maths is gibberish, especially the odd magic number. + "#ifdef PBR\n" + "const float PI = 3.141592653589793;\n" + "vec3 diffuse(vec3 diffuseColor)\n" //Basic Lambertian diffuse + "{\n" + "return diffuseColor / PI;\n" + "}\n" + "vec3 specularReflection(vec3 reflectance0, vec3 reflectance90, float VdotH)\n" + "{\n" + "return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);\n" + "}\n" + "float geometricOcclusion(float NdotL, float NdotV, float r)\n" + "{\n" + "float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));\n" + "float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));\n" + "return attenuationL * attenuationV;\n" + "}\n" + "float microfacetDistribution(float alphaRoughness, float NdotH)\n" //Trowbridge-Reitz + "{\n" + "float roughnessSq = alphaRoughness * alphaRoughness;\n" + "float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;\n" + "return roughnessSq / (PI * f * f);\n" + "}\n" + "vec3 DoPBR(vec3 n, vec3 v, vec3 l, float perceptualRoughness, vec3 diffuseColor, vec3 specularColor, vec3 scales)\n" + "{\n" + // Compute reflectance. + "float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\n" + "float alphaRoughness = perceptualRoughness * perceptualRoughness;\n" + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + "float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);\n" + "vec3 specularEnvironmentR0 = specularColor.rgb;\n" + "vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;\n" + + "vec3 h = normalize(l+v);\n" // Half vector between both l and v + "vec3 reflection = -normalize(reflect(v, n));\n" + + "float NdotL = clamp(dot(n, l), 0.001, 1.0);\n" + "float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);\n" + "float NdotH = clamp(dot(n, h), 0.0, 1.0);\n" + "float LdotH = clamp(dot(l, h), 0.0, 1.0);\n" + "float VdotH = clamp(dot(v, h), 0.0, 1.0);\n" + + // Calculate the shading terms for the microfacet specular shading model + "vec3 F = specularReflection(specularEnvironmentR0, specularEnvironmentR90, VdotH);\n" + "float G = geometricOcclusion(NdotL, NdotV, alphaRoughness);\n" + "float D = microfacetDistribution(alphaRoughness, NdotH);\n" + + // Calculation of analytical lighting contribution + "vec3 diffuseContrib = (1.0 - F) * diffuse(diffuseColor) * scales.y;\n" + "vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV) * scales.z;\n" + // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law) + "return NdotL * (diffuseContrib + specContrib);\n" + "}\n" + "#endif\n" + , "sys/pcf.h", //!!cvardf r_glsl_pcf "#ifndef PCF\n" @@ -2792,6 +2863,8 @@ static void GLSlang_ProgAutoFields(program_t *prog, struct programpermu_s *pp, c pp->numparms = 0; pp->parm = NULL; + pp->factorsuniform = qglGetUniformLocationARB(pp->h.glsl.handle, "factors"); + for (i = 0; shader_unif_names[i].name; i++) { uniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, shader_unif_names[i].name); diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 8c9827eb2..1b0459317 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -2828,6 +2828,9 @@ static void GetEvent(void) break; case FocusIn: + //don't care about it if its just someone wiggling the mouse + if (event.xfocus.detail == NotifyPointer) + break; //activeapp is if the game window is focused vid.activeapp = true; ClearAllStates(); //just in case. @@ -2848,6 +2851,9 @@ static void GetEvent(void) // x11.pXUnmapWindow(vid_dpy, vid_decoywindow); break; case FocusOut: + //don't care about it if its just someone wiggling the mouse + if (event.xfocus.detail == NotifyPointer) + break; //if we're already active, the decoy window shouldn't be focused anyway. if (event.xfocus.window == vid_window) x11.ime_shown = -1; diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 722fc07ee..13dd31f2b 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -2958,10 +2958,16 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!cvardf r_tessellation_level=5\n" "!!samps !EIGHTBIT diffuse normalmap specular fullbright upper lower reflectmask reflectcube\n" "!!samps =EIGHTBIT paletted 1\n" -//!!permu VC -- adds rgba vertex colour multipliers -//!!permu SPECULAR -- auto-added when gl_specular>0 -//!!permu OFFSETMAPPING -- auto-added when r_glsl_offsetmapping is set -//!!permu NONORMALS -- states that there's no normals available, which affects lighting. +//!!permu VC // adds rgba vertex colour multipliers +//!!permu SPECULAR // auto-added when gl_specular>0 +//!!permu OFFSETMAPPING // auto-added when r_glsl_offsetmapping is set +//!!permu NONORMALS // states that there's no normals available, which affects lighting. +//!!permu ORM // specularmap is r:Occlusion, g:Roughness, b:Metalness +//!!permu SG // specularmap is rgb:F0, a:Roughness (instead of exponent) +//!!permu PBR // an attempt at pbr logic (enabled from ORM or SG) +//!!permu NOOCCLUDE // ignores the use of ORM's occlusion... yeah, stupid. +//!!permu EIGHTBIT // uses software-style paletted colourmap lookups +//!!permu ALPHATEST // if defined, this is the required alpha level (more versatile than doing it at the q3shader level) "#include \"sys/defs.h\"\n" @@ -2975,6 +2981,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#define affine\n" "#endif\n" +"#if defined(ORM) || defined(SG)\n" +"#define PBR\n" +"#endif\n" "#ifdef NONORMALS //lots of things need normals to work properly. make sure nothing breaks simply because they added an extra texture.\n" "#undef BUMP\n" @@ -2986,7 +2995,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND - "#ifdef VERTEX_SHADER\n" "#include \"sys/skeletal.h\"\n" @@ -2995,7 +3003,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "varying vec3 eyevector;\n" "#endif\n" -"#ifdef REFLECTCUBEMASK\n" +"#if defined(PBR)||defined(REFLECTCUBEMASK)\n" "varying mat3 invsurface;\n" "#endif\n" "#ifdef TESS\n" @@ -3015,22 +3023,28 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec3 n, s, t, w;\n" "gl_Position = skeletaltransform_wnst(w,n,s,t);\n" "n = normalize(n);\n" +"s = normalize(s);\n" +"t = normalize(t);\n" +"#ifndef PBR\n" "float d = dot(n,e_light_dir);\n" "if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex.\n" "d = 0.0; //this avoids the dark side going below the ambient level.\n" "light.rgb += (d*e_light_mul);\n" +"#else\n" +"light.rgb = vec3(1.0);\n" +"#endif\n" "#endif\n" -"#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" +"#if defined(PBR)\n" +"eyevector = e_eyepos - w.xyz;\n" +"#elif defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "vec3 eyeminusvertex = e_eyepos - w.xyz;\n" "eyevector.x = dot(eyeminusvertex, s.xyz);\n" "eyevector.y = dot(eyeminusvertex, t.xyz);\n" "eyevector.z = dot(eyeminusvertex, n.xyz);\n" "#endif\n" -"#ifdef REFLECTCUBEMASK\n" -"invsurface[0] = s;\n" -"invsurface[1] = t;\n" -"invsurface[2] = n;\n" +"#if defined(PBR) || defined(REFLECTCUBEMASK)\n" +"invsurface = mat3(s, t, n);\n" "#endif\n" "tc = v_texcoord;\n" @@ -3183,10 +3197,39 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "varying vec3 eyevector;\n" "#endif\n" -"#ifdef REFLECTCUBEMASK\n" +"#if defined(PBR) || defined(REFLECTCUBEMASK)\n" "varying mat3 invsurface;\n" "#endif\n" +"#ifdef PBR\n" +"#include \"sys/pbr.h\"\n" +"#if 0\n" +"vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)\n" +"{\n" +"float mipCount = 9.0; // resolution of 512x512\n" +"float lod = (pbrInputs.perceptualRoughness * mipCount);\n" +// retrieve a scale and bias to F0. See [1], Figure 3 +"vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb;\n" +"vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;\n" + +"#ifdef USE_TEX_LOD\n" +"vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb;\n" +"#else\n" +"vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb;\n" +"#endif\n" + +"vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;\n" +"vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);\n" + +// For presentation, this allows us to disable IBL terms +"diffuse *= u_ScaleIBLAmbient.x;\n" +"specular *= u_ScaleIBLAmbient.y;\n" + +"return diffuse + specular;\n" +"}\n" +"#endif\n" +"#endif\n" + "void main ()\n" "{\n" @@ -3218,33 +3261,87 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "col.rgb += lc.rgb*e_lowercolour*lc.a;\n" "#endif\n" -"#if defined(BUMP) && defined(SPECULAR)\n" -"vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\n" -"vec4 specs = texture2D(s_specular, tc);\n" +"col *= factor_base;\n" -"vec3 halfdir = normalize(normalize(eyevector) + e_light_dir);\n" -"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\n" -"col.rgb += FTE_SPECULAR_MULTIPLIER * spec * specs.rgb;\n" -"#elif defined(REFLECTCUBEMASK)\n" -"vec3 bumps = vec3(0, 0, 1);\n" +"#define dielectricSpecular 0.04\n" +"#ifdef SPECULAR\n" +"vec4 specs = texture2D(s_specular, tc)*factor_spec;\n" +"#ifdef ORM\n" +"#define occlusion specs.r\n" +"#define roughness clamp(specs.g, 0.04, 1.0)\n" +"#define metalness specs.b\n" +"#define gloss 1.0 //sqrt(1.0-roughness)\n" +"#define ambientrgb (specrgb+col.rgb)\n" +"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\n" +"col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n" +"#elif defined(SG) //pbr-style specular+glossiness\n" +//occlusion needs to be baked in. :( +"#define roughness (1.0-specs.a)\n" +"#define gloss (specs.a)\n" +"#define specrgb specs.rgb\n" +"#define ambientrgb (specs.rgb+col.rgb)\n" +"#else //blinn-phong\n" +"#define roughness (1.0-specs.a)\n" +"#define gloss specs.a\n" +"#define specrgb specs.rgb\n" +"#define ambientrgb col.rgb\n" +"#endif\n" +"#else\n" +"#define roughness 0.3\n" +"#define specrgb 1.0 //vec3(dielectricSpecular)\n" +"#endif\n" + +"#ifdef BUMP\n" +"#ifdef PBR //to modelspace\n" +"vec3 bumps = normalize(invsurface * (texture2D(s_normalmap, tc).rgb*2.0 - 1.0));\n" +"#else //stay in tangentspace\n" +"vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\n" +"#endif\n" +"#else\n" +"#ifdef PBR //to modelspace\n" +"#define bumps normalize(invsurface[2])\n" +"#else //tangent space\n" +"#define bumps vec3(0.0, 0.0, 1.0)\n" +"#endif\n" +"#endif\n" + +"#ifdef PBR\n" +//move everything to model space +"col.rgb = DoPBR(bumps, normalize(eyevector), -e_light_dir, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0))*e_light_mul + e_light_ambient*.25*ambientrgb;\n" +"#elif defined(gloss)\n" +"vec3 halfdir = normalize(normalize(eyevector) - e_light_dir);\n" +"float specmag = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss);\n" +"col.rgb += FTE_SPECULAR_MULTIPLIER * specmag * specrgb;\n" "#endif\n" "#ifdef REFLECTCUBEMASK\n" "vec3 rtc = reflect(-eyevector, bumps);\n" +"#ifndef PBR\n" "rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\n" +"#endif\n" "rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\n" "col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n" "#endif\n" +"#if defined(occlusion) && !defined(NOOCCLUDE)\n" +"col.rgb *= occlusion;\n" +"#endif\n" "col *= light * e_colourident;\n" "#ifdef FULLBRIGHT\n" "vec4 fb = texture2D(s_fullbright, tc);\n" // col.rgb = mix(col.rgb, fb.rgb, fb.a); -"col.rgb += fb.rgb * fb.a * e_glowmod.rgb;\n" +"col.rgb += fb.rgb * fb.a * e_glowmod.rgb * factor_emit.rgb;\n" +"#elif defined(PBR)\n" +"col.rgb += e_glowmod.rgb * factor_emit.rgb;\n" "#endif\n" "#endif\n" +"#ifdef ALPHATEST\n" +"if (!(col.a ALPHATEST))\n" +"discard;\n" +"#endif\n" + "gl_FragColor = fog4(col);\n" "}\n" "#endif\n" @@ -5508,6 +5605,10 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!samps lightmap deluxemap\n" "!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3\n" +"#if defined(ORM) || defined(SG)\n" +"#define PBR\n" +"#endif\n" + "#include \"sys/defs.h\"\n" //this is what normally draws all of your walls, even with rtlights disabled @@ -5551,9 +5652,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "eyevector.z = dot(eyeminusvertex, v_normal.xyz);\n" "#endif\n" "#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\n" -"invsurface[0] = v_svector;\n" -"invsurface[1] = v_tvector;\n" -"invsurface[2] = v_normal;\n" +"invsurface = mat3(v_svector, v_tvector, v_normal);\n" "#endif\n" "tc = v_texcoord;\n" "#ifdef FLOW\n" @@ -5723,6 +5822,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef FRAGMENT_SHADER\n" "#define s_colourmap s_t0\n" +"#include \"sys/pbr.h\"\n" + "#ifdef OFFSETMAPPING\n" "#include \"sys/offsetmapping.h\"\n" "#endif\n" @@ -5749,12 +5850,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#endif\n" -//yay, regular texture! -"gl_FragColor = texture2D(s_diffuse, tc);\n" +//Read the base texture (with EIGHTBIT only alpha is needed) +"vec4 col = texture2D(s_diffuse, tc);\n" "#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))\n" "vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\n" -"#elif defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\n" +"#elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\n" "vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist.\n" "#endif\n" @@ -5799,61 +5900,89 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#endif\n" "#endif\n" -//add in specular, if applicable. +// col *= factor_base; +"#define dielectricSpecular 0.04\n" "#ifdef SPECULAR\n" -"vec4 specs = texture2D(s_specular, tc);\n" +"vec4 specs = texture2D(s_specular, tc);//*factor_spec;\n" +"#ifdef ORM\n" +"#define occlusion specs.r\n" +"#define roughness specs.g\n" +"#define metalness specs.b\n" +"#define gloss (1.0-roughness)\n" +"#define ambientrgb (specrgb+col.rgb)\n" +"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\n" +"col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n" +"#elif defined(SG) //pbr-style specular+glossiness\n" +//occlusion needs to be baked in. :( +"#define roughness (1.0-specs.a)\n" +"#define gloss specs.a\n" +"#define specrgb specs.rgb\n" +"#define ambientrgb (specs.rgb+col.rgb)\n" +"#else //blinn-phong\n" +"#define roughness (1.0-specs.a)\n" +"#define gloss specs.a\n" +"#define specrgb specs.rgb\n" +"#define ambientrgb col.rgb\n" +"#endif\n" +"#else\n" +"#define roughness 0.3\n" +"#define specrgb 1.0 //vec3(dielectricSpecular)\n" +"#endif\n" + +//add in specular, if applicable. +"#ifdef PBR\n" +"col.rgb = DoPBR(norm, normalize(eyevector), deluxe, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0));//*e_light_mul + e_light_ambient*.25*ambientrgb;\n" +"#elif defined(gloss)\n" "vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead\n" -"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\n" +"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * gloss);\n" "spec *= FTE_SPECULAR_MULTIPLIER;\n" //NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool. //As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway, //we default to something that is not garish when the light value is directly infront of every single pixel. //we can justify this difference due to the rtlight editor etc showing the *4. -"gl_FragColor.rgb += spec * specs.rgb;\n" +"col.rgb += spec * specrgb;\n" "#endif\n" "#ifdef REFLECTCUBEMASK\n" "vec3 rtc = reflect(normalize(-eyevector), norm);\n" "rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\n" "rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\n" -"gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n" +"col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n" "#endif\n" "#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.\n" "lightmaps *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\n" "float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated.\n" "lightmaps -= 1.0 / 128.0; //software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\n" -"gl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\n" -"gl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.\n" -"gl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.\n" +"col.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\n" +"col.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.\n" +"col.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.\n" "#else\n" //now we have our diffuse+specular terms, modulate by lightmap values. -"gl_FragColor.rgb *= lightmaps.rgb;\n" +"col.rgb *= lightmaps.rgb;\n" //add on the fullbright "#ifdef FULLBRIGHT\n" -"gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb;\n" +"col.rgb += texture2D(s_fullbright, tc).rgb;\n" "#endif\n" "#endif\n" //entity modifiers -"gl_FragColor = gl_FragColor * e_colourident;\n" +"col *= e_colourident;\n" "#if defined(MASK)\n" "#if defined(MASKLT)\n" -"if (gl_FragColor.a < MASK)\n" +"if (col.a < MASK)\n" "discard;\n" "#else\n" -"if (gl_FragColor.a >= MASK)\n" +"if (col.a >= MASK)\n" "discard;\n" "#endif\n" -"gl_FragColor.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\n" +"col.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\n" "#endif\n" //and finally hide it all if we're fogged. -"#ifdef FOG\n" -"gl_FragColor = fog4(gl_FragColor);\n" -"#endif\n" +"gl_FragColor = fog4(col);\n" "}\n" "#endif\n" @@ -10935,6 +11064,10 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!samps =PCF shadowmap\n" "!!samps =CUBE projectionmap\n" +"#if defined(ORM) || defined(SG)\n" +"#define PBR\n" +"#endif\n" + "#include \"sys/defs.h\"\n" //this is the main shader responsible for realtime dlights. @@ -10991,6 +11124,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "{\n" "vec3 n, s, t, w;\n" "gl_Position = skeletaltransform_wnst(w,n,s,t);\n" +"n = normalize(n);\n" +"s = normalize(s);\n" +"t = normalize(t);\n" "tcbase = v_texcoord; //pass the texture coords straight through\n" "#ifdef ORTHO\n" "vec3 lightminusvertex = -l_lightdirection;\n" @@ -11019,9 +11155,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "eyevector.z = dot(eyeminusvertex, n.xyz);\n" "#endif\n" "#ifdef REFLECTCUBEMASK\n" -"invsurface[0] = v_svector;\n" -"invsurface[1] = v_tvector;\n" -"invsurface[2] = v_normal;\n" +"invsurface = mat3(v_svector, v_tvector, v_normal);\n" "#endif\n" "#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)\n" //for texture projections/shadowmapping on dlights @@ -11151,6 +11285,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#include \"sys/offsetmapping.h\"\n" "#endif\n" +"#include \"sys/pbr.h\"\n" + "void main ()\n" "{\n" "#ifdef ORTHO\n" @@ -11199,6 +11335,36 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec4 specs = texture2D(s_specular, tcbase);\n" "#endif\n" +"#define dielectricSpecular 0.04\n" +"#ifdef SPECULAR\n" +"#ifdef ORM //pbr-style occlusion+roughness+metalness\n" +"#define occlusion specs.r\n" +"#define roughness clamp(specs.g, 0.04, 1.0)\n" +"#define metalness specs.b\n" +"#define gloss 1.0 //sqrt(1.0-roughness)\n" +"#define ambientrgb (specrgb+col.rgb)\n" +"vec3 specrgb = mix(vec3(dielectricSpecular), bases.rgb, metalness);\n" +"bases.rgb = bases.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n" +"#elif defined(SG) //pbr-style specular+glossiness\n" +//occlusion needs to be baked in. :( +"#define roughness (1.0-specs.a)\n" +"#define gloss specs.a\n" +"#define specrgb specs.rgb\n" +"#define ambientrgb (specs.rgb+col.rgb)\n" +"#else //blinn-phong\n" +"#define roughness (1.0-specs.a)\n" +"#define gloss specs.a\n" +"#define specrgb specs.rgb\n" +"#define ambientrgb col.rgb\n" +"#endif\n" +"#else\n" +"#define roughness 0.3\n" +"#define specrgb 1.0 //vec3(dielectricSpecular)\n" +"#endif\n" + +"#ifdef PBR\n" +"vec3 diff = DoPBR(bumps, normalize(eyevector), normalize(lightvector), roughness, bases.rgb, specrgb, l_lightcolourscale);\n" +"#else\n" "vec3 diff;\n" "#ifdef NOBUMP\n" //surface can only support ambient lighting, even for lights that try to avoid it. @@ -11212,12 +11378,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\n" "#endif\n" "#endif\n" - - "#ifdef SPECULAR\n" "vec3 halfdir = normalize(normalize(eyevector) + nl);\n" -"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a)*float(SPECMUL);\n" -"diff += l_lightcolourscale.z * spec * specs.rgb;\n" +"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss)*float(SPECMUL);\n" +"diff += l_lightcolourscale.z * spec * specrgb;\n" +"#endif\n" "#endif\n" "#ifdef REFLECTCUBEMASK\n" @@ -11236,11 +11401,15 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND /*2d projection, not used*/ // diff *= texture2d(s_projectionmap, shadowcoord); "#endif\n" +"#if defined(occlusion) && !defined(NOOCCLUDE)\n" +"diff *= occlusion;\n" +"#endif\n" "#if defined(VERTEXCOLOURS)\n" "diff *= vc.rgb * vc.a;\n" "#endif\n" -"gl_FragColor = vec4(fog3additive(diff*colorscale*l_lightcolour), 1.0);\n" +"diff *= colorscale*l_lightcolour;\n" +"gl_FragColor = vec4(fog3additive(diff), 1.0);\n" "}\n" "#endif\n" diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 77d16a710..cb0e1d437 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -29,7 +29,8 @@ lights are then added over the top based upon the diffusemap, bumpmap and specul #ifndef SHADER_H #define SHADER_H -typedef void (shader_gen_t)(const char *name, shader_t*, const void *args); +struct shaderparsestate_s; +typedef void (shader_gen_t)(struct shaderparsestate_s *ps, const char *name, const void *args); #define SHADER_TMU_MAX 16 #define SHADER_PASS_MAX 16 @@ -512,6 +513,9 @@ struct programpermu_s } hlsl; #endif } h; +#endif +#ifdef GLQUAKE + int factorsuniform; #endif unsigned int permutation; unsigned int attrmask; @@ -675,6 +679,12 @@ struct shader_s bucket_t bucket; +#define MATERIAL_FACTOR_BASE 0 +#define MATERIAL_FACTOR_SPEC 1 +#define MATERIAL_FACTOR_EMIT 2 +#define MATERIAL_FACTOR_COUNT 3 + vec4_t factors[MATERIAL_FACTOR_COUNT]; + //arranged as a series of vec4s /* struct { @@ -711,15 +721,17 @@ cin_t *R_ShaderGetCinematic(shader_t *s); cin_t *R_ShaderFindCinematic(const char *name); shader_t *R_ShaderFind(const char *name); //does NOT increase the shader refcount. -void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultWaterShader(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *args); -void Shader_DefaultScript(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultSkin (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultSkinShell (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_Default2D (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultBSPLM (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultBSPQ1 (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultBSPQ2 (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultWaterShader (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultSkybox (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultCinematic (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_DefaultScript (struct shaderparsestate_s *ps, const char *shortname, const void *args); +void Shader_PolygonShader (struct shaderparsestate_s *ps, const char *shortname, const void *args); void Shader_ResetRemaps(void); //called on map changes to reset remapped shaders. void Shader_DoReload(void); //called when the shader system dies. diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 2522889f1..e4f644640 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -381,7 +381,7 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals) return; } - progfuncs->funcs.debug_trace = -10; + progfuncs->funcs.debug_trace = -10; //PR_StringToNative(+via PR_ValueString) has various error conditions that we want to mute instead of causing recursive errors. //point this to the function's locals globalbase = (int *)pr_globals + pr_xfunction->parm_start + pr_xfunction->locals; @@ -413,16 +413,16 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals) { progs = prnum; - externs->Printf ("<%s>\n", pr_progstate[progs].filename); + externs->DPrintf ("<%s>\n", pr_progstate[progs].filename); } if (!f->s_file) - externs->Printf ("stripped : %s\n", PR_StringToNative(ppf, f->s_name)); + externs->Printf ("unknown-file : %s\n", PR_StringToNative(ppf, f->s_name)); else { if (pr_progstate[progs].linenums) externs->Printf ("%12s:%i: %s\n", PR_StringToNative(ppf, f->s_file), pr_progstate[progs].linenums[st], PR_StringToNative(ppf, f->s_name)); else - externs->Printf ("%12s : %s\n", PR_StringToNative(ppf, f->s_file), PR_StringToNative(ppf, f->s_name)); + externs->Printf ("%12s : %s+%i\n", PR_StringToNative(ppf, f->s_file), PR_StringToNative(ppf, f->s_name), st-f->first_statement); } //locals:0 = no locals @@ -460,7 +460,7 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals) } else { - externs->Printf(" %s: %s\n", local->s_name+progfuncs->funcs.stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false)); + externs->Printf(" %s: %s\n", PR_StringToNative(ppf, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false)); if (local->type == ev_vector) ofs+=2; } diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index f8e11f543..14f3841f2 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -6262,7 +6262,7 @@ static void QCBUILTIN PF_sv_serverkeyblob (pubprogfuncs_t *prinst, struct global { size_t blobsize = 0; const char *key = PR_GetStringOfs(prinst, OFS_PARM0); - const char *blobvalue = InfoBuf_BlobForKey(&svs.info, key, &blobsize); + const char *blobvalue = InfoBuf_BlobForKey(&svs.info, key, &blobsize, NULL); if ((prinst->callargc<2) || G_INT(OFS_PARM1) == 0) G_INT(OFS_RETURN) = blobsize; //no pointer to write to, just return the length. @@ -6302,7 +6302,7 @@ static void QCBUILTIN PF_getlocalinfo (pubprogfuncs_t *prinst, struct globalvars { size_t blobsize = 0; const char *key = PR_GetStringOfs(prinst, OFS_PARM0); - const char *blobvalue = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize); + const char *blobvalue = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize, NULL); if ((prinst->callargc<2) || G_INT(OFS_PARM1) == 0) G_INT(OFS_RETURN) = blobsize; //no pointer to write to, just return the length. @@ -10565,7 +10565,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs #ifndef QUAKETC //mvdsv (don't require ebfs usage in qw) {"executecommand", PF_ExecuteCommand, 0, 0, 0, 83, D("void()","Attempt to flush the localcmd buffer NOW. This is unsafe, as many events might cause the map to be purged while still executing qc code."), true}, - {"mvdtokenize", PF_Tokenize, 0, 0, 0, 84, D("void(string str)",NULL), true}, + {"mvdtokenize", PF_tokenize_console,0, 0, 0, 84, D("void(string str)",NULL), true}, {"mvdargc", PF_ArgC, 0, 0, 0, 85, D("float()",NULL), true}, {"mvdargv", PF_ArgV, 0, 0, 0, 86, D("string(float num)",NULL), true}, diff --git a/engine/server/server.h b/engine/server/server.h index 05d781d88..c2678f100 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -562,6 +562,9 @@ typedef struct client_s #define SENDFLAGS_PRESENT 0x80000000u //this entity is present on that client #define SENDFLAGS_REMOVED 0x40000000u //to handle remove packetloss +#ifndef NOLEGACY + char *dlqueue; //name\name delimited list of files to ask the client to download. +#endif char downloadfn[MAX_QPATH]; vfsfile_t *download; // file being downloaded qofs_t downloadsize; // total bytes @@ -1313,6 +1316,10 @@ void SV_UpdateToReliableMessages (void); void SV_FlushBroadcasts (void); qboolean SV_CanTrack(client_t *client, int entity); +#ifndef NOLEGACY +void SV_DownloadQueueNext(client_t *client); +void SV_DownloadQueueClear(client_t *client); +#endif #ifdef NQPROT void SV_DarkPlacesDownloadChunk(client_t *cl, sizebuf_t *msg); #endif diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 8f11c3e76..8845a6ed6 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -117,8 +117,8 @@ extern cvar_t net_enable_dtls; cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once."); cvar_t sv_heartbeat_interval = CVARD("sv_heartbeat_interval", "110", "Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues."); cvar_t sv_highchars = CVAR("sv_highchars", "1"); -cvar_t sv_maxrate = CVARCD("sv_maxrate", "50k", CvarPostfixKMG, "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar."); -cvar_t sv_maxdrate = CVARAFCD("sv_maxdrate", "500k", +cvar_t sv_maxrate = CVARCD("sv_maxrate", "50000", CvarPostfixKMG, "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar."); +cvar_t sv_maxdrate = CVARAFCD("sv_maxdrate", "500000", "sv_maxdownloadrate", 0, CvarPostfixKMG, "This cvar controls the maximum number of bytes sent to each player per second while that player is downloading.\nIf this cvar is set to 0, there will be NO CAP for download rates (if the user's drate is empty/0 too, then expect really fast+abusive downloads that could potentially be considered denial of service attacks)"); cvar_t sv_minping = CVARFD("sv_minping", "", CVAR_SERVERINFO, "Simulate fake lag for any players with a ping under the value specified here. Value is in milliseconds."); @@ -619,6 +619,9 @@ void SV_DropClient (client_t *drop) Con_TPrintf ("Client \"%s\" removed\n",drop->name); } +#ifndef NOLEGACY + SV_DownloadQueueClear(drop); +#endif if (drop->download) { VFS_CLOSE (drop->download); @@ -5456,7 +5459,7 @@ into a more C freindly form. */ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) { - char *val, *p; + const char *val, *p; int i; client_t *client; int dupc = 1; @@ -5464,6 +5467,8 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) #ifdef SVRANKING extern cvar_t rank_filename; #endif + size_t blobsize; + qboolean large; int bottom = atoi(InfoBuf_ValueForKey(&cl->userinfo, "bottomcolor")); @@ -5481,16 +5486,19 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) Q_strncpyz (cl->team, val, sizeof(cl->teambuf)); // name for C code - val = InfoBuf_ValueForKey (&cl->userinfo, "name"); - - if (cl->protocol != SCP_BAD || *val) + val = InfoBuf_BlobForKey (&cl->userinfo, "name", &blobsize, &large); + if (!val) + val = ""; + //we block large names here because a) they're unwieldly. b) they might cause players to be invisible to older clients/server browsers/etc. + //bots with no name skip the fixup, to avoid default names(they're expected to be given a name eventually, so are allowed to be invisible for now) + if (large || (cl->protocol == SCP_BAD && !*val)) + newname[0] = 0; + else { SV_FixupName(val, newname, sizeof(newname)); if (strlen(newname) > 40) newname[40] = 0; } - else - newname[0] = 0; deleetstring(basic, newname); if (cl->protocol != SCP_BAD) diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 05dcb57eb..d9a6d0c54 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -2786,14 +2786,13 @@ static qboolean SV_SyncInfoBuf(client_t *client) infobuf_t *info = client->infosync.keys[0].context; size_t bloboffset = client->infosync.keys[0].syncpos; //unsigned int seat = info - cls.userinfo; + qboolean large; size_t blobsize; - const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize); + const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize, &large); size_t sendsize; size_t bufferspace; qboolean final; - char enckey[2048]; - char encval[2048]; if (client->protocol == SCP_QUAKE2) { //q2 gamecode is fully responsible for networking this via configstrings. @@ -2806,36 +2805,17 @@ static qboolean SV_SyncInfoBuf(client_t *client) if (client->netchan.message.cursize >= MAX_BACKBUFLEN/2) return false; //don't bother trying to send anything. - if (!InfoBuf_EncodeString(key, strlen(key), enckey, sizeof(enckey))) - { - InfoSync_Remove(&client->infosync, 0); - return false; - } - - sendsize = blobsize - bloboffset; - bufferspace = MAX_BACKBUFLEN - client->netchan.message.cursize; - bufferspace -= 7 - strlen(enckey) - 2; //extra overhead - bufferspace = (bufferspace/4)*3; //encoding overhead - sendsize = min(bufferspace, sendsize); - final = (bloboffset+sendsize >= blobsize); - - if (!InfoBuf_EncodeString(blobdata+bloboffset, sendsize, encval, sizeof(encval))) - { - InfoSync_Remove(&client->infosync, 0); - return false; - } - - if (final && !bloboffset && *enckey != '\xff' && *encval != '\xff') + if (!large) { //vanilla-compatible info. if (ISNQCLIENT(client)) { //except that nq never had any userinfo const char *s; if (info == &svs.info) - s = va("//svi \"%s\" \"%s\"\n", enckey, encval); + s = va("//svi \"%s\" \"%s\"\n", key, blobdata); else { int playerslot = (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients; - s = va("//ui %i \"%s\" \"%s\"\n", playerslot, enckey, encval); + s = va("//ui %i \"%s\" \"%s\"\n", playerslot, key, blobdata); } ClientReliableWrite_Begin(client, svc_stufftext, strlen(s)+2); ClientReliableWrite_String(client, s); @@ -2843,41 +2823,53 @@ static qboolean SV_SyncInfoBuf(client_t *client) else { if (info == &svs.info) - ClientReliableWrite_Begin(client, svc_serverinfo, 1+strlen(enckey)+1+strlen(encval)+1); + ClientReliableWrite_Begin(client, svc_serverinfo, 1+strlen(key)+1+strlen(blobdata)+1); else { - ClientReliableWrite_Begin(client, svc_setinfo, 2+strlen(enckey)+1+strlen(encval)+1); + ClientReliableWrite_Begin(client, svc_setinfo, 2+strlen(key)+1+strlen(blobdata)+1); ClientReliableWrite_Byte(client, (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients); } - ClientReliableWrite_String(client, enckey); - ClientReliableWrite_String(client, encval); + ClientReliableWrite_String(client, key); + ClientReliableWrite_String(client, blobdata); } } else if (client->fteprotocolextensions2 & PEXT2_INFOBLOBS) { - int pl; + char enckey[2048]; + unsigned int pl; if (info == &svs.info) - pl = 255; //colourmaps being 1-based with these being 0-based means that only 0-254 are valid players, and 255 is unused, so lets use it for serverinfo blobs. + pl = 0; //colourmaps being 1-based with these being 0-based means that only 0-254 are valid players, and 255 is unused, so lets use it for serverinfo blobs. else - pl = (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients; + pl = 1+((client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients); - ClientReliableWrite_Begin(client, svc_setinfo, 7+strlen(enckey)+1+strlen(encval)+1); - ClientReliableWrite_Byte(client, 255); //special meaning to say that this is a partial update - ClientReliableWrite_Byte(client, pl); - ClientReliableWrite_Long(client, (final?0x80000000:0)|bloboffset); + if (!InfoBuf_EncodeString(key, strlen(key), enckey, sizeof(enckey))) + { + InfoSync_Remove(&client->infosync, 0); + return false; + } + + sendsize = blobsize - bloboffset; + bufferspace = MAX_BACKBUFLEN - client->netchan.message.cursize; + bufferspace -= 8 - strlen(enckey) - 1; //extra overhead + sendsize = min(bufferspace, sendsize); + final = (bloboffset+sendsize >= blobsize); + + ClientReliableWrite_Begin(client, svcfte_setinfoblob, 8+strlen(enckey)+1+sendsize); + ClientReliableWrite_Byte(client, pl); //special meaning to say that this is a partial update ClientReliableWrite_String(client, enckey); - ClientReliableWrite_String(client, encval); - } - else - { //client can't receive this info, stop trying to send it. - InfoSync_Remove(&client->infosync, 0); - return true; - } + ClientReliableWrite_Long(client, (final?0x80000000:0)|bloboffset); + ClientReliableWrite_Short(client, sendsize); + ClientReliableWrite_SZ(client, blobdata+bloboffset, sendsize); - if (bloboffset+sendsize == blobsize) - InfoSync_Remove(&client->infosync, 0); - else - client->infosync.keys[0].syncpos += sendsize; + if (!final) + { + client->infosync.keys[0].syncpos += sendsize; + return true; + } + } + //else client can't receive this info, stop trying to send it. + + InfoSync_Remove(&client->infosync, 0); return true; } diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index a375b2eec..7990e9a6a 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -2472,6 +2472,10 @@ static void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezf host_client->downloadstarted = false; + +#ifndef NOLEGACY + SV_DownloadQueueNext(host_client); +#endif } } @@ -2551,6 +2555,9 @@ void SV_NextDownload_f (void) VFS_CLOSE (host_client->download); host_client->download = NULL; +#ifndef NOLEGACY + SV_DownloadQueueNext(host_client); +#endif } void VARGS OutofBandPrintf(netadr_t *where, char *fmt, ...) @@ -2996,6 +3003,8 @@ qboolean SV_AllowDownload (const char *name) return false; if (*name == '/') //no absolute. return false; + if (strchr(name, ':')) //no drives, alternative resources, etc. the filesystem should refuse such a file so this should just be paranoia. + return false; if (strchr(name, '\\')) //no windows paths - grow up you lame windows users. return false; @@ -3037,7 +3046,7 @@ qboolean SV_AllowDownload (const char *name) if (Q_strncasecmp(name, "sound/", 6) == 0) return !!allow_download_sounds.value; //particles - if (Q_strncasecmp(name, "particles/", 6) == 0) + if (Q_strncasecmp(name, "particles/", 10) == 0) return !!allow_download_particles.value; //demos if (Q_strncasecmp(name, "demos/", 6) == 0) @@ -3290,6 +3299,50 @@ void SV_DownloadSize_f(void) } #ifdef MVD_RECORDING + +#ifndef NOLEGACY +void SV_DownloadQueueAdd(client_t *client, const char *name) +{ + if (!client->dlqueue) + { + client->dlqueue = Z_StrDup(name); + SV_ClientPrintf (client, PRINT_HIGH, "Using legacy serverside download queue. This is subject to race conditions, be careful.\n"); + } + else + { + Z_StrCat(&client->dlqueue, "\\"); + Z_StrCat(&client->dlqueue, name); + } +} +void SV_DownloadQueueNext(client_t *client) +{ + char buf[MAX_QPATH*2]; + char *name = client->dlqueue; + char *next; + if (!name) + return; + next = strchr(name, '\\'); + if (next) + { + host_client->dlqueue = Z_StrDup(next+1); + *next = 0; + } + else + client->dlqueue = NULL; + + next = va("download \"%s\"\n", COM_QuotedString(name, buf, sizeof(buf), true)); + ClientReliableWrite_Begin (client, svc_stufftext, 2+strlen(next)); + ClientReliableWrite_String (client, next); + Z_Free(name); +} +void SV_DownloadQueueClear(client_t *client) +{ + if (client->dlqueue) + Z_Free(client->dlqueue); + client->dlqueue = NULL; +} +#endif + void SV_DemoDownload_f(void) { int arg; @@ -3306,7 +3359,16 @@ void SV_DemoDownload_f(void) name = Cmd_Argv(1); if (!strcmp(name, "\\") || !Q_strcasecmp(name, "stop") || !Q_strcasecmp(name, "cancel")) { - //fte servers don't do download queues, as it is impossible to avoid race conditions with vanilla clients anyway. + if (strcmp(name, "\\")) + { //cancel/stop kill any current download too. which is annoying + if (host_client->download) + VFS_CLOSE (host_client->download); + host_client->download = NULL; + host_client->downloadstarted = false; + } +#ifndef NOLEGACY + SV_DownloadQueueClear(host_client); +#endif return; } } @@ -3339,6 +3401,14 @@ void SV_DemoDownload_f(void) if (!mvdname) SV_ClientPrintf (host_client, PRINT_HIGH, "%s is an invalid MVD demonum.\n", name); +#ifndef NOLEGACY + else if (!(host_client->protocol & PEXT_CHUNKEDDOWNLOADS) || !strncmp(InfoBuf_ValueForKey(&host_client->userinfo, "*client"), "ezQuake", 7)) + { //chunked downloads was built around the client being in control (because only it knows which files are needed) + //but ezquake never implemented that part + SV_DownloadQueueAdd(host_client, va("demos/%s", mvdname)); + continue; + } +#endif else { const char *s = va("download \"demos/%s\"\n", mvdname); @@ -3346,6 +3416,10 @@ void SV_DemoDownload_f(void) ClientReliableWrite_String (host_client, s); } } +#ifndef NOLEGACY + if (!host_client->download) + SV_DownloadQueueNext(host_client); +#endif } #endif @@ -3458,7 +3532,7 @@ void SV_BeginDownload_f(void) } } - if (!host_client->download) + if (!host_client->download && !result) result = -1; //this isn't likely, but hey. //handle errors @@ -3514,6 +3588,7 @@ void SV_BeginDownload_f(void) } if (ISNQCLIENT(host_client)) host_client->send_message = true; + SV_DownloadQueueNext(host_client); return; } @@ -3588,6 +3663,11 @@ void SV_StopDownload_f(void) SV_ClientPrintf(host_client, PRINT_HIGH, "Can't stop download - not downloading anything\n"); host_client->downloadstarted = false; + +#ifndef NOLEGACY + SV_DownloadQueueNext(host_client); +// SV_DownloadQueueClear(host_client); +#endif } //============================================================================= diff --git a/engine/shaders/glsl/defaultskin.glsl b/engine/shaders/glsl/defaultskin.glsl index 37ea7dd9d..b671c2991 100644 --- a/engine/shaders/glsl/defaultskin.glsl +++ b/engine/shaders/glsl/defaultskin.glsl @@ -18,8 +18,11 @@ //!!permu OFFSETMAPPING // auto-added when r_glsl_offsetmapping is set //!!permu NONORMALS // states that there's no normals available, which affects lighting. //!!permu ORM // specularmap is r:Occlusion, g:Roughness, b:Metalness -//!!permu F0R // specularmap is rgb:F0, a:Roughness (instead of exponent) -//!!permu PBR // an attempt at pbr logic +//!!permu SG // specularmap is rgb:F0, a:Roughness (instead of exponent) +//!!permu PBR // an attempt at pbr logic (enabled from ORM or SG) +//!!permu NOOCCLUDE // ignores the use of ORM's occlusion... yeah, stupid. +//!!permu EIGHTBIT // uses software-style paletted colourmap lookups +//!!permu ALPHATEST // if defined, this is the required alpha level (more versatile than doing it at the q3shader level) #include "sys/defs.h" @@ -33,6 +36,9 @@ #define affine #endif +#if defined(ORM) || defined(SG) + #define PBR +#endif #ifdef NONORMALS //lots of things need normals to work properly. make sure nothing breaks simply because they added an extra texture. #undef BUMP @@ -44,7 +50,6 @@ - #ifdef VERTEX_SHADER #include "sys/skeletal.h" @@ -53,7 +58,7 @@ varying vec4 light; #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) varying vec3 eyevector; #endif -#ifdef REFLECTCUBEMASK +#if defined(PBR)||defined(REFLECTCUBEMASK) varying mat3 invsurface; #endif #ifdef TESS @@ -73,22 +78,28 @@ void main () vec3 n, s, t, w; gl_Position = skeletaltransform_wnst(w,n,s,t); n = normalize(n); - float d = dot(n,e_light_dir); - if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex. - d = 0.0; //this avoids the dark side going below the ambient level. - light.rgb += (d*e_light_mul); + s = normalize(s); + t = normalize(t); + #ifndef PBR + float d = dot(n,e_light_dir); + if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex. + d = 0.0; //this avoids the dark side going below the ambient level. + light.rgb += (d*e_light_mul); + #else + light.rgb = vec3(1.0); + #endif #endif -#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) +#if defined(PBR) + eyevector = e_eyepos - w.xyz; +#elif defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) vec3 eyeminusvertex = e_eyepos - w.xyz; eyevector.x = dot(eyeminusvertex, s.xyz); eyevector.y = dot(eyeminusvertex, t.xyz); eyevector.z = dot(eyeminusvertex, n.xyz); #endif -#ifdef REFLECTCUBEMASK - invsurface[0] = s; - invsurface[1] = t; - invsurface[2] = n; +#if defined(PBR) || defined(REFLECTCUBEMASK) + invsurface = mat3(s, t, n); #endif tc = v_texcoord; @@ -241,10 +252,39 @@ varying vec4 light; #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) varying vec3 eyevector; #endif -#ifdef REFLECTCUBEMASK +#if defined(PBR) || defined(REFLECTCUBEMASK) varying mat3 invsurface; #endif +#ifdef PBR +#include "sys/pbr.h" +#if 0 +vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection) +{ + float mipCount = 9.0; // resolution of 512x512 + float lod = (pbrInputs.perceptualRoughness * mipCount); + // retrieve a scale and bias to F0. See [1], Figure 3 + vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb; + vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb; + +#ifdef USE_TEX_LOD + vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb; +#else + vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb; +#endif + + vec3 diffuse = diffuseLight * pbrInputs.diffuseColor; + vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); + + // For presentation, this allows us to disable IBL terms + diffuse *= u_ScaleIBLAmbient.x; + specular *= u_ScaleIBLAmbient.y; + + return diffuse + specular; +} +#endif +#endif + void main () { @@ -276,33 +316,87 @@ void main () col.rgb += lc.rgb*e_lowercolour*lc.a; #endif - #if defined(BUMP) && defined(SPECULAR) - vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5); - vec4 specs = texture2D(s_specular, tc); + col *= factor_base; - vec3 halfdir = normalize(normalize(eyevector) + e_light_dir); - float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a); - col.rgb += FTE_SPECULAR_MULTIPLIER * spec * specs.rgb; - #elif defined(REFLECTCUBEMASK) - vec3 bumps = vec3(0, 0, 1); + #define dielectricSpecular 0.04 + #ifdef SPECULAR + vec4 specs = texture2D(s_specular, tc)*factor_spec; + #ifdef ORM + #define occlusion specs.r + #define roughness clamp(specs.g, 0.04, 1.0) + #define metalness specs.b + #define gloss 1.0 //sqrt(1.0-roughness) + #define ambientrgb (specrgb+col.rgb) + vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness); + col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness); + #elif defined(SG) //pbr-style specular+glossiness + //occlusion needs to be baked in. :( + #define roughness (1.0-specs.a) + #define gloss (specs.a) + #define specrgb specs.rgb + #define ambientrgb (specs.rgb+col.rgb) + #else //blinn-phong + #define roughness (1.0-specs.a) + #define gloss specs.a + #define specrgb specs.rgb + #define ambientrgb col.rgb + #endif + #else + #define roughness 0.3 + #define specrgb 1.0 //vec3(dielectricSpecular) + #endif + + #ifdef BUMP + #ifdef PBR //to modelspace + vec3 bumps = normalize(invsurface * (texture2D(s_normalmap, tc).rgb*2.0 - 1.0)); + #else //stay in tangentspace + vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5); + #endif + #else + #ifdef PBR //to modelspace + #define bumps normalize(invsurface[2]) + #else //tangent space + #define bumps vec3(0.0, 0.0, 1.0) + #endif + #endif + + #ifdef PBR + //move everything to model space + col.rgb = DoPBR(bumps, normalize(eyevector), -e_light_dir, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0))*e_light_mul + e_light_ambient*.25*ambientrgb; + #elif defined(gloss) + vec3 halfdir = normalize(normalize(eyevector) - e_light_dir); + float specmag = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss); + col.rgb += FTE_SPECULAR_MULTIPLIER * specmag * specrgb; #endif #ifdef REFLECTCUBEMASK vec3 rtc = reflect(-eyevector, bumps); - rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2]; + #ifndef PBR + rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2]; + #endif rtc = (m_model * vec4(rtc.xyz,0.0)).xyz; col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb; #endif +#if defined(occlusion) && !defined(NOOCCLUDE) + col.rgb *= occlusion; +#endif col *= light * e_colourident; #ifdef FULLBRIGHT vec4 fb = texture2D(s_fullbright, tc); // col.rgb = mix(col.rgb, fb.rgb, fb.a); - col.rgb += fb.rgb * fb.a * e_glowmod.rgb; + col.rgb += fb.rgb * fb.a * e_glowmod.rgb * factor_emit.rgb; + #elif defined(PBR) + col.rgb += e_glowmod.rgb * factor_emit.rgb; #endif #endif +#ifdef ALPHATEST + if (!(col.a ALPHATEST)) + discard; +#endif + gl_FragColor = fog4(col); } #endif diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index a4fabb2bb..f39135f0d 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -15,6 +15,10 @@ !!samps lightmap deluxemap !!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3 +#if defined(ORM) || defined(SG) + #define PBR +#endif + #include "sys/defs.h" //this is what normally draws all of your walls, even with rtlights disabled @@ -58,9 +62,7 @@ void main () eyevector.z = dot(eyeminusvertex, v_normal.xyz); #endif #if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE) - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; + invsurface = mat3(v_svector, v_tvector, v_normal); #endif tc = v_texcoord; #ifdef FLOW @@ -230,6 +232,8 @@ void main() #ifdef FRAGMENT_SHADER #define s_colourmap s_t0 +#include "sys/pbr.h" + #ifdef OFFSETMAPPING #include "sys/offsetmapping.h" #endif @@ -256,12 +260,12 @@ void main () #endif -//yay, regular texture! - gl_FragColor = texture2D(s_diffuse, tc); +//Read the base texture (with EIGHTBIT only alpha is needed) + vec4 col = texture2D(s_diffuse, tc); #if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK)) vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5); -#elif defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK) +#elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK) vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist. #endif @@ -306,61 +310,89 @@ void main () #endif #endif -//add in specular, if applicable. -#ifdef SPECULAR - vec4 specs = texture2D(s_specular, tc); - vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead - float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a); - spec *= FTE_SPECULAR_MULTIPLIER; -//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool. -//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway, -//we default to something that is not garish when the light value is directly infront of every single pixel. -//we can justify this difference due to the rtlight editor etc showing the *4. - gl_FragColor.rgb += spec * specs.rgb; -#endif +// col *= factor_base; + #define dielectricSpecular 0.04 + #ifdef SPECULAR + vec4 specs = texture2D(s_specular, tc);//*factor_spec; + #ifdef ORM + #define occlusion specs.r + #define roughness specs.g + #define metalness specs.b + #define gloss (1.0-roughness) + #define ambientrgb (specrgb+col.rgb) + vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness); + col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness); + #elif defined(SG) //pbr-style specular+glossiness + //occlusion needs to be baked in. :( + #define roughness (1.0-specs.a) + #define gloss specs.a + #define specrgb specs.rgb + #define ambientrgb (specs.rgb+col.rgb) + #else //blinn-phong + #define roughness (1.0-specs.a) + #define gloss specs.a + #define specrgb specs.rgb + #define ambientrgb col.rgb + #endif + #else + #define roughness 0.3 + #define specrgb 1.0 //vec3(dielectricSpecular) + #endif + + //add in specular, if applicable. + #ifdef PBR + col.rgb = DoPBR(norm, normalize(eyevector), deluxe, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0));//*e_light_mul + e_light_ambient*.25*ambientrgb; + #elif defined(gloss) + vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead + float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * gloss); + spec *= FTE_SPECULAR_MULTIPLIER; + //NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool. + //As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway, + //we default to something that is not garish when the light value is directly infront of every single pixel. + //we can justify this difference due to the rtlight editor etc showing the *4. + col.rgb += spec * specrgb; + #endif #ifdef REFLECTCUBEMASK vec3 rtc = reflect(normalize(-eyevector), norm); rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2]; rtc = (m_model * vec4(rtc.xyz,0.0)).xyz; - gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb; + col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb; #endif #ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant. lightmaps *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1. float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated. lightmaps -= 1.0 / 128.0; //software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest - gl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those. - gl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly. - gl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical. + col.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those. + col.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly. + col.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical. #else //now we have our diffuse+specular terms, modulate by lightmap values. - gl_FragColor.rgb *= lightmaps.rgb; + col.rgb *= lightmaps.rgb; //add on the fullbright #ifdef FULLBRIGHT - gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb; + col.rgb += texture2D(s_fullbright, tc).rgb; #endif #endif //entity modifiers - gl_FragColor = gl_FragColor * e_colourident; + col *= e_colourident; #if defined(MASK) #if defined(MASKLT) - if (gl_FragColor.a < MASK) + if (col.a < MASK) discard; #else - if (gl_FragColor.a >= MASK) + if (col.a >= MASK) discard; #endif - gl_FragColor.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog. + col.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog. #endif //and finally hide it all if we're fogged. -#ifdef FOG - gl_FragColor = fog4(gl_FragColor); -#endif + gl_FragColor = fog4(col); } #endif diff --git a/engine/shaders/glsl/rtlight.glsl b/engine/shaders/glsl/rtlight.glsl index b9eda3059..822975dbe 100644 --- a/engine/shaders/glsl/rtlight.glsl +++ b/engine/shaders/glsl/rtlight.glsl @@ -13,6 +13,10 @@ !!samps =PCF shadowmap !!samps =CUBE projectionmap +#if defined(ORM) || defined(SG) + #define PBR +#endif + #include "sys/defs.h" //this is the main shader responsible for realtime dlights. @@ -69,6 +73,9 @@ void main () { vec3 n, s, t, w; gl_Position = skeletaltransform_wnst(w,n,s,t); +n = normalize(n); +s = normalize(s); +t = normalize(t); tcbase = v_texcoord; //pass the texture coords straight through #ifdef ORTHO vec3 lightminusvertex = -l_lightdirection; @@ -97,9 +104,7 @@ void main () eyevector.z = dot(eyeminusvertex, n.xyz); #endif #ifdef REFLECTCUBEMASK - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; + invsurface = mat3(v_svector, v_tvector, v_normal); #endif #if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO) //for texture projections/shadowmapping on dlights @@ -229,6 +234,8 @@ void main() #include "sys/offsetmapping.h" #endif +#include "sys/pbr.h" + void main () { #ifdef ORTHO @@ -277,25 +284,54 @@ void main () vec4 specs = texture2D(s_specular, tcbase); #endif - vec3 diff; -#ifdef NOBUMP - //surface can only support ambient lighting, even for lights that try to avoid it. - diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y); -#else - vec3 nl = normalize(lightvector); - #ifdef BUMP - diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0)); + #define dielectricSpecular 0.04 + #ifdef SPECULAR + #ifdef ORM //pbr-style occlusion+roughness+metalness + #define occlusion specs.r + #define roughness clamp(specs.g, 0.04, 1.0) + #define metalness specs.b + #define gloss 1.0 //sqrt(1.0-roughness) + #define ambientrgb (specrgb+col.rgb) + vec3 specrgb = mix(vec3(dielectricSpecular), bases.rgb, metalness); + bases.rgb = bases.rgb * (1.0 - dielectricSpecular) * (1.0-metalness); + #elif defined(SG) //pbr-style specular+glossiness + //occlusion needs to be baked in. :( + #define roughness (1.0-specs.a) + #define gloss specs.a + #define specrgb specs.rgb + #define ambientrgb (specs.rgb+col.rgb) + #else //blinn-phong + #define roughness (1.0-specs.a) + #define gloss specs.a + #define specrgb specs.rgb + #define ambientrgb col.rgb + #endif #else - //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. - diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); + #define roughness 0.3 + #define specrgb 1.0 //vec3(dielectricSpecular) #endif -#endif - -#ifdef SPECULAR - vec3 halfdir = normalize(normalize(eyevector) + nl); - float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a)*float(SPECMUL); - diff += l_lightcolourscale.z * spec * specs.rgb; +#ifdef PBR + vec3 diff = DoPBR(bumps, normalize(eyevector), normalize(lightvector), roughness, bases.rgb, specrgb, l_lightcolourscale); +#else + vec3 diff; + #ifdef NOBUMP + //surface can only support ambient lighting, even for lights that try to avoid it. + diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y); + #else + vec3 nl = normalize(lightvector); + #ifdef BUMP + diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0)); + #else + //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. + diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); + #endif + #endif + #ifdef SPECULAR + vec3 halfdir = normalize(normalize(eyevector) + nl); + float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss)*float(SPECMUL); + diff += l_lightcolourscale.z * spec * specrgb; + #endif #endif #ifdef REFLECTCUBEMASK @@ -314,11 +350,15 @@ void main () /*2d projection, not used*/ // diff *= texture2d(s_projectionmap, shadowcoord); #endif +#if defined(occlusion) && !defined(NOOCCLUDE) + diff *= occlusion; +#endif #if defined(VERTEXCOLOURS) diff *= vc.rgb * vc.a; #endif - gl_FragColor = vec4(fog3additive(diff*colorscale*l_lightcolour), 1.0); + diff *= colorscale*l_lightcolour; + gl_FragColor = vec4(fog3additive(diff), 1.0); } #endif diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 9babeb7d4..64ca50d38 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -4668,7 +4668,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre } #endif } - if (info->srgb != 1 && (vid.flags & VID_SRGB_FB)) + if (info->srgb > 0 && (vid.flags & VID_SRGB_FB)) vid.flags |= VID_SRGBAWARE; return true; } diff --git a/plugins/models/gltf.c b/plugins/models/gltf.c index 9d5914a61..aa34fff6c 100644 --- a/plugins/models/gltf.c +++ b/plugins/models/gltf.c @@ -8,6 +8,12 @@ extern modplugfuncs_t *modfuncs; #define GLTFMODELS + +//'The units for all linear distances are meters.' +//'feh: 1 metre is approx. 26.24671916 qu.' +//if the player is 1.6m tall, and the player's model is around 48qu, then 1m=30qu, which is a slightly nicer number to work with, and 1qu is a really poorly defined unit. +#define GLTFSCALE 30 + #ifdef GLTFMODELS typedef struct json_s { @@ -283,6 +289,10 @@ static qintptr_t JSON_GetInteger(json_t *t, const char *child, int fallback) l = MAX_QPATH-1; memcpy(tmp, t->bodystart, l); tmp[l] = 0; + if (!strcmp(tmp, "false")) //special cases, for booleans + return 0; + if (!strcmp(tmp, "true")) //special cases, for booleans + return 1; return (qintptr_t)strtoll(tmp, NULL, 0); } return fallback; @@ -490,11 +500,12 @@ typedef struct gltf_s unsigned int numsurfaces; json_t *r; - int bonemap[MAX_BONES]; //remap skinned bones. I hate that we have to do this. + int *bonemap;//[MAX_BONES]; //remap skinned bones. I hate that we have to do this. struct gltfbone_s { char name[32]; int parent; + int camera; double amatrix[16]; double inverse[16]; struct @@ -507,7 +518,7 @@ typedef struct gltf_s struct gltf_accessor *input; struct gltf_accessor *output; } *rot, *scale, *translation; - } bones[MAX_BONES]; + } *bones;//[MAX_BONES]; unsigned int numbones; int warnlimit; //don't spam warnings. this is a loader, not a spammer @@ -924,7 +935,7 @@ static void *GLTF_AccessorToDataUB(gltf_t *gltf, size_t outverts, unsigned int o static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_accessor *a) { //input should only be ubytes||ushorts. const unsigned int outcomponents = 4; - unsigned char *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o; + boneidx_t *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o; char *in = a->data; @@ -964,7 +975,7 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_ for (c = 0; c < ic; c++) { v = ((unsigned short*)in)[c]; - if (v > 255) + if (v > MAX_BONES) v = 0; o[c] = gltf->bonemap[v]; } @@ -974,6 +985,7 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_ in += a->bytestride; } break; + //the spec doesn't require these. // case 5125: //UNSIGNED_INT /* case 5126: //FLOAT while(outverts --> 0) @@ -1117,8 +1129,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert { qboolean doubleSided; int alphaMode; - //double alphaCutoff; + double alphaCutoff; char shader[8192]; + char alphaCutoffmodifier[128]; json_t *mat = JSON_FindIndexedChild(gltf->r, "materials", material); galiasskin_t *ret; @@ -1130,19 +1143,8 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert pbrmr = JSON_FindChild(mat, "pbrMetallicRoughness"); blinn = JSON_FindChild(mat, "extensions.KHR_materials_cmnBlinnPhong"); -/* JSON_WarnIfChild(mat, "name"); - JSON_WarnIfChild(pbrsg, "diffuseFactor"); - JSON_WarnIfChild(pbrsg, "diffuseTexture"); - JSON_WarnIfChild(pbrsg, "specularFactor"); - JSON_WarnIfChild(pbrsg, "glossinessFactor"); - JSON_WarnIfChild(pbrsg, "specularGlossinessTexture"); - JSON_WarnIfChild(mat, "normalTexture"); - JSON_WarnIfChild(mat, "occlusionTexture"); - JSON_WarnIfChild(mat, "emissiveTexture"); - JSON_WarnIfChild(mat, "emissiveFactor"); //0,0,0 -*/ doubleSided = JSON_GetInteger(mat, "doubleSided", false); - //alphaCutoff = JSON_GetInteger(mat, "alphaCutoff", 0.5); + alphaCutoff = JSON_GetFloat(mat, "alphaCutoff", 0.5); if (JSON_Equals(mat, "alphaMode", "MASK")) alphaMode = 1; else if (JSON_Equals(mat, "alphaMode", "BLEND")) @@ -1162,6 +1164,11 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert else Q_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), "%i", material); + if (alphaMode == 1) + Q_snprintf(alphaCutoffmodifier, sizeof(alphaCutoffmodifier), "#ALPHATEST=>%f", alphaCutoff); + else + *alphaCutoffmodifier = 0; + if (unlit) { //if this extension was present, then we don't get ANY lighting info. int albedo = JSON_GetInteger(pbrmr, "baseColorTexture.index", -1); //.rgba @@ -1171,7 +1178,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert "{\n" "surfaceparm nodlight\n" "%s"//cull - "program default2d\n" //fixme: there's no gpu skeletal stuff with this prog + "program default2d%s\n" //fixme: there's no gpu skeletal stuff with this prog "{\n" "map $diffuse\n" "%s" //blend @@ -1179,8 +1186,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert "}\n" "fte_basefactor %f %f %f %f\n" "}\n", - doubleSided?"cullface disable\n":"", - (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + doubleSided?"cull disable\n":"", + alphaCutoffmodifier, + (alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"", vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", JSON_GetFloat(pbrmr, "baseColorFactor.0", 1), JSON_GetFloat(pbrmr, "baseColorFactor.1", 1), @@ -1204,7 +1212,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert Q_snprintf(shader, sizeof(shader), "{\n" "%s"//cull - "program defaultskin#VC\n" + "program defaultskin#VC%s\n" "{\n" "map $diffuse\n" "%s" //blend @@ -1214,20 +1222,21 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert "fte_specularfactor %f %f %f %f\n" "fte_fullbrightfactor %f %f %f 1.0\n" "}\n", - doubleSided?"cullface disable\n":"", - (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + doubleSided?"cull disable\n":"", + alphaCutoffmodifier, + (alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"", vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", JSON_GetFloat(pbrsg, "diffuseFactor.0", 1), JSON_GetFloat(pbrsg, "diffuseFactor.1", 1), JSON_GetFloat(pbrsg, "diffuseFactor.2", 1), JSON_GetFloat(pbrsg, "diffuseFactor.3", 1), - JSON_GetFloat(pbrsg, "specularFactor.0", 1), + JSON_GetFloat(pbrsg, "specularFactor.0", 1), //FIXME: divide by gl_specular JSON_GetFloat(pbrsg, "specularFactor.1", 1), JSON_GetFloat(pbrsg, "specularFactor.2", 1), - JSON_GetFloat(pbrsg, "shininessFactor", 1), - JSON_GetFloat(mat, "emissiveFactor.0", 1), - JSON_GetFloat(mat, "emissiveFactor.1", 1), - JSON_GetFloat(mat, "emissiveFactor.2", 1) + JSON_GetFloat(pbrsg, "shininessFactor", 1), //FIXME: divide by gl_specular_power + JSON_GetFloat(mat, "emissiveFactor.0", 0), + JSON_GetFloat(mat, "emissiveFactor.1", 0), + JSON_GetFloat(mat, "emissiveFactor.2", 0) ); } else if (pbrsg) @@ -1238,7 +1247,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert Q_snprintf(shader, sizeof(shader), "{\n" "%s"//cull - "program defaultskin#VC\n" + "program defaultskin#SG#VC#NOOCCLUDE%s\n" "{\n" "map $diffuse\n" "%s" //blend @@ -1247,9 +1256,11 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert "fte_basefactor %f %f %f %f\n" "fte_specularfactor %f %f %f %f\n" "fte_fullbrightfactor %f %f %f 1.0\n" + "bemode rtlight rtlight_sg\n" "}\n", - doubleSided?"cullface disable\n":"", - (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + doubleSided?"cull disable\n":"", + alphaCutoffmodifier, + (alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"", vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", JSON_GetFloat(pbrsg, "diffuseFactor.0", 1), JSON_GetFloat(pbrsg, "diffuseFactor.1", 1), @@ -1259,9 +1270,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert JSON_GetFloat(pbrsg, "specularFactor.1", 1), JSON_GetFloat(pbrsg, "specularFactor.2", 1), JSON_GetFloat(pbrsg, "glossinessFactor", 1)*32, //this is fucked. - JSON_GetFloat(mat, "emissiveFactor.0", 1), - JSON_GetFloat(mat, "emissiveFactor.1", 1), - JSON_GetFloat(mat, "emissiveFactor.2", 1) + JSON_GetFloat(mat, "emissiveFactor.0", 0), + JSON_GetFloat(mat, "emissiveFactor.1", 0), + JSON_GetFloat(mat, "emissiveFactor.2", 0) ); } else if (pbrmr) @@ -1270,45 +1281,52 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert int mrt = JSON_GetInteger(pbrmr, "metallicRoughnessTexture.index", -1); //.r = unused, .g = roughness, .b = metalic, .a = unused int occ = JSON_GetInteger(mat, "occlusionTexture.index", -1); //.r - //now work around potential lame exporters. + //now work around potential lame exporters (yay dds?). occ = JSON_GetInteger(mat, "extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index", occ); mrt = JSON_GetInteger(mat, "extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index", mrt); if (occ != mrt && occ != -1) //if its -1 then the mrt should have an unused channel set to 1. however, this isn't guarenteed... + { + occ = -1; //not supported. fixme: support some weird loadtexture channel merging stuff if (gltf->warnlimit --> 0) Con_Printf(CON_WARNING"%s: Separate occlusion and metallicRoughness textures are not supported\n", gltf->mod->name); + } //note: extensions.MSFT_packing_normalRoughnessMetallic.normalRoughnessMetallicTexture.index gives rg=normalxy, b=roughness, .a=metalic //(would still need an ao map, and probably wouldn't work well as bc3 either) ret->frame->texnums.base = GLTF_LoadTexture(gltf, albedo, 0); - ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, 0); + ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, IF_NOSRGB); Q_snprintf(shader, sizeof(shader), "{\n" "%s"//cull - "program defaultskin#PBR_ORM#VC\n" + "program defaultskin#ORM#VC%s%s\n" "{\n" "map $diffuse\n" "%s" //blend "%s" //rgbgen "}\n" "fte_basefactor %f %f %f %f\n" - "fte_specularfactor 1.0 %f %f 1.0\n" + "fte_specularfactor %f %f %f 1.0\n" "fte_fullbrightfactor %f %f %f 1.0\n" + "bemode rtlight rtlight_orm\n" "}\n", - doubleSided?"cullface disable\n":"", - (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + doubleSided?"cull disable\n":"", + (occ==-1)?"#NOOCCLUDE":"", + alphaCutoffmodifier, + (alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"", vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", JSON_GetFloat(pbrmr, "baseColorFactor.0", 1), JSON_GetFloat(pbrmr, "baseColorFactor.1", 1), JSON_GetFloat(pbrmr, "baseColorFactor.2", 1), JSON_GetFloat(pbrmr, "baseColorFactor.3", 1), - JSON_GetFloat(pbrmr, "metallicFactor", 1), - JSON_GetFloat(pbrmr, "roughnessFactor", 1), - JSON_GetFloat(mat, "emissiveFactor.0", 1), - JSON_GetFloat(mat, "emissiveFactor.1", 1), - JSON_GetFloat(mat, "emissiveFactor.2", 1) + JSON_GetFloat(mat, "occlusionTexture.strength", 1), + JSON_GetFloat(pbrmr, "metallicFactor", 1), + JSON_GetFloat(pbrmr, "roughnessFactor", 1), + JSON_GetFloat(mat, "emissiveFactor.0", 0), + JSON_GetFloat(mat, "emissiveFactor.1", 0), + JSON_GetFloat(mat, "emissiveFactor.2", 0) ); } ret->frame->texnums.bump = GLTF_LoadTexture(gltf, JSON_GetInteger(mat, "normalTexture.index", -1), IF_NOSRGB|IF_TRYBUMP); @@ -1374,9 +1392,14 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double surf = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf)); - surf->surfaceid = meshidx; - surf->contents = FTECONTENTS_BODY; - surf->csurface.flags = 0; + surf->surfaceid = surf->contents = JSON_GetInteger(prim, "extras.fte.surfaceid", meshidx); + surf->contents = JSON_GetInteger(prim, "extras.fte.contents", FTECONTENTS_BODY); + surf->csurface.flags = JSON_GetInteger(prim, "extras.fte.surfaceflags", 0); + surf->geomset = JSON_GetInteger(prim, "extras.fte.geomset", ~0u); + surf->geomid = JSON_GetInteger(prim, "extras.fte.geomid", 0); + surf->mindist = JSON_GetInteger(prim, "extras.fte.mindist", 0); + surf->maxdist = JSON_GetInteger(prim, "extras.fte.maxdist", 0); + surf->shares_bones = gltf->numsurfaces; surf->shares_verts = gltf->numsurfaces; JSON_ReadBody(meshname, surf->surfacename, sizeof(surf->surfacename)); @@ -1474,6 +1497,12 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double surf->numskins = 1; surf->ofsskins = GLTF_LoadMaterial(gltf, mat, surf->ofs_rgbaub||surf->ofs_rgbaf); + if (!tang.data) + { + modfuncs->AccumulateTextureVectors(surf->ofs_skel_xyz, surf->ofs_st_array, surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->ofs_indexes, surf->numindexes, !norm.data); + modfuncs->NormaliseTextureVectors(surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->numverts, !norm.data); + } + gltf->numsurfaces++; surf->nextsurf = mod->meshinfo; mod->meshinfo = surf; @@ -1551,10 +1580,16 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16], int skinidx; struct gltfbone_s *b; if (nodeidx < 0 || nodeidx >= gltf->numbones) + { + Con_Printf(CON_WARNING"%s: Invalid node index %i\n", gltf->mod->name, nodeidx); return false; + } node = JSON_FindIndexedChild(gltf->r, "nodes", nodeidx); if (!node) + { + Con_Printf(CON_WARNING"%s: Invalid node index %i\n", gltf->mod->name, nodeidx); return false; + } b = &gltf->bones[nodeidx]; b->parent = parentidx; @@ -1638,7 +1673,7 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16], inversef = inverse.data; if (inverse.componentType != 5126/*FLOAT*/ || inverse.type != ((4<<8) | 4)/*mat4x4*/) inverse.count = 0; - for (j = 0; j < countof(gltf->bonemap); j++, inversef+=inverse.bytestride/sizeof(float)) + for (j = 0; j < MAX_BONES; j++, inversef+=inverse.bytestride/sizeof(float)) { int b = JSON_GetIndexedInteger(joints, j, -1); if (b < 0) @@ -1705,7 +1740,7 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16], GLTF_ProcessNode(gltf, JSON_GetInteger(c, NULL, -1), b->amatrix, nodeidx, isjoint); } - JSON_FlagAsUsed(node, "camera"); + b->camera = JSON_GetInteger(node, "camera", -1); JSON_WarnIfChild(node, "weights", &gltf->warnlimit); //default value for morph weight animations JSON_WarnIfChild(node, "extensions", &gltf->warnlimit); @@ -1798,8 +1833,8 @@ static void LerpAnimData(gltf_t *gltf, struct gltf_animsampler *samp, float time struct gltf_accessor *in = &samp->input; struct gltf_accessor *out = &samp->output; - t1 = t2 = Anim_GetTime(in, 0); - for (f2 = 1 ; f2 < in->count; f2++) + t1 = t2 = Anim_GetTime(in, f1); + for (f2 = 1; f2 < in->count; f2++) { t2 = Anim_GetTime(in, f2); if (t2 > time) @@ -1808,19 +1843,35 @@ static void LerpAnimData(gltf_t *gltf, struct gltf_animsampler *samp, float time f1 = f2; } - //assume linear - if (f1==f2 || t1==t2) - { - Anim_GetVal(out, f1, result, elems); - return; + if (time <= t1) + { //if before the first time, clamp it. + w1 = 1; + w2 = 0; + } + else if (time >= t2) + { //if after tha last frame we could find, clamp it to the last. + w1 = 0; + w2 = 1; + } + else + { //assume linear + w2 = (time-t1)/(t2-t1); +// if (1) //step it. it'll still get lerped though. :( +// w2 = (w2>0.5)?1:0; + w1 = 1-w2; } - w2 = (time-t1)/(t2-t1); - w1 = 1-w2; - Anim_GetVal(out, f1, v1, elems); - Anim_GetVal(out, f2, v2, elems); - for (c = 0; c < elems; c++) - result[c] = v1[c]*w1 + w2*v2[c]; + if (w1 >= 1) + Anim_GetVal(out, f1, result, elems); + else if (w2 >= 1) + Anim_GetVal(out, f2, result, elems); + else + { + Anim_GetVal(out, f1, v1, elems); + Anim_GetVal(out, f2, v2, elems); + for (c = 0; c < elems; c++) + result[c] = v1[c]*w1 + w2*v2[c]; + } } static void GLTF_RemapBone(gltf_t *gltf, int *nextidx, int b) @@ -1877,6 +1928,20 @@ static void GLTF_RewriteBoneTree(gltf_t *gltf) //we do NOT supported nested nodes right now... static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, void *buffer, size_t buffersize) { + static struct + { + const char *name; + qboolean supported; //unsupported extensions don't really need to be listed, but they do prevent warnings from unkown-but-used extensions + } extensions[] = + { + {"KHR_materials_pbrSpecularGlossiness", true}, +//draft {"KHR_materials_cmnBlinnPhong", true}, + {"KHR_materials_unlit", true}, + {"KHR_texture_transform", false}, + {"KHR_draco_mesh_compression", false}, + {"MSFT_texture_dds", true}, + {"MSFT_packing_occlusionRoughnessMetallic", true}, + }; gltf_t gltf; int pos=0, j, k; json_t *scene, *n, *anim; @@ -1888,6 +1953,9 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, unsigned int numframegroups = 0; float *baseframe; memset(&gltf, 0, sizeof(gltf)); + gltf.bonemap = malloc(sizeof(*gltf.bonemap)*MAX_BONES); + gltf.bones = malloc(sizeof(*gltf.bones)*MAX_BONES); + memset(gltf.bones, 0, sizeof(*gltf.bones)*MAX_BONES); gltf.r = JSON_Parse(NULL, mod->name, NULL, json, &pos, jsonsize); gltf.mod = mod; gltf.buffers[0].data = buffer; @@ -1909,23 +1977,33 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, { char extname[256]; JSON_ReadBody(n, extname, sizeof(extname)); - Con_Printf(CON_ERROR "%s: Required gltf2 extension \"%s\" not supported\n", mod->name, extname); + for (j = 0; j < countof(extensions); j++) + { + if (!strcmp(extname, extensions[j].name)) + break; + } + if (j==countof(extensions) || !extensions[j].supported) + Con_Printf(CON_ERROR "%s: Required gltf2 extension \"%s\" not supported\n", mod->name, extname); - JSON_Destroy(gltf.r); - return false; + goto abort; } + for(n = JSON_FindIndexedChild(gltf.r, "extensionsUsed", 0); n; n = n->sibling) { //must be a superset of the above. char extname[256]; JSON_ReadBody(n, extname, sizeof(extname)); - if (!strcmp(extname, "KHR_materials_pbrSpecularGlossiness")) - ; - else if (!strcmp(extname, "KHR_texture_transform")) - ; - else + for (j = 0; j < countof(extensions); j++) + { + if (!strcmp(extname, extensions[j].name)) + break; + } + if (j==countof(extensions) || !extensions[j].supported) Con_Printf(CON_WARNING "%s: gltf2 extension \"%s\" not known\n", mod->name, extname); } + VectorClear(mod->maxs); + VectorClear(mod->mins); + //we don't really care about cameras. JSON_FlagAsUsed(gltf.r, "cameras"); @@ -1933,16 +2011,21 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, memset(&rootmatrix, 0, sizeof(rootmatrix)); #if 1 //transform from gltf to quake. mostly only needed for the base pose. - rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = 1; rootmatrix[15] = 1; + rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = GLTFSCALE; rootmatrix[15] = 1; #else rootmatrix[0] = rootmatrix[5] = rootmatrix[10] = 1; rootmatrix[15] = 1; #endif - for (j = 0; j < countof(gltf.bones); j++) + for (j = 0; ; j++) { n = JSON_FindIndexedChild(gltf.r, "nodes", j); if (!n) break; + if (j == MAX_BONES) + { + Con_Printf(CON_WARNING"%s: too many nodes (max %i)\n", mod->name, MAX_BONES); + break; + } if (!JSON_ReadBody(JSON_FindChild(n, "name"), gltf.bones[j].name, sizeof(gltf.bones[j].name))) { if (n) @@ -1950,6 +2033,7 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, else Q_snprintf(gltf.bones[j].name, sizeof(gltf.bones[j].name), "bone%i", j); } + gltf.bones[j].camera = -1; gltf.bones[j].parent = -1; gltf.bones[j].amatrix[0] = gltf.bones[j].amatrix[5] = gltf.bones[j].amatrix[10] = gltf.bones[j].amatrix[15] = 1; gltf.bones[j].inverse[0] = gltf.bones[j].inverse[5] = gltf.bones[j].inverse[10] = gltf.bones[j].inverse[15] = 1; @@ -1966,8 +2050,9 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, if (!n) break; n->used = true; - if (!GLTF_ProcessNode(&gltf, JSON_GetInteger(n, NULL, -1), rootmatrix, -1, false)) - break; +// if (! + GLTF_ProcessNode(&gltf, JSON_GetInteger(n, NULL, -1), rootmatrix, -1, false); +// break; } GLTF_RewriteBoneTree(&gltf); @@ -1979,6 +2064,9 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, Q_strlcpy(bone[j].name, gltf.bones[j].name, sizeof(bone[j].name)); bone[j].parent = gltf.bones[j].parent; + if (gltf.bones[j].camera >= 0 && !mod->camerabone) + mod->camerabone = j+1; + for(k = 0; k < 12; k++) { baseframe[j*12+k] = gltf.bones[j].amatrix[k]; @@ -1990,6 +2078,10 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, numframegroups++; if (numframegroups) { + struct + { + struct gltf_animsampler rot,scale,trans; + } *b = malloc(sizeof(*b)*gltf.numbones); framegroups = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*framegroups)*numframegroups); for (k = 0; k < numframegroups; k++) { @@ -1999,11 +2091,7 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, json_t *samps = JSON_FindChild(anim, "samplers"); int f, l; float maxtime = 0; - struct - { - struct gltf_animsampler rot,scale,trans; - } b[MAX_BONES]; - memset(b, 0, sizeof(b)); + memset(b, 0, sizeof(*b)*gltf.numbones); if (!JSON_ReadBody(JSON_FindChild(anim, "name"), fg->name, sizeof(fg->name))) { @@ -2041,14 +2129,14 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, else if (gltf.warnlimit --> 0) { //these are unsupported if (JSON_Equals(path, NULL, "weights")) //morph weights - Con_Printf("%s: morph animations are not supported\n", mod->name); + Con_Printf(CON_WARNING"%s: morph animations are not supported\n", mod->name); else Con_Printf("%s: undocumented animation type\n", mod->name); } } //TODO: make a guess at the framerate according to sampler intervals - fg->rate = 30; + fg->rate = 60; fg->numposes = max(1, maxtime*fg->rate); if (maxtime) fg->rate = fg->numposes/maxtime; //fix up the rate so we hit the exact end of the animation (so it doesn't have to be quite so exact). @@ -2089,13 +2177,14 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, if (gltf.bones[j].parent < 0) { //rotate any root bones from gltf to quake's orientation. float fnar[12]; - static float toquake[12]={0,0,1,0,1,0,0,0,0,1,0,0}; + static float toquake[12]={0,0,GLTFSCALE,0,GLTFSCALE,0,0,0,0,GLTFSCALE,0,0}; memcpy(fnar, bonematrix, sizeof(fnar)); modfuncs->ConcatTransforms((void*)toquake, (void*)fnar, (void*)bonematrix); } } } } + free(b); } for(surf = mod->meshinfo; surf; surf = surf->nextsurf) @@ -2111,13 +2200,19 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, surf->geomset = ~0; //invalid set = always visible. FIXME: set this according to scene numbers? surf->geomid = 0; } + VectorScale(mod->mins, GLTFSCALE, mod->mins); + VectorScale(mod->maxs, GLTFSCALE, mod->maxs); + if (!mod->meshinfo) + Con_Printf("%s: Doesn't contain any meshes...\n", mod->name); JSON_WarnUnused(gltf.r, &gltf.warnlimit); } else Con_Printf("%s: unsupported gltf version (%.2f)\n", mod->name, gltfver); +abort: JSON_Destroy(gltf.r); - + free(gltf.bones); + free(gltf.bonemap); mod->type = mod_alias; diff --git a/specs/multiprogs.txt b/specs/multiprogs.txt index fe6c4895a..e9999908f 100644 --- a/specs/multiprogs.txt +++ b/specs/multiprogs.txt @@ -1,7 +1,7 @@ FTE_MULTIPROGS void() init; //called before ents are available (spawn() will fail, but you can use addprogs() here) -void() initents; //called just before worldspawn, after ents are spawnable spawn() works but addprogs() may fail) +void() initents; //called just before worldspawn, after ents are spawnable (spawn() works but addprogs() may fail) float thisprogs; //set by the engine. main progs will always be 0, addons will be non-zero and unique. float(string modname) addprogs = #202; //load another progs as well - may fail if too many fields are added. @@ -54,4 +54,4 @@ initents permits the use of spawn(). spawned ents will be *after* world, even th using addprogs later than the main progs' init function can fail if too many(any) new fields are defined. note that a great use of this extension is to add frikbots to any mod without modding it. -externvalue and externset do work for the current progs too. it can be used to implement arrays or some kind of scripts (fteqcc's builtin arrays are more efficient). \ No newline at end of file +externvalue and externset do work for the current progs too. it can be used to implement arrays or some kind of scripts (fteqcc's builtin arrays are more efficient).