//======================================================================= // Copyright XashXT Group 2008 © // r_backend.c - render backend utilites //======================================================================= #include "r_local.h" #include "r_meshbuffer.h" #include "byteorder.h" #include "mathlib.h" #include "matrixlib.h" #include "const.h" #define TABLE_SIZE 1024 #define TABLE_MASK TABLE_SIZE - 1 #define TABLE_CLAMP(x) (((uint)(( x ) * TABLE_SIZE ) & ( TABLE_MASK ))) #define TABLE_EVALUATE(t,x) ((t)[TABLE_CLAMP( x )]) #define NOISE_SIZE 256 #define NOISE_MASK NOISE_SIZE - 1 #define NOISE_VAL(a) rb_noisePerm[( a ) & ( NOISE_MASK )] #define NOISE_INDEX(x,y,z,t) NOISE_VAL( x + NOISE_VAL( y + NOISE_VAL( z + NOISE_VAL( t )))) #define NOISE_LERP(a,b,w) ( a * ( 1.0f - w ) + b * w ) static char r_speeds_msg[MAX_SYSPATH]; static float rb_identityLighting; static float rb_sinTable[TABLE_SIZE]; static float rb_triangleTable[TABLE_SIZE]; static float rb_squareTable[TABLE_SIZE]; static float rb_sawtoothTable[TABLE_SIZE]; static float rb_inverseSawtoothTable[TABLE_SIZE]; static float rb_noiseTable[NOISE_SIZE]; static int rb_noisePerm[NOISE_SIZE]; // permutation table static float rb_sinTableByte[256]; static float rb_warpSinTable[256] = { #include "warpsin.h" }; static uint rb_vertexBuffers[MAX_VERTEX_BUFFERS]; static int rb_numVertexBuffers; static int rb_staticBytes; static int rb_staticCount; static int rb_streamBytes; static int rb_streamCount; bool r_triangleOutlines; static bool r_arraysLocked; static bool r_normalsEnabled; static const meshbuffer_t *m_pRenderMeshBuffer; static uint r_currentDlightBits; static uint r_currentShadowBits; vbo_t rb_vbo; int m_iInfoKey; float m_fShaderTime; static int r_currentShaderState; static int r_currentShaderPassMask; static const mfog_t *r_texFog, *r_colorFog; static const shadowGroup_t *r_currentCastGroup; static int r_lightmapStyleNum[MAX_TEXTURE_UNITS]; static superLightStyle_t *r_superLightStyle; static shaderStage_t rb_dlightsStage, r_fogStage; static shaderStage_t rb_lightmapStages[MAX_TEXTURE_UNITS+1]; static shaderStage_t rb_GLSLstages[4]; // dlights and base static shaderStage_t *rb_accumStages[MAX_TEXTURE_UNITS]; static int rb_numAccumStages; ALIGN vec4_t inVertsArray[MAX_ARRAY_VERTS]; ALIGN vec4_t inNormalArray[MAX_ARRAY_VERTS]; vec4_t inSVectorsArray[MAX_ARRAY_VERTS]; uint inElemsArray[MAX_ARRAY_ELEMENTS]; vec2_t inTexCoordArray[MAX_ARRAY_VERTS]; vec2_t inLMCoordsArray[LM_STYLES][MAX_ARRAY_VERTS]; vec4_t inColorArray[LM_STYLES][MAX_ARRAY_VERTS]; vec2_t tUnitCoordsArray[MAX_TEXTURE_UNITS][MAX_ARRAY_VERTS]; uint *indexArray; vec4_t *vertexArray; vec4_t *normalArray; vec4_t *sVectorArray; vec2_t *texCoordArray; vec2_t *lmCoordArray[LM_STYLES]; vec4_t colorArray[MAX_ARRAY_VERTS]; static GLenum rb_drawMode; static GLboolean rb_CheckFlush; static GLint rb_vertexState; static void RB_SetVertex( float x, float y, float z ) { GLuint oldIndex = r_stats.numIndices; switch( rb_drawMode ) { case GL_LINES: inElemsArray[r_stats.numIndices++] = r_stats.numVertices; if( rb_vertexState++ == 1 ) { RB_SetVertex( x + 1, y + 1, z + 1 ); rb_vertexState = 0; rb_CheckFlush = true; // Flush for long sequences of quads. } break; case GL_TRIANGLES: inElemsArray[r_stats.numIndices++] = r_stats.numVertices; if( rb_vertexState++ == 2 ) { rb_vertexState = 0; rb_CheckFlush = true; // Flush for long sequences of triangles. } break; case GL_QUADS: if( rb_vertexState++ < 3 ) { inElemsArray[r_stats.numIndices++] = r_stats.numVertices; } else { // we've already done triangle (0, 1, 2), now draw (2, 3, 0) inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 1; inElemsArray[r_stats.numIndices++] = r_stats.numVertices; inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 3; rb_vertexState = 0; rb_CheckFlush = true; // flush for long sequences of quads. } break; case GL_TRIANGLE_STRIP: if( r_stats.numVertices + rb_vertexState > MAX_VERTICES ) { // This is a strip that's too big for us to buffer. // (We can't just flush the buffer because we have to keep // track of the last two vertices. Host_Error( "RB_SetVertex: overflow: %i > MAX_VERTICES\n", r_stats.numVertices + rb_vertexState ); } if( rb_vertexState++ < 3 ) { inElemsArray[r_stats.numIndices++] = r_stats.numVertices; } else { // flip triangles between clockwise and counter clockwise if( rb_vertexState & 1 ) { // draw triangle [n-2 n-1 n] inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 2; inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 1; inElemsArray[r_stats.numIndices++] = r_stats.numVertices; } else { // draw triangle [n-1 n-2 n] inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 1; inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 2; inElemsArray[r_stats.numIndices++] = r_stats.numVertices; } } break; case GL_POLYGON: case GL_TRIANGLE_FAN: // same as polygon if( r_stats.numVertices + rb_vertexState > MAX_VERTICES ) { // This is a polygon or fan that's too big for us to buffer. // (We can't just flush the buffer because we have to keep // track of the starting vertex. Host_Error( "RB_SetVertex: overflow: %i > MAX_VERTICES\n", r_stats.numVertices + rb_vertexState ); } if( rb_vertexState++ < 3 ) { inElemsArray[r_stats.numIndices++] = r_stats.numVertices; } else { // draw triangle [0 n-1 n] inElemsArray[r_stats.numIndices++] = r_stats.numVertices - ( rb_vertexState - 1 ); inElemsArray[r_stats.numIndices++] = r_stats.numVertices - 1; inElemsArray[r_stats.numIndices++] = r_stats.numVertices; } break; default: Host_Error( "RB_SetVertex: unsupported mode: %i\n", rb_drawMode ); break; } // copy current vertex vertexArray[r_stats.numVertices][0] = x; vertexArray[r_stats.numVertices][1] = y; vertexArray[r_stats.numVertices][2] = z; r_stats.numVertices++; // flush buffer if needed if( rb_CheckFlush ) Host_Error( "rb_CheckMeshOverflow called!\n" ); // RB_CheckMeshOverflow( r_stats.numIndices - oldIndex, rb_vertexState ); } static void RB_SetTexCoord( GLfloat s, GLfloat t ) { inTexCoordArray[r_stats.numVertices][0] = s; inTexCoordArray[r_stats.numVertices][1] = t; } static void RB_SetLmCoord( GLfloat s, GLfloat t ) { // FIXME: these set coords only for fisrt lightmap inLMCoordsArray[0][r_stats.numVertices][0] = s; inLMCoordsArray[0][r_stats.numVertices][1] = t; } static void RB_SetColor( GLfloat r, GLfloat g, GLfloat b, GLfloat a ) { inColorArray[0][r_stats.numVertices][0] = r; inColorArray[0][r_stats.numVertices][1] = g; inColorArray[0][r_stats.numVertices][2] = b; inColorArray[0][r_stats.numVertices][3] = b; } static void RB_SetNormal( GLfloat x, GLfloat y, GLfloat z ) { inNormalArray[r_stats.numVertices][0] = x; inNormalArray[r_stats.numVertices][1] = y; inNormalArray[r_stats.numVertices][2] = z; } /* ============== RB_DisableTexGen ============== */ static void RB_DisableTexGen( void ) { GL_EnableTexGen( GL_S, GL_FALSE ); GL_EnableTexGen( GL_T, GL_FALSE ); GL_EnableTexGen( GL_R, GL_FALSE ); GL_EnableTexGen( GL_Q, GL_FALSE ); } /* ================= GL subsystem arrays backend that emulate basic opengl funcs ================= */ void GL_Begin( GLuint drawMode ) { rb_drawMode = drawMode; rb_vertexState = 0; rb_CheckFlush = false; } void GL_End( void ) { if( r_stats.numIndices == 0 ) return; // RB_CheckMeshOverflow( 0, 0 ); } void GL_Vertex2f( GLfloat x, GLfloat y ) { RB_SetVertex( x, y, 0 ); } void GL_Vertex3f( GLfloat x, GLfloat y, GLfloat z ) { RB_SetVertex( x, y, z ); } void GL_Vertex3fv( const GLfloat *v ) { RB_SetVertex( v[0], v[1], v[2] ); } void GL_Normal3f( GLfloat x, GLfloat y, GLfloat z ) { RB_SetNormal( x, y, z ); } void GL_Normal3fv( const GLfloat *v ) { RB_SetNormal( v[0], v[1], v[2] ); } void GL_TexCoord2f( GLfloat s, GLfloat t ) { RB_SetTexCoord( s, t ); } void GL_TexCoord4f( GLfloat s, GLfloat t, GLfloat ls, GLfloat lt ) { RB_SetTexCoord( s, t ); RB_SetLmCoord( ls, lt ); } void GL_TexCoord4fv( const GLfloat *v ) { RB_SetTexCoord( v[0], v[1] ); RB_SetLmCoord( v[2], v[3] ); } void GL_Color3f( GLfloat r, GLfloat g, GLfloat b ) { RB_SetColor( r, g, b, 1.0f ); } void GL_Color3fv( const GLfloat *v ) { RB_SetColor( v[0], v[1], v[2], 1.0f ); } void GL_Color4f( GLfloat r, GLfloat g, GLfloat b, GLfloat a ) { RB_SetColor( r, g, b, a ); } void GL_Color4fv( const GLfloat *v ) { RB_SetColor( v[0], v[1], v[2], v[3] ); } void GL_Color4ub( GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha ) { GL_Color4fv(UnpackRGBA(MakeRGBA( red, green, blue, alpha ))); } void GL_Color4ubv( const GLubyte *v ) { GL_Color4fv(UnpackRGBA(BuffLittleLong( v ))); } /* ================= RB_BuildTables ================= */ static void RB_BuildTables( void ) { int i; float f; if( gl_config.deviceSupportsGamma && r_hwgamma->integer ) rb_identityLighting = ( 1.0f / pow( 2.0f, max( 0, floor( r_overbrightbits->value )))); else rb_identityLighting = 1.0f; for( i = 0; i < TABLE_SIZE; i++ ) { f = (float)i / (float)TABLE_SIZE; rb_sinTable[i] = sin(f * M_PI2); if( f < 0.25 ) rb_triangleTable[i] = 4.0 * f; else if( f < 0.75 ) rb_triangleTable[i] = 2.0 - 4.0 * f; else rb_triangleTable[i] = (f - 0.75) * 4.0 - 1.0; if( f < 0.5 ) rb_squareTable[i] = 1.0; else rb_squareTable[i] = -1.0; rb_sawtoothTable[i] = f; rb_inverseSawtoothTable[i] = 1.0 - f; } // init the noise table for( i = 0; i < NOISE_SIZE; i++ ) { rb_noiseTable[i] = Com_RandomFloat( -1.0f, 1.0f ); // premutation in range (0 - NOISE_SIZE) rb_noisePerm[i] = Com_RandomLong( 0, NOISE_MASK ); } for( i = 0; i < 256; i++ ) rb_sinTableByte[i] = sin((float)i / 255.0 * M_PI2 ); } /* ============== R_FastSin get sin values from table ============== */ static float R_FastSin( float t ) { return TABLE_EVALUATE( rb_sinTable, t ); } /* ============= R_LatLongToNorm ============= */ void R_LatLongToNorm( const byte latlong[2], vec3_t out ) { float sin_a, sin_b, cos_a, cos_b; cos_a = rb_sinTableByte[(latlong[0] + 64 ) & 255]; sin_a = rb_sinTableByte[latlong[0]]; cos_b = rb_sinTableByte[(latlong[1] + 64 ) & 255]; sin_b = rb_sinTableByte[latlong[1]]; VectorSet( out, cos_b * sin_a, sin_b * sin_a, cos_a ); } /* ================= RB_TableForFunc ================= */ static float *RB_TableForFunc( const waveFunc_t *func ) { switch( func->type ) { case WAVEFORM_SIN: return rb_sinTable; case WAVEFORM_TRIANGLE: return rb_triangleTable; case WAVEFORM_SQUARE: return rb_squareTable; case WAVEFORM_SAWTOOTH: return rb_sawtoothTable; case WAVEFORM_INVERSESAWTOOTH: return rb_inverseSawtoothTable; case WAVEFORM_NOISE: return rb_noiseTable; } Host_Error( "RB_TableForFunc: unknown waveform type %i in shader '%s'\n", func->type, Ref.m_pCurrentShader->name ); return NULL; } /* ============== R_BackendGetNoiseValue ============== */ static float RB_GetNoiseValue( float x, float y, float z, float t ) { int ix, iy, iz, it; float fx, fy, fz, ft; float front[4], back[4]; float fvalue, bvalue, value[2]; int i; ix = (int)floor( x ); fx = x - ix; iy = (int)floor( y ); fy = y - iy; iz = (int)floor( z ); fz = z - iz; it = (int)floor( t ); ft = t - it; for( i = 0; i < 2; i++ ) { front[0] = rb_noiseTable[NOISE_INDEX( ix, iy, iz, it + i )]; front[1] = rb_noiseTable[NOISE_INDEX( ix+1, iy, iz, it + i )]; front[2] = rb_noiseTable[NOISE_INDEX( ix, iy+1, iz, it + i )]; front[3] = rb_noiseTable[NOISE_INDEX( ix+1, iy+1, iz, it + i )]; back[0] = rb_noiseTable[NOISE_INDEX( ix, iy, iz + 1, it + i )]; back[1] = rb_noiseTable[NOISE_INDEX( ix+1, iy, iz + 1, it + i )]; back[2] = rb_noiseTable[NOISE_INDEX( ix, iy+1, iz + 1, it + i )]; back[3] = rb_noiseTable[NOISE_INDEX( ix+1, iy+1, iz + 1, it + i )]; fvalue = NOISE_LERP( NOISE_LERP( front[0], front[1], fx ), NOISE_LERP( front[2], front[3], fx ), fy ); bvalue = NOISE_LERP( NOISE_LERP( back[0], back[1], fx ), NOISE_LERP( back[2], back[3], fx ), fy ); value[i] = NOISE_LERP( fvalue, bvalue, fz ); } return NOISE_LERP( value[0], value[1], ft ); } /* ============== R_LockArrays ============== */ void R_LockArrays( int numverts ) { if( r_arraysLocked ) return; if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglVertexPointer( 3, GL_FLOAT, 16, vertexArray ); if( r_features & MF_ENABLENORMALS ) { r_normalsEnabled = true; pglEnableClientState( GL_NORMAL_ARRAY ); pglNormalPointer( GL_FLOAT, 16, normalArray ); } } if( GL_Support( R_CUSTOM_VERTEX_ARRAY_EXT )) pglLockArraysEXT( 0, numverts ); r_arraysLocked = true; } /* ============== R_UnlockArrays ============== */ void R_UnlockArrays( void ) { if( !r_arraysLocked ) return; if( GL_Support( R_CUSTOM_VERTEX_ARRAY_EXT )) pglUnlockArraysEXT(); if( r_normalsEnabled ) { r_normalsEnabled = false; pglDisableClientState( GL_NORMAL_ARRAY ); } r_arraysLocked = false; } /* ============== R_ClearArrays ============== */ void R_ClearArrays( void ) { int i; r_stats.numVertices = 0; r_stats.numIndices = 0; r_stats.numColors = 0; vertexArray = inVertsArray; indexArray = inElemsArray; normalArray = inNormalArray; sVectorArray = inSVectorsArray; texCoordArray = inTexCoordArray; for( i = 0; i < LM_STYLES; i++ ) lmCoordArray[i] = inLMCoordsArray[i]; } /* ============== R_FlushArrays ============== */ void R_FlushArrays( void ) { if( !r_stats.numVertices || !r_stats.numIndices ) return; if( r_stats.numColors == 1 ) { pglColor4fv( colorArray[0] ); } else if( r_stats.numColors > 1 ) { pglEnableClientState( GL_COLOR_ARRAY ); if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) pglColorPointer( 4, GL_FLOAT, 0, colorArray ); } if( GL_Support( R_DRAW_RANGEELEMENTS_EXT )) pglDrawRangeElementsEXT( GL_TRIANGLES, 0, r_stats.numVertices, r_stats.numIndices, GL_UNSIGNED_INT, indexArray ); else pglDrawElements( GL_TRIANGLES, r_stats.numIndices, GL_UNSIGNED_INT, indexArray ); if( r_stats.numColors > 1 ) pglDisableClientState( GL_COLOR_ARRAY ); r_stats.totalTris += r_stats.numIndices / 3; r_stats.totalFlushes++; } /* ============== RB_CleanUpTextureUnits FIXME: rename not a RB_CleanUpTextureUnit! ============== */ void RB_CleanUpTextureUnits( int last ) { int i; for( i = gl_state.activeTMU; i > last - 1; i-- ) { RB_DisableTexGen(); GL_TexCoordMode( GL_FALSE ); pglDisable( GL_TEXTURE_2D ); GL_SelectTexture( i - 1 ); } } /* ================ R_CleanUpTextureUnits disable custom texcoords and texgen ================ */ void R_CleanUpTextureUnits( void ) { RB_CleanUpTextureUnits( GL_TEXTURE1 ); GL_LoadIdentityTexMatrix(); pglMatrixMode( GL_MODELVIEW ); RB_DisableTexGen(); GL_TexCoordMode( GL_NONE ); } /* ============== RB_ResetCounters ============== */ void RB_ResetCounters( void ) { Mem_Set( &r_stats, 0, sizeof( r_stats )); } /* ================ RB_SetPassMask ================ */ void RB_SetPassMask( int mask ) { r_currentShaderPassMask = mask; } /* ================ RB_ResetPassMask ================ */ void RB_ResetPassMask( void ) { r_currentShaderPassMask = GLSTATE_MASK; } /* ================ RB_BeginTriangleOutlines ================ */ void RB_BeginTriangleOutlines( void ) { r_triangleOutlines = true; pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); GL_CullFace( GL_NONE ); GL_SetState( GLSTATE_NO_DEPTH_TEST ); pglDisable( GL_TEXTURE_2D ); pglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); } /* ================ RB_EndTriangleOutlines ================ */ void RB_EndTriangleOutlines( void ) { r_triangleOutlines = false; pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); GL_SetState( 0 ); pglEnable( GL_TEXTURE_2D ); pglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); } /* ================ RB_SetColorForOutlines ================ */ static void RB_SetColorForOutlines( void ) { int type = m_pRenderMeshBuffer->sortKey & 3; switch( type ) { case MESH_MODEL: if( m_pRenderMeshBuffer->infoKey < 0 ) pglColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); else pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); break; case MESH_SPRITE: pglColor4f( 0.0f, 0.0f, 1.0f, 1.0f ); break; case MESH_POLY: pglColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); break; } } /* ================ RB_DrawTriangles ================ */ static void RB_DrawTriangles( void ) { if( r_showtris->integer == 2 ) RB_SetColorForOutlines(); if( GL_Support( R_DRAW_RANGEELEMENTS_EXT )) pglDrawRangeElementsEXT( GL_TRIANGLES, 0, r_stats.numVertices, r_stats.numIndices, GL_UNSIGNED_INT, indexArray ); else pglDrawElements( GL_TRIANGLES, r_stats.numIndices, GL_UNSIGNED_INT, indexArray ); } /* ================= RB_DrawNormals ================= */ static void RB_DrawNormals( void ) { int i; vec3_t v; if( r_shownormals->integer == 2 ) RB_SetColorForOutlines(); pglBegin( GL_LINES ); for( i = 0; i < r_stats.numVertices; i++ ) { VectorAdd( vertexArray[i], normalArray[i], v ); pglVertex3fv( vertexArray[i] ); pglVertex3fv( v ); } pglEnd(); } /* ================= RB_DrawTangentSpace ================= */ static void RB_DrawTangentSpace( void ) { #if 0 // FIXME: get to work int i; vec3_t v; // tangent pglColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); pglBegin( GL_LINES ); for( i = 0; i < r_stats.numVertices; i++ ) { VectorAdd( vertexArray[i], tangentArray[i], v ); pglVertex3fv( vertexArray[i] ); pglVertex3fv( v ); } pglEnd(); // binormal pglColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); pglBegin( GL_LINES ); for( i = 0; i < r_stats.numVertices; i++ ) { VectorAdd( vertexArray[i], binormalArray[i], v ); pglVertex3fv( vertexArray[i] ); pglVertex3fv( v ); } pglEnd(); // normal pglColor4f( 0.0f, 0.0f, 1.0f, 1.0f ); pglBegin( GL_LINES ); for( i = 0; i < r_stats.numVertices; i++ ) { VectorAdd( vertexArray[i], normalArray[i], v ); pglVertex3fv( vertexArray[i] ); pglVertex3fv( v ); } pglEnd(); #endif } /* ================= RB_DrawModelBounds ================= */ static void RB_DrawModelBounds( void ) { #if 0 rmodel_t *model; vec3_t bbox[8]; int i; if( m_pCurrentEntity == r_worldEntity ) return; if( m_pRenderMesh->meshType == MESH_SURFACE ) { model = m_pCurrentEntity->model; // compute a full bounding box for( i = 0; i < 8; i++ ) { bbox[i][0] = (i & 1) ? model->mins[0] : model->maxs[0]; bbox[i][1] = (i & 2) ? model->mins[1] : model->maxs[1]; bbox[i][2] = (i & 4) ? model->mins[2] : model->maxs[2]; } } else if( m_pRenderMesh->meshType == MESH_STUDIO ) { R_StudioComputeBBox( bbox ); } else return; // draw it pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); pglBegin( GL_LINES ); for( i = 0; i < 2; i += 1 ) { pglVertex3fv(bbox[i+0]); pglVertex3fv(bbox[i+2]); pglVertex3fv(bbox[i+4]); pglVertex3fv(bbox[i+6]); pglVertex3fv(bbox[i+0]); pglVertex3fv(bbox[i+4]); pglVertex3fv(bbox[i+2]); pglVertex3fv(bbox[i+6]); pglVertex3fv(bbox[i*2+0]); pglVertex3fv(bbox[i*2+1]); pglVertex3fv(bbox[i*2+4]); pglVertex3fv(bbox[i*2+5]); } pglEnd(); #endif } /* ============== RB_StartFrame ============== */ void RB_StartFrame( void ) { r_speeds_msg[0] = '\0'; RB_ResetCounters(); } /* ============== RB_EndFrame ============== */ void RB_EndFrame( void ) { // unlock arrays if any R_UnlockArrays(); // clean up texture units RB_CleanUpTextureUnits( GL_TEXTURE1 ); if( r_speeds->integer && !( Ref.refdef.rdflags & RDF_NOWORLDMODEL )) { com.snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%4i wpoly %4i leafs %4i verts %4i tris %4i flushes %3i locks", r_stats.brushPolys, r_stats.worldLeafs, r_stats.totalVerts, r_stats.totalTris, r_stats.totalFlushes, r_stats.totalKeptLocks ); } } /* ================= RB_DeformVertexes ================= */ static void RB_DeformVertexes( void ) { deformVerts_t *deformVertexes = Ref.m_pCurrentShader->deformVertexes; uint deformVertexesNum = Ref.m_pCurrentShader->deformVertexesNum; float deflect, *quad[4]; double temp, params[4]; vec3_t tv, rot_centre; int i, j, k; const float *table; for( i = 0; i < deformVertexesNum; i++, deformVertexes++ ) { switch( deformVertexes->type ) { case DEFORMVERTEXES_NONE: break; case DEFORMVERTEXES_WAVE: table = RB_TableForFunc( &deformVertexes->func ); // deflect vertex along its normal by wave amount if( deformVertexes->func.params[3] == 0 ) { temp = deformVertexes->func.params[2]; deflect = TABLE_EVALUATE( table, temp ) * deformVertexes->func.params[1] + deformVertexes->func.params[0]; for( j = 0; j < r_stats.numVertices; j++ ) VectorMA( inVertsArray[j], deflect, inNormalArray[j], inVertsArray[j] ); } else { params[0] = deformVertexes->func.params[0]; params[1] = deformVertexes->func.params[1]; params[2] = deformVertexes->func.params[2] + deformVertexes->func.params[3] * m_fShaderTime; params[3] = deformVertexes->func.params[0]; for( j = 0; j < r_stats.numVertices; j++ ) { temp = params[2] + params[3] * ( inVertsArray[j][0] + inVertsArray[j][1] + inVertsArray[j][2] ); deflect = TABLE_EVALUATE( table, temp ) * params[1] + params[0]; VectorMA( inVertsArray[j], deflect, inNormalArray[j], inVertsArray[j] ); } } break; case DEFORMVERTEXES_NORMAL: // without this * 0.1f deformation looks wrong, although q3a doesn't have it params[0] = deformVertexes->func.params[3] * m_fShaderTime * 0.1f; params[1] = deformVertexes->func.params[1]; for( j = 0; j < r_stats.numVertices; j++ ) { VectorScale( inVertsArray[j], 0.98f, tv ); inNormalArray[j][0] += params[1] * RB_GetNoiseValue( tv[0], tv[1], tv[2], params[0] ); inNormalArray[j][1] += params[1] * RB_GetNoiseValue( tv[0] + 100, tv[1], tv[2], params[0] ); inNormalArray[j][2] += params[1] * RB_GetNoiseValue( tv[0] + 200, tv[1], tv[2], params[0] ); VectorNormalizeFast( inNormalArray[j] ); } break; case DEFORMVERTEXES_MOVE: table = RB_TableForFunc( &deformVertexes->func ); temp = deformVertexes->func.params[2] + m_fShaderTime * deformVertexes->func.params[3]; deflect = TABLE_EVALUATE( table, temp ) * deformVertexes->func.params[1] + deformVertexes->func.params[0]; for( j = 0; j < r_stats.numVertices; j++ ) VectorMA( inVertsArray[j], deflect, deformVertexes->func.params, inVertsArray[j] ); break; case DEFORMVERTEXES_BULGE: params[0] = deformVertexes->func.params[0]; params[1] = deformVertexes->func.params[1]; params[2] = m_fShaderTime * deformVertexes->func.params[2]; for( j = 0; j < r_stats.numVertices; j++ ) { temp = ( texCoordArray[j][0] * params[0] + params[2] ) / M_PI2; deflect = R_FastSin( temp ) * params[1]; VectorMA( inVertsArray[j], deflect, inNormalArray[j], inVertsArray[j] ); } break; case DEFORMVERTEXES_AUTOSPRITE: { vec4_t *v; vec2_t *st; uint *elem; float radius; vec3_t point, v_centre, v_right, v_up; if( r_stats.numVertices % 4 || r_stats.numIndices % 6 ) break; if( Ref.m_pCurrentEntity && (Ref.m_pCurrentModel != r_worldModel )) { Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, Ref.right, v_right ); Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, Ref.up, v_up ); } else { VectorCopy( Ref.right, v_right ); VectorCopy( Ref.up, v_up ); } radius = Ref.m_pCurrentEntity->scale; if( radius && radius != 1.0f ) { radius = 1.0f / radius; VectorScale( v_right, radius, v_right ); VectorScale( v_up, radius, v_up ); } for( k = 0, v = inVertsArray, st = texCoordArray, elem = indexArray; k < r_stats.numVertices; k += 4, v += 4, st += 4, elem += 6 ) { for( j = 0; j < 3; j++ ) v_centre[j] = (v[0][j] + v[1][j] + v[2][j] + v[3][j]) * 0.25f; VectorSubtract( v[0], v_centre, point ); radius = VectorLength( point ) * 0.707106f; // 1.0f / sqrt(2) // very similar to R_PushSprite VectorMA( v_centre, -radius, v_up, point ); VectorMA( point, -radius, v_right, v[0] ); VectorMA( point, radius, v_right, v[3] ); VectorMA( v_centre, radius, v_up, point ); VectorMA( point, -radius, v_right, v[1] ); VectorMA( point, radius, v_right, v[2] ); // reset texcoords Vector2Set( st[0], 0, 1 ); Vector2Set( st[1], 0, 0 ); Vector2Set( st[2], 1, 0 ); Vector2Set( st[3], 1, 1 ); // trifan elems elem[0] = k; elem[1] = k + 2 - 1; elem[2] = k + 2; elem[3] = k; elem[4] = k + 3 - 1; elem[5] = k + 3; } } break; case DEFORMVERTEXES_AUTOSPRITE2: if( r_stats.numIndices % 6 ) break; for( k = 0; k < r_stats.numIndices; k += 6 ) { int long_axis = 0, short_axis = 0; vec3_t axis, tmp; float len[3]; matrix3x3 m0, m1, m2, result; quad[0] = (float *)(inVertsArray + indexArray[k+0]); quad[1] = (float *)(inVertsArray + indexArray[k+1]); quad[2] = (float *)(inVertsArray + indexArray[k+2]); for( j = 2; j >= 0; j-- ) { quad[3] = (float *)(inVertsArray + indexArray[k+3+j]); if( !VectorCompare( quad[3], quad[0] ) && !VectorCompare( quad[3], quad[1] ) && !VectorCompare( quad[3], quad[2] )) break; } // build a matrix were the longest axis of the billboard is the Y-Axis VectorSubtract( quad[1], quad[0], m0[0] ); VectorSubtract( quad[2], quad[0], m0[1] ); VectorSubtract( quad[2], quad[1], m0[2] ); len[0] = DotProduct( m0[0], m0[0] ); len[1] = DotProduct( m0[1], m0[1] ); len[2] = DotProduct( m0[2], m0[2] ); if( ( len[2] > len[1] ) && ( len[2] > len[0] ) ) { if( len[1] > len[0] ) { long_axis = 1; short_axis = 0; } else { long_axis = 0; short_axis = 1; } } else if( ( len[1] > len[2] ) && ( len[1] > len[0] ) ) { if( len[2] > len[0] ) { long_axis = 2; short_axis = 0; } else { long_axis = 0; short_axis = 2; } } else if( ( len[0] > len[1] ) && ( len[0] > len[2] ) ) { if( len[2] > len[1] ) { long_axis = 2; short_axis = 1; } else { long_axis = 1; short_axis = 2; } } if( !len[long_axis] ) break; len[long_axis] = rsqrt( len[long_axis] ); VectorScale( m0[long_axis], len[long_axis], axis ); if( DotProduct( m0[long_axis], m0[short_axis] ) ) { VectorCopy( axis, m0[1] ); if( axis[0] || axis[1] ) VectorVectors( m0[1], m0[0], m0[2] ); else VectorVectors( m0[1], m0[2], m0[0] ); } else { if( !len[short_axis] ) break; len[short_axis] = rsqrt( len[short_axis] ); VectorScale( m0[short_axis], len[short_axis], m0[0] ); VectorCopy( axis, m0[1] ); CrossProduct( m0[0], m0[1], m0[2] ); } for( j = 0; j < 3; j++ ) rot_centre[j] = ( quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j] ) * 0.25; if( Ref.m_pCurrentEntity && ( Ref.m_pCurrentModel != r_worldModel )) { VectorAdd( Ref.m_pCurrentEntity->origin, rot_centre, tv ); VectorSubtract( Ref.vieworg, tv, tmp ); Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, tmp, tv ); } else { VectorCopy( rot_centre, tv ); VectorSubtract( Ref.vieworg, tv, tv ); } // filter any longest-axis-parts off the camera-direction deflect = -DotProduct( tv, axis ); VectorMA( tv, deflect, axis, m1[2] ); VectorNormalizeFast( m1[2] ); VectorCopy( axis, m1[1] ); CrossProduct( m1[1], m1[2], m1[0] ); Matrix3x3_Transpose( m1, m2 ); Matrix3x3_Concat( result, m2, m0 ); for( j = 0; j < 4; j++ ) { VectorSubtract( quad[j], rot_centre, tv ); Matrix3x3_Transform( result, tv, quad[j] ); VectorAdd( rot_centre, quad[j], quad[j] ); } } break; case DEFORMVERTEXES_PROJECTION_SHADOW: // R_DeformVPlanarShadow( r_stats.numVertices, inVertsArray[0] ); break; case DEFORMVERTEXES_AUTOPARTICLE: { float scale; matrix3x3 m0, m1, m2, result; if( r_stats.numIndices % 6 ) break; if( Ref.m_pCurrentEntity && ( Ref.m_pCurrentModel != r_worldModel )) Matrix4x4_ToMatrix3x3( m1, Ref.modelViewMatrix ); else Matrix4x4_ToMatrix3x3( m1, Ref.worldMatrix ); Matrix3x3_Transpose( m1, m2 ); for( k = 0; k < r_stats.numIndices; k += 6 ) { quad[0] = (float *)(inVertsArray + indexArray[k+0]); quad[1] = (float *)(inVertsArray + indexArray[k+1]); quad[2] = (float *)(inVertsArray + indexArray[k+2]); for( j = 2; j >= 0; j-- ) { quad[3] = (float *)(inVertsArray + indexArray[k+3+j]); if(!VectorCompare( quad[3], quad[0] ) && !VectorCompare( quad[3], quad[1] ) && !VectorCompare( quad[3], quad[2] )) break; } Matrix3x3_FromPoints( quad[0], quad[1], quad[2], m0 ); Matrix3x3_Concat( result, m2, m0 ); // HACK a scale up to keep particles from disappearing scale = ( quad[0][0] - Ref.vieworg[0] ) * Ref.forward[0] + ( quad[0][1] - Ref.vieworg[1] ) * Ref.forward[1] + ( quad[0][2] - Ref.vieworg[2] ) * Ref.forward[2]; if( scale < 20 ) scale = 1.5; else scale = 1.5 + scale * 0.006f; for( j = 0; j < 3; j++ ) rot_centre[j] = ( quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j] ) * 0.25; for( j = 0; j < 4; j++ ) { VectorSubtract( quad[j], rot_centre, tv ); Matrix3x3_Transform( result, tv, quad[j] ); VectorMA( rot_centre, scale, quad[j], quad[j] ); } } } break; default: Host_Error( "RB_DeformVertexes: unknown deformVertexes type %i in shader '%s'\n", deformVertexes->type, Ref.m_pCurrentShader->name ); } } } /* ================= RB_CalcVertexColors ================= */ static void RB_CalcVertexColors( const shaderStage_t *stage ) { const rgbGen_t *rgbGen = &stage->rgbGen; const alphaGen_t *alphaGen = &stage->alphaGen; bool noArray, identityAlpha, entityAlpha; float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; float *fArray, *inArray, a; vec3_t v, t, style; int i, c, bits = 1; // to avoid division by zero const float *table; double temp; fArray = colorArray[0]; inArray = inColorArray[0][0]; noArray = ( stage->flags & SHADERSTAGE_NOCOLORARRAY ) && !r_colorFog; r_stats.numColors = noArray ? 1 : r_stats.numVertices; if( r_overbrightbits->integer > 0 && r_hwgamma->integer ) bits = (1<integer); if( rgbGen->type != RGBGEN_IDENTITYLIGHTING && rgbGen->type != RGBGEN_EXACTVERTEX ) { entityAlpha = false; identityAlpha = true; Mem_Set( fArray, 1.0f, sizeof( vec4_t ) * r_stats.numColors ); } switch( rgbGen->type ) { case RGBGEN_IDENTITY: break; case RGBGEN_IDENTITYLIGHTING: entityAlpha = identityAlpha = false; Mem_Set( fArray, rb_identityLighting, sizeof( vec4_t ) * r_stats.numColors ); break; case RGBGEN_EXACTVERTEX: entityAlpha = identityAlpha = false; Mem_Copy( fArray, inArray, sizeof( vec4_t ) * r_stats.numColors ); break; case RGBGEN_CONST: rgba[0] = rgbGen->params[0]; rgba[1] = rgbGen->params[1]; rgba[2] = rgbGen->params[2]; for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( rgba, fArray ); break; case RGBGEN_WAVE: if( rgbGen->func.type == WAVEFORM_NOISE ) { temp = RB_GetNoiseValue( 0, 0, 0, ( m_fShaderTime + rgbGen->func.params[2] ) * rgbGen->func.params[3] ); } else { table = RB_TableForFunc( &rgbGen->func ); temp = m_fShaderTime * rgbGen->func.params[3] + rgbGen->func.params[2]; temp = TABLE_EVALUATE( table, temp ) * rgbGen->func.params[1] + rgbGen->func.params[0]; } temp = temp * rgbGen->func.params[1] + rgbGen->func.params[0]; rgba[0] = bound( 0.0f, rgbGen->func.params[0] * temp, 1.0f ); rgba[1] = bound( 0.0f, rgbGen->func.params[1] * temp, 1.0f ); rgba[2] = bound( 0.0f, rgbGen->func.params[2] * temp, 1.0f ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( rgba, fArray ); break; case RGBGEN_ENTITY: entityAlpha = true; identityAlpha = ( Ref.m_pCurrentEntity->rendercolor[3] == 1.0f ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( Ref.m_pCurrentEntity->rendercolor, fArray ); break; case RGBGEN_ONEMINUSENTITY: rgba[0] = 1.0f - Ref.m_pCurrentEntity->rendercolor[0]; rgba[1] = 1.0f - Ref.m_pCurrentEntity->rendercolor[1]; rgba[2] = 1.0f - Ref.m_pCurrentEntity->rendercolor[2]; for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( rgba, fArray ); break; case RGBGEN_VERTEX: VectorSet( style, -1, -1, -1 ); if( !r_superLightStyle || r_superLightStyle->vStyles[1] == 255 ) { VectorSet( style, 1, 1, 1 ); if( r_superLightStyle && r_superLightStyle->vStyles[0] != 255 ) VectorCopy( r_lightStyles[r_superLightStyle->vStyles[0]].rgb, style ); } if( style[0] == style[1] && style[1] == style[2] && style[2] == 1 ) { for( i = 0; i < r_stats.numColors; i++, fArray += 4, inArray += 4 ) VectorDivide( inArray, bits, fArray ); } else { int j; float *tc; vec3_t temp[MAX_ARRAY_VERTS]; Mem_Set( temp, 0, sizeof( vec3_t ) * r_stats.numColors ); for( j = 0; j < LM_STYLES && r_superLightStyle->vStyles[j] != 255; j++ ) { VectorCopy( r_lightStyles[r_superLightStyle->vStyles[j]].rgb, style ); if( VectorIsNull( style )) continue; inArray = inColorArray[j][0]; for( i = 0, tc = temp[0]; i < r_stats.numColors; i++, tc += 3, inArray += 4 ) { tc[0] += ( inArray[0] / bits ) * style[0]; tc[1] += ( inArray[1] / bits ) * style[1]; tc[2] += ( inArray[2] / bits ) * style[2]; } } for( i = 0, tc = temp[0]; i < r_stats.numColors; i++, tc += 3, fArray += 4 ) { fArray[0] = bound( 0.0f, tc[0], 1.0f ); fArray[1] = bound( 0.0f, tc[1], 1.0f ); fArray[2] = bound( 0.0f, tc[2], 1.0f ); } } break; case RGBGEN_ONEMINUSVERTEX: for( i = 0; i < r_stats.numColors; i++, fArray += 4, inArray += 4 ) { fArray[0] = 1.0f - ( inArray[0] / bits ); fArray[1] = 1.0f - ( inArray[1] / bits ); fArray[2] = 1.0f - ( inArray[2] / bits ); } break; case RGBGEN_LIGHTINGDIFFUSE: if( Ref.m_pCurrentEntity ) R_LightForEntity( Ref.m_pCurrentEntity, fArray ); break; case RGBGEN_LIGHTINGDIFFUSE_ONLY: if( Ref.m_pCurrentEntity && !( Ref.params & RP_SHADOWMAPVIEW ) ) { if( Ref.m_pCurrentEntity->renderfx & RF_FULLBRIGHT ) VectorSet( rgba, 1.0f, 1.0f, 1.0f ); else R_LightForPoint( Ref.m_pCurrentEntity->lightOrg, t, NULL, rgba, Ref.m_pCurrentModel->radius * Ref.m_pCurrentEntity->scale ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( rgba, fArray ); } break; case RGBGEN_LIGHTINGAMBIENT_ONLY: if( Ref.m_pCurrentEntity && !( Ref.params & RP_SHADOWMAPVIEW )) { if( Ref.m_pCurrentEntity->renderfx & RF_FULLBRIGHT ) VectorSet( rgba, 1.0f, 1.0f, 1.0f ); else R_LightForPoint( Ref.m_pCurrentEntity->lightOrg, t, rgba, NULL, Ref.m_pCurrentEntity->radius * Ref.m_pCurrentEntity->scale ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( rgba, fArray ); } break; case RGBGEN_FOG: for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) Vector4Copy( r_texFog->shader->fogColor, fArray ); break; case RGBGEN_ENVIRONMENT: // disabled break; default: break; } fArray = colorArray[0]; inArray = inColorArray[0][0]; switch( alphaGen->type ) { case ALPHAGEN_IDENTITY: if( identityAlpha ) break; for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) fArray[3] = 1.0f; break; case ALPHAGEN_CONST: for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) fArray[3] = alphaGen->params[0]; break; case ALPHAGEN_WAVE: if( alphaGen->func.type == WAVEFORM_NOISE ) { a = RB_GetNoiseValue( 0.0f, 0.0f, 0.0f, ( m_fShaderTime + alphaGen->func.params[2] ) * alphaGen->func.params[3] ); } else { table = RB_TableForFunc( &alphaGen->func ); a = alphaGen->func.params[2] + m_fShaderTime * alphaGen->func.params[3]; a = TABLE_EVALUATE( table, a ); } a = bound( 0.0f, a * alphaGen->func.params[1] + alphaGen->func.params[0], 1.0f ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) fArray[3] = a; break; case ALPHAGEN_FADE: case ALPHAGEN_PORTAL: VectorAdd( vertexArray[0], Ref.m_pCurrentEntity->origin, v ); VectorSubtract( Ref.vieworg, v, t ); a = VectorLength( t ) * alphaGen->func.params[0]; a = bound( 0.0f, a, 1.0f ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) fArray[3] = a; break; case ALPHAGEN_VERTEX: for( i = 0; i < r_stats.numColors; i++, fArray += 4, inArray += 4 ) fArray[3] = inArray[3]; break; case ALPHAGEN_ONEMINUSVERTEX: for( i = 0; i < r_stats.numColors; i++, fArray += 4, inArray += 4 ) fArray[3] = 1.0f - inArray[3]; break; case ALPHAGEN_ENTITY: if( entityAlpha ) break; for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) fArray[3] = Ref.m_pCurrentEntity->renderamt; break; case ALPHAGEN_ONEMINUSENTITY: if( entityAlpha ) break; for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) fArray[3] = 1.0f - Ref.m_pCurrentEntity->renderamt; break; case ALPHAGEN_SPECULAR: VectorSubtract( Ref.vieworg, Ref.m_pCurrentEntity->origin, t ); if( !Matrix3x3_Compare( Ref.m_pCurrentEntity->matrix, matrix3x3_identity )) Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, t, v ); else VectorCopy( t, v ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) { VectorSubtract( v, vertexArray[i], t ); c = VectorLength( t ); a = DotProduct( t, normalArray[i] ) / max( 0.1f, c ); a = pow( a, alphaGen->func.params[0] ); fArray[3] = bound( 0.0f, a, 1.0f ); } break; case ALPHAGEN_DOT: if( !Matrix3x3_Compare( Ref.m_pCurrentEntity->matrix, matrix3x3_identity )) Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, Ref.forward, v ); else VectorCopy( Ref.forward, v ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) { a = DotProduct( v, inNormalArray[i] ); if( a < 0 ) a = -a; fArray[3] = bound( alphaGen->func.params[0], a, alphaGen->func.params[1] ); } break; case ALPHAGEN_ONEMINUSDOT: if( !Matrix3x3_Compare( Ref.m_pCurrentEntity->matrix, matrix3x3_identity )) Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, Ref.forward, v ); else VectorCopy( Ref.forward, v ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) { a = DotProduct( v, inNormalArray[i] ); if( a < 0 ) a = -a; a = 1.0f - a; fArray[3] = bound( alphaGen->func.params[0], a, alphaGen->func.params[1] ); } break; default: break; } if( r_colorFog ) { float dist, vdist; cplane_t *fogPlane; vec3_t viewtofog; float fogNormal[3], vpnNormal[3]; float fogDist, vpnDist, fogShaderDistScale; int blendsrc, blenddst; int fogptype; bool alphaFog; blendsrc = stage->flags & GLSTATE_SRCBLEND_MASK; blenddst = stage->flags & GLSTATE_DSTBLEND_MASK; if(( blendsrc != GLSTATE_SRCBLEND_SRC_ALPHA && blenddst != GLSTATE_DSTBLEND_SRC_ALPHA ) && ( blendsrc != GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA && blenddst != GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA )) alphaFog = false; else alphaFog = true; fogPlane = r_colorFog->visible; fogShaderDistScale = 1.0 / (r_colorFog->shader->fog_dist - r_colorFog->shader->fogClearDist); dist = Ref.fog_dist_to_eye[r_colorFog - r_worldBrushModel->fogs]; if( Ref.m_pCurrentShader->flags & SHADER_SKY ) { if( dist > 0 ) VectorScale( fogPlane->normal, -dist, viewtofog ); else VectorClear( viewtofog ); } else VectorCopy( Ref.m_pCurrentEntity->origin, viewtofog ); vpnNormal[0] = DotProduct( Ref.m_pCurrentEntity->matrix[0], Ref.forward ) * fogShaderDistScale * Ref.m_pCurrentEntity->scale; vpnNormal[1] = DotProduct( Ref.m_pCurrentEntity->matrix[1], Ref.forward ) * fogShaderDistScale * Ref.m_pCurrentEntity->scale; vpnNormal[2] = DotProduct( Ref.m_pCurrentEntity->matrix[2], Ref.forward ) * fogShaderDistScale * Ref.m_pCurrentEntity->scale; vpnDist = (((Ref.vieworg[0]-viewtofog[0])*Ref.forward[0]+(Ref.vieworg[1]-viewtofog[1])*Ref.forward[1]+(Ref.vieworg[2]-viewtofog[2])*Ref.forward[2])+r_colorFog->shader->fogClearDist) * fogShaderDistScale; fArray = colorArray[0]; if( dist < 0 ) { // camera is inside the fog for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) { temp = DotProduct( vertexArray[i], vpnNormal ) - vpnDist; c = ( 1.0f - bound( 0.0f, temp, 1.0f )) * 0xFFFF; if( alphaFog ) { fArray[3] = (int)(fArray[3] * c)>>16; } else { fArray[0] = (int)(fArray[0] * c)>>16; fArray[1] = (int)(fArray[1] * c)>>16; fArray[2] = (int)(fArray[2] * c)>>16; } } } else { fogNormal[0] = DotProduct( Ref.m_pCurrentEntity->matrix[0], fogPlane->normal ) * Ref.m_pCurrentEntity->scale; fogNormal[1] = DotProduct( Ref.m_pCurrentEntity->matrix[1], fogPlane->normal ) * Ref.m_pCurrentEntity->scale; fogNormal[2] = DotProduct( Ref.m_pCurrentEntity->matrix[2], fogPlane->normal ) * Ref.m_pCurrentEntity->scale; fogptype = ( fogNormal[0] == 1.0 ? PLANE_X : ( fogNormal[1] == 1.0 ? PLANE_Y : ( fogNormal[2] == 1.0 ? PLANE_Z : 3 ))); fogDist = fogPlane->dist - DotProduct( viewtofog, fogPlane->normal ); for( i = 0; i < r_stats.numColors; i++, fArray += 4 ) { if( fogptype < 3 ) vdist = vertexArray[i][fogptype] - fogDist; else vdist = DotProduct( vertexArray[i], fogNormal ) - fogDist; if( vdist < 0 ) { temp = ( DotProduct( vertexArray[i], vpnNormal ) - vpnDist ) * vdist / ( vdist - dist ); c = ( 1.0f - bound( 0, temp, 1.0f )) * 0xFFFF; if( alphaFog ) { fArray[3] = (int)(fArray[3] * c)>>16; } else { fArray[0] = (int)(fArray[0] * c)>>16; fArray[1] = (int)(fArray[1] * c)>>16; fArray[2] = (int)(fArray[2] * c)>>16; } } } } } } /* ================= RB_CalcTextureCoords ================= */ static bool RB_CalcTextureCoords( const stageBundle_t *bundle, uint unit, matrix4x4 matrix ) { const tcGen_t *tcGen = &bundle->tcGen; bool identityMatrix = false; vec3_t projection, transform; float *outCoords; GLfloat genVector[4][4]; float depth, *n; matrix4x4 m1, m2; int i; Matrix4x4_LoadIdentity( matrix ); switch( tcGen->type ) { case TCGEN_BASE: RB_DisableTexGen(); if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglTexCoordPointer( 2, GL_FLOAT, 0, texCoordArray ); return true; } break; case TCGEN_LIGHTMAP: RB_DisableTexGen(); if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglTexCoordPointer( 2, GL_FLOAT, 0, lmCoordArray[r_lightmapStyleNum[unit]] ); return true; } break; case TCGEN_ENVIRONMENT: if( gl_state.orthogonal ) return true; if(!( Ref.params & RP_SHADOWMAPVIEW )) { VectorSubtract( Ref.vieworg, Ref.m_pCurrentEntity->origin, projection ); Matrix3x3_Transform( Ref.m_pCurrentEntity->matrix, projection, transform ); outCoords = tUnitCoordsArray[unit][0]; for( i = 0, n = normalArray[0]; i < r_stats.numVertices; i++, outCoords += 2, n += 4 ) { VectorSubtract( transform, vertexArray[i], projection ); VectorNormalizeFast( projection ); depth = DotProduct( n, projection ); depth += depth; outCoords[0] = 0.5 + ( n[1] * depth - projection[1] ) * 0.5; outCoords[1] = 0.5 - ( n[2] * depth - projection[2] ) * 0.5; } } RB_DisableTexGen(); if(!GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglTexCoordPointer( 2, GL_FLOAT, 0, tUnitCoordsArray[unit] ); return true; } break; case TCGEN_VECTOR: for( i = 0; i < 3; i++ ) { genVector[0][i] = tcGen->params[i]; genVector[1][i] = tcGen->params[i+4]; } genVector[0][3] = genVector[1][3] = 0; // FIXME: HACK #ifdef OPENGL_STYLE matrix[3][0] = tcGen->params[3]; matrix[3][1] = tcGen->params[7]; #else matrix[0][3] = tcGen->params[3]; matrix[1][3] = tcGen->params[7]; #endif GL_TexCoordMode( GL_FALSE ); GL_EnableTexGen( GL_S, GL_OBJECT_LINEAR ); GL_EnableTexGen( GL_T, GL_OBJECT_LINEAR ); GL_EnableTexGen( GL_R, 0 ); GL_EnableTexGen( GL_Q, 0 ); pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] ); pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] ); return false; case TCGEN_WARP: outCoords = tUnitCoordsArray[unit][0]; for( i = 0; i < r_stats.numVertices; i++ ) { outCoords[0] = texCoordArray[i][0] + rb_warpSinTable[((int)((texCoordArray[i][1] * 8.0 + m_fShaderTime) * (256.0/M_PI2))) & 255] * (1.0/64); outCoords[1] = texCoordArray[i][1] + rb_warpSinTable[((int)((texCoordArray[i][0] * 8.0 + m_fShaderTime) * (256.0/M_PI2))) & 255] * (1.0/64); } RB_DisableTexGen(); if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglTexCoordPointer( 2, GL_FLOAT, 0, texCoordArray ); return true; } break; case TCGEN_PROJECTION: GL_TexCoordMode( GL_FALSE ); Matrix4x4_Copy( matrix, Ref.worldProjectionMatrix ); Matrix4x4_LoadIdentity( m1 ); Matrix4x4_ConcatScale( m1, 0.5f ); Matrix4x4_Concat( m2, m1, matrix ); Matrix4x4_LoadIdentity( m1 ); Matrix4x4_ConcatTranslate( m1, 0.5f, 0.5f, 0.5f ); Matrix4x4_Concat( matrix, m1, m2 ); for( i = 0; i < 4; i++ ) { genVector[0][i] = i == 0 ? 1 : 0; genVector[1][i] = i == 1 ? 1 : 0; genVector[2][i] = i == 2 ? 1 : 0; genVector[3][i] = i == 3 ? 1 : 0; } GL_EnableTexGen( GL_S, GL_OBJECT_LINEAR ); GL_EnableTexGen( GL_T, GL_OBJECT_LINEAR ); GL_EnableTexGen( GL_R, GL_OBJECT_LINEAR ); GL_EnableTexGen( GL_Q, GL_OBJECT_LINEAR ); pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] ); pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] ); pglTexGenfv( GL_R, GL_OBJECT_PLANE, genVector[2] ); pglTexGenfv( GL_Q, GL_OBJECT_PLANE, genVector[3] ); return false; case TCGEN_REFLECTION: GL_EnableTexGen( GL_S, GL_REFLECTION_MAP_ARB ); GL_EnableTexGen( GL_T, GL_REFLECTION_MAP_ARB ); GL_EnableTexGen( GL_R, GL_REFLECTION_MAP_ARB ); GL_EnableTexGen( GL_Q, 0 ); return true; case TCGEN_NORMAL: GL_EnableTexGen( GL_S, GL_NORMAL_MAP_ARB); GL_EnableTexGen( GL_T, GL_NORMAL_MAP_ARB); GL_EnableTexGen( GL_R, GL_NORMAL_MAP_ARB); GL_EnableTexGen( GL_Q, 0 ); return true; case TCGEN_FOG: { int fogPtype; cplane_t *fogPlane; shader_t *fogShader; vec3_t viewtofog; float fogNormal[3], vpnNormal[3]; float dist, vdist, fogDist, vpnDist; fogPlane = r_texFog->visible; fogShader = r_texFog->shader; matrix[0][0] = matrix[1][1] = 1.0f / ( fogShader->fog_dist - fogShader->fogClearDist ); // FIXME: HACK #ifdef OPENGL_STYLE matrix[3][1] = 1.5f / (float)FOG_TEXTURE_HEIGHT; #else matrix[1][3] = 1.5f / (float)FOG_TEXTURE_HEIGHT; #endif // distance to fog dist = Ref.fog_dist_to_eye[r_texFog - r_worldBrushModel->fogs]; if( Ref.m_pCurrentShader->flags & SHADER_SKY ) { if( dist > 0 ) VectorMA( Ref.vieworg, -dist, fogPlane->normal, viewtofog ); else VectorCopy( Ref.vieworg, viewtofog ); } else VectorCopy( Ref.m_pCurrentEntity->origin, viewtofog ); // some math tricks to take entity's rotation matrix into account // for fog texture coordinates calculations: // M is rotation matrix, v is vertex, t is transform vector // n is plane's normal, d is plane's dist, r is view origin // (M*v + t)*n - d = (M*n)*v - ((d - t*n)) // (M*v + t - r)*n = (M*n)*v - ((r - t)*n) fogNormal[0] = DotProduct( Ref.m_pCurrentEntity->matrix[0], fogPlane->normal ) * Ref.m_pCurrentEntity->scale; fogNormal[1] = DotProduct( Ref.m_pCurrentEntity->matrix[1], fogPlane->normal ) * Ref.m_pCurrentEntity->scale; fogNormal[2] = DotProduct( Ref.m_pCurrentEntity->matrix[2], fogPlane->normal ) * Ref.m_pCurrentEntity->scale; fogPtype = ( fogNormal[0] == 1.0 ? PLANE_X : ( fogNormal[1] == 1.0 ? PLANE_Y : ( fogNormal[2] == 1.0 ? PLANE_Z : 3 ))); fogDist = ( fogPlane->dist - DotProduct( viewtofog, fogPlane->normal ) ); vpnNormal[0] = DotProduct(Ref.m_pCurrentEntity->matrix[0], Ref.forward) * Ref.m_pCurrentEntity->scale; vpnNormal[1] = DotProduct(Ref.m_pCurrentEntity->matrix[1], Ref.forward) * Ref.m_pCurrentEntity->scale; vpnNormal[2] = DotProduct(Ref.m_pCurrentEntity->matrix[2], Ref.forward) * Ref.m_pCurrentEntity->scale; vpnDist = (( Ref.vieworg[0] - viewtofog[0] ) * Ref.forward[0] + ( Ref.vieworg[1] - viewtofog[1] ) * Ref.forward[1] + ( Ref.vieworg[2] - viewtofog[2] ) * Ref.forward[2] ) + fogShader->fogClearDist; outCoords = tUnitCoordsArray[unit][0]; if( dist < 0 ) { // camera is inside the fog brush for( i = 0; i < r_stats.numVertices; i++, outCoords += 2 ) { outCoords[0] = DotProduct( vertexArray[i], vpnNormal ) - vpnDist; if( fogPtype < 3 ) outCoords[1] = -( vertexArray[i][fogPtype] - fogDist ); else outCoords[1] = -( DotProduct( vertexArray[i], fogNormal ) - fogDist ); } } else { for( i = 0; i < r_stats.numVertices; i++, outCoords += 2 ) { if( fogPtype < 3 ) vdist = vertexArray[i][fogPtype] - fogDist; else vdist = DotProduct( vertexArray[i], fogNormal ) - fogDist; outCoords[0] = (( vdist < 0 ) ? ( DotProduct( vertexArray[i], vpnNormal ) - vpnDist ) * vdist / ( vdist - dist ) : 0.0f ); outCoords[1] = -vdist; } } RB_DisableTexGen(); if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglTexCoordPointer( 2, GL_FLOAT, 0, tUnitCoordsArray[unit] ); return false; } break; } case TCGEN_SVECTORS: RB_DisableTexGen(); if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { pglTexCoordPointer( 4, GL_FLOAT, 0, sVectorArray ); return true; } break; case TCGEN_PROJECTION_SHADOW: GL_TexCoordMode( GL_FALSE ); RB_DisableTexGen(); Matrix4x4_Concat( matrix, r_currentCastGroup->worldProjectionMatrix, Ref.entityMatrix ); break; default: break; } return identityMatrix; } /* ================ RB_SetupTCMods ================ */ static void RB_SetupTCMods( const shaderStage_t *stage, const stageBundle_t *bundle, matrix4x4 result ) { int i; const float *table; double t1, t2, sint, cost; matrix4x4 m1, m2; const tcMod_t *tcMod; for( i = 0, tcMod = bundle->tcMod; i < bundle->tcModNum; i++, tcMod++ ) { switch( tcMod->type ) { case TCMOD_TRANSLATE: Matrix4x4_Translate2D( result, tcMod->params[0], tcMod->params[1] ); break; case TCMOD_SCALE: Matrix4x4_Scale2D( result, tcMod->params[0], tcMod->params[1] ); break; case TCMOD_ROTATE: cost = tcMod->params[0] * m_fShaderTime; sint = R_FastSin( cost ); cost = R_FastSin( cost + 0.25 ); m2[0][0] = m2[1][1] = cost; // FIXME: HACK #ifdef OPENGL_STYLE m2[0][1] = sint; m2[3][0] = 0.5f * ( sint - cost + 1 ); m2[1][0] = -sint; m2[3][1] = -0.5f * ( sint + cost - 1 ); #else m2[1][0] = sint; m2[0][3] = 0.5f * ( sint - cost + 1 ); m2[0][1] = -sint; m2[1][3] = -0.5f * ( sint + cost - 1 ); #endif Matrix4x4_Copy2D( m1, result ); Matrix4x4_Concat2D( result, m2, m1 ); break; case TCMOD_TURB: t1 = ( 1.0f / 4.0f ); t2 = tcMod->params[2] + m_fShaderTime * tcMod->params[3]; Matrix4x4_Scale2D( result, 1.0f+(tcMod->params[1]*R_FastSin(t2)+tcMod->params[0])*t1, 1+(tcMod->params[1]*R_FastSin(t2+0.25f)+tcMod->params[0])*t1 ); break; case TCMOD_STRETCH: table = RB_TableForFunc( &tcMod->func ); t2 = tcMod->params[3] + m_fShaderTime * tcMod->params[4]; t1 = TABLE_EVALUATE( table, t2 ) * tcMod->params[2] + tcMod->params[1]; t1 = t1 ? 1.0f / t1 : 1.0f; t2 = 0.5f - 0.5f * t1; Matrix4x4_Stretch2D( result, t1, t2 ); break; case TCMOD_SCROLL: t1 = tcMod->params[0] * m_fShaderTime; t2 = tcMod->params[1] * m_fShaderTime; if( stage->progType != PROGRAM_DISTORTION ) { // HACKHACK t1 = t1 - floor( t1 ); t2 = t2 - floor( t2 ); } Matrix4x4_Translate2D( result, t1, t2 ); break; case TCMOD_TRANSFORM: m2[0][0] = tcMod->params[0]; m2[1][1] = tcMod->params[1]; // FIXME: HACK #ifdef OPENGL_STYLE m2[0][1] = tcMod->params[2]; m2[3][0] = tcMod->params[4]; m2[1][0] = tcMod->params[3]; m2[3][1] = tcMod->params[5]; #else m2[1][0] = tcMod->params[2]; m2[0][3] = tcMod->params[4]; m2[0][1] = tcMod->params[3]; m2[1][3] = tcMod->params[5]; #endif Matrix4x4_Copy2D( m1, result ); Matrix4x4_Concat2D( m2, m1, result ); break; default: break; } } } /* ================= RB_SetupTextureCombiners ================= */ static void RB_SetupTextureCombiners( const stageBundle_t *bundle ) { const texEnvCombine_t *texEnvCombine = &bundle->texEnvCombine; pglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, texEnvCombine->rgbCombine); pglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, texEnvCombine->rgbSource[0]); pglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, texEnvCombine->rgbSource[1]); pglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, texEnvCombine->rgbSource[2]); pglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, texEnvCombine->rgbOperand[0]); pglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, texEnvCombine->rgbOperand[1]); pglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, texEnvCombine->rgbOperand[2]); pglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, texEnvCombine->rgbScale); pglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, texEnvCombine->alphaCombine); pglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, texEnvCombine->alphaSource[0]); pglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, texEnvCombine->alphaSource[1]); pglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, texEnvCombine->alphaSource[2]); pglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, texEnvCombine->alphaOperand[0]); pglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, texEnvCombine->alphaOperand[1]); pglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, texEnvCombine->alphaOperand[2]); pglTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, texEnvCombine->alphaScale); pglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texEnvCombine->constColor); } /* ================= RB_SetShaderState ================= */ static void RB_SetShaderState( void ) { if( Ref.m_pCurrentShader->flags & SHADER_CULL ) { GL_Enable( GL_CULL_FACE ); GL_CullFace( Ref.m_pCurrentShader->cull.mode ); } else GL_Disable( GL_CULL_FACE ); if( Ref.m_pCurrentShader->flags & SHADER_POLYGONOFFSET ) { GL_Enable( GL_POLYGON_OFFSET_FILL ); GL_PolygonOffset( r_offsetfactor->value, r_offsetunits->value ); } else GL_Disable( GL_POLYGON_OFFSET_FILL ); if( Ref.m_pCurrentShader->flags & SHADER_FLARE ) GL_Disable( GL_DEPTH_TEST ); } /* ================= RB_SetShaderStageState ================= */ static void RB_SetShaderStageState( const shaderStage_t *stage ) { if( stage->flags & SHADERSTAGE_ALPHAFUNC ) { GL_Enable( GL_ALPHA_TEST ); GL_AlphaFunc( stage->alphaFunc.func, stage->alphaFunc.ref ); } else GL_Disable( GL_ALPHA_TEST ); if( stage->flags & SHADERSTAGE_BLENDFUNC ) { GL_Enable( GL_BLEND ); GL_BlendFunc( stage->blendFunc.src, stage->blendFunc.dst ); } else GL_Disable( GL_BLEND ); if( stage->flags & SHADERSTAGE_DEPTHFUNC ) { GL_Enable( GL_DEPTH_TEST ); GL_DepthFunc( stage->depthFunc.func ); } else GL_Disable( GL_DEPTH_TEST ); if( stage->flags & SHADERSTAGE_DEPTHWRITE ) GL_DepthMask( GL_TRUE ); else GL_DepthMask( GL_FALSE ); } /* ================= RB_SetupTextureUnit ================= */ static void RB_SetupTextureUnit( const shaderStage_t *stage, const stageBundle_t *bundle, uint unit ) { texture_t *image; matrix4x4 m1, m2, result; bool identityMatrix; GL_SelectTexture( unit ); switch( bundle->texType ) { case TEX_GENERIC: if( bundle->numTextures == 1 ) image = bundle->textures[0]; else image = bundle->textures[(int)(bundle->animFrequency * m_fShaderTime) % bundle->numTextures]; break; case TEX_LIGHTMAP: image = r_lightmapTextures[r_superLightStyle->lmapNum[r_lightmapStyleNum[unit]]]; break; case TEX_CINEMATIC: // FIXME: implement image = r_defaultTexture; //CIN_RunCinematic( bundle->cinematicHandle ); //CIN_DrawCinematic( bundle->cinematicHandle ); break; case TEX_PORTAL: image = r_portaltexture; break; case TEX_DLIGHT: image = r_dlightTexture; break; default: Host_Error( "RB_SetupTextureUnit: unknown texture type %i in shader '%s'\n", bundle->texType, Ref.m_pCurrentShader->name ); } GL_BindTexture( image ); if( unit < gl_config.textureunits ) { if( unit && !stage->program ) pglEnable( GL_TEXTURE_2D ); if( bundle->flags & STAGEBUNDLE_CUBEMAP ) GL_TexCoordMode( GL_TEXTURE_CUBE_MAP_ARB ); else GL_TexCoordMode( GL_TEXTURE_COORD_ARRAY ); GL_TexEnv( bundle->texEnv ); if( bundle->flags & STAGEBUNDLE_TEXENVCOMBINE ) RB_SetupTextureCombiners( bundle ); } identityMatrix = RB_CalcTextureCoords( bundle, unit, result ); if( bundle->tcModNum ) { identityMatrix = false; RB_SetupTCMods( stage, bundle, result ); } if( bundle->tcGen.type == TCGEN_REFLECTION || bundle->tcGen.type == TCGEN_NORMAL ) { Matrix4x4_Transpose( Ref.modelViewMatrix, m1 ); Matrix4x4_Copy( result, m2 ); Matrix4x4_Concat( result, m2, m1 ); GL_LoadTexMatrix( result ); return; } if( !identityMatrix ) GL_LoadTexMatrix( result ); else GL_LoadIdentityTexMatrix(); } /* ================= RB_CleanupTextureUnit ================= */ static void RB_CleanupTextureUnit( const stageBundle_t *bundle, uint unit ) { GL_SelectTexture( unit ); if( bundle->tcGen.type == TCGEN_REFLECTION || bundle->tcGen.type == TCGEN_NORMAL ) { RB_DisableTexGen(); GL_TexCoordMode( GL_NONE ); } if( unit < gl_config.textureunits ) { if( bundle->flags & STAGEBUNDLE_CUBEMAP ) pglDisable( GL_TEXTURE_CUBE_MAP_ARB ); else pglDisable( GL_TEXTURE_2D ); } } /* ================ RB_ShaderStageBlendmode stupid code!!!!! ================ */ static int RB_ShaderStageBlendmode( const shaderStage_t *stage ) { //if( stage->flags & SHADERSTAGE_BLENDFUNC ) { return GL_MODULATE; } //return 0; } /* ================ RB_RenderMeshGeneric TEST ONLY, another modes probably not needed ================ */ static void RB_RenderMeshGeneric( void ) { const shaderStage_t *stage = rb_accumStages[0]; const stageBundle_t *bundle; int i, j; RB_SetShaderStageState( stage ); RB_SetupTextureUnit( stage, stage->bundles[0], GL_TEXTURE0 ); RB_CalcVertexColors( stage ); //if( stage->flags & SHADERSTAGE_BLENDFUNC ) // GL_TexEnv( stage->blendFunc.func ); GL_TexEnv( GL_MODULATE ); GL_SetState( r_currentShaderState|( stage->flags & r_currentShaderPassMask )); /*for( i = 0; i < rb_numAccumStages; i++ ) { stage = Ref.m_pCurrentShader->stages[i]; RB_SetShaderStageState( stage ); RB_CalcVertexColors( stage ); for( j = 0; j < stage->numBundles; j++ ) { bundle = stage->bundles[j]; RB_SetupTextureUnit( stage, bundle, j ); } for( j = stage->numBundles - 1; j >= 0; j-- ) { bundle = stage->bundles[j]; RB_CleanupTextureUnit( bundle, j ); } }*/ R_FlushArrays(); } /* ================ RB_RenderAccumulatedStages ================ */ static void RB_RenderAccumulatedStages( void ) { const shaderStage_t *stage = rb_accumStages[0]; RB_CleanUpTextureUnits( rb_numAccumStages ); if( stage->program ) { rb_numAccumStages = 0; return; } if( stage->flags & SHADERSTAGE_DLIGHT ) { rb_numAccumStages = 0; R_AddDynamicLights( r_currentDlightBits, r_currentShaderState|(stage->flags & r_currentShaderPassMask)); return; } if( stage->flags & SHADERSTAGE_STENCILSHADOW ) { rb_numAccumStages = 0; // R_PlanarShadowPass( r_currentShaderState|( stage->flags & r_currentShaderPassMask )); return; } RB_RenderMeshGeneric(); rb_numAccumStages = 0; } /* ================ RB_AccumulateStage ================ */ static void RB_AccumulateStage( shaderStage_t *stage ) { bool accumulate, renderNow; const shaderStage_t *prevStage; // for depth texture we render light's view to, ignore passes that do not write into depth buffer if(( Ref.params & RP_SHADOWMAPVIEW ) && !( stage->flags & SHADERSTAGE_DEPTHWRITE )) return; // see if there are any free texture units renderNow = ( stage->flags & (SHADERSTAGE_DLIGHT|SHADERSTAGE_STENCILSHADOW )) || stage->program; accumulate = ( rb_numAccumStages < gl_config.textureunits ) && !renderNow; if( accumulate ) { if( !rb_numAccumStages ) { rb_accumStages[rb_numAccumStages++] = stage; return; } // ok, we've got several passes, diff against the previous prevStage = rb_accumStages[rb_numAccumStages-1]; // see if depthfuncs and colors are good if( ((( prevStage->flags ^ stage->flags ) & SHADERSTAGE_DEPTHFUNC) && ( stage->depthFunc.func == GL_EQUAL )) || (( prevStage->flags & SHADERSTAGE_ALPHAFUNC ) && !((stage->flags & SHADERSTAGE_DEPTHFUNC) && ( stage->depthFunc.func == GL_EQUAL ))) || ( stage->flags & SHADERSTAGE_ALPHAFUNC ) || ( stage->rgbGen.type != RGBGEN_IDENTITY ) || ( stage->alphaGen.type != ALPHAGEN_IDENTITY ) ) accumulate = false; // see if blendmodes are good if( accumulate ) { int mode, prevMode; mode = RB_ShaderStageBlendmode( stage ); if( mode ) { prevMode = RB_ShaderStageBlendmode( prevStage ); if( GL_Support( R_COMBINE_EXT )) { if( prevMode == GL_REPLACE ) accumulate = (mode == GL_ADD) ? GL_Support( R_TEXTURE_ENV_ADD_EXT ) : true; else if( prevMode == GL_ADD ) accumulate = (mode == GL_ADD) && GL_Support( R_TEXTURE_ENV_ADD_EXT ); else if( prevMode == GL_MODULATE ) accumulate = (mode == GL_MODULATE || mode == GL_REPLACE); else accumulate = false; } else // if( GL_Support( R_ARB_MULTITEXTURE )) { if( prevMode == GL_REPLACE ) accumulate = (mode == GL_ADD) ? GL_Support( R_TEXTURE_ENV_ADD_EXT ) : (mode != GL_DECAL); else if( prevMode == GL_ADD ) accumulate = (mode == GL_ADD) && GL_Support( R_TEXTURE_ENV_ADD_EXT ); else if( prevMode == GL_MODULATE ) accumulate = (mode == GL_MODULATE || mode == GL_REPLACE); else accumulate = false; } } else accumulate = false; } } // no, failed to accumulate if( !accumulate ) { if( rb_numAccumStages ) RB_RenderAccumulatedStages(); } rb_accumStages[rb_numAccumStages++] = stage; if( renderNow ) RB_RenderAccumulatedStages(); } /* ================ R_RenderMeshBuffer ================ */ void R_RenderMeshBuffer( const meshbuffer_t *mb ) { int i; msurface_t *surf; shaderStage_t *stage; mfog_t *fog; if( !r_stats.numVertices || !r_stats.numIndices ) { R_ClearArrays(); return; } surf = mb->infoKey > 0 ? &r_worldBrushModel->surfaces[mb->infoKey - 1] : NULL; if( surf ) r_superLightStyle = &r_superLightStyles[surf->superLightStyle]; else r_superLightStyle = NULL; m_pRenderMeshBuffer = mb; R_SHADER_FOR_KEY( mb->shaderKey, Ref.m_pCurrentShader ); if( gl_state.orthogonal ) { m_fShaderTime = Sys_DoubleTime(); } else { m_fShaderTime = Ref.refdef.time; if( Ref.m_pCurrentEntity ) { m_fShaderTime -= Ref.m_pCurrentEntity->shaderTime; if( m_fShaderTime < 0 ) m_fShaderTime = 0; } } if( !r_triangleOutlines ) RB_SetShaderState(); if( Ref.m_pCurrentShader->deformVertexesNum ) RB_DeformVertexes(); if( r_features & MF_KEEPLOCK ) r_stats.totalKeptLocks++; else R_UnlockArrays(); if( r_triangleOutlines ) { R_LockArrays( r_stats.numVertices ); if( Ref.params & RP_TRISOUTLINES ) RB_DrawTriangles(); if( Ref.params & RP_SHOWNORMALS ) RB_DrawNormals(); if( Ref.params & RP_SHOWTANGENTS ) RB_DrawTangentSpace(); R_ClearArrays(); return; } // extract the fog volume number from sortkey if( !r_worldModel ) fog = NULL; else R_FOG_FOR_KEY( mb->sortKey, fog ); if( fog && !fog->shader ) fog = NULL; // can we fog the geometry with alpha texture? r_texFog = ( fog && (( Ref.m_pCurrentShader->sort <= SORT_ALPHATEST && ( Ref.m_pCurrentShader->flags & ( SHADER_DEPTHWRITE|SHADER_SKY ))) || Ref.m_pCurrentShader->fog_dist )) ? fog : NULL; // check if the fog volume is present but we can't use alpha texture r_colorFog = ( fog && !r_texFog ) ? fog : NULL; if( Ref.m_pCurrentShader->flags & SHADER_FLARE ) r_currentDlightBits = 0; else r_currentDlightBits = surf ? mb->dlightbits : 0; r_currentShadowBits = mb->shadowbits & Ref.shadowBits; R_LockArrays( r_stats.numVertices ); for( i = 0; i < Ref.m_pCurrentShader->numStages; i++ ) { stage = Ref.m_pCurrentShader->stages[i]; RB_AccumulateStage( stage ); } // flush any remaining passes if( rb_numAccumStages ) RB_RenderAccumulatedStages(); R_ClearArrays(); pglMatrixMode( GL_MODELVIEW ); } static void RB_DrawLine( int color, int numpoints, const float *points ) { int i = numpoints - 1; vec3_t p0, p1; VectorSet( p0, points[i*3+0], points[i*3+1], points[i*3+2] ); if( r_physbdebug->integer == 1 ) ConvertPositionToGame( p0 ); for (i = 0; i < numpoints; i ++) { VectorSet( p1, points[i*3+0], points[i*3+1], points[i*3+2] ); if( r_physbdebug->integer == 1 ) ConvertPositionToGame( p1 ); pglColor4fv(UnpackRGBA( color )); pglVertex3fv( p0 ); pglVertex3fv( p1 ); VectorCopy( p1, p0 ); } } void RB_DebugGraphics( void ) { if( r_refdef.rdflags & RDF_NOWORLDMODEL ) return; if( r_physbdebug->integer ) { // physic debug GL_LoadMatrix( Ref.worldMatrix ); pglBegin( GL_LINES ); ri.ShowCollision( RB_DrawLine ); pglEnd(); } if( r_showtextures->integer ) { RB_ShowTextures(); } } static vec4_t pic_points[4] = { { 0, 0, 0, 1 }, { 0, 0, 0, 1 }, { 0, 0, 0, 1 }, { 0, 0, 0, 1 } }; static vec2_t pic_st[4]; static vec4_t pic_colors[4]; static rb_mesh_t pic_mesh = { 4, pic_points, pic_points, NULL, pic_st, { 0, 0, 0, 0 }, { pic_colors, pic_colors, pic_colors, pic_colors }, 6, NULL }; meshbuffer_t pic_mbuffer; /* =============== RB_DrawStretchPic =============== */ void RB_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, shader_t *shader ) { if( !shader ) return; // lower-left Vector2Set( pic_points[0], x, y ); Vector2Set( pic_st[0], s1, t1 ); Vector4Copy( gl_state.draw_color, pic_colors[0] ); // lower-right Vector2Set( pic_points[1], x+w, y ); Vector2Set( pic_st[1], s2, t1 ); Vector4Copy( pic_colors[0], pic_colors[1] ); // upper-right Vector2Set( pic_points[2], x+w, y+h ); Vector2Set( pic_st[2], s2, t2 ); Vector4Copy( pic_colors[0], pic_colors[2] ); // upper-left Vector2Set( pic_points[3], x, y+h ); Vector2Set( pic_st[3], s1, t2 ); Vector4Copy( pic_colors[0], pic_colors[3] ); if( pic_mbuffer.shaderKey != (int)shader->sortKey || -pic_mbuffer.infoKey-1+4 > MAX_ARRAY_VERTS ) { if( pic_mbuffer.shaderKey ) { pic_mbuffer.infoKey = -1; R_RenderMeshBuffer( &pic_mbuffer ); } } pic_mbuffer.infoKey -= 4; pic_mbuffer.shaderKey = shader->sortKey; // upload video right before rendering // FIXME: implement // if( shader->flags & SHADER_VIDEOMAP ) R_UploadCinematicShader( shader ); R_PushMesh( &pic_mesh, MF_TRIFAN|shader->features|( r_shownormals->integer ? MF_NORMALS : 0 )); } /* ================= RB_VBOInfo_f ================= */ void RB_VBOInfo_f( void ) { if( !GL_Support( R_ARB_VERTEX_BUFFER_OBJECT_EXT )) { Msg( "GL_ARB_vertex_buffer_object extension is disabled or not supported\n" ); return; } Msg( "%i bytes in %i static buffers\n", rb_staticBytes, rb_staticCount ); Msg( "%i bytes in %i stream buffers\n", rb_streamBytes, rb_streamCount ); } /* ================= RB_InitBackend ================= */ void RB_InitBackend( void ) { rb_numAccumStages = 0; r_arraysLocked = false; r_triangleOutlines = false; R_ClearArrays(); RB_ResetPassMask(); // build waveform tables RB_BuildTables(); // Set default GL state GL_SetDefaultState(); } /* ================= RB_ShutdownBackend ================= */ void RB_ShutdownBackend( void ) { }