From 586a92ec47e9a17a25f8705f144e5bde278d314c Mon Sep 17 00:00:00 2001 From: Shpoike Date: Mon, 26 Apr 2021 00:22:50 +0100 Subject: [PATCH] Try to make sense of hl2 materials. --- engine/gl/gl_model.h | 2 +- engine/gl/gl_shader.c | 336 ++++++++++++++++++++++++++- engine/gl/gl_warp.c | 39 +++- engine/shaders/glsl/defaultwall.glsl | 19 +- 4 files changed, 377 insertions(+), 19 deletions(-) diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 2be65ecc..106a1a2d 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -355,7 +355,7 @@ void GL_DeselectVAO(void); typedef struct texture_s { - char name[64]; + char name[128]; unsigned vwidth, vheight; //used for lightmap coord generation struct shader_s *shader; diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 69993d32..4b2b2e1e 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -627,7 +627,10 @@ qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *ima "%s_%s", "%s%s", "env/%s%s", - "gfx/env/%s%s" + "gfx/env/%s%s", +#ifdef HL2BSPS + "materials/skybox/%s%s", +#endif }; if (*texturename == '$') @@ -663,7 +666,8 @@ qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *ima } if (!images[i]->width) { - Con_Printf("Sky \"%s\" missing texture: %s\n", shadername, path); + if (allokay) + Con_Printf("Sky \"%s\" missing texture: [gfx/env/]%s%s\n", shadername, texturename, skyname_suffix[countof(skyname_suffix)-1][i]); images[i] = missing_texture; allokay = false; } @@ -7069,8 +7073,12 @@ void Shader_DefaultSkinShell(parsestate_t *ps, const char *shortname, const void void Shader_Default2D(parsestate_t *ps, const char *shortname, const void *genargs) { shader_t *s = ps->s; + qboolean wrap; if (Shader_ParseShader(ps, "default2d")) return; + + wrap = Com_FloatArgument(s->name, "WRAP", 4, 0); + if (sh_config.progs_supported && qrenderer != QR_DIRECT3D9 #ifdef HAVE_LEGACY && !dpcompat_nopremulpics.ival @@ -7080,35 +7088,35 @@ void Shader_Default2D(parsestate_t *ps, const char *shortname, const void *genar //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(ps, shortname, - "{\n" + va("{\n" "affine\n" "nomipmaps\n" "program default2d#PREMUL\n" "{\n" - "clampmap $diffuse\n" + "%s $diffuse\n" "blendfunc gl_one gl_one_minus_src_alpha\n" "}\n" "sort additive\n" - "}\n" - ); - TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP|IF_HIGHPRIORITY)); + "}\n", (wrap?"map":"clampmap") + )); + TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|(wrap?0:IF_CLAMP)|IF_HIGHPRIORITY)); } else { Shader_DefaultScript(ps, shortname, - "{\n" + va("{\n" "affine\n" "nomipmaps\n" "{\n" - "clampmap $diffuse\n" + "%s $diffuse\n" "rgbgen vertex\n" "alphagen vertex\n" "blendfunc gl_src_alpha gl_one_minus_src_alpha\n" "}\n" "sort additive\n" - "}\n" - ); - TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP|IF_HIGHPRIORITY)); + "}\n", (wrap?"map":"clampmap") + )); + TEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|(wrap?0:IF_CLAMP)|IF_HIGHPRIORITY)); } } void Shader_PolygonShader(struct shaderparsestate_s *ps, const char *shortname, const void *args) @@ -7276,6 +7284,305 @@ static void Shader_ReadShader(parsestate_t *ps, const char *shadersource, shader } } +#ifdef MATERIAL_VMT +typedef struct +{ + char type[MAX_QPATH]; + struct + { + char name[MAX_QPATH]; + } tex[5]; + char envmap[MAX_QPATH]; + float alphatestref; + qboolean alphatest; + qboolean culldisable; + qboolean ignorez; + char *replaceblock; +} vmtstate_t; + +static qboolean R_ReadVMT(const char *materialname, vmtstate_t *st); +static char *R_ParseVMTBlock(const char *fname, vmtstate_t *st, char *line) +{ //assumes the open { was already parsed, but will parse the close. + char *replace = NULL; + com_tokentype_t ttype; + char key[MAX_OSPATH]; + char value[MAX_OSPATH]; + char *qmark; + qboolean cond; + for(;line;) + { + line = COM_ParseType(line, key, sizeof(key), &ttype); + if (ttype == TTP_RAWTOKEN && !strcmp(key, "}")) + break; //end-of-block + line = COM_ParseType(line, value, sizeof(value), &ttype); + if (ttype == TTP_RAWTOKEN && !strcmp(value, "{")) + { //sub block. we don't go into details here. + if (!Q_strcasecmp(key, "replace")) + replace = line; + else + Con_DPrintf("%s: Unknown block \"%s\"\n", fname, key); + line = R_ParseVMTBlock(fname, NULL, line); + continue; + } + + while ((qmark = strchr(key, '?'))) + { + *qmark++ = 0; + if (!Q_strcasecmp(key, "srgb")) + cond = !!(vid.flags & VID_SRGBAWARE); + else + { + Con_DPrintf("%s: Unknown vmt conditional \"%s\"\n", fname, key); + cond = false; + } + if (!cond) + { + *key = 0; + break; + } + else + memmove(key, qmark, strlen(qmark)+1); + } + + if (!*key || !st) + ; + else if (!Q_strcasecmp(key, "include")) + { + if (!R_ReadVMT(value, st)) + return NULL; + } + else if (!Q_strcasecmp(key, "$basetexture") || !Q_strcasecmp(key, "$hdrbasetexture")) //fixme: hdr version should probably override the other. order matters. + Q_strncpyz(st->tex[0].name, value, sizeof(st->tex[0].name)); + else if (!Q_strcasecmp(key, "$hdrcompressedtexture")) //named texture is R8G8B8E8 and needs to be decompressed manually... should probably just use e5bgr9 but we don't have a way to transcode it here. + ; + else if (!Q_strcasecmp(key, "$basetexturetransform")) + ; + else if (!Q_strcasecmp(key, "$bumpmap")) + ; + else if (!Q_strcasecmp(key, "$ssbump")) + ; + else if (!Q_strcasecmp(key, "$ssbumpmathfix")) + ; + else if (!Q_strcasecmp(key, "$basetexture2") || !strcmp(key, "$texture2")) + Q_strncpyz(st->tex[1].name, value, sizeof(st->tex[1].name)); + else if (!Q_strcasecmp(key, "$basetexturetransform2")) + ; + else if (!Q_strcasecmp(key, "$surfaceprop")) + ; + + else if (!Q_strcasecmp(key, "$ignorez")) + st->ignorez = !!atoi(value); + else if (!Q_strcasecmp(key, "$nocull") && (!strcmp(value, "1")||!strcmp(value, "0"))) + st->culldisable = atoi(value); + else if (!Q_strcasecmp(key, "$alphatest") && (!strcmp(value, "1")||!strcmp(value, "0"))) + st->alphatest = atoi(value); + else if (!Q_strcasecmp(key, "$alphatestreference")) + st->alphatestref = atof(value); + else if (!Q_strcasecmp(key, "$alphafunc")) + { + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + } + else if (!Q_strcasecmp(key, "$alpha")) + { + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + } + else if (!Q_strcasecmp(key, "$translucent")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$additive")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$color")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$vertexcolor")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$vertexalpha")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$decal")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$decalscale")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$decalsize")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$envmap")) + Q_strncpyz(st->envmap, value, sizeof(st->envmap)); + else if (!Q_strcasecmp(key, "$envmaptint")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$envmapmask")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$envmapcontrast")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$envmaptint")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$envmapsaturation")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$basealphaenvmapmask")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$normalmapalphaenvmapmask")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$crackmaterial")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$selfillum")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$selfillummask")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$selfillumtint")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$nofog")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$nomip")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$nodecal")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$detail")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$detailscale")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$detailtint")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$detailblendfactor")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$detailblendmode")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + + else if (!Q_strcasecmp(key, "$surfaceprop2")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$AllowAlphaToCoverage")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$blendmodulatetexture")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + + //water/reflection stuff + else if (!Q_strcasecmp(key, "$refracttexture")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$refractamount")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$refracttint")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$reflecttexture")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$reflectamount")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$reflecttint")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$fresnelpower")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$minreflectivity")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$maxreflectivity")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$normalmap")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$bumpframe")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$fogenable")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$fogcolor")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$fogstart")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$fogend")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$abovewater")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$underwateroverlay")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$reflectentities")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$scale")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$bottommaterial")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$scroll1")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + else if (!Q_strcasecmp(key, "$scroll2")) + Con_DPrintf("%s: %s \"%s\"\n", fname, key, value); + + else if (*key == '%') + ; //editor lines + else + Con_Printf("%s: Unknown field \"%s\"\n", fname, key); + } + if (replace) + R_ParseVMTBlock(fname, st, replace); + return line; +} +static void Shader_GenerateFromVMT(parsestate_t *ps, vmtstate_t *st, const char *shortname) +{ + size_t offset = 0; + char script[8192]; + if (!*st->tex[0].name) + Q_strncpyz(st->tex[0].name, shortname, sizeof(st->tex[0].name)); + + //Q_strlcatfz(script, &offset, sizeof(script), "{\n"); + if (!Q_strcasecmp(st->type, "WorldVertexTransition")) + { //attempt to do terrain blending + Q_strncpyz(st->type, "defaultwall#TWOWAY", sizeof(st->type)); + Q_strlcatfz(script, &offset, sizeof(script), "program \"%s\"\n", st->type); + Q_strlcatfz(script, &offset, sizeof(script), "diffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name); + Q_strlcatfz(script, &offset, sizeof(script), "uppermap \"%s%s.vtf\"\n", strcmp(st->tex[1].name, "materials/")?"materials/":"", st->tex[1].name); + } + else + { + Q_strncpyz(st->type, "defaultwall", sizeof(st->type)); //FIXME + + Q_strlcatfz(script, &offset, sizeof(script), "program \"%s\"\n", st->type); + Q_strlcatfz(script, &offset, sizeof(script), "diffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name); + } + if (*st->envmap) + Q_strlcatfz(script, &offset, sizeof(script), "reflectcube \"%s%s.vtf\"\n", strcmp(st->envmap, "materials/")?"materials/":"", st->envmap); + if (st->alphatest) + Q_strlcatfz(script, &offset, sizeof(script), "alphatest ge128\n"); + if (st->culldisable) + Q_strlcatfz(script, &offset, sizeof(script), "cull disable\n"); + if (st->ignorez) + Q_strlcatfz(script, &offset, sizeof(script), "nodepth\n"); + Q_strlcatfz(script, &offset, sizeof(script), "}\n"); + + Shader_Reset(ps->s); + Shader_ReadShader(ps, script, NULL); +} +static qboolean R_ReadVMT(const char *fname, vmtstate_t *st) +{ + char *line, *file = NULL; + com_tokentype_t ttype; + char token[MAX_QPATH]; + char *prefix="", *postfix=""; + + //don't dupe the mandatory materials/ prefix + if (strncmp(fname, "materials/", 10)) + prefix = "materials/"; + if (strcmp(COM_GetFileExtension(fname, NULL), ".vmt")) + postfix = ".vmt"; + Q_snprintfz(token, sizeof(token), "%s%s%s", prefix, fname, postfix); + + file = FS_LoadMallocFile(token, NULL); + if (file) + { + line = file; + line = COM_ParseType(line, st->type, sizeof(st->type), &ttype); + line = COM_ParseType(line, token, sizeof(token), &ttype); + if (!strcmp(token, "{")) + { + line = R_ParseVMTBlock(fname, st, line); + } + + + BZ_Free(file); + return !!line; + } + return false; +} +static qboolean Shader_ReadVMT(parsestate_t *ps, const char *filename) +{ + vmtstate_t st; + memset(&st, 0, sizeof(st)); + if (!R_ReadVMT(filename, &st)) + return false; + + Shader_GenerateFromVMT(ps, &st, filename); + return true; +} +#endif + static qboolean Shader_ParseShader(parsestate_t *ps, const char *parsename) { size_t offset = 0, length; @@ -7293,6 +7600,11 @@ static qboolean Shader_ParseShader(parsestate_t *ps, const char *parsename) char shaderfile[MAX_QPATH]; if (!*token) { +#ifdef MATERIAL_VMT + if (Shader_ReadVMT(ps, parsename)) + return true; +#endif + Q_snprintfz(shaderfile, sizeof(shaderfile), "%s.mat", parsename); file = COM_LoadTempMoreFile(shaderfile, &length); } diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index d82cdc0f..c59c70ba 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -42,6 +42,9 @@ cvar_t r_skyboxname = CVARFC ("r_skybox", "", CVAR_RENDERERCALLBACK | CVAR cvar_t r_skybox_orientation = CVARFD ("r_glsl_skybox_orientation", "0 0 0 0", CVAR_SHADERSYSTEM, "Defines the axis around which skyboxes will rotate (the first three values). The fourth value defines the speed the skybox rotates at, in degrees per second."); cvar_t r_skyfog = CVARD ("r_skyfog", "0.5", "This controls an alpha-blend value for fog on skyboxes, cumulative with regular fog alpha."); +#ifdef MATERIAL_VMT +static shader_t *forcedskyfaces[6]; +#endif static shader_t *forcedsky; static shader_t *skyboxface; static shader_t *skygridface; @@ -55,6 +58,9 @@ void R_SkyShutdown(void) skyboxface = NULL; skygridface = NULL; forcedsky = NULL; +#ifdef MATERIAL_VMT + memset(forcedskyfaces, 0, sizeof(forcedskyfaces)); +#endif } //lets the backend know which fallback envmap it can use. @@ -89,8 +95,21 @@ void R_SetSky(const char *sky) if (!*sky) return; //no need to do anything +#ifdef MATERIAL_VMT //hl2 skies have specific shaders (with their own maps) for each face. can't automatically use cubemap optimisations - we would need to interrogate the shaders. :( + for (i = 0; i < 6; i++) + { + static const char *facenames[] = {"rt", "bk", "lf", "ft", "up", "dn"}; + forcedskyfaces[i] = R_RegisterCustom(va("materials/skybox/%s%s", sky, facenames[i]), SUF_NONE, NULL, NULL); //don't allow a fallback, so we know when it fails. + if (!forcedskyfaces[i]) + break; + } + if (i == 6) + return; //okay, we have all 6 faces okay... + memset(forcedskyfaces, 0, sizeof(forcedskyfaces)); +#endif memset(&tex, 0, sizeof(tex)); + //equirectangular skies tex.base = R_LoadHiResTexture(sky, "env:gfx/env", IF_LOADNOW|IF_NOMIPMAP); if (tex.reflectcube && tex.reflectcube->status == TEX_LOADING) COM_WorkerPartialSync(tex.reflectcube, &tex.reflectcube->status, TEX_LOADING); @@ -442,6 +461,13 @@ qboolean R_DrawSkyChain (batch_t *batch) if (!opaque) GL_DrawSkySphere(batch, skyshader); } +#ifdef MATERIAL_VMT + else if (forcedskyfaces[0]) + { + R_CalcSkyChainBounds(batch); + GL_DrawSkyBox (NULL, batch); + } +#endif else if (skyboxtex && TEXVALID(*skyboxtex)) { //draw a skybox if we were given the textures R_CalcSkyChainBounds(batch); @@ -1130,7 +1156,7 @@ static void GL_DrawSkyBox (texid_t *texnums, batch_t *s) skyfacemesh.numindexes = 6; skyfacemesh.numvertexes = 4; - if (!skyboxface) + if (texnums && !skyboxface) skyboxface = R_RegisterShader("skyboxface", SUF_NONE, "{\n" "program default2d\n" @@ -1152,8 +1178,15 @@ static void GL_DrawSkyBox (texid_t *texnums, batch_t *s) GL_MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i, skyface_vertex[2], skyface_texcoord[2]); GL_MakeSkyVec (skymaxs[0][i], skymins[1][i], i, skyface_vertex[3], skyface_texcoord[3]); - skyboxface->defaulttextures->base = texnums[skytexorder[i]]; - R_DrawSkyMesh(s, &skyfacemesh, skyboxface); +#ifdef MATERIAL_VMT + if (!texnums) + R_DrawSkyMesh(s, &skyfacemesh, forcedskyfaces[skytexorder[i]]); + else +#endif + { + skyboxface->defaulttextures->base = texnums[skytexorder[i]]; + R_DrawSkyMesh(s, &skyfacemesh, skyboxface); + } } } diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index 99e9f680..c0d9286d 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -7,6 +7,8 @@ !!permu BUMP !!permu SPECULAR !!permu REFLECTCUBEMASK +//!!permu TWOWAY //blend between diffuse and the 'upper' texture. +//!!permu EIGHTBIT //our fancy software banding. !!permu FAKESHADOWS !!cvarf r_glsl_offsetmapping_scale !!cvardf r_glsl_pcf @@ -16,12 +18,14 @@ !!samps !EIGHTBIT =BUMP normalmap !!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse. -!!samps =EIGHTBIT paletted 1 +!!samps =EIGHTBIT colourmap=0 +!!samps =EIGHTBIT paletted !!samps =SPECULAR specular !!samps !VERTEXLIT lightmap !!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 !!samps =DELUXE deluxemap !!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 +!!samps =TWOWAY upper !!samps =FAKESHADOWS shadowmap #if defined(ORM) || defined(SG) @@ -56,7 +60,9 @@ varying vec2 lm0; #endif #endif - + #ifdef TWOWAY + varying float alpha; + #endif #ifdef FAKESHADOWS varying vec4 vtexprojcoord; #endif @@ -81,6 +87,9 @@ void main () #ifdef FLOW tc.s += e_time * -0.5; #endif +#ifdef TWOWAY + alpha = v_colour.a; +#endif #ifdef VERTEXLIT #ifdef LIGHTSTYLED //FIXME, only one colour. @@ -249,7 +258,6 @@ void main() #ifdef FRAGMENT_SHADER -#define s_colourmap s_t0 #include "sys/pbr.h" #include "sys/pcf.h" @@ -283,6 +291,11 @@ void main () //Read the base texture (with EIGHTBIT only alpha is needed) vec4 col = texture2D(s_diffuse, tc); +#ifdef TWOWAY + gl_FragColor *= (1.0-alpha); + gl_FragColor += alpha*texture2D(s_upper, tc); +#endif + #if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK)) vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5); #elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)