cnq3/code/renderer/tr_shade.cpp
myT 718b966414 depth fade fixes and improvements
- renamed r_softSprites to r_depthFade
  the term's more descriptive and it helps that UE4 uses it
- fixed the GL3 fragment shader halving the depth bias
- fixed the D3D11 pixel shader only fetching depth sample 0
  not fixed for GL3 yet, see the code comments for that
- added support for more blend states
- added the q3map_cnq3_depthFade general shader directive
2020-04-26 16:32:51 +02:00

231 lines
7 KiB
C++

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_shade.c
#include "tr_local.h"
/*
=============================================================
SURFACE SHADERS
=============================================================
*/
shaderCommands_t tess;
// we must set some things up before beginning any tesselation
// because a surface may be forced to perform a RB_End due to overflow
void RB_BeginSurface( const shader_t* shader, int fogNum )
{
tess.numIndexes = 0;
tess.numVertexes = 0;
tess.shader = shader;
tess.fogNum = fogNum;
tess.xstages = (const shaderStage_t**)shader->stages;
tess.depthFade = DFT_NONE;
tess.deformsPreApplied = qfalse;
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) {
tess.shaderTime = tess.shader->clampTime;
}
}
static void RB_DrawDynamicLight()
{
backEnd.pc[RB_LIT_VERTICES_LATECULLTEST] += tess.numVertexes;
static byte clipBits[SHADER_MAX_VERTEXES];
const dlight_t* dl = tess.light;
const cullType_t cullType = tess.shader->cullType;
for (int i = 0; i < tess.numVertexes; ++i) {
vec3_t dist;
VectorSubtract(dl->transformed, tess.xyz[i], dist);
const float dp = DotProduct(dist, tess.normal[i]);
if (cullType == CT_FRONT_SIDED && dp <= 0.0f ||
cullType == CT_BACK_SIDED && dp >= 0.0f) {
clipBits[i] = byte(-1);
continue;
}
int clip = 0;
if (dist[0] > dl->radius)
clip |= 1;
else if (dist[0] < -dl->radius)
clip |= 2;
if (dist[1] > dl->radius)
clip |= 4;
else if (dist[1] < -dl->radius)
clip |= 8;
if (dist[2] > dl->radius)
clip |= 16;
else if (dist[2] < -dl->radius)
clip |= 32;
clipBits[i] = clip;
}
// build a list of triangles that need light
int numIndexes = 0;
for (int i = 0; i < tess.numIndexes; i += 3) {
const int a = tess.indexes[i + 0];
const int b = tess.indexes[i + 1];
const int c = tess.indexes[i + 2];
if (!(clipBits[a] & clipBits[b] & clipBits[c])) {
tess.dlIndexes[numIndexes + 0] = a;
tess.dlIndexes[numIndexes + 1] = b;
tess.dlIndexes[numIndexes + 2] = c;
numIndexes += 3;
}
}
tess.dlNumIndexes = numIndexes;
backEnd.pc[RB_LIT_INDICES_LATECULL_IN] += numIndexes;
backEnd.pc[RB_LIT_INDICES_LATECULL_OUT] += tess.numIndexes - numIndexes;
if (numIndexes <= 0)
return;
backEnd.pc[RB_LIT_BATCHES]++;
backEnd.pc[RB_LIT_VERTICES] += tess.numVertexes;
backEnd.pc[RB_LIT_INDICES] += tess.numIndexes;
gal.Draw(DT_DYNAMIC_LIGHT);
}
static void RB_DrawGeneric()
{
if (tess.depthFade == DFT_NONE && tess.fogNum && tess.shader->fogPass) {
tess.drawFog = qtrue;
unsigned int fogStateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
if (tess.shader->fogPass == FP_EQUAL)
fogStateBits |= GLS_DEPTHFUNC_EQUAL;
tess.fogStateBits = fogStateBits;
const fog_t* fog = tr.world->fogs + tess.fogNum;
for (int i = 0; i < tess.numVertexes; ++i) {
*(int*)&tess.svarsFog.colors[i] = fog->colorInt;
}
RB_CalcFogTexCoords((float*)tess.svarsFog.texcoords, 0, tess.numVertexes);
tess.svarsFog.texcoordsptr = tess.svarsFog.texcoords;
} else {
tess.drawFog = qfalse;
}
backEnd.pc[RB_BATCHES]++;
backEnd.pc[RB_VERTICES] += tess.numVertexes;
backEnd.pc[RB_INDICES] += tess.numIndexes;
gal.Draw(tess.depthFade != DFT_NONE ? DT_SOFT_SPRITE : DT_GENERIC);
}
void RB_EndSurface()
{
shaderCommands_t* input = &tess;
if (!input->numIndexes || !input->numVertexes)
return;
if (input->indexes[SHADER_MAX_INDEXES-1] != 0) {
ri.Error( ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit" );
}
if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) {
ri.Error( ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit" );
}
// for debugging of sort order issues, stop rendering after a given sort value
if ( r_debugSort->value > 0.0f && r_debugSort->value < tess.shader->sort ) {
return;
}
const shader_t* shader = input->shader;
if (shader->sort == SS_ENVIRONMENT) {
RB_DrawSky();
} else {
if (!tess.deformsPreApplied) {
RB_DeformTessGeometry(0, tess.numVertexes, 0, tess.numIndexes);
for (int i = 0; i < shader->numStages; ++i) {
R_ComputeColors(shader->stages[i], tess.svars[i], 0, tess.numVertexes);
R_ComputeTexCoords(shader->stages[i], tess.svars[i], 0, tess.numVertexes, qtrue);
}
}
if (input->pass == shaderCommands_t::TP_LIGHT)
RB_DrawDynamicLight();
else
RB_DrawGeneric();
}
// draw debugging stuff
if (!backEnd.projection2D &&
(tess.pass == shaderCommands_t::TP_BASE) &&
tess.numIndexes > 0 &&
tess.numVertexes > 0) {
if (r_showtris->integer) {
RB_PushSingleStageShader(GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE, CT_FRONT_SIDED);
R_ComputeColors(tess.shader->stages[0], tess.svars[0], 0, tess.numVertexes);
gal.SetDepthRange(0, 0);
gal.Draw(DT_GENERIC);
gal.SetDepthRange(0, 1);
RB_PopShader();
}
if (r_shownormals->integer) {
// we only draw the normals for the first (SHADER_MAX_VERTEXES / 2 - 1) vertices
int nv = tess.numVertexes;
if (nv >= SHADER_MAX_VERTEXES / 2)
nv = SHADER_MAX_VERTEXES / 2 - 1;
for (int i = 0, j = nv; i < nv; ++i, ++j) {
VectorMA(input->xyz[i], 2, input->normal[i], tess.xyz[j]);
}
for (int i = 0, j = 0; i < nv; ++i, j += 3) {
tess.indexes[j + 0] = i;
tess.indexes[j + 1] = i;
tess.indexes[j + 2] = i + nv;
}
tess.numVertexes = nv * 2;
tess.numIndexes = nv * 3;
RB_PushSingleStageShader(GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE, CT_FRONT_SIDED);
shaderStage_t* const stage = tess.shader->stages[0];
stage->rgbGen = CGEN_CONST;
stage->constantColor[0] = 0;
stage->constantColor[1] = 0;
stage->constantColor[2] = 255;
stage->constantColor[3] = 255;
R_ComputeColors(tess.shader->stages[0], tess.svars[0], 0, tess.numVertexes);
gal.SetDepthRange(0, 0);
gal.Draw(DT_GENERIC);
gal.SetDepthRange(0, 1);
RB_PopShader();
}
}
// clear shader so we can tell we don't have any unclosed surfaces
tess.numIndexes = 0;
}