cvar_t r_glsl_offsetmapping_bias = {0, "r_glsl_offsetmapping_bias", "0.04", "pushes the effect closer/further"};
cvar_t r_glsl_usehalffloat = {0, "r_glsl_usehalffloat", "0", "use half and hvec variables in GLSL shader for a speed gain (NVIDIA only)"};
cvar_t r_glsl_surfacenormalize = {0, "r_glsl_surfacenormalize", "1", "normalize bumpmap texels in GLSL shader, produces a more rounded look on small bumps and dents"};
+cvar_t r_glsl_deluxemapping = {0, "r_glsl_deluxemapping", "1", "use per pixel lighting on deluxemap-compiled q3bsp maps (or a value of 2 forces deluxemap shading even without deluxemaps)"};
cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1", "enables animation smoothing on sprites (requires r_lerpmodels 1)"};
cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smoothing on models"};
"varying vec3 EyeVector;\n"
"#endif\n"
"\n"
+"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
+"varying myhvec3 VectorS; // direction of S texcoord (sometimes crudely called tangent)\n"
+"varying myhvec3 VectorT; // direction of T texcoord (sometimes crudely called binormal)\n"
+"varying myhvec3 VectorR; // direction of R texcoord (surface normal)\n"
+"#endif\n"
+"\n"
"\n"
"\n"
"\n"
" EyeVector.z = dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
"#endif\n"
"\n"
+"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
+" VectorS = gl_MultiTexCoord1.xyz;\n"
+" VectorT = gl_MultiTexCoord2.xyz;\n"
+" VectorR = gl_MultiTexCoord3.xyz;\n"
+"#endif\n"
+"\n"
" // transform vertex to camera space, using ftransform to match non-VS\n"
" // rendering\n"
" gl_Position = ftransform();\n"
"uniform myhalf OffsetMapping_Bias;\n"
"#endif\n"
"\n"
-"#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTIONMAP) || defined(MODE_LIGHTDIRECTION) || defined(USEOFFSETMAPPING)\n"
+"#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE) || defined(MODE_LIGHTDIRECTION) || defined(USEOFFSETMAPPING)\n"
"uniform sampler2D Texture_Normal;\n"
"#endif\n"
"\n"
"#if !defined(MODE_LIGHTSOURCE) && !defined(MODE_LIGHTDIRECTION)\n"
"uniform sampler2D Texture_Lightmap;\n"
"#endif\n"
-"#ifdef MODE_LIGHTDIRECTIONMAP\n"
+"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
"uniform sampler2D Texture_Deluxemap;\n"
"#endif\n"
"\n"
"{\n"
" // apply offsetmapping\n"
"#ifdef USEOFFSETMAPPING\n"
+" myhvec3 eyedir = myhvec3(normalize(EyeVector));\n"
+" myhalf depthbias = 1.0 - eyedir.z; // should this be a -?\n"
+" depthbias = 1.0 - depthbias * depthbias;\n"
" // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
-" myhvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
+" myhvec2 OffsetVector = (EyeVector.xy * (1.0 / EyeVector.z) * depthbias) * vec2(-0.333, 0.333);\n"
" myhvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
" TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
" TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
"\n"
"\n"
"\n"
-"#elif defined(MODE_LIGHTDIRECTIONMAP)\n"
-" // deluxemap lightmapping\n"
+"#elif defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE)\n"
+" // deluxemap lightmapping using light vectors in modelspace (evil q3map2)\n"
+"\n"
+" // get the surface normal and light normal\n"
+"#ifdef SURFACENORMALIZE\n"
+" myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"#else\n"
+" myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
+"#endif\n"
+" myhvec3 diffusenormal_modelspace = myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5;\n"
+" myhvec3 diffusenormal = normalize(myhvec3(dot(diffusenormal_modelspace, VectorS), dot(diffusenormal_modelspace, VectorT), dot(diffusenormal_modelspace, VectorR)));\n"
+"\n"
+" // calculate directional shading\n"
+" myhvec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"#ifdef USESPECULAR\n"
+" myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
+" tempcolor += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"#endif\n"
+"\n"
+" // apply lightmap color\n"
+" color.rgb = tempcolor * myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * myhvec3(AmbientScale);\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"#elif defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
+" // deluxemap lightmapping using light vectors in tangentspace\n"
"\n"
" // get the surface normal and light normal\n"
"#ifdef SURFACENORMALIZE\n"
fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTSOURCE\n";
strlcat(permutationname, " lightsource", sizeof(permutationname));
}
- if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP)
+ if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE)
{
- vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP\n";
- fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP\n";
- strlcat(permutationname, " lightdirectionmap", sizeof(permutationname));
+ vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n";
+ fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n";
+ strlcat(permutationname, " lightdirectionmap_modelspace", sizeof(permutationname));
+ }
+ if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)
+ {
+ vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n";
+ fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n";
+ strlcat(permutationname, " lightdirectionmap_tangentspace", sizeof(permutationname));
}
if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
{
if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
permutation |= SHADERPERMUTATION_CUBEFILTER;
}
- else if (modellighting)
- {
- permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
- if (texture->skin.glow)
- permutation |= SHADERPERMUTATION_GLOW;
- }
- else if (r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
- {
- permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP;
- if (texture->skin.glow)
- permutation |= SHADERPERMUTATION_GLOW;
- }
else
{
+ if (modellighting)
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
+ else if (r_glsl_deluxemapping.integer >= 1 && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
+ {
+ if (r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
+ else
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+ }
+ else if (r_glsl_deluxemapping.integer >= 2) // fake mode
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
if (texture->skin.glow)
permutation |= SHADERPERMUTATION_GLOW;
}
Cvar_RegisterVariable(&r_glsl_offsetmapping_bias);
Cvar_RegisterVariable(&r_glsl_usehalffloat);
Cvar_RegisterVariable(&r_glsl_surfacenormalize);
+ Cvar_RegisterVariable(&r_glsl_deluxemapping);
Cvar_RegisterVariable(&r_lerpsprites);
Cvar_RegisterVariable(&r_lerpmodels);
Cvar_RegisterVariable(&r_waterscroll);
{
R_Mesh_TexBind(7, R_GetTexture(surface->lightmaptexture));
if (r_glsl_permutation->loc_Texture_Deluxemap >= 0)
- R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
- //R_Mesh_TexBind(8, R_GetTexture(surface->deluxemaptexture));
+ R_Mesh_TexBind(8, R_GetTexture(surface->deluxemaptexture));
R_Mesh_ColorPointer(NULL);
}
else
surface->lightmapinfo->styles[i] = in->styles[i];
surface->lightmapinfo->lightmaptexturestride = 0;
surface->lightmaptexture = NULL;
+ surface->deluxemaptexture = r_texture_blanknormalmap;
i = LittleLong(in->lightofs);
if (i == -1)
{
{
surface->lightmapinfo->lightmaptexturestride = ssize;
surface->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surface->lightmapinfo->lightmaptexturestride, tsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+ surface->deluxemaptexture = r_texture_blanknormalmap;
}
else
{
surface->lightmapinfo->lightmaptexturestride = R_CompatibleFragmentWidth(ssize, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
surface->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surface->lightmapinfo->lightmaptexturestride, tsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+ surface->deluxemaptexture = r_texture_blanknormalmap;
}
R_FragmentLocation(surface->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
uscale = (uscale - ubase) / ssize;
loadmodel->brushq3.data_lightmaps = out;
loadmodel->brushq3.num_lightmaps = count;
+ loadmodel->brushq3.deluxemapping_modelspace = false;
for (i = 0;i < count;i++, in++, out++)
+ {
+ // if this may be a deluxemap, check if it's in modelspace or not
+ if ((i & 1) && !loadmodel->brushq3.deluxemapping_modelspace)
+ {
+ int j;
+ unsigned char *b = in->rgb;
+ for (j = 2;j < 128*128*3;j += 3)
+ {
+ // if this is definitely negative Z, it is not facing outward,
+ // and thus must be in modelspace, as negative Z would never
+ // occur in tangentspace
+ if (b[j] < 120)
+ {
+ loadmodel->brushq3.deluxemapping_modelspace = true;
+ break;
+ }
+ }
+ }
*out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
- // deluxemapped bsp files have an even number of lightmaps, and surfaces
- // always index even numbered ones (0, 2, 4, ...), the odd numbered
- // lightmaps are the deluxemaps (light direction textures), so if we
- // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
- // is also not a deluxemapped bsp if it has an odd number of lightmaps or
- // less than 2
- loadmodel->brushq3.deluxemapping = true;
- if ((count & 1) || count < 2)
- loadmodel->brushq3.deluxemapping = false;
+ }
}
static void Mod_Q3BSP_LoadFaces(lump_t *l)
loadmodel->data_surfaces = out;
loadmodel->num_surfaces = count;
+ // deluxemapped q3bsp files have an even number of lightmaps, and surfaces
+ // always index even numbered ones (0, 2, 4, ...), the odd numbered
+ // lightmaps are the deluxemaps (light direction textures), so if we
+ // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
+ // is also not a deluxemapped bsp if it has an odd number of lightmaps or
+ // less than 2
+ loadmodel->brushq3.deluxemapping = true;
+ if (count >= 2 && !(count & 1))
+ {
+ for (i = 0;i < count;i++)
+ {
+ n = LittleLong(in[i].lightmapindex);
+ if (n >= 0 && ((n & 1) || n + 1 >= loadmodel->brushq3.num_lightmaps))
+ {
+ loadmodel->brushq3.deluxemapping = false;
+ break;
+ }
+ }
+ }
+ else
+ loadmodel->brushq3.deluxemapping = false;
+ Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
+
i = 0;
for (meshnum = 0;i < count;meshnum++)
{
else if (n < 0)
n = -1;
if (n == -1)
+ {
out->lightmaptexture = NULL;
+ out->deluxemaptexture = r_texture_blanknormalmap;
+ }
else
{
- // deluxemapped q3bsp files have lightmaps and deluxemaps in
- // pairs, no odd numbers ever appear, so if we encounter an
- // odd lightmap index, it's not deluxemapped.
- if (n & 1)
- loadmodel->brushq3.deluxemapping = false;
out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
+ if (loadmodel->brushq3.deluxemapping)
+ out->deluxemaptexture = loadmodel->brushq3.data_lightmaps[n+1];
+ else
+ out->deluxemaptexture = r_texture_blanknormalmap;
}
firstvertex = LittleLong(in->firstvertex);
texture_t *texture;
// the lightmap texture fragment to use on the rendering mesh
rtexture_t *lightmaptexture;
+ // the lighting direction texture fragment to use on the rendering mesh
+ rtexture_t *deluxemaptexture;
// this surface is part of this mesh
surfmesh_t *groupmesh;
// (lightmap texture pairs, every odd one is never directly refernced,
// and contains lighting normals, not colors)
qboolean deluxemapping;
+ // true if the detected deluxemaps are the modelspace kind, rather than
+ // the faster tangentspace kind
+ qboolean deluxemapping_modelspace;
}
model_brushq3_t;
extern cvar_t r_glsl_offsetmapping_bias;
extern cvar_t r_glsl_usehalffloat;
extern cvar_t r_glsl_surfacenormalize;
+extern cvar_t r_glsl_deluxemapping;
extern cvar_t gl_polyblend;
extern cvar_t gl_dither;
void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces);
#define SHADERPERMUTATION_MODE_LIGHTSOURCE (1<<0) // (lightsource) use directional pixel shading from light source (rtlight)
-#define SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP (1<<1) // (lightmap) use directional pixel shading from texture containing light directions (deluxemap)
-#define SHADERPERMUTATION_MODE_LIGHTDIRECTION (1<<2) // (lightmap) use directional pixel shading from fixed light direction (q3bsp)
-#define SHADERPERMUTATION_GLOW (1<<3) // (lightmap) blend in an additive glow texture
-#define SHADERPERMUTATION_FOG (1<<4) // tint the color by fog color or black if using additive blend mode
-#define SHADERPERMUTATION_COLORMAPPING (1<<5) // indicates this is a colormapped skin
-#define SHADERPERMUTATION_SPECULAR (1<<6) // (lightsource or deluxemapping) render specular effects
-#define SHADERPERMUTATION_CUBEFILTER (1<<7) // (lightsource) use cubemap light filter
-#define SHADERPERMUTATION_OFFSETMAPPING (1<<8) // adjust texcoords to roughly simulate a displacement mapped surface
-#define SHADERPERMUTATION_SURFACENORMALIZE (1<<9) // (lightsource or deluxemapping) improved bumpmapping
-#define SHADERPERMUTATION_GEFORCEFX (1<<10) // use half vector types if available (NVIDIA specific)
-#define SHADERPERMUTATION_COUNT (1<<11) // how many permutations are possible
+#define SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE (1<<1) // (lightmap) use directional pixel shading from texture containing modelspace light directions (deluxemap)
+#define SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE (1<<2) // (lightmap) use directional pixel shading from texture containing tangentspace light directions (deluxemap)
+#define SHADERPERMUTATION_MODE_LIGHTDIRECTION (1<<3) // (lightmap) use directional pixel shading from fixed light direction (q3bsp)
+#define SHADERPERMUTATION_GLOW (1<<4) // (lightmap) blend in an additive glow texture
+#define SHADERPERMUTATION_FOG (1<<5) // tint the color by fog color or black if using additive blend mode
+#define SHADERPERMUTATION_COLORMAPPING (1<<6) // indicates this is a colormapped skin
+#define SHADERPERMUTATION_SPECULAR (1<<7) // (lightsource or deluxemapping) render specular effects
+#define SHADERPERMUTATION_CUBEFILTER (1<<8) // (lightsource) use cubemap light filter
+#define SHADERPERMUTATION_OFFSETMAPPING (1<<9) // adjust texcoords to roughly simulate a displacement mapped surface
+#define SHADERPERMUTATION_SURFACENORMALIZE (1<<10) // (lightsource or deluxemapping) improved bumpmapping
+#define SHADERPERMUTATION_GEFORCEFX (1<<11) // use half vector types if available (NVIDIA specific)
+#define SHADERPERMUTATION_COUNT (1<<12) // how many permutations are possible
typedef struct r_glsl_permutation_s
{