cvar_t r_glsl_offsetmapping_reliefmapping = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"};
cvar_t r_glsl_offsetmapping_scale = {CVAR_SAVE, "r_glsl_offsetmapping_scale", "0.04", "how deep the offset mapping effect is"};
cvar_t r_glsl_deluxemapping = {CVAR_SAVE, "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_glsl_contrastboost = {CVAR_SAVE, "r_glsl_contrastboost", "1", "by how much to multiply the contrast in dark areas (1 is no change)"};
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"};
"\n"
"uniform myhalf GlowScale;\n"
"uniform myhalf SceneBrightness;\n"
+"#ifdef USECONTRASTBOOST\n"
+"uniform myhalf ContrastBoostCoeff;\n"
+"#endif\n"
"\n"
"uniform float OffsetMapping_Scale;\n"
"uniform float OffsetMapping_Bias;\n"
" color.rgb = mix(FogColor, color.rgb, myhalf(texture2D(Texture_FogMask, myhvec2(length(EyeVectorModelSpace)*FogRangeRecip, 0.0))));\n"
"#endif\n"
"\n"
+"#ifdef USECONTRASTBOOST\n"
+" color.rgb = color.rgb * SceneBrightness / (ContrastBoostCoeff * color.rgb + myhvec3(1, 1, 1));\n"
+"#else\n"
" color.rgb *= SceneBrightness;\n"
+"#endif\n"
"\n"
" gl_FragColor = vec4(color);\n"
"}\n"
{"#define USEFOG\n", " fog"},
{"#define USECOLORMAPPING\n", " colormapping"},
{"#define USEDIFFUSE\n", " diffuse"},
+ {"#define USECONTRASTBOOST\n", " contrastboost"},
{"#define USESPECULAR\n", " specular"},
{"#define USECUBEFILTER\n", " cubefilter"},
{"#define USEOFFSETMAPPING\n", " offsetmapping"},
p->loc_DiffuseColor = qglGetUniformLocationARB(p->program, "DiffuseColor");
p->loc_SpecularColor = qglGetUniformLocationARB(p->program, "SpecularColor");
p->loc_LightDir = qglGetUniformLocationARB(p->program, "LightDir");
+ p->loc_ContrastBoostCoeff = qglGetUniformLocationARB(p->program, "ContrastBoostCoeff");
// initialize the samplers to refer to the texture units we use
if (p->loc_Texture_Normal >= 0) qglUniform1iARB(p->loc_Texture_Normal, 0);
if (p->loc_Texture_Color >= 0) qglUniform1iARB(p->loc_Texture_Color, 1);
if (r_glsl_offsetmapping_reliefmapping.integer)
permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
}
+ if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
+ permutation |= SHADERPERMUTATION_CONTRASTBOOST;
}
else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
{
if (r_glsl_offsetmapping_reliefmapping.integer)
permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
}
+ if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
+ permutation |= SHADERPERMUTATION_CONTRASTBOOST;
}
else if (modellighting)
{
if (r_glsl_offsetmapping_reliefmapping.integer)
permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
}
+ if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
+ permutation |= SHADERPERMUTATION_CONTRASTBOOST;
}
else
{
if (r_glsl_offsetmapping_reliefmapping.integer)
permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
}
+ if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
+ permutation |= SHADERPERMUTATION_CONTRASTBOOST;
}
if (!r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].program)
{
{
// remove features until we find a valid permutation
unsigned int i;
- for (i = SHADERPERMUTATION_MASK;;i>>=1)
+ for (i = (SHADERPERMUTATION_MAX >> 1);;i>>=1)
{
if (!i)
- return 0; // utterly failed
+ return 0; // no bit left to clear
// reduce i more quickly whenever it would not remove any bits
- if (permutation < i)
+ if (!(permutation & i))
continue;
- permutation &= i;
+ permutation &= ~i;
if (!r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].compiled)
R_GLSL_CompilePermutation(shaderfilename, permutation);
if (r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].program)
//if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
if (r_glsl_permutation->loc_Texture_Glow >= 0) R_Mesh_TexBind(9, R_GetTexture(rsurface.texture->currentskinframe->glow));
if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
- if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_view.colorscale);
+ if (r_glsl_permutation->loc_ContrastBoostCoeff >= 0)
+ {
+ // The formula used is actually:
+ // color.rgb *= SceneBrightness;
+ // color.rgb *= ContrastBoost / ((ContrastBoost - 1) * color.rgb + 1);
+ // I simplify that to
+ // color.rgb *= [[SceneBrightness * ContrastBoost]];
+ // color.rgb /= [[(ContrastBoost - 1) / ContrastBoost]] * color.rgb + 1;
+ // and Black:
+ // color.rgb = [[SceneBrightness * ContrastBoost]] / ([[(ContrastBoost - 1) * SceneBrightness]] + 1 / color.rgb);
+ // and do [[calculations]] here in the engine
+ qglUniform1fARB(r_glsl_permutation->loc_ContrastBoostCoeff, (r_glsl_contrastboost.value - 1) * r_view.colorscale);
+ if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_view.colorscale * r_glsl_contrastboost.value);
+ }
+ else
+ if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_view.colorscale);
if (r_glsl_permutation->loc_FogColor >= 0)
{
// additive passes are only darkened by fog, not tinted
Cvar_RegisterVariable(&r_bloom_colorsubtract);
Cvar_RegisterVariable(&r_hdr);
Cvar_RegisterVariable(&r_hdr_scenebrightness);
+ Cvar_RegisterVariable(&r_glsl_contrastboost);
Cvar_RegisterVariable(&r_hdr_glowintensity);
Cvar_RegisterVariable(&r_hdr_range);
Cvar_RegisterVariable(&r_smoothnormals_areaweighting);
- slopex = 1.0 / r_view.frustum_x;
- slopey = 1.0 / r_view.frustum_y;
- VectorMA(r_view.forward, -slopex, r_view.left, r_view.frustum[0].normal);
- VectorMA(r_view.forward, slopex, r_view.left, r_view.frustum[1].normal);
- VectorMA(r_view.forward, -slopey, r_view.up , r_view.frustum[2].normal);
- VectorMA(r_view.forward, slopey, r_view.up , r_view.frustum[3].normal);
- VectorCopy(r_view.forward, r_view.frustum[4].normal);
- VectorNormalize(r_view.frustum[0].normal);
- VectorNormalize(r_view.frustum[1].normal);
- VectorNormalize(r_view.frustum[2].normal);
- VectorNormalize(r_view.frustum[3].normal);
- r_view.frustum[0].dist = DotProduct (r_view.origin, r_view.frustum[0].normal);
- r_view.frustum[1].dist = DotProduct (r_view.origin, r_view.frustum[1].normal);
- r_view.frustum[2].dist = DotProduct (r_view.origin, r_view.frustum[2].normal);
- r_view.frustum[3].dist = DotProduct (r_view.origin, r_view.frustum[3].normal);
- r_view.frustum[4].dist = DotProduct (r_view.origin, r_view.frustum[4].normal) + r_refdef.nearclip;
+ if (r_view.useperspective)
+ {
+ slopex = 1.0 / r_view.frustum_x;
+ slopey = 1.0 / r_view.frustum_y;
+ VectorMA(r_view.forward, -slopex, r_view.left, r_view.frustum[0].normal);
+ VectorMA(r_view.forward, slopex, r_view.left, r_view.frustum[1].normal);
+ VectorMA(r_view.forward, -slopey, r_view.up , r_view.frustum[2].normal);
+ VectorMA(r_view.forward, slopey, r_view.up , r_view.frustum[3].normal);
+ VectorCopy(r_view.forward, r_view.frustum[4].normal);
+
+ // calculate frustum corners, which are used to calculate deformed frustum planes for shadow caster culling
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, -1024 * slopex, r_view.left, -1024 * slopey, r_view.up, r_view.frustumcorner[0]);
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, 1024 * slopex, r_view.left, -1024 * slopey, r_view.up, r_view.frustumcorner[1]);
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, -1024 * slopex, r_view.left, 1024 * slopey, r_view.up, r_view.frustumcorner[2]);
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, 1024 * slopex, r_view.left, 1024 * slopey, r_view.up, r_view.frustumcorner[3]);
+
+ r_view.frustum[0].dist = DotProduct (r_view.origin, r_view.frustum[0].normal);
+ r_view.frustum[1].dist = DotProduct (r_view.origin, r_view.frustum[1].normal);
+ r_view.frustum[2].dist = DotProduct (r_view.origin, r_view.frustum[2].normal);
+ r_view.frustum[3].dist = DotProduct (r_view.origin, r_view.frustum[3].normal);
+ r_view.frustum[4].dist = DotProduct (r_view.origin, r_view.frustum[4].normal) + r_refdef.nearclip;
+ }
+ else
+ {
+ VectorScale(r_view.left, -r_view.ortho_x, r_view.frustum[0].normal);
+ VectorScale(r_view.left, r_view.ortho_x, r_view.frustum[1].normal);
+ VectorScale(r_view.up, -r_view.ortho_y, r_view.frustum[2].normal);
+ VectorScale(r_view.up, r_view.ortho_y, r_view.frustum[3].normal);
+ VectorCopy(r_view.forward, r_view.frustum[4].normal);
+ r_view.frustum[0].dist = DotProduct (r_view.origin, r_view.frustum[0].normal) + r_view.ortho_x;
+ r_view.frustum[1].dist = DotProduct (r_view.origin, r_view.frustum[1].normal) + r_view.ortho_x;
+ r_view.frustum[2].dist = DotProduct (r_view.origin, r_view.frustum[2].normal) + r_view.ortho_y;
+ r_view.frustum[3].dist = DotProduct (r_view.origin, r_view.frustum[3].normal) + r_view.ortho_y;
+ r_view.frustum[4].dist = DotProduct (r_view.origin, r_view.frustum[4].normal) + r_refdef.nearclip;
+ }
+
PlaneClassify(&r_view.frustum[0]);
PlaneClassify(&r_view.frustum[1]);
PlaneClassify(&r_view.frustum[2]);
PlaneClassify(&r_view.frustum[3]);
PlaneClassify(&r_view.frustum[4]);
- // calculate frustum corners, which are used to calculate deformed frustum planes for shadow caster culling
- VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, -1024 * slopex, r_view.left, -1024 * slopey, r_view.up, r_view.frustumcorner[0]);
- VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, 1024 * slopex, r_view.left, -1024 * slopey, r_view.up, r_view.frustumcorner[1]);
- VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, -1024 * slopex, r_view.left, 1024 * slopey, r_view.up, r_view.frustumcorner[2]);
- VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, 1024 * slopex, r_view.left, 1024 * slopey, r_view.up, r_view.frustumcorner[3]);
-
// LordHavoc: note to all quake engine coders, Quake had a special case
// for 90 degrees which assumed a square view (wrong), so I removed it,
// Quake2 has it disabled as well.
void R_SetupView(const matrix4x4_t *matrix)
{
- if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows)
+ if (!r_view.useperspective)
+ GL_SetupView_Mode_Ortho(-r_view.ortho_x, -r_view.ortho_y, r_view.ortho_x, r_view.ortho_y, -r_refdef.farclip, r_refdef.farclip);
+ else if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows)
GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip);
else
GL_SetupView_Mode_Perspective(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip, r_refdef.farclip);
{
// use an alternate animation if the entity's frame is not 0,
// and only if the texture has an alternate animation
- if (ent->frame != 0 && t->anim_total[1])
+ if (ent->frame2 != 0 && t->anim_total[1])
t = t->anim_frames[1][(t->anim_total[1] >= 2) ? ((int)(r_refdef.time * 5.0f) % t->anim_total[1]) : 0];
else
t = t->anim_frames[0][(t->anim_total[0] >= 2) ? ((int)(r_refdef.time * 5.0f) % t->anim_total[0]) : 0];
{
const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
const float *v1, *v2;
+ vec3_t start, end;
float f, l;
struct
{
float length2;
- int quadedge;
+ const float *v1;
+ const float *v2;
}
shortest[2];
+ memset(shortest, 0, sizeof(shortest));
// a single autosprite surface can contain multiple sprites...
for (j = 0;j < surface->num_vertices - 3;j += 4)
{
for (i = 0;i < 4;i++)
VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
VectorScale(center, 0.25f, center);
- shortest[0].quadedge = shortest[1].quadedge = 0;
- shortest[0].length2 = shortest[1].length2 = 0;
// find the two shortest edges, then use them to define the
// axis vectors for rotating around the central axis
for (i = 0;i < 6;i++)
{
v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][0]);
v2 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][1]);
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(v1[0], v1[1], v1[2], 0, 0, 1, 0, 0, 1);
+ Debug_PolygonVertex((v1[0] + v2[0]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, (v1[1] + v2[1]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1], (v1[2] + v2[2]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2], 0, 0, 1, 1, 0, 1);
+ Debug_PolygonVertex(v2[0], v2[1], v2[2], 0, 0, 1, 0, 0, 1);
+ Debug_PolygonEnd();
+#endif
l = VectorDistance2(v1, v2);
+ // this length bias tries to make sense of square polygons, assuming they are meant to be upright
+ if (v1[2] != v2[2])
+ l += (1.0f / 1024.0f);
if (shortest[0].length2 > l || i == 0)
{
shortest[1] = shortest[0];
shortest[0].length2 = l;
- shortest[0].quadedge = i;
+ shortest[0].v1 = v1;
+ shortest[0].v2 = v2;
}
else if (shortest[1].length2 > l || i == 1)
{
shortest[1].length2 = l;
- shortest[1].quadedge = i;
+ shortest[1].v1 = v1;
+ shortest[1].v2 = v2;
}
}
- // this calculates the midpoints *2 (not bothering to average) of the two shortest edges, and subtracts one from the other to get the up vector
- for (i = 0;i < 3;i++)
- {
- right[i] = rsurface.vertex3f[3 * (surface->num_firstvertex + quadedges[shortest[1].quadedge][1]) + i]
- + rsurface.vertex3f[3 * (surface->num_firstvertex + quadedges[shortest[1].quadedge][0]) + i];
- up[i] = rsurface.vertex3f[3 * (surface->num_firstvertex + quadedges[shortest[1].quadedge][0]) + i]
- + rsurface.vertex3f[3 * (surface->num_firstvertex + quadedges[shortest[1].quadedge][1]) + i]
- - rsurface.vertex3f[3 * (surface->num_firstvertex + quadedges[shortest[0].quadedge][0]) + i]
- - rsurface.vertex3f[3 * (surface->num_firstvertex + quadedges[shortest[0].quadedge][1]) + i];
- }
+ VectorLerp(shortest[0].v1, 0.5f, shortest[0].v2, start);
+ VectorLerp(shortest[1].v1, 0.5f, shortest[1].v2, end);
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(start[0], start[1], start[2], 0, 0, 1, 1, 0, 1);
+ Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 4, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 4, 0, 0, 0, 1, 0, 1);
+ Debug_PolygonVertex(end[0], end[1], end[2], 0, 0, 0, 1, 1, 1);
+ Debug_PolygonEnd();
+#endif
+ // this calculates the right vector from the shortest edge
+ // and the up vector from the edge midpoints
+ VectorSubtract(shortest[0].v1, shortest[0].v2, right);
+ VectorNormalize(right);
+ VectorSubtract(end, start, up);
+ VectorNormalize(up);
// calculate a forward vector to use instead of the original plane normal (this is how we get a new right vector)
- VectorSubtract(rsurface.modelorg, center, forward);
+ //VectorSubtract(rsurface.modelorg, center, forward);
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.forward, forward);
+ VectorNegate(forward, forward);
+ VectorReflect(forward, 0, up, forward);
+ VectorNormalize(forward);
CrossProduct(up, forward, newright);
- // normalize the vectors involved
- VectorNormalize(right);
VectorNormalize(newright);
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 8, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 8, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 8, 0, 0, 1, 0, 0, 1);
+ Debug_PolygonVertex(center[0] + right[0] * 8, center[1] + right[1] * 8, center[2] + right[2] * 8, 0, 0, 0, 1, 0, 1);
+ Debug_PolygonVertex(center[0] + up [0] * 8, center[1] + up [1] * 8, center[2] + up [2] * 8, 0, 0, 0, 0, 1, 1);
+ Debug_PolygonEnd();
+#endif
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(center[0] + forward [0] * 8, center[1] + forward [1] * 8, center[2] + forward [2] * 8, 0, 0, 1, 0, 0, 1);
+ Debug_PolygonVertex(center[0] + newright[0] * 8, center[1] + newright[1] * 8, center[2] + newright[2] * 8, 0, 0, 0, 1, 0, 1);
+ Debug_PolygonVertex(center[0] + up [0] * 8, center[1] + up [1] * 8, center[2] + up [2] * 8, 0, 0, 0, 0, 1, 1);
+ Debug_PolygonEnd();
+#endif
// rotate the quad around the up axis vector, this is made
// especially easy by the fact we know the quad is flat,
// so we only have to subtract the center position and
// displacement from the center, which we do with a
// DotProduct, the subtraction/addition of center is also
// optimized into DotProducts here
- l = DotProduct(newright, center) - DotProduct(right, center);
+ l = DotProduct(right, center);
for (i = 0;i < 4;i++)
{
v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + j + i);
- f = DotProduct(right, v1) - DotProduct(newright, v1) + l;
- VectorMA(v1, f, newright, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
+ f = DotProduct(right, v1) - l;
+ VectorMAMAM(1, v1, -f, right, f, newright, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
}
}
Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer);