📄 tr_shader.c
字号:
// skip stuff that only q3map or the server needs
else if ( !Q_stricmp( token, "surfaceParm" ) ) {
ParseSurfaceParm( text );
continue;
}
// no mip maps
else if ( !Q_stricmp( token, "nomipmaps" ) )
{
shader.noMipMaps = qtrue;
shader.noPicMip = qtrue;
continue;
}
// no picmip adjustment
else if ( !Q_stricmp( token, "nopicmip" ) )
{
shader.noPicMip = qtrue;
continue;
}
// polygonOffset
else if ( !Q_stricmp( token, "polygonOffset" ) )
{
shader.polygonOffset = qtrue;
continue;
}
// entityMergable, allowing sprite surfaces from multiple entities
// to be merged into one batch. This is a savings for smoke
// puffs and blood, but can't be used for anything where the
// shader calcs (not the surface function) reference the entity color or scroll
else if ( !Q_stricmp( token, "entityMergable" ) )
{
shader.entityMergable = qtrue;
continue;
}
// fogParms
else if ( !Q_stricmp( token, "fogParms" ) )
{
if ( !ParseVector( text, 3, shader.fogParms.color ) ) {
return qfalse;
}
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name );
continue;
}
shader.fogParms.depthForOpaque = atof( token );
// skip any old gradient directions
SkipRestOfLine( text );
continue;
}
// portal
else if ( !Q_stricmp(token, "portal") )
{
shader.sort = SS_PORTAL;
continue;
}
// skyparms <cloudheight> <outerbox> <innerbox>
else if ( !Q_stricmp( token, "skyparms" ) )
{
ParseSkyParms( text );
continue;
}
// light <value> determines flaring in q3map, not needed here
else if ( !Q_stricmp(token, "light") )
{
token = COM_ParseExt( text, qfalse );
continue;
}
// cull <face>
else if ( !Q_stricmp( token, "cull") )
{
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name );
continue;
}
if ( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "twosided" ) || !Q_stricmp( token, "disable" ) )
{
shader.cullType = CT_TWO_SIDED;
}
else if ( !Q_stricmp( token, "back" ) || !Q_stricmp( token, "backside" ) || !Q_stricmp( token, "backsided" ) )
{
shader.cullType = CT_BACK_SIDED;
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name );
}
continue;
}
// sort
else if ( !Q_stricmp( token, "sort" ) )
{
ParseSort( text );
continue;
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name );
return qfalse;
}
}
//
// ignore shaders that don't have any stages, unless it is a sky or fog
//
if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) {
return qfalse;
}
shader.explicitlyDefined = qtrue;
return qtrue;
}
/*
========================================================================================
SHADER OPTIMIZATION AND FOGGING
========================================================================================
*/
/*
===================
ComputeStageIteratorFunc
See if we can use on of the simple fastpath stage functions,
otherwise set to the generic stage function
===================
*/
static void ComputeStageIteratorFunc( void )
{
shader.optimalStageIteratorFunc = RB_StageIteratorGeneric;
//
// see if this should go into the sky path
//
if ( shader.isSky )
{
shader.optimalStageIteratorFunc = RB_StageIteratorSky;
goto done;
}
if ( r_ignoreFastPath->integer )
{
return;
}
//
// see if this can go into the vertex lit fast path
//
if ( shader.numUnfoggedPasses == 1 )
{
if ( stages[0].rgbGen == CGEN_LIGHTING_DIFFUSE )
{
if ( stages[0].alphaGen == AGEN_IDENTITY )
{
if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE )
{
if ( !shader.polygonOffset )
{
if ( !shader.multitextureEnv )
{
if ( !shader.numDeforms )
{
shader.optimalStageIteratorFunc = RB_StageIteratorVertexLitTexture;
goto done;
}
}
}
}
}
}
}
//
// see if this can go into an optimized LM, multitextured path
//
if ( shader.numUnfoggedPasses == 1 )
{
if ( ( stages[0].rgbGen == CGEN_IDENTITY ) && ( stages[0].alphaGen == AGEN_IDENTITY ) )
{
if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE &&
stages[0].bundle[1].tcGen == TCGEN_LIGHTMAP )
{
if ( !shader.polygonOffset )
{
if ( !shader.numDeforms )
{
if ( shader.multitextureEnv )
{
shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture;
goto done;
}
}
}
}
}
}
done:
return;
}
typedef struct {
int blendA;
int blendB;
int multitextureEnv;
int multitextureBlend;
} collapse_t;
static collapse_t collapse[] = {
{ 0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO,
GL_MODULATE, 0 },
{ 0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR,
GL_MODULATE, 0 },
{ GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR,
GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR },
{ GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR,
GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR },
{ GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO,
GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR },
{ GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO,
GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR },
{ 0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE,
GL_ADD, 0 },
{ GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE,
GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE },
#if 0
{ 0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA,
GL_DECAL, 0 },
#endif
{ -1 }
};
/*
================
CollapseMultitexture
Attempt to combine two stages into a single multitexture stage
FIXME: I think modulated add + modulated add collapses incorrectly
=================
*/
static qboolean CollapseMultitexture( void ) {
int abits, bbits;
int i;
textureBundle_t tmpBundle;
if ( !qglActiveTextureARB ) {
return qfalse;
}
// make sure both stages are active
if ( !stages[0].active || !stages[1].active ) {
return qfalse;
}
// on voodoo2, don't combine different tmus
if ( glConfig.driverType == GLDRV_VOODOO ) {
if ( stages[0].bundle[0].image[0]->TMU ==
stages[1].bundle[0].image[0]->TMU ) {
return qfalse;
}
}
abits = stages[0].stateBits;
bbits = stages[1].stateBits;
// make sure that both stages have identical state other than blend modes
if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) !=
( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) {
return qfalse;
}
abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
// search for a valid multitexture blend function
for ( i = 0; collapse[i].blendA != -1 ; i++ ) {
if ( abits == collapse[i].blendA
&& bbits == collapse[i].blendB ) {
break;
}
}
// nothing found
if ( collapse[i].blendA == -1 ) {
return qfalse;
}
// GL_ADD is a separate extension
if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) {
return qfalse;
}
// make sure waveforms have identical parameters
if ( ( stages[0].rgbGen != stages[1].rgbGen ) ||
( stages[0].alphaGen != stages[1].alphaGen ) ) {
return qfalse;
}
// an add collapse can only have identity colors
if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) {
return qfalse;
}
if ( stages[0].rgbGen == CGEN_WAVEFORM )
{
if ( memcmp( &stages[0].rgbWave,
&stages[1].rgbWave,
sizeof( stages[0].rgbWave ) ) )
{
return qfalse;
}
}
if ( stages[0].alphaGen == CGEN_WAVEFORM )
{
if ( memcmp( &stages[0].alphaWave,
&stages[1].alphaWave,
sizeof( stages[0].alphaWave ) ) )
{
return qfalse;
}
}
// make sure that lightmaps are in bundle 1 for 3dfx
if ( stages[0].bundle[0].isLightmap )
{
tmpBundle = stages[0].bundle[0];
stages[0].bundle[0] = stages[1].bundle[0];
stages[0].bundle[1] = tmpBundle;
}
else
{
stages[0].bundle[1] = stages[1].bundle[0];
}
// set the new blend state bits
shader.multitextureEnv = collapse[i].multitextureEnv;
stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
stages[0].stateBits |= collapse[i].multitextureBlend;
//
// move down subsequent shaders
//
memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) );
Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) );
return qtrue;
}
/*
=============
FixRenderCommandList
https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493
Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated
but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces
to be rendered with bad shaders. To fix this, need to go through all render commands and fix
sortedIndex.
==============
*/
static void FixRenderCommandList( int newShader ) {
renderCommandList_t *cmdList = &backEndData[tr.smpFrame]->commands;
if( cmdList ) {
const void *curCmd = cmdList->cmds;
while ( 1 ) {
switch ( *(const int *)curCmd ) {
case RC_SET_COLOR:
{
const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd;
curCmd = (const void *)(sc_cmd + 1);
break;
}
case RC_STRETCH_PIC:
{
const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd;
curCmd = (const void *)(sp_cmd + 1);
break;
}
case RC_DRAW_SURFS:
{
int i;
drawSurf_t *drawSurf;
shader_t *shader;
int fogNum;
int entityNum;
int dlightMap;
int sortedIndex;
const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd;
for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) {
R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap );
sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1));
if( sortedIndex >= newShader ) {
sortedIndex++;
drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | (int)dlightMap;
}
}
curCmd = (const void *)(ds_cmd + 1);
break;
}
case RC_DRAW_BUFFER:
{
const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd;
curCmd = (const void *)(db_cmd + 1);
break;
}
case RC_SWAP_BUFFERS:
{
const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd;
curCmd = (const void *)(sb_cmd + 1);
break;
}
case RC_END_OF_LIST:
default:
return;
}
}
}
}
/*
==============
SortNewShader
Positions the most recently created shader in the tr.sortedShaders[]
array so that the shader->sort key is sorted reletive to the other
shaders.
Sets shader->sortedIndex
==============
*/
static void SortNewShader( void ) {
int i;
float sort;
shader_t *newShader;
newShader = tr.shaders[ tr.numShaders - 1 ];
sort = newShader->sort;
for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) {
if ( tr.sortedShaders[ i ]->sort <= sort ) {
break;
}
tr.sortedShaders[i+1] = tr.sortedShaders[i];
tr.sortedShaders[i+1]->sortedIndex++;
}
// Arnout: fix rendercommandlist
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493
FixRenderCommandList( i+1 );
newShader->sortedIndex = i+1;
tr.sortedShaders[i+1] = newShader;
}
/*
====================
GeneratePermanentShader
====================
*/
static shader_t *GeneratePermanentShader( void ) {
shader_t *newShader;
int i, b;
int size, hash;
if ( tr.numShaders == MAX_SHADERS ) {
ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n");
return tr.defaultShader;
}
newShader = ri.Hunk_Alloc( sizeof( shader_t ), h_low );
*newShader = shader;
if ( shader.sort <= SS_OPAQUE ) {
newShader->fogPass = FP_EQUAL;
} else if ( shader.contentFlags & CONTENTS_FOG ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -