From 09196f3d14249bc2a7b2f946055b06417a3c2065 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 7 Feb 2015 22:34:22 +0000 Subject: [PATCH] fix some nq-server issues. make trying to close the window shut down properly while debugging. added r_softwarebanding cvar to approximate 8bit software rendering. fix issue with invalid skeletal lerps. external q3 lightmaps now uses IF_NOMIPMAP and respects gl_lightmap_nearest. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4838 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 2 +- engine/client/cl_parse.c | 2 + engine/client/image.c | 39 +++++++++- engine/client/m_options.c | 1 + engine/client/merged.h | 17 +++-- engine/client/pr_csqc.c | 5 ++ engine/client/quakedef.h | 1 + engine/client/r_surf.c | 3 +- engine/client/renderer.c | 2 + engine/client/sys_win.c | 1 + engine/common/com_mesh.c | 25 ++++++- engine/common/common.c | 3 + engine/common/pr_bgcmd.c | 9 ++- engine/gl/gl_backend.c | 7 ++ engine/gl/gl_draw.c | 8 +- engine/gl/gl_model.c | 14 +++- engine/gl/gl_shader.c | 71 +++++++++++++++++- engine/gl/gl_vidnt.c | 1 + engine/gl/r_bishaders.h | 106 ++++++++++++++++++--------- engine/gl/shader.h | 2 + engine/server/pr_cmds.c | 23 +++++- engine/server/server.h | 1 + engine/server/sv_phys.c | 9 +-- engine/server/sv_send.c | 16 ++-- engine/server/sv_user.c | 16 ++-- engine/shaders/glsl/defaultwall.glsl | 106 ++++++++++++++++++--------- 26 files changed, 384 insertions(+), 106 deletions(-) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index a4ab640bc..db516cfac 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -635,7 +635,7 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->u.q1.msec = 0; } - if (!(predbits & UFP_VIEWANGLE) || (cls.fteprotocolextensions2 & PEXT2_PREDINFO)) + if (!(predbits & UFP_VIEWANGLE) || !(cls.fteprotocolextensions2 & PEXT2_PREDINFO)) { news->u.q1.vangle[0] = ANGLE2SHORT(news->angles[0]); news->u.q1.vangle[1] = ANGLE2SHORT(news->angles[1]); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 83287e8fb..4536d8f34 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -4028,7 +4028,9 @@ void CL_ParseStatic (int version) // copy it to the current state ent->model = cl.model_precache[es.modelindex]; + memset(&ent->framestate, 0, sizeof(ent->framestate)); ent->framestate.g[FS_REG].frame[0] = ent->framestate.g[FS_REG].frame[1] = es.frame; + ent->framestate.g[FS_REG].lerpweight[0] = 1; ent->skinnum = es.skinnum; ent->drawflags = es.hexen2flags; diff --git a/engine/client/image.c b/engine/client/image.c index f0aebbf7b..9da74eb30 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -2813,6 +2813,8 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla switch(mips->encoding) { + case PTI_R8: + return; case PTI_RGBA8: case PTI_RGBX8: case PTI_BGRA8: @@ -3296,9 +3298,39 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, uploadfmt_t orig return; //blurgh //if that format isn't supported/desired, try converting it. - if (sh_config.texfmt[PTI_RGBX8]) + if (sh_config.texfmt[mips->encoding]) return; + if (mips->encoding == PTI_R8) + { + if (sh_config.texfmt[PTI_BGRX8]) //bgra8 is typically faster when supported. + mips->encoding = PTI_BGRX8; + else if (sh_config.texfmt[PTI_BGRX8]) + mips->encoding = PTI_RGBX8; + else if (sh_config.texfmt[PTI_BGRA8]) + mips->encoding = PTI_BGRA8; + else + mips->encoding = PTI_RGBA8; + for (mip = 0; mip < mips->mipcount; mip++) + { + unsigned int i; + unsigned int *out; + unsigned char *in; + in = mips->mip[mip].data; + out = BZ_Malloc(mips->mip[mip].width*mips->mip[mip].height*sizeof(*out)); + for (i = 0; i < mips->mip[mip].width*mips->mip[mip].height; i++) + out[i] = in[i] * 0x01010101; + if (mips->mip[mip].needfree) + BZ_Free(in); + mips->mip[mip].data = out; + mips->mip[mip].needfree = true; + mips->mip[mip].datasize = mips->mip[mip].width*mips->mip[mip].height*sizeof(*out); + } + + if (sh_config.texfmt[mips->encoding]) + return; + } + //should we just use 5551 always? if (mips->encoding == PTI_RGBX8 || mips->encoding == PTI_BGRX8) { @@ -3318,6 +3350,7 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, uploadfmt_t orig } } else*/ + if (sh_config.texfmt[PTI_RGB565]) { for (mip = 0; mip < mips->mipcount; mip++) Image_8888to565(mips->mip[mip].data, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height, mips->encoding == PTI_BGRX8); @@ -3410,6 +3443,9 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case TF_BGRA32: mips->encoding = PTI_BGRA8; break; + case TF_LUM8: + mips->encoding = PTI_R8; + break; case TF_SOLID8: rgbadata = BZ_Malloc(imgwidth * imgheight*4); if (sh_config.texfmt[PTI_BGRX8]) @@ -4184,6 +4220,7 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned pb = 4*256; b = 1; break; + case TF_LUM8: case TF_SOLID8: case TF_TRANS8: case TF_TRANS8_FULLBRIGHT: diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 759a2e2be..d76336dfd 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2691,6 +2691,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ ent.playerindex = -1; ent.skinnum = mods->skingroup; ent.shaderTime = realtime; + ent.framestate.g[FS_REG].lerpweight[0] = 1; ent.framestate.g[FS_REG].frame[0] = mods->framegroup; ent.framestate.g[FS_REG].frametime[0] = realtime - mods->framechangetime; ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; diff --git a/engine/client/merged.h b/engine/client/merged.h index 61281d829..6b671782c 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -16,7 +16,8 @@ typedef enum SKEL_RELATIVE, //relative to parent. SKEL_ABSOLUTE, //relative to model. doesn't blend very well. SKEL_INVERSE_RELATIVE, //pre-inverted. faster than regular relative but has weirdness with skeletal objects. blends okay. - SKEL_INVERSE_ABSOLUTE //final renderable type. + SKEL_INVERSE_ABSOLUTE, //final renderable type. + SKEL_IDENTITY //PANIC } skeltype_t; #ifdef HALFLIFEMODELS @@ -274,6 +275,9 @@ struct pendingtextureinfo //floating point formats PTI_RGBA16F, PTI_RGBA32F, + //small formats. + PTI_R8, + PTI_RG8, //compressed formats PTI_S3RGB1, PTI_S3RGBA1, @@ -341,11 +345,12 @@ typedef struct } srect_t; typedef struct texnums_s { - texid_t base; - texid_t bump; - texid_t specular; - texid_t upperoverlay; - texid_t loweroverlay; + texid_t base; //regular diffuse texture. may have alpha if surface is transparent + texid_t bump; //normalmap. height values packed in alpha. + texid_t specular; //specular lighting values. + texid_t upperoverlay; //diffuse texture for the upper body(shirt colour). no alpha channel. added to base.rgb + texid_t loweroverlay; //diffuse texture for the lower body(trouser colour). no alpha channel. added to base.rgb + texid_t paletted; //8bit paletted data, just because. texid_t fullbright; } texnums_t; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index fbc3eacae..818f57378 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -2712,6 +2712,11 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo csqc_deprecated("runplayerphysics with no ent"); return; } + if (ent->readonly) + { + csqc_deprecated("runplayerphysics called on read-only entity"); + return; + } if (!cl.worldmodel) return; //urm.. diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index a9d175f88..dc135b1bb 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -322,6 +322,7 @@ extern qboolean msg_suppress_1; // suppresses resolution and cache size consol #if !defined(SERVERONLY) && !defined(CLIENTONLY) extern qboolean isDedicated; #endif +extern qboolean wantquit; //flagged if we want to force a quit, safely breaking out of any modal stuff diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index fda1de940..0178eb339 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -50,6 +50,7 @@ extern cvar_t r_stains; extern cvar_t r_loadlits; extern cvar_t r_stainfadetime; extern cvar_t r_stainfadeammount; +extern cvar_t gl_lightmap_nearest; static int lightmap_shift; int Surf_LightmapShift (model_t *model) @@ -2607,7 +2608,7 @@ int Surf_NewExternalLightmaps(int count, char *filepattern, qboolean deluxe) Q_snprintfz(nname, sizeof(nname), filepattern, i - numlightmaps); - TEXASSIGN(lightmap[i]->lightmap_texture, R_LoadHiResTexture(nname, NULL, 0)); + TEXASSIGN(lightmap[i]->lightmap_texture, R_LoadHiResTexture(nname, NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP)); if (lightmap[i]->lightmap_texture->status == TEX_LOADING) COM_WorkerPartialSync(lightmap[i]->lightmap_texture, &lightmap[i]->lightmap_texture->status, TEX_LOADING); lightmap[i]->width = lightmap[i]->lightmap_texture->width; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 0340b4163..01e29a92f 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -127,6 +127,7 @@ cvar_t r_part_rain = CVARFD ("r_part_rain", "0", "Enable particle effects to emit off of surfaces. Mainly used for weather or lava/slime effects."); cvar_t r_skyboxname = SCVARF ("r_skybox", "", CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM); +cvar_t r_softwarebanding = CVARFD ("r_softwarebanding", "0", CVAR_SHADERSYSTEM | CVAR_RENDERERLATCH, "Utilise the Quake colormap in order to emulate 8bit software rendering. This results in banding as well as other artifacts that some believe adds character. Also forces nearest sampling on affected surfaces (palette indicies do not interpolate well)."); cvar_t r_speeds = SCVAR ("r_speeds", "0"); cvar_t r_stainfadeammount = SCVAR ("r_stainfadeammount", "1"); cvar_t r_stainfadetime = SCVAR ("r_stainfadetime", "1"); @@ -622,6 +623,7 @@ void Renderer_Init(void) Cvar_Register (&r_mirroralpha, GLRENDEREROPTIONS); Cvar_Register (&r_skyboxname, GRAPHICALNICETIES); Cbuf_AddText("alias sky r_skybox\n", RESTRICT_LOCAL); /*alternative name for users*/ + Cvar_Register (&r_softwarebanding, GRAPHICALNICETIES); Cvar_Register(&r_dodgytgafiles, "Bug fixes"); Cvar_Register(&r_dodgypcxfiles, "Bug fixes"); diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index a0c62ce57..a967118ef 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -2037,6 +2037,7 @@ void Sys_SendKeyEvents (void) HANDLE input = GetStdHandle(STD_INPUT_HANDLE); if (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL)) { + wantquit = true; Cmd_ExecuteString("quit force", RESTRICT_LOCAL); } else if (avail) diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 506e46792..b3e3071ae 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -622,6 +622,20 @@ const float *Alias_ConvertBoneData(skeltype_t sourcetype, const float *sourcedat sourcedata = dest; sourcetype = SKEL_INVERSE_ABSOLUTE; } + + if (sourcetype == SKEL_IDENTITY) + { //we can 'convert' identity matricies to anything. but we only want to do this when everything else is bad, because there really is no info here + float *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer; + memset(dest, 0, bonecount*12*sizeof(float)); + for (i = 0; i < bonecount; i++) + { //is this right? does it matter? + dest[i*12+0] = 1; + dest[i*12+5] = 1; + dest[i*12+10] = 1; + } + sourcedata = dest; + sourcetype = desttype; //panic + } if (sourcetype != desttype) Sys_Error("Alias_ConvertBoneData: %i->%i not supported\n", (int)sourcetype, (int)desttype); @@ -1137,6 +1151,9 @@ static qboolean Alias_BuildSkelLerps(skellerps_t *lerps, struct framestateregion galiasgroup_t *g; unsigned int b; float totalweight = 0; + + lerps->skeltype = SKEL_IDENTITY; //sometimes nothing else is valid. + for (b = 0; b < FRAME_BLENDS; b++) { if (fs->lerpweight[b]) @@ -1165,7 +1182,7 @@ static qboolean Alias_BuildSkelLerps(skellerps_t *lerps, struct framestateregion frame2=(frame2>g->numposes-1)?g->numposes-1:frame2; } - if (!l) + if (lerps->skeltype == SKEL_IDENTITY) lerps->skeltype = g->skeltype; else if (lerps->skeltype != g->skeltype) continue; //oops, can't cope with mixed blend types @@ -1685,6 +1702,12 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.ent = e; +#ifdef _DEBUG + if (!e->framestate.g[FS_REG].lerpweight[0] && !e->framestate.g[FS_REG].lerpweight[1] && !e->framestate.g[FS_REG].lerpweight[2] && !e->framestate.g[FS_REG].lerpweight[3]) + Con_Printf("Entity with no lerp info\n"); +#endif + + #ifndef SERVERONLY mesh->trneighbors = inf->ofs_trineighbours; mesh->normals_array = meshcache.norm; diff --git a/engine/common/common.c b/engine/common/common.c index d4f81a620..c1fef9969 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -104,6 +104,7 @@ qboolean static_registered = true; // only for startup check, then set qboolean msg_suppress_1 = false; int isPlugin; //if 2, we qcdebug to external program +qboolean wantquit; void COM_Path_f (void); void COM_Dir_f (void); @@ -5018,6 +5019,8 @@ void COM_Init (void) { qbyte swaptest[2] = {1,0}; + wantquit = false; + // set the qbyte swapping variables in a portable manner if ( *(short *)swaptest == 1) { diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 2f36d43d8..9adb23f09 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -112,7 +112,7 @@ void QCLoadBreakpoints(const char *vmname, const char *progsname) printf("qcreloaded \"%s\" \"%s\"\n", vmname, progsname); fflush(stdout); INS_UpdateGrabs(false, false); - while(debuggerresume == -1) + while(debuggerresume == -1 && !wantquit) { Sleep(10); Sys_SendKeyEvents(); @@ -236,6 +236,7 @@ qboolean QCExternalDebuggerCommand(char *text) if (sv.state) Cbuf_AddText("restart\n", RESTRICT_LOCAL); #endif + Host_EndGame("Reloading QC"); } else if (!strncmp(text, "qcbreakpoint ", 13)) { @@ -273,6 +274,8 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int #if defined(_WIN32) && !defined(SERVERONLY) && !defined(FTE_SDL) if (isPlugin >= 2) { + if (wantquit) + return DEBUG_TRACE_ABORT; if (!*filename || !line || !*line) //don't try editing an empty line, it won't work return DEBUG_TRACE_OFF; Sys_SendKeyEvents(); @@ -295,7 +298,7 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int Con_Footerf(false, "^bDebugging: %s", reason); else Con_Footerf(false, "^bDebugging"); - while(debuggerresume == -1) + while(debuggerresume == -1 && !wantquit) { Sleep(10); Sys_SendKeyEvents(); @@ -316,6 +319,8 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *line = debuggerresumeline; debuggerinstance = NULL; debuggerfile = NULL; + if (wantquit) + return DEBUG_TRACE_ABORT; return debuggerresume; } #endif diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 7e03dd900..3be3d7f8a 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -1163,6 +1163,12 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) else t = missing_texture; break; + case T_GEN_PALETTED: + if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->paletted)) + t = shaderstate.curtexnums->paletted; + else + t = missing_texture; + break; case T_GEN_NORMALMAP: t = (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->bump))?shaderstate.curtexnums->bump:missing_texture_normal; break; @@ -5171,6 +5177,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) #endif shaderstate.identitylighting = 1; shaderstate.identitylightmap = shaderstate.identitylighting / (1<flags & IF_MIPCAP)) { qglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, min(mips->mipcount-1, gl_mipcap_min)); - qglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, min(mips->mipcount, gl_mipcap_max)); + qglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, min(mips->mipcount-1, gl_mipcap_max)); } } diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 37f5caa6c..af23538c2 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern cvar_t r_shadow_bumpscale_basetexture; extern cvar_t r_replacemodels; extern cvar_t gl_lightmap_average; +extern cvar_t r_softwarebanding; cvar_t mod_loadentfiles = CVAR("sv_loadentfiles", "1"); cvar_t mod_external_vis = CVARD("mod_external_vis", "1", "Attempt to load .vis patches for quake maps, allowing transparent water to work properly."); cvar_t mod_warnmodels = CVARD("mod_warnmodels", "1", "Warn if any models failed to load. Set to 0 if your mod is likely to lack optional models (like its in development)."); //set to 0 for hexen2 and its otherwise-spammy-as-heck demo. @@ -1397,6 +1398,7 @@ void Mod_FinishTexture(texture_t *tx) #define LMT_FULLBRIGHT 2 #define LMT_BUMP 4 #define LMT_SPEC 8 +#define LMT_PALETTED 16 static void Mod_LoadMiptex(model_t *loadmodel, char *loadname, texture_t *tx, miptex_t *mt, int maps) { #ifndef SERVERONLY @@ -1455,6 +1457,12 @@ static void Mod_LoadMiptex(model_t *loadmodel, char *loadname, texture_t *tx, mi mipheight = tx->height; } + if (maps & LMT_PALETTED) + { + snprintf(altname, sizeof(altname)-1, "%s_pal", mt->name); + tx->texnums.paletted = R_LoadReplacementTexture(altname, loadname, ((*mt->name == '{')?0:IF_NOALPHA)|IF_MIPCAP|IF_NEAREST, mipbase, mipwidth, mipheight, TF_LUM8); + } + if (maps & LMT_DIFFUSE) { tx->texnums.base = R_LoadReplacementTexture(mt->name, loadname, ((*mt->name == '{')?0:IF_NOALPHA)|IF_MIPCAP, mipbase, mipwidth, mipheight, (*mt->name == '{')?TF_TRANS8:TF_SOLID8); @@ -1580,7 +1588,11 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); #ifndef SERVERONLY if (qrenderer != QR_NONE) { - maps |= LMT_DIFFUSE; + //FIXME: we really need to handle this stuff better, but the shader isn't known yet. + if (r_softwarebanding.ival) + maps |= LMT_PALETTED; +// else + maps |= LMT_DIFFUSE; if (r_fb_bmodels.ival) maps |= LMT_FULLBRIGHT; if (r_loadbumpmapping || (r_waterstyle.ival > 1 && *tx->name == '*')) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 2f1e9ebc1..edd4e1367 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -48,7 +48,7 @@ sh_config_t sh_config; cvar_t r_vertexlight = CVARFD("r_vertexlight", "0", CVAR_SHADERSYSTEM, "Hack loaded shaders to remove detail pass and lightmap sampling for faster rendering."); extern cvar_t r_glsl_offsetmapping_reliefmapping; extern cvar_t r_deluxemapping; -extern cvar_t r_fastturb, r_fastsky, r_skyboxname; +extern cvar_t r_fastturb, r_fastsky, r_skyboxname, r_softwarebanding; extern cvar_t r_drawflat; extern cvar_t r_shaderblobs; @@ -652,6 +652,26 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam return flags; } +texid_t R_LoadColourmapImage(void) +{ + unsigned int w = 256, h = VID_GRADES-1; + unsigned int x; + unsigned int data[256*(VID_GRADES-1)]; + qbyte *colourmappal = (qbyte *)FS_LoadMallocFile ("gfx/colormap.lmp", NULL); + if (colourmappal) + { + for (x = 0; x < sizeof(data)/sizeof(data[0]); x++) + data[x] = d_8to24rgbtable[colourmappal[x]]; + } + else + { //erk + for (x = 0; x < sizeof(data)/sizeof(data[0]); x++) + data[x] = d_8to24rgbtable[x & 0xff]; + } + BZ_Free(colourmappal); + 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 ) { if (parsestate.mode == SPM_DOOM3) @@ -670,6 +690,8 @@ static texid_t Shader_FindImage ( char *name, int flags ) { if (!Q_stricmp (name, "$whiteimage")) return r_whiteimage; + if (!Q_stricmp (name, "$colourmap")) + return R_LoadColourmapImage(); } if (flags & IF_RENDERTARGET) return R2D_RT_Configure(name, 0, 0, TF_INVALID); @@ -2068,6 +2090,11 @@ static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *t pass->texgen = T_GEN_DIFFUSE; shader->flags |= SHADER_HASDIFFUSE; } + else if (!Q_stricmp (tname, "$paletted")) + { + pass->texgen = T_GEN_PALETTED; + shader->flags |= SHADER_HASPALETTED; + } else if (!Q_stricmp (tname, "$normalmap")) { pass->texgen = T_GEN_NORMALMAP; @@ -3797,6 +3824,9 @@ done:; if (pass->texgen != T_GEN_ANIMMAP && pass->texgen != T_GEN_SINGLEMAP && pass->texgen != T_GEN_VIDEOMAP) weight += 1000; + + if ((pass->texgen == T_GEN_ANIMMAP || pass->texgen == T_GEN_SINGLEMAP) && pass->anim_frames[0] && *pass->anim_frames[0]->ident == '$') + weight += 1500; if (weight < bestweight) { @@ -4042,6 +4072,17 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) TEXASSIGN(shader->defaulttextures.base, tn->base); } + if (!TEXVALID(shader->defaulttextures.paletted)) + { + /*dlights/realtime lighting needs some stuff*/ +// if (!TEXVALID(tn->paletted) && *shader->mapname)// && (shader->flags & SHADER_HASDIFFUSE)) +// tn->paletted = R_LoadHiResTexture(shader->mapname, NULL, 0); +// if (!TEXVALID(tn->paletted)) +// tn->paletted = R_LoadHiResTexture(imagename, subpath, (*imagename=='{')?0:IF_NOALPHA); + + TEXASSIGN(shader->defaulttextures.paletted, tn->paletted); + } + COM_StripExtension(imagename, imagename, sizeof(imagename)); if (!TEXVALID(shader->defaulttextures.bump)) @@ -4667,6 +4708,34 @@ void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) ); } + if (!builtin && r_softwarebanding.ival) + { + /*alpha bended*/ + builtin = ( + "{\n" + "program defaultwall#EIGHTBIT\n" + "{\n" + "map $paletted\n"//$diffuse\n" + "}\n" + "{\n" + "map $lightmap\n" + "}\n" + "{\n" + "map $normalmap\n" + "}\n" + "{\n" + "map $deluxmap\n" + "}\n" + "{\n" + "map $colourmap\n"//$fullbright\n" + "}\n" + "{\n" + "map $specular\n" + "}\n" + "}\n" + ); + } + if (builtin) Shader_DefaultScript(shortname, s, builtin); else diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index 7e00e0010..b61ef0a52 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -2255,6 +2255,7 @@ LONG WINAPI GLMainWndProc ( MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { Cbuf_AddText("\nquit\n", RESTRICT_LOCAL); + wantquit = true; } break; diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index fdac4a8b4..20eca636e 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -1057,9 +1057,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef LIGHTSTYLED\n" //we could use an offset, but that would still need to be per-surface which would break batches //fixme: merge attributes? -"varying vec2 lm, lm2, lm3, lm4;\n" +"varying vec2 lm0, lm1, lm2, lm3;\n" "#else\n" -"varying vec2 lm;\n" +"varying vec2 lm0;\n" "#endif\n" "#ifdef VERTEX_SHADER\n" @@ -1085,11 +1085,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "eyevector.z = dot(eyeminusvertex, v_normal.xyz);\n" "#endif\n" "tc = v_texcoord;\n" -"lm = v_lmcoord;\n" +"lm0 = v_lmcoord;\n" "#ifdef LIGHTSTYLED\n" -"lm2 = v_lmcoord2;\n" -"lm3 = v_lmcoord3;\n" -"lm4 = v_lmcoord4;\n" +"lm1 = v_lmcoord2;\n" +"lm2 = v_lmcoord3;\n" +"lm3 = v_lmcoord4;\n" "#endif\n" "gl_Position = ftetransform();\n" "}\n" @@ -1097,28 +1097,44 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef FRAGMENT_SHADER\n" + //samplers -"uniform sampler2D s_t0; //diffuse\n" -"uniform sampler2D s_t1; //lightmap0\n" +"#define s_diffuse s_t0\n" +"#define s_lightmap0 s_t1\n" +"#define s_normalmap s_t2\n" +"#define s_delux0 s_t3\n" +"#define s_fullbright s_t4\n" +"#define s_specular s_t5\n" +"#define s_lightmap1 s_t6\n" +"#define s_lightmap2 s_t7\n" +"#define s_lightmap3 s_t8\n" +"#define s_delux1 s_t9\n" +"#define s_delux2 s_t10\n" +"#define s_delux3 s_t11\n" +"#define s_paletted s_diffuse\n" +"#define s_colourmap s_fullbright\n" + +"uniform sampler2D s_diffuse;\n" +"uniform sampler2D s_lightmap0;\n" "#if defined(BUMP) && (defined(OFFSETMAPPING) || defined(DELUXE) || defined(SPECULAR))\n" -"uniform sampler2D s_t2; //normal.rgb+height.a\n" +"uniform sampler2D s_normalmap;\n" "#endif\n" "#ifdef DELUXE\n" -"uniform sampler2D s_t3; //deluxe0\n" +"uniform sampler2D s_delux0;\n" "#endif\n" -"#ifdef FULLBRIGHT\n" -"uniform sampler2D s_t4; //fullbright\n" +"#if defined(FULLBRIGHT) || defined(EIGHTBIT)\n" +"uniform sampler2D s_fullbright;\n" "#endif\n" "#ifdef SPECULAR\n" -"uniform sampler2D s_t5; //specular\n" +"uniform sampler2D s_specular;\n" "#endif\n" "#ifdef LIGHTSTYLED\n" -"uniform sampler2D s_t6; //lightmap1\n" -"uniform sampler2D s_t7; //lightmap2\n" -"uniform sampler2D s_t8; //lightmap3\n" -"uniform sampler2D s_t9; //deluxe1\n" -"uniform sampler2D s_t10; //deluxe2\n" -"uniform sampler2D s_t11; //deluxe3\n" +"uniform sampler2D s_lightmap1;\n" +"uniform sampler2D s_lightmap2;\n" +"uniform sampler2D s_lightmap3;\n" +"uniform sampler2D s_delux1;\n" +"uniform sampler2D s_delux2;\n" +"uniform sampler2D s_delux3;\n" "#endif\n" "#ifdef LIGHTSTYLED\n" @@ -1137,15 +1153,24 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "{\n" //adjust texture coords for offsetmapping "#ifdef OFFSETMAPPING\n" -"vec2 tcoffsetmap = offsetmap(s_t2, tc, eyevector);\n" +"vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\n" "#define tc tcoffsetmap\n" "#endif\n" +"#if defined(EIGHTBIT) && !defined(LIGHTSTYLED)\n" +//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise. +//don't bother if its lightstyled, such cases will have unpredictable correlations anyway. +//FIXME: this rounding is likely not correct with respect to software rendering. oh well. +"vec2 lmcoord0 = floor(lm0 * 256.0*8.0)/(256.0*8.0);\n" +"#define lm0 lmcoord0\n" +"#endif\n" + + //yay, regular texture! -"gl_FragColor = texture2D(s_t0, tc);\n" +"gl_FragColor = texture2D(s_diffuse, tc);\n" "#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR))\n" -"vec3 norm = normalize(texture2D(s_t2, tc).rgb - 0.5);\n" +"vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\n" "#elif defined(SPECULAR) || defined(DELUXE)\n" "vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist.\n" "#endif\n" @@ -1154,29 +1179,30 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef LIGHTSTYLED\n" "vec3 lightmaps;\n" "#ifdef DELUXE\n" -"lightmaps = texture2D(s_t1, lm ).rgb * e_lmscale[0].rgb * dot(norm, texture2D(s_t3, lm ).rgb);\n" -"lightmaps += texture2D(s_t6, lm2).rgb * e_lmscale[1].rgb * dot(norm, texture2D(s_t9, lm2).rgb);\n" -"lightmaps += texture2D(s_t7, lm3).rgb * e_lmscale[2].rgb * dot(norm, texture2D(s_t10, lm3).rgb);\n" -"lightmaps += texture2D(s_t8, lm4).rgb * e_lmscale[3].rgb * dot(norm, texture2D(s_t11,lm4).rgb);\n" +"lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_delux0, lm0).rgb-0.5);\n" +"lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_delux1, lm1).rgb-0.5);\n" +"lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_delux2, lm2).rgb-0.5);\n" +"lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_delux3, lm3).rgb-0.5);\n" "#else\n" -"lightmaps = texture2D(s_t1, lm ).rgb * e_lmscale[0].rgb;\n" -"lightmaps += texture2D(s_t6, lm2).rgb * e_lmscale[1].rgb;\n" -"lightmaps += texture2D(s_t7, lm3).rgb * e_lmscale[2].rgb;\n" -"lightmaps += texture2D(s_t8, lm4).rgb * e_lmscale[3].rgb;\n" +"lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb;\n" +"lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb;\n" +"lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb;\n" +"lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb;\n" "#endif\n" "#else\n" -"vec3 lightmaps = (texture2D(s_t1, lm) * e_lmscale).rgb;\n" +"vec3 lightmaps = (texture2D(s_lightmap0, lm0) * e_lmscale).rgb;\n" //modulate by the bumpmap dot light "#ifdef DELUXE\n" -"lightmaps *= dot(norm, 2.0*(texture2D(s_t3, lm).rgb-0.5));\n" +"lightmaps *= dot(norm, 2.0*(texture2D(s_delux0, lm0).rgb-0.5));\n" "#endif\n" "#endif\n" +//add in specular, if applicable. "#ifdef SPECULAR\n" -"vec4 specs = texture2D(s_t5, tc);\n" +"vec4 specs = texture2D(s_specular, tc);\n" "#ifdef DELUXE\n" //not lightstyled... -"vec3 halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_t3, lm).rgb-0.5)); //this norm should be the deluxemap info instead\n" +"vec3 halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_delux0, lm).rgb-0.5)); //this norm should be the deluxemap info instead\n" "#else\n" "vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0)); //this norm should be the deluxemap info instead\n" "#endif\n" @@ -1189,13 +1215,23 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "gl_FragColor.rgb += spec * specs.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_diffuse, 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" +"#else\n" //now we have our diffuse+specular terms, modulate by lightmap values. "gl_FragColor.rgb *= lightmaps.rgb;\n" //add on the fullbright "#ifdef FULLBRIGHT\n" -"gl_FragColor.rgb += texture2D(s_t4, tc).rgb;\n" +"gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb;\n" "#endif\n" +"#endif\n" + //entity modifiers "gl_FragColor = gl_FragColor * e_colourident;\n" diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 6f9328143..c7201e6d3 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -255,6 +255,7 @@ typedef struct shaderpass_s { T_GEN_UPPEROVERLAY, //texture's default personal colour T_GEN_LOWEROVERLAY, //texture's default team colour T_GEN_FULLBRIGHT, //texture's default fullbright overlay + T_GEN_PALETTED, //texture's original paletted data (8bit) T_GEN_CURRENTRENDER,//copy the current screen to a texture, and draw that @@ -544,6 +545,7 @@ struct shader_s SHADER_NOSHADOWS = 1 << 25, //don't cast shadows SHADER_HASFULLBRIGHT = 1 << 26, //needs a fullbright texture, if possible. SHADER_HASDIFFUSE = 1 << 27, //has a T_GEN_DIFFUSE pass + SHADER_HASPALETTED = 1 << 28, //has a T_GEN_PALETTED pass } flags; program_t *prog; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index be9b819f1..0eda95dae 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -8614,6 +8614,19 @@ static void QCBUILTIN PF_globalstat(pubprogfuncs_t *prinst, struct globalvars_s #endif } +//void(float num, float type, void *ptr) pointerstat +static void QCBUILTIN PF_pointerstat(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int num = G_FLOAT(OFS_PARM0); + int type = G_FLOAT(OFS_PARM1); + int addr = G_INT(OFS_PARM2); + int size = (type == ev_vector)?sizeof(vec3_t):sizeof(float); + if (addr < 0 || addr+size >= prinst->stringtablesize) + prinst->RunError(prinst, "QCVM address %#x is not valid.", addr); + else + SV_QCStatPtr(type, prinst->stringtable+addr, num); +} + //EXT_CSQC_1 static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -8624,6 +8637,13 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars extern cvar_t sv_gravity; edict_t *ent = G_EDICT(prinst, OFS_PARM0); edict_t *touched; + + if (ent->readonly) + { + Con_Printf("runplayerphysics called on read-only entity"); + return; + } + if (pr_global_ptrs->clientcommandframe) pmove.sequence = *pr_global_ptrs->clientcommandframe; else @@ -9443,7 +9463,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //EXT_CSQC {"clientstat", PF_clientstat, 0, 0, 0, 232, D("void(float num, float type, .__variant fld)", "Specifies what data to use in order to send various stats, in a client-specific way.\n'num' should be a value between 32 and 127, other values are reserved.\n'type' must be set to one of the EV_* constants, one of EV_FLOAT, EV_STRING, EV_INTEGER, EV_ENTITY.\nfld must be a reference to the field used, each player will be sent only their own copy of these fields.")}, //EXT_CSQC - {"globalstat", PF_globalstat, 0, 0, 0, 233, D("void(float num, float type, string name)", "Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string.")}, //EXT_CSQC_1 actually + {"globalstat", PF_globalstat, 0, 0, 0, 233, D("void(float num, float type, string name)", "Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string (pass \"foo\").")}, //EXT_CSQC_1 actually + {"pointerstat", PF_pointerstat, 0, 0, 0, 0, D("void(float num, float type, __variant *address)", "Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, address however, is the address of the variable you would like to use (pass &foo).")}, //END EXT_CSQC {"isbackbuffered", PF_isbackbuffered, 0, 0, 0, 234, D("float(entity player)", "Returns if the given player's network buffer will take multiple network frames in order to clear. If this builtin returns non-zero, you should delay or reduce the amount of reliable (and also unreliable) data that you are sending to that client.")}, {"rotatevectorsbyangle",PF_rotatevectorsbyangles,0,0, 0, 235, "void(vector angle)"}, // #235 diff --git a/engine/server/server.h b/engine/server/server.h index 4c64281e8..630d50340 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -581,6 +581,7 @@ typedef struct client_s //note, nq is nq+ } protocol; + unsigned int lastruncmd; //for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat //speed cheat testing #define NEWSPEEDCHEATPROT int msecs; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 420b93b5d..8127ab5d5 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2316,16 +2316,15 @@ void World_Physics_Frame(world_t *w) } else { - float newt; - float delt; + unsigned int newt; + unsigned int delt; newt = sv.time*1000; - delt = newt - svs.clients[i-1].msecs; - if (delt > 1000/77.0 || delt < -10) + delt = newt - svs.clients[i-1].lastruncmd; + if (delt > (int)(1000/77.0) || delt < -10) { float ft = host_frametime; host_client = &svs.clients[i-1]; sv_player = svs.clients[i-1].edict; - svs.clients[i-1].msecs = newt; SV_PreRunCmd(); #ifndef NEWSPEEDCHEATPROT svs.clients[i-1].last_check = 0; diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index cb1853af0..ccc8c186a 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -1559,15 +1559,15 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) if (nqjunk) { MSG_WriteShort (msg, ent->v->health); - MSG_WriteByte (msg, ent->v->currentammo); - MSG_WriteByte (msg, ent->v->ammo_shells); - MSG_WriteByte (msg, ent->v->ammo_nails); - MSG_WriteByte (msg, ent->v->ammo_rockets); - MSG_WriteByte (msg, ent->v->ammo_cells); + MSG_WriteByte (msg, min(ent->v->currentammo, 255)); + MSG_WriteByte (msg, min(ent->v->ammo_shells, 255)); + MSG_WriteByte (msg, min(ent->v->ammo_nails, 255)); + MSG_WriteByte (msg, min(ent->v->ammo_rockets, 255)); + MSG_WriteByte (msg, min(ent->v->ammo_cells, 255)); if (standard_quake) { - MSG_WriteByte (msg, ent->v->weapon); + MSG_WriteByte (msg, (unsigned int)ent->v->weapon & 0xff); } else { @@ -1618,7 +1618,8 @@ void SV_QCStatEval(int type, char *name, evalc_t *field, eval_t *global, int sta for (i = 0; i < numqcstats; i++) { - if (qcstats[i].statnum == statnum) + //strings use a different namespace. + if (qcstats[i].statnum == statnum && ((qcstats[i].type == ev_string||qcstats[i].type == -ev_string) == (type == ev_string||type == -ev_string))) break; } if (i == numqcstats) @@ -2756,6 +2757,7 @@ void SV_SendClientMessages (void) if (c->nextservertimeupdate > pt + 6) c->nextservertimeupdate = 0; + c->netchan.cleartime = realtime - 100; c->netchan.nqunreliableonly = !c->send_message; c->datagram.cursize = 0; if (!c->send_message && c->nextservertimeupdate < pt) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 4408c129c..9f6e66fe5 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -4642,7 +4642,11 @@ void Cmd_Observe_f (void) PR_ExecuteProgram (svprogfuncs, SpectatorConnect); } else + { sv_player->v->movetype = MOVETYPE_NOCLIP; + sv_player->v->model = 0; + sv_player->v->modelindex = 0; + } sv.spawned_observer_slots++; // send notification to all clients @@ -4854,12 +4858,10 @@ void SVNQ_Spawn_f (void) ClientReliableWrite_Long (host_client, pr_global_struct->killed_monsters); } - MSG_WriteByte (&host_client->netchan.message, svc_signonnum); - MSG_WriteByte (&host_client->netchan.message, 3); - + ClientReliableWrite_Begin (host_client, svc_signonnum, 2); + ClientReliableWrite_Byte (host_client, 3); host_client->send_message = true; - } void SVNQ_Begin_f (void) { @@ -4984,7 +4986,7 @@ void SVNQ_PreSpawn_f (void) host_client->prespawn_idx = 0; if (sv_mapcheck.value) - Con_Printf("Warning: %s does cannot be applied to NQ clients.\n", sv_mapcheck.name); //as you can fake it in a client anyway, this is hardly a significant issue. + Con_Printf("Warning: %s cannot be enforced on NQ clients.\n", sv_mapcheck.name); //as you can fake it in a client anyway, this is hardly a significant issue. } host_client->send_message = true; @@ -5852,6 +5854,10 @@ void SV_PreRunCmd(void) playertouch = BZ_Malloc((playertouchmax>>3)+1); } memset(playertouch, 0, playertouchmax>>3); + + //timestamp it, so things can't go weird + if (host_client) + host_client->lastruncmd = sv.time * 1000; } void SV_RunCmdCleanup(void) { diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index 211d401e5..5f995a6d4 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -19,9 +19,9 @@ varying vec2 tc; #ifdef LIGHTSTYLED //we could use an offset, but that would still need to be per-surface which would break batches //fixme: merge attributes? -varying vec2 lm, lm2, lm3, lm4; +varying vec2 lm0, lm1, lm2, lm3; #else -varying vec2 lm; +varying vec2 lm0; #endif #ifdef VERTEX_SHADER @@ -47,11 +47,11 @@ void main () eyevector.z = dot(eyeminusvertex, v_normal.xyz); #endif tc = v_texcoord; - lm = v_lmcoord; + lm0 = v_lmcoord; #ifdef LIGHTSTYLED - lm2 = v_lmcoord2; - lm3 = v_lmcoord3; - lm4 = v_lmcoord4; + lm1 = v_lmcoord2; + lm2 = v_lmcoord3; + lm3 = v_lmcoord4; #endif gl_Position = ftetransform(); } @@ -59,28 +59,44 @@ void main () #ifdef FRAGMENT_SHADER + //samplers -uniform sampler2D s_t0; //diffuse -uniform sampler2D s_t1; //lightmap0 +#define s_diffuse s_t0 +#define s_lightmap0 s_t1 +#define s_normalmap s_t2 +#define s_delux0 s_t3 +#define s_fullbright s_t4 +#define s_specular s_t5 +#define s_lightmap1 s_t6 +#define s_lightmap2 s_t7 +#define s_lightmap3 s_t8 +#define s_delux1 s_t9 +#define s_delux2 s_t10 +#define s_delux3 s_t11 +#define s_paletted s_diffuse +#define s_colourmap s_fullbright + +uniform sampler2D s_diffuse; +uniform sampler2D s_lightmap0; #if defined(BUMP) && (defined(OFFSETMAPPING) || defined(DELUXE) || defined(SPECULAR)) -uniform sampler2D s_t2; //normal.rgb+height.a +uniform sampler2D s_normalmap; #endif #ifdef DELUXE -uniform sampler2D s_t3; //deluxe0 +uniform sampler2D s_delux0; #endif -#ifdef FULLBRIGHT -uniform sampler2D s_t4; //fullbright +#if defined(FULLBRIGHT) || defined(EIGHTBIT) +uniform sampler2D s_fullbright; #endif #ifdef SPECULAR -uniform sampler2D s_t5; //specular +uniform sampler2D s_specular; #endif #ifdef LIGHTSTYLED -uniform sampler2D s_t6; //lightmap1 -uniform sampler2D s_t7; //lightmap2 -uniform sampler2D s_t8; //lightmap3 -uniform sampler2D s_t9; //deluxe1 -uniform sampler2D s_t10; //deluxe2 -uniform sampler2D s_t11; //deluxe3 +uniform sampler2D s_lightmap1; +uniform sampler2D s_lightmap2; +uniform sampler2D s_lightmap3; +uniform sampler2D s_delux1; +uniform sampler2D s_delux2; +uniform sampler2D s_delux3; #endif #ifdef LIGHTSTYLED @@ -99,15 +115,24 @@ void main () { //adjust texture coords for offsetmapping #ifdef OFFSETMAPPING - vec2 tcoffsetmap = offsetmap(s_t2, tc, eyevector); + vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector); #define tc tcoffsetmap #endif +#if defined(EIGHTBIT) && !defined(LIGHTSTYLED) + //optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise. + //don't bother if its lightstyled, such cases will have unpredictable correlations anyway. + //FIXME: this rounding is likely not correct with respect to software rendering. oh well. + vec2 lmcoord0 = floor(lm0 * 256.0*8.0)/(256.0*8.0); +#define lm0 lmcoord0 +#endif + + //yay, regular texture! - gl_FragColor = texture2D(s_t0, tc); + gl_FragColor = texture2D(s_diffuse, tc); #if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR)) - vec3 norm = normalize(texture2D(s_t2, tc).rgb - 0.5); + vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5); #elif defined(SPECULAR) || defined(DELUXE) vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist. #endif @@ -116,29 +141,30 @@ void main () #ifdef LIGHTSTYLED vec3 lightmaps; #ifdef DELUXE - lightmaps = texture2D(s_t1, lm ).rgb * e_lmscale[0].rgb * dot(norm, texture2D(s_t3, lm ).rgb); - lightmaps += texture2D(s_t6, lm2).rgb * e_lmscale[1].rgb * dot(norm, texture2D(s_t9, lm2).rgb); - lightmaps += texture2D(s_t7, lm3).rgb * e_lmscale[2].rgb * dot(norm, texture2D(s_t10, lm3).rgb); - lightmaps += texture2D(s_t8, lm4).rgb * e_lmscale[3].rgb * dot(norm, texture2D(s_t11,lm4).rgb); + lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_delux0, lm0).rgb-0.5); + lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_delux1, lm1).rgb-0.5); + lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_delux2, lm2).rgb-0.5); + lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_delux3, lm3).rgb-0.5); #else - lightmaps = texture2D(s_t1, lm ).rgb * e_lmscale[0].rgb; - lightmaps += texture2D(s_t6, lm2).rgb * e_lmscale[1].rgb; - lightmaps += texture2D(s_t7, lm3).rgb * e_lmscale[2].rgb; - lightmaps += texture2D(s_t8, lm4).rgb * e_lmscale[3].rgb; + lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb; + lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb; + lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb; + lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb; #endif #else - vec3 lightmaps = (texture2D(s_t1, lm) * e_lmscale).rgb; + vec3 lightmaps = (texture2D(s_lightmap0, lm0) * e_lmscale).rgb; //modulate by the bumpmap dot light #ifdef DELUXE - lightmaps *= dot(norm, 2.0*(texture2D(s_t3, lm).rgb-0.5)); + lightmaps *= dot(norm, 2.0*(texture2D(s_delux0, lm0).rgb-0.5)); #endif #endif +//add in specular, if applicable. #ifdef SPECULAR - vec4 specs = texture2D(s_t5, tc); + vec4 specs = texture2D(s_specular, tc); #ifdef DELUXE //not lightstyled... - vec3 halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_t3, lm).rgb-0.5)); //this norm should be the deluxemap info instead + vec3 halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_delux0, lm).rgb-0.5)); //this norm should be the deluxemap info instead #else vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0)); //this norm should be the deluxemap info instead #endif @@ -151,13 +177,23 @@ void main () gl_FragColor.rgb += spec * specs.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_diffuse, 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. +#else //now we have our diffuse+specular terms, modulate by lightmap values. gl_FragColor.rgb *= lightmaps.rgb; //add on the fullbright #ifdef FULLBRIGHT - gl_FragColor.rgb += texture2D(s_t4, tc).rgb; + gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb; #endif +#endif + //entity modifiers gl_FragColor = gl_FragColor * e_colourident;