📄 tr_shade.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 Foobar; 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"
/*
THIS ENTIRE FILE IS BACK END
This file deals with applying shaders to surface data in the tess struct.
*/
/*
================
R_ArrayElementDiscrete
This is just for OpenGL conformance testing, it should never be the fastest
================
*/
static void APIENTRY R_ArrayElementDiscrete( GLint index ) {
qglColor4ubv( tess.svars.colors[ index ] );
if ( glState.currenttmu ) {
qglMultiTexCoord2fARB( 0, tess.svars.texcoords[ 0 ][ index ][0], tess.svars.texcoords[ 0 ][ index ][1] );
qglMultiTexCoord2fARB( 1, tess.svars.texcoords[ 1 ][ index ][0], tess.svars.texcoords[ 1 ][ index ][1] );
} else {
qglTexCoord2fv( tess.svars.texcoords[ 0 ][ index ] );
}
qglVertex3fv( tess.xyz[ index ] );
}
/*
===================
R_DrawStripElements
===================
*/
static int c_vertexes; // for seeing how long our average strips are
static int c_begins;
static void R_DrawStripElements( int numIndexes, const glIndex_t *indexes, void ( APIENTRY *element )(GLint) ) {
int i;
int last[3] = { -1, -1, -1 };
qboolean even;
c_begins++;
if ( numIndexes <= 0 ) {
return;
}
qglBegin( GL_TRIANGLE_STRIP );
// prime the strip
element( indexes[0] );
element( indexes[1] );
element( indexes[2] );
c_vertexes += 3;
last[0] = indexes[0];
last[1] = indexes[1];
last[2] = indexes[2];
even = qfalse;
for ( i = 3; i < numIndexes; i += 3 )
{
// odd numbered triangle in potential strip
if ( !even )
{
// check previous triangle to see if we're continuing a strip
if ( ( indexes[i+0] == last[2] ) && ( indexes[i+1] == last[1] ) )
{
element( indexes[i+2] );
c_vertexes++;
assert( indexes[i+2] < tess.numVertexes );
even = qtrue;
}
// otherwise we're done with this strip so finish it and start
// a new one
else
{
qglEnd();
qglBegin( GL_TRIANGLE_STRIP );
c_begins++;
element( indexes[i+0] );
element( indexes[i+1] );
element( indexes[i+2] );
c_vertexes += 3;
even = qfalse;
}
}
else
{
// check previous triangle to see if we're continuing a strip
if ( ( last[2] == indexes[i+1] ) && ( last[0] == indexes[i+0] ) )
{
element( indexes[i+2] );
c_vertexes++;
even = qfalse;
}
// otherwise we're done with this strip so finish it and start
// a new one
else
{
qglEnd();
qglBegin( GL_TRIANGLE_STRIP );
c_begins++;
element( indexes[i+0] );
element( indexes[i+1] );
element( indexes[i+2] );
c_vertexes += 3;
even = qfalse;
}
}
// cache the last three vertices
last[0] = indexes[i+0];
last[1] = indexes[i+1];
last[2] = indexes[i+2];
}
qglEnd();
}
/*
==================
R_DrawElements
Optionally performs our own glDrawElements that looks for strip conditions
instead of using the single glDrawElements call that may be inefficient
without compiled vertex arrays.
==================
*/
static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) {
int primitives;
primitives = r_primitives->integer;
// default is to use triangles if compiled vertex arrays are present
if ( primitives == 0 ) {
if ( qglLockArraysEXT ) {
primitives = 2;
} else {
primitives = 1;
}
}
if ( primitives == 2 ) {
qglDrawElements( GL_TRIANGLES,
numIndexes,
GL_INDEX_TYPE,
indexes );
return;
}
if ( primitives == 1 ) {
R_DrawStripElements( numIndexes, indexes, qglArrayElement );
return;
}
if ( primitives == 3 ) {
R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete );
return;
}
// anything else will cause no drawing
}
/*
=============================================================
SURFACE SHADERS
=============================================================
*/
shaderCommands_t tess;
static qboolean setArraysOnce;
/*
=================
R_BindAnimatedImage
=================
*/
static void R_BindAnimatedImage( textureBundle_t *bundle ) {
int index;
if ( bundle->isVideoMap ) {
ri.CIN_RunCinematic(bundle->videoMapHandle);
ri.CIN_UploadCinematic(bundle->videoMapHandle);
return;
}
if ( bundle->numImageAnimations <= 1 ) {
GL_Bind( bundle->image[0] );
return;
}
// it is necessary to do this messy calc to make sure animations line up
// exactly with waveforms of the same frequency
index = myftol( tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE );
index >>= FUNCTABLE_SIZE2;
if ( index < 0 ) {
index = 0; // may happen with shader time offsets
}
index %= bundle->numImageAnimations;
GL_Bind( bundle->image[ index ] );
}
/*
================
DrawTris
Draws triangle outlines for debugging
================
*/
static void DrawTris (shaderCommands_t *input) {
GL_Bind( tr.whiteImage );
qglColor3f (1,1,1);
GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
qglDepthRange( 0, 0 );
qglDisableClientState (GL_COLOR_ARRAY);
qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD
if (qglLockArraysEXT) {
qglLockArraysEXT(0, input->numVertexes);
GLimp_LogComment( "glLockArraysEXT\n" );
}
R_DrawElements( input->numIndexes, input->indexes );
if (qglUnlockArraysEXT) {
qglUnlockArraysEXT();
GLimp_LogComment( "glUnlockArraysEXT\n" );
}
qglDepthRange( 0, 1 );
}
/*
================
DrawNormals
Draws vertex normals for debugging
================
*/
static void DrawNormals (shaderCommands_t *input) {
int i;
vec3_t temp;
GL_Bind( tr.whiteImage );
qglColor3f (1,1,1);
qglDepthRange( 0, 0 ); // never occluded
GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
qglBegin (GL_LINES);
for (i = 0 ; i < input->numVertexes ; i++) {
qglVertex3fv (input->xyz[i]);
VectorMA (input->xyz[i], 2, input->normal[i], temp);
qglVertex3fv (temp);
}
qglEnd ();
qglDepthRange( 0, 1 );
}
/*
==============
RB_BeginSurface
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( shader_t *shader, int fogNum ) {
shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader;
tess.numIndexes = 0;
tess.numVertexes = 0;
tess.shader = state;
tess.fogNum = fogNum;
tess.dlightBits = 0; // will be OR'd in by surface functions
tess.xstages = state->stages;
tess.numPasses = state->numUnfoggedPasses;
tess.currentStageIteratorFunc = state->optimalStageIteratorFunc;
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) {
tess.shaderTime = tess.shader->clampTime;
}
}
/*
===================
DrawMultitextured
output = t0 * t1 or t0 + t1
t0 = most upstream according to spec
t1 = most downstream according to spec
===================
*/
static void DrawMultitextured( shaderCommands_t *input, int stage ) {
shaderStage_t *pStage;
pStage = tess.xstages[stage];
GL_State( pStage->stateBits );
// this is an ugly hack to work around a GeForce driver
// bug with multitexture and clip planes
if ( backEnd.viewParms.isPortal ) {
qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
}
//
// base
//
GL_SelectTexture( 0 );
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] );
R_BindAnimatedImage( &pStage->bundle[0] );
//
// lightmap/secondary pass
//
GL_SelectTexture( 1 );
qglEnable( GL_TEXTURE_2D );
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
if ( r_lightmap->integer ) {
GL_TexEnv( GL_REPLACE );
} else {
GL_TexEnv( tess.shader->multitextureEnv );
}
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] );
R_BindAnimatedImage( &pStage->bundle[1] );
R_DrawElements( input->numIndexes, input->indexes );
//
// disable texturing on TEXTURE1, then select TEXTURE0
//
//qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
qglDisable( GL_TEXTURE_2D );
GL_SelectTexture( 0 );
}
/*
===================
ProjectDlightTexture
Perform dynamic lighting with another rendering pass
===================
*/
static void ProjectDlightTexture( void ) {
int i, l;
#if idppc_altivec
vec_t origin0, origin1, origin2;
float texCoords0, texCoords1;
vector float floatColorVec0, floatColorVec1;
vector float modulateVec, colorVec, zero;
vector short colorShort;
vector signed int colorInt;
vector unsigned char floatColorVecPerm, modulatePerm, colorChar;
vector unsigned char vSel = (vector unsigned char)(0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0xff);
#else
vec3_t origin;
#endif
float *texCoords;
byte *colors;
byte clipBits[SHADER_MAX_VERTEXES];
MAC_STATIC float texCoordsArray[SHADER_MAX_VERTEXES][2];
byte colorArray[SHADER_MAX_VERTEXES][4];
unsigned hitIndexes[SHADER_MAX_INDEXES];
int numIndexes;
float scale;
float radius;
vec3_t floatColor;
float modulate;
if ( !backEnd.refdef.num_dlights ) {
return;
}
#if idppc_altivec
// There has to be a better way to do this so that floatColor
// and/or modulate are already 16-byte aligned.
floatColorVecPerm = vec_lvsl(0,(float *)floatColor);
modulatePerm = vec_lvsl(0,(float *)&modulate);
modulatePerm = (vector unsigned char)vec_splat((vector unsigned int)modulatePerm,0);
zero = (vector float)vec_splat_s8(0);
#endif
for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) {
dlight_t *dl;
if ( !( tess.dlightBits & ( 1 << l ) ) ) {
continue; // this surface definately doesn't have any of this light
}
texCoords = texCoordsArray[0];
colors = colorArray[0];
dl = &backEnd.refdef.dlights[l];
#if idppc_altivec
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -