1393 lines
36 KiB
C++
1393 lines
36 KiB
C++
// Filename:- R_Surface.cpp
|
|
//
|
|
// a container module for paste-code to do with surface rendering
|
|
//
|
|
#include "stdafx.h"
|
|
#include "includes.h"
|
|
#include "R_Common.h"
|
|
#include "text.h"
|
|
//
|
|
#include "r_surface.h"
|
|
|
|
|
|
int giRenderedBoneWeights;
|
|
int giOmittedBoneWeights;
|
|
|
|
|
|
void GetWeightColour(int iNumWeights, byte &r, byte &g, byte &b)
|
|
{
|
|
switch (iNumWeights)
|
|
{
|
|
case 0:
|
|
|
|
//assert(0); // this shouldn't happen, but...
|
|
//
|
|
r=255,g=255,b=255; // white
|
|
break;
|
|
|
|
case 1:
|
|
|
|
r=0,g=255,b=0; // bright green
|
|
break;
|
|
|
|
case 2:
|
|
|
|
r=0,g=128,b=0; // dark green
|
|
break;
|
|
|
|
case 3:
|
|
|
|
r=255,g=255,b=0; // bright yellow
|
|
// r=128,g=128,b=0; // dark yellow
|
|
break;
|
|
|
|
case 4:
|
|
|
|
r=0,g=255,b=255; // bright cyan
|
|
break;
|
|
|
|
default:
|
|
|
|
// anything > 4 (shouldn't happen, because carcass will limit it)...
|
|
//
|
|
r=255,g=0,b=0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void RB_StageIteratorGeneric( void );
|
|
|
|
void RB_EndSurface( void ) {
|
|
shaderCommands_t *input;
|
|
|
|
input = &tess;
|
|
|
|
if (input->numIndexes == 0) {
|
|
return;
|
|
}
|
|
|
|
if (input->indexes[ACTUAL_SHADER_MAX_INDEXES-1] != 0) {
|
|
ri.Error (ERR_DROP, "RB_EndSurface() - ACTUAL_SHADER_MAX_INDEXES hit");
|
|
}
|
|
if (input->xyz[ACTUAL_SHADER_MAX_VERTEXES-1][0] != 0) {
|
|
ri.Error (ERR_DROP, "RB_EndSurface() - ACTUAL_SHADER_MAX_VERTEXES hit");
|
|
}
|
|
/*MODVIEWREM
|
|
if ( tess.shader == tr.shadowShader ) {
|
|
RB_ShadowTessEnd();
|
|
return;
|
|
}
|
|
|
|
// for debugging of sort order issues, stop rendering after a given sort value
|
|
if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// update performance counters
|
|
//
|
|
backEnd.pc.c_shaders++;
|
|
backEnd.pc.c_vertexes += tess.numVertexes;
|
|
backEnd.pc.c_indexes += tess.numIndexes;
|
|
backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses;
|
|
|
|
//
|
|
// call off to shader specific tess end function
|
|
//
|
|
tess.currentStageIteratorFunc();
|
|
|
|
//
|
|
// draw debugging stuff
|
|
//
|
|
if ( r_showtris->integer ) {
|
|
DrawTris (input);
|
|
}
|
|
if ( r_shownormals->integer ) {
|
|
DrawNormals (input);
|
|
}
|
|
|
|
*/
|
|
|
|
RB_StageIteratorGeneric();
|
|
|
|
// clear shader so we can tell we don't have any unclosed surfaces
|
|
tess.numIndexes = 0;
|
|
|
|
//MODVIEWREM GLimp_LogComment( "----------\n" );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
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, GLuint gluiTextureBind ) {
|
|
tess.numIndexes = 0;
|
|
tess.numVertexes = 0;
|
|
//MODVIEWREM tess.shader = shader;
|
|
//MODVIEWREM tess.fogNum = fogNum;
|
|
//MODVIEWREM tess.dlightBits = 0; // will be OR'd in by surface functions
|
|
//MODVIEWREM tess.xstages = shader->stages;
|
|
//MODVIEWREM tess.numPasses = shader->numUnfoggedPasses;
|
|
//MODVIEWREM tess.currentStageIteratorFunc = shader->optimalStageIteratorFunc;
|
|
tess.gluiTextureBind = gluiTextureBind;
|
|
tess.bSurfaceIsG2Tag = false;
|
|
}
|
|
|
|
|
|
|
|
void RB_CheckOverflow( int verts, int indexes ) {
|
|
if (tess.numVertexes + verts < ACTUAL_SHADER_MAX_VERTEXES
|
|
&& tess.numIndexes + indexes < ACTUAL_SHADER_MAX_INDEXES) {
|
|
return;
|
|
}
|
|
|
|
RB_EndSurface();
|
|
|
|
if ( verts >= ACTUAL_SHADER_MAX_VERTEXES ) {
|
|
ri.Error(ERR_DROP, "RB_CheckOverflow: verts > MAX (%d > %d)", verts, ACTUAL_SHADER_MAX_VERTEXES );
|
|
}
|
|
if ( indexes >= ACTUAL_SHADER_MAX_INDEXES ) {
|
|
ri.Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%d > %d)", indexes, ACTUAL_SHADER_MAX_INDEXES );
|
|
}
|
|
|
|
RB_BeginSurface(NULL/*MODVIEWREM tess.shader*/, 0/*MODVIEWREM tess.fogNum*/, tess.gluiTextureBind );
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////
|
|
//
|
|
// 2 hack functions for ModView to emulate cgame ent-adding...
|
|
//
|
|
void R_ModView_BeginEntityAdd()
|
|
{
|
|
tr.refdef.num_entities = 0;
|
|
tr.refdef.numDrawSurfs = 0;
|
|
}
|
|
|
|
void R_ModView_AddEntity(ModelHandle_t hModel, int iFrame_Primary, int iOldFrame_Primary,
|
|
int iBoneNum_SecondaryStart,int iFrame_Secondary, int iOldFrame_Secondary,
|
|
int iSurfaceNum_RootOverride,
|
|
float fLerp,
|
|
surfaceInfo_t *slist, // pointer to list of surfaces turned off
|
|
boneInfo_t *blist, // pointer to list of bones to be overriden
|
|
mdxaBone_t *pXFormedG2Bones, // feedback array for after model has rendered
|
|
bool *pXFormedG2BonesValid, // and a validity check because of deactivated bones
|
|
mdxaBone_t *pXFormedG2TagSurfs, // feedback array for after model has rendered
|
|
bool *pXFormedG2TagSurfsValid, // and a validity check because of deactivated surfs
|
|
//
|
|
int *piRenderedTris,
|
|
int *piRenderedVerts,
|
|
int *piRenderedSurfs,
|
|
int *piXformedG2Bones,
|
|
// int *piRenderedBoneWeightsThisSurface,
|
|
int *piRenderedBoneWeights,
|
|
int *piOmittedBoneWeights
|
|
)
|
|
{
|
|
trRefEntity_t trHackJob;
|
|
|
|
// general params...
|
|
//
|
|
trHackJob.e.hModel = hModel;
|
|
trHackJob.e.iFrame_Primary = iFrame_Primary;
|
|
trHackJob.e.iOldFrame_Primary = iOldFrame_Primary;
|
|
trHackJob.e.iFrame_Secondary = iFrame_Secondary;
|
|
trHackJob.e.iOldFrame_Secondary = iOldFrame_Secondary;
|
|
trHackJob.e.iBoneNum_SecondaryStart = iBoneNum_SecondaryStart;
|
|
trHackJob.e.iSurfaceNum_RootOverride= iSurfaceNum_RootOverride;
|
|
|
|
trHackJob.e.backlerp = fLerp; // 0.0 = current, 1.0 = old
|
|
|
|
|
|
// Ghoul2 params... (even though model not nec. g2 format)
|
|
//
|
|
//
|
|
trHackJob.e.slist = slist;
|
|
trHackJob.e.blist = blist;
|
|
|
|
// some other crap to make life simpler...
|
|
//
|
|
trHackJob.e.renderfx = RF_CAP_FRAMES;
|
|
trHackJob.e.piRenderedTris = piRenderedTris;
|
|
trHackJob.e.piRenderedVerts = piRenderedVerts;
|
|
trHackJob.e.piRenderedSurfs = piRenderedSurfs;
|
|
trHackJob.e.piXformedG2Bones = piXformedG2Bones;
|
|
// trHackJob.e.piRenderedBoneWeightsThisSurface = piRenderedBoneWeightsThisSurface;
|
|
trHackJob.e.piRenderedBoneWeights = piRenderedBoneWeights;
|
|
trHackJob.e.piOmittedBoneWeights = piOmittedBoneWeights;
|
|
|
|
trHackJob.e.pXFormedG2Bones = pXFormedG2Bones;
|
|
trHackJob.e.pXFormedG2BonesValid = pXFormedG2BonesValid;
|
|
trHackJob.e.pXFormedG2TagSurfs = pXFormedG2TagSurfs;
|
|
trHackJob.e.pXFormedG2TagSurfsValid = pXFormedG2TagSurfsValid;
|
|
|
|
// now add it to the list to be processed...
|
|
//
|
|
tr.refdef.entities[ tr.refdef.num_entities++ ] = trHackJob;
|
|
}
|
|
//
|
|
///////////////////////////////////////////////////
|
|
|
|
|
|
void R_AddEntitySurfaces (void)
|
|
{
|
|
trRefEntity_t *ent;
|
|
//MODVIEWREM shader_t *shader;
|
|
|
|
for ( tr.currentEntityNum = 0;
|
|
tr.currentEntityNum < tr.refdef.num_entities;
|
|
tr.currentEntityNum++ )
|
|
{
|
|
ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum];
|
|
|
|
// ent->needDlights = qfalse;
|
|
|
|
// we must set up parts of tr.or for model culling
|
|
//MODVIEWREM R_RotateForEntity( ent, &tr.viewParms, &tr.or );
|
|
|
|
tr.currentModel = R_GetModelByHandle( ent->e.hModel );
|
|
if (!tr.currentModel)
|
|
{
|
|
assert(0);
|
|
//MODVIEWREM R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 );
|
|
}
|
|
else
|
|
{
|
|
switch ( tr.currentModel->type )
|
|
{
|
|
case MOD_MESH:
|
|
R_AddMD3Surfaces( ent );
|
|
break;
|
|
case MOD_MD4:
|
|
R_AddAnimSurfaces( ent );
|
|
break;
|
|
case MOD_MDXM:
|
|
R_AddGhoulSurfaces( ent);
|
|
break;
|
|
case MOD_BRUSH:
|
|
assert(0);
|
|
// R_AddBrushModelSurfaces( ent );
|
|
break;
|
|
case MOD_BAD: // null model axis
|
|
assert(0);
|
|
/*MODVIEWREM
|
|
if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal)
|
|
{
|
|
break;
|
|
}
|
|
shader = R_GetShaderByHandle( ent->e.customShader );
|
|
R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 );
|
|
*/
|
|
break;
|
|
default:
|
|
ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// entry point from ModView draw function to setup all surfaces ready for actual render call later
|
|
void RE_GenerateDrawSurfs( void )
|
|
{
|
|
/*
|
|
R_AddWorldSurfaces ();
|
|
|
|
R_AddPolygonSurfaces();
|
|
|
|
// set the projection matrix with the minimum zfar
|
|
// now that we have the world bounded
|
|
// this needs to be done before entities are
|
|
// added, because they use the projection
|
|
// matrix for lod calculation
|
|
R_SetupProjection ();
|
|
*/
|
|
R_AddEntitySurfaces ();
|
|
}
|
|
|
|
|
|
|
|
void RB_NULL( surfaceInfo_t *surf )
|
|
{
|
|
assert(0);
|
|
ri.Error( ERR_DROP, "RB_NULL() reached" );
|
|
}
|
|
|
|
int giSurfaceVertsDrawn;
|
|
int giSurfaceTrisDrawn;
|
|
int giRenderedBoneWeightDrawn;
|
|
void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = {
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceBad, // SF_BAD,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceSkip, // SF_SKIP,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceFace, // SF_FACE,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceGrid, // SF_GRID,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfacePolychain, // SF_POLY,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceMesh, // SF_MD3,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceAnim, // SF_MD4,
|
|
(void(*)(void*))RB_SurfaceGhoul, // SF_MDX,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceFlare, // SF_FLARE,
|
|
(void(*)(void*))RB_NULL,// (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY
|
|
(void(*)(void*))RB_NULL // (void(*)(void*))RB_SurfaceDisplayList // SF_DISPLAY_LIST
|
|
};
|
|
|
|
|
|
// findme label: finaldrawpos
|
|
//
|
|
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;
|
|
|
|
glBegin( GL_TRIANGLE_STRIP );
|
|
c_begins++;
|
|
|
|
if ( numIndexes <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
// 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
|
|
{
|
|
glEnd();
|
|
|
|
glBegin( 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
|
|
{
|
|
glEnd();
|
|
|
|
glBegin( 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];
|
|
}
|
|
|
|
glEnd();
|
|
}
|
|
|
|
|
|
|
|
static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) {
|
|
int primitives;
|
|
|
|
primitives = 1;//MODVIEWREM: r_primitives->integer;
|
|
/*MODVIEWREM
|
|
// 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, glArrayElement );
|
|
return;
|
|
}
|
|
|
|
/* MODVIEWREM
|
|
if ( primitives == 3 ) {
|
|
R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete );
|
|
return;
|
|
}
|
|
*/
|
|
// anything else will cause no drawing
|
|
}
|
|
|
|
|
|
static inline void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross )
|
|
{
|
|
cross[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
|
|
cross[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
|
|
cross[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
|
|
}
|
|
|
|
|
|
static vec_t VectorNormalize2( const vec3_t v, vec3_t out) {
|
|
float length, ilength;
|
|
|
|
length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
|
|
length = sqrt (length);
|
|
|
|
if (length)
|
|
{
|
|
ilength = 1/length;
|
|
out[0] = v[0]*ilength;
|
|
out[1] = v[1]*ilength;
|
|
out[2] = v[2]*ilength;
|
|
} else {
|
|
VectorClear( out );
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
/*
|
|
// Fuck this maths shit, it doesn't work
|
|
#define real_nclip(x0,y0,x1,y1,x2,y2) ( (y1-y0)*(x2-x1) - (x1-x0)*(y2-y1) )
|
|
|
|
static bool MouseOverTri(float x0, float x1, float x2, float y0, float y1, float y2, float mX, float mY)
|
|
{
|
|
// Check that winding of all 3 lines of x/y(n) mX,mY is in same direction
|
|
// Don't know winding direction, so use first winding check to pick further winding direction checks
|
|
//
|
|
float a = real_nclip(x0, y0, y1, y1, mX,mY);
|
|
if (a == 0) return true; // p0 exactly on edge
|
|
if (a > 0)
|
|
{
|
|
// all further winding checks should be greater of equal to zero for the point to lie inside the polygon
|
|
//
|
|
if (real_nclip(y1,y1,y2,y2,mX,mY)<0) return false;
|
|
if (real_nclip(y2,y2,y0,y0,mX,mY)<0) return false;
|
|
}
|
|
else
|
|
{
|
|
// all further winding checks should be less than zero for the point to lie inside the polygon
|
|
//
|
|
if (real_nclip(y1,y1,y2,y2,mX,mY)>0) return false;
|
|
if (real_nclip(y2,y2,y0,y0,mX,mY)>0) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
** RB_IterateStagesGeneric
|
|
*/
|
|
static void RB_IterateStagesGeneric( shaderCommands_t *input )
|
|
{
|
|
/* MODVIEWREM
|
|
int stage;
|
|
|
|
for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ )
|
|
{
|
|
shaderStage_t *pStage = tess.xstages[stage];
|
|
|
|
if ( !pStage )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ComputeColors( pStage );
|
|
ComputeTexCoords( pStage );
|
|
|
|
if ( !setArraysOnce )
|
|
{
|
|
qglEnableClientState( GL_COLOR_ARRAY );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors );
|
|
}
|
|
|
|
//
|
|
// do multitexture
|
|
//
|
|
if ( pStage->bundle[1].image[0] != 0 )
|
|
{
|
|
DrawMultitextured( input, stage );
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( !setArraysOnce )
|
|
{
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] );
|
|
}
|
|
|
|
//
|
|
// set state
|
|
//
|
|
if ( pStage->bundle[0].vertexLightmap && ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer )
|
|
{
|
|
GL_Bind( tr.whiteImage );
|
|
}
|
|
else
|
|
R_BindAnimatedImage( &pStage->bundle[0] );
|
|
|
|
GL_State( pStage->stateBits );
|
|
|
|
//
|
|
// draw
|
|
//
|
|
R_DrawElements( input->numIndexes, input->indexes );
|
|
}
|
|
|
|
// allow skipping out to show just lightmaps during development
|
|
if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
glBindTexture( GL_TEXTURE_2D, input->gluiTextureBind );
|
|
|
|
|
|
|
|
|
|
{// note additional loop I put here for overriding polys to be wireframe - Ste.
|
|
|
|
glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT); // preserves GL_CULL_FACE, GL_CULL_FACE_MODE, GL_POLYGON_MODE
|
|
{
|
|
if (!input->bSurfaceIsG2Tag // don't draw G2 surface tags
|
|
|| AppVars.bShowTagSurfaces // ... unless you really want to
|
|
)
|
|
{
|
|
bool bSurfaceIsUnshadowable = AppVars.bShowUnshadowableSurfaces && (input->numVertexes > (SHADER_MAX_VERTEXES/2));
|
|
bool bSurfaceIsHighlighted = AppVars.bSurfaceHighlight &&
|
|
input->hModel == AppVars.hModelToHighLight &&
|
|
(
|
|
(AppVars.iSurfaceNumToHighlight == iITEMHIGHLIGHT_ALL)
|
|
||
|
|
(AppVars.iSurfaceNumToHighlight == iITEMHIGHLIGHT_ALL_TAGSURFACES && input->bSurfaceIsG2Tag)
|
|
||
|
|
(AppVars.iSurfaceNumToHighlight == input->iSurfaceNum)
|
|
);
|
|
bool bSpecialCaseHighlightSoNoYellowNumberClash = (AppVars.bVertIndexes && AppVars.bVertWeighting && AppVars.iSurfaceNumToHighlight < 0);
|
|
|
|
if (bSurfaceIsHighlighted && !bSpecialCaseHighlightSoNoYellowNumberClash)
|
|
glLineWidth(2);
|
|
else
|
|
glLineWidth(1);
|
|
bool b2PassForWire = AppVars.bWireFrame || (AppVars.bWireFrame && bSurfaceIsHighlighted);
|
|
|
|
if (b2PassForWire)
|
|
{
|
|
if (AppVars.bShowPolysAsDoubleSided && !AppVars.bForceWhite)
|
|
{
|
|
glEnable(GL_CULL_FACE);
|
|
}
|
|
}
|
|
|
|
// for (int iPass=0; iPass<(AppVars.bWireFrame?2:1); iPass++)
|
|
for (int iPass=0; iPass<(b2PassForWire?2:1); iPass++)
|
|
{
|
|
if (b2PassForWire)
|
|
{
|
|
if (!iPass)
|
|
{
|
|
glCullFace(GL_BACK);
|
|
|
|
if (bSurfaceIsHighlighted && !bSpecialCaseHighlightSoNoYellowNumberClash)
|
|
glColor3f(0.5,0.5,0.0); // dim yellow
|
|
else
|
|
glColor3f(0.5,0.5,0.5); // dim white
|
|
}
|
|
else
|
|
{
|
|
glCullFace(GL_FRONT);
|
|
if (bSurfaceIsHighlighted && !bSpecialCaseHighlightSoNoYellowNumberClash)
|
|
glColor3f( 1,1,0); // yellow
|
|
else
|
|
glColor3f( 1,1,1); // white
|
|
}
|
|
}
|
|
|
|
R_DrawElements( input->numIndexes, input->indexes ); // the standard surface draw code
|
|
}
|
|
|
|
if (b2PassForWire)
|
|
{
|
|
if (AppVars.bShowPolysAsDoubleSided && !AppVars.bForceWhite)
|
|
{
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
}
|
|
|
|
|
|
glLineWidth(1);
|
|
|
|
// draw surface-highlights?... (2 types, so do 2 passes to keep code down)
|
|
//
|
|
if (!AppVars.bWireFrame && bSurfaceIsHighlighted && !bSpecialCaseHighlightSoNoYellowNumberClash)
|
|
{
|
|
// do these 3 in case we're not already in wireframe...
|
|
//
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glLineWidth(2);
|
|
glColor3f(1,1,0); // yellow
|
|
|
|
if (AppVars.iSurfaceNumToHighlight > 0)
|
|
{
|
|
SurfaceOnOff_t eOnOff = Model_GLMSurface_GetStatus( input->hModel, input->iSurfaceNum );
|
|
if (eOnOff != SURF_ON)
|
|
{
|
|
// then we must be ON only because of highlighting an OFF surface in the treeview,
|
|
// so show it dimmer (particualrly if they've just turned it off and wonder why they
|
|
// can still see it...
|
|
//
|
|
glColor3f(0.5,0.5,0); // dim yellow
|
|
}
|
|
}
|
|
|
|
for (int iVert = 0; iVert<input->numIndexes; iVert+=3)
|
|
{
|
|
glBegin(GL_LINE_LOOP);
|
|
{
|
|
glVertex3fv( input->xyz[input->indexes[iVert+0]] );
|
|
glVertex3fv( input->xyz[input->indexes[iVert+1]] );
|
|
glVertex3fv( input->xyz[input->indexes[iVert+2]] );
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
glLineWidth(1);
|
|
}
|
|
|
|
// draw unshadowable surfaces...
|
|
//
|
|
if (bSurfaceIsUnshadowable)
|
|
{
|
|
// do these 3 in case we're not already in wireframe...
|
|
//
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glLineStipple( 8, 0xAAAA);
|
|
glEnable(GL_LINE_STIPPLE);
|
|
glColor3f(1,0,0); // red
|
|
|
|
if (bSurfaceIsHighlighted)
|
|
{
|
|
glLineWidth(4); // ... or it won't stand out much over the existing yellow highlights
|
|
}
|
|
|
|
for (int iVert = 0; iVert<input->numIndexes; iVert+=3)
|
|
{
|
|
glBegin(GL_LINE_LOOP);
|
|
{
|
|
glVertex3fv( input->xyz[input->indexes[iVert+0]] );
|
|
glVertex3fv( input->xyz[input->indexes[iVert+1]] );
|
|
glVertex3fv( input->xyz[input->indexes[iVert+2]] );
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
glDisable(GL_LINE_STIPPLE);
|
|
|
|
if (bSurfaceIsHighlighted)
|
|
{
|
|
glLineWidth(1);
|
|
}
|
|
}
|
|
|
|
if (AppVars.bCrackHighlight && bSurfaceIsHighlighted)
|
|
{
|
|
extern ModelContainer_t* gpContainerBeingRendered;
|
|
if (gpContainerBeingRendered) // arrrghhh!!!!
|
|
{
|
|
int iCappedLOD = Model_EnsureGenerated_VertEdgeInfo(gpContainerBeingRendered, AppVars.iLOD);
|
|
|
|
SurfaceEdgeVertBools_t &SurfaceEdgeVertBools = gpContainerBeingRendered->SurfaceEdgeInfoPerLOD[iCappedLOD];
|
|
SurfaceEdgeVertBools_t::iterator it = SurfaceEdgeVertBools.find(input->iSurfaceNum);
|
|
if (it != SurfaceEdgeVertBools.end())
|
|
{
|
|
VertIsEdge_t &vrVertIsEdge = (*it).second;
|
|
|
|
// highlight the edge verts...
|
|
//
|
|
for (int iIndex=0; iIndex<input->numIndexes; iIndex++)
|
|
{
|
|
int iVert = input->indexes[iIndex];
|
|
if (vrVertIsEdge[iVert])
|
|
{
|
|
Text_Display("*",input->xyz[iVert],0,255,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (1)
|
|
{
|
|
extern int g_iScreenWidth;
|
|
extern int g_iScreenHeight;
|
|
extern int g_iViewAreaMouseX;
|
|
extern int g_iViewAreaMouseY;
|
|
|
|
// Header: Declared in Glu.h.
|
|
// Library: Use Glu32.lib.
|
|
|
|
GLdouble modelMatrix[16];
|
|
GLdouble projMatrix[16];
|
|
GLint viewPort[4];
|
|
int iOpenGLMouseX = g_iViewAreaMouseX;
|
|
int iOpenGLMouseY = (g_iScreenHeight - g_iViewAreaMouseY)-1;
|
|
|
|
glGetDoublev ( GL_MODELVIEW_MATRIX, modelMatrix);
|
|
glGetDoublev ( GL_PROJECTION_MATRIX, projMatrix);
|
|
glGetIntegerv ( GL_VIEWPORT, viewPort);
|
|
|
|
for (int iVert = 0; iVert<input->numIndexes; iVert+=3)
|
|
{
|
|
GLdouble dX[3],dY[3],dZ[3];
|
|
|
|
int iSuccess = 0;
|
|
for (int i=0; i<3; i++)
|
|
{
|
|
iSuccess += gluProject( input->xyz[input->indexes[iVert+i]][0], // GLdouble objx,
|
|
input->xyz[input->indexes[iVert+i]][1], // GLdouble objy,
|
|
input->xyz[input->indexes[iVert+i]][2], // GLdouble objz,
|
|
modelMatrix, // const GLdouble modelMatrix[16],
|
|
projMatrix, // const GLdouble projMatrix[16],
|
|
viewPort, // const GLint viewport[4],
|
|
&dX[i],&dY[i],&dZ[i]
|
|
);
|
|
}
|
|
|
|
if (iSuccess == i)
|
|
{
|
|
// got the 3 vert coords as screen coords, now see if the mouse is within this poly
|
|
//
|
|
if (MouseOverTri(dX[0],dX[1],dX[2],dY[0],dY[1],dY[2], iOpenGLMouseX, iOpenGLMouseY))
|
|
{
|
|
AppVars.iSurfaceNumToHighlight = input->iSurfaceNum;
|
|
OutputDebugString(va("Over surface %d\n",input->iSurfaceNum));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
// draw normals?...
|
|
//
|
|
if (AppVars.bVertexNormals)
|
|
{
|
|
// do these 3 in case we're doing normals but not wireframe...
|
|
//
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
for (int iNormal = 0; iNormal<input->numVertexes/*numIndexes*/; iNormal++)
|
|
{
|
|
glColor3f(1,0.5,1); // purple
|
|
glBegin(GL_LINES);
|
|
{
|
|
glVertex3fv( input->xyz[iNormal] );
|
|
glVertex3f ( input->xyz[iNormal][0] + input->normal[iNormal][0],
|
|
input->xyz[iNormal][1] + input->normal[iNormal][1],
|
|
input->xyz[iNormal][2] + input->normal[iNormal][2]
|
|
);
|
|
}
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
// show vertex indexes?...
|
|
//
|
|
if (AppVars.bVertIndexes && bSurfaceIsHighlighted &&
|
|
(
|
|
(AppVars.iSurfaceNumToHighlight != iITEMHIGHLIGHT_ALL || AppVars.bVertWeighting) // or it drops the framerate through the floor!
|
|
&&
|
|
AppVars.iSurfaceNumToHighlight != iITEMHIGHLIGHT_ALL_TAGSURFACES
|
|
)
|
|
)
|
|
{
|
|
for (int iVert = 0; iVert<input->numIndexes; iVert++)
|
|
{
|
|
byte r=255,g=0,b=0; // red
|
|
|
|
int iNumWeights = 0;
|
|
|
|
if (AppVars.bVertWeighting)
|
|
{
|
|
iNumWeights = input->WeightsUsed[input->indexes[iVert]];
|
|
|
|
// if (gpContainerBeingRendered)
|
|
// gpContainerBeingRendered->iRenderedBoneWeightsThisSurface += iNumWeights;
|
|
|
|
GetWeightColour(iNumWeights,r,g,b);
|
|
|
|
AppVars.bAtleast1VertWeightDisplayed = true;
|
|
}
|
|
|
|
if (AppVars.iSurfaceNumToHighlight != iITEMHIGHLIGHT_ALL
|
|
|| iNumWeights>=3
|
|
)
|
|
{
|
|
Text_Display(va(" %d",input->indexes[iVert]),input->xyz[input->indexes[iVert]],r,g,b);
|
|
}
|
|
}
|
|
}
|
|
|
|
// show triangle indexes?...
|
|
//
|
|
if (AppVars.bTriIndexes && bSurfaceIsHighlighted &&
|
|
(
|
|
(AppVars.iSurfaceNumToHighlight != iITEMHIGHLIGHT_ALL) // or it drops the framerate through the floor!
|
|
&&
|
|
AppVars.iSurfaceNumToHighlight != iITEMHIGHLIGHT_ALL_TAGSURFACES
|
|
)
|
|
)
|
|
{
|
|
for (int iTri = 0; iTri<input->numIndexes; iTri+=3) // iTri is effectively like stepval 3 for vert parsing
|
|
{
|
|
byte r=0,g=255,b=255; // magenta
|
|
|
|
vec3_t v3TriCentre;
|
|
|
|
v3TriCentre[0] = (
|
|
input->xyz[input->indexes[iTri+0]][0] +
|
|
input->xyz[input->indexes[iTri+1]][0] +
|
|
input->xyz[input->indexes[iTri+2]][0]
|
|
)/3;
|
|
|
|
v3TriCentre[1] = (
|
|
input->xyz[input->indexes[iTri+0]][1] +
|
|
input->xyz[input->indexes[iTri+1]][1] +
|
|
input->xyz[input->indexes[iTri+2]][1]
|
|
)/3;
|
|
|
|
v3TriCentre[2] = (
|
|
input->xyz[input->indexes[iTri+0]][2] +
|
|
input->xyz[input->indexes[iTri+1]][2] +
|
|
input->xyz[input->indexes[iTri+2]][2]
|
|
)/3;
|
|
|
|
Text_Display(va("T:%d",iTri/3), v3TriCentre ,r,g,b);
|
|
}
|
|
}
|
|
|
|
// show vertexes with omitted bone-weights (threshholding)?...
|
|
//
|
|
if (AppVars.bBoneWeightThreshholdingActive && AppVars.bWireFrame)
|
|
{
|
|
// glDisable(GL_TEXTURE_2D);
|
|
// glDisable(GL_BLEND);
|
|
// glDisable(GL_LIGHTING);
|
|
|
|
// glLineWidth(9);
|
|
{
|
|
// glColor3f(0,1,0); // green
|
|
|
|
// glBegin(GL_POINTS);
|
|
{
|
|
for (int iVert=0; iVert<input->numIndexes; iVert++)
|
|
{
|
|
if (input->WeightsOmitted[input->indexes[iVert]])
|
|
{
|
|
Text_Display("*",input->xyz[input->indexes[iVert]],0,255,0);
|
|
}
|
|
}
|
|
}
|
|
// glEnd();
|
|
}
|
|
// glLineWidth(1);
|
|
}
|
|
}
|
|
|
|
// if this is a G2 tag surface, then work out a matrix from it and store for later use...
|
|
//
|
|
if (input->bSurfaceIsG2Tag)
|
|
{
|
|
// not a clever place to do this, but WTF...
|
|
//
|
|
// Anyway, this is some of Jake's mysterious code to turn a one-triangle tag-surface into a matrix...
|
|
//
|
|
vec3_t axes[3], sides[3];
|
|
float pTri[3][3], d;
|
|
|
|
memcpy(pTri[0],input->xyz[0],sizeof(vec3_t));
|
|
memcpy(pTri[1],input->xyz[1],sizeof(vec3_t));
|
|
memcpy(pTri[2],input->xyz[2],sizeof(vec3_t));
|
|
|
|
// clear out used arrays
|
|
memset( axes, 0, sizeof( axes ) );
|
|
memset( sides, 0, sizeof( sides ) );
|
|
|
|
// work out actual sides of the tag triangle
|
|
for ( int j = 0; j < 3; j++ )
|
|
{
|
|
sides[j][0] = pTri[(j+1)%3][0] - pTri[j][0];
|
|
sides[j][1] = pTri[(j+1)%3][1] - pTri[j][1];
|
|
sides[j][2] = pTri[(j+1)%3][2] - pTri[j][2];
|
|
}
|
|
|
|
// do math trig to work out what the matrix will be from this triangle's translated position
|
|
VectorNormalize2( sides[iG2_TRISIDE_LONGEST], axes[0] );
|
|
VectorNormalize2( sides[iG2_TRISIDE_SHORTEST], axes[1] );
|
|
|
|
// project shortest side so that it is exactly 90 degrees to the longer side
|
|
d = DotProduct( axes[0], axes[1] );
|
|
VectorMA( axes[0], -d, axes[1], axes[0] );
|
|
VectorNormalize2( axes[0], axes[0] );
|
|
|
|
CrossProduct( sides[iG2_TRISIDE_LONGEST], sides[iG2_TRISIDE_SHORTEST], axes[2] );
|
|
VectorNormalize2( axes[2], axes[2] );
|
|
|
|
//float Jmatrix[3][4];
|
|
mdxaBone_t Jmatrix;
|
|
|
|
#define MDX_TAG_ORIGIN 2
|
|
|
|
// set up location in world space of the origin point in out going matrix
|
|
Jmatrix.matrix[0][3] = pTri[MDX_TAG_ORIGIN][0];
|
|
Jmatrix.matrix[1][3] = pTri[MDX_TAG_ORIGIN][1];
|
|
Jmatrix.matrix[2][3] = pTri[MDX_TAG_ORIGIN][2];
|
|
|
|
// copy axis to matrix - do some magic to orient minus Y to positive X and so on so bolt on stuff is oriented correctly
|
|
Jmatrix.matrix[0][0] = axes[1][0];
|
|
Jmatrix.matrix[0][1] = axes[0][0];
|
|
Jmatrix.matrix[0][2] = -axes[2][0];
|
|
|
|
Jmatrix.matrix[1][0] = axes[1][1];
|
|
Jmatrix.matrix[1][1] = axes[0][1];
|
|
Jmatrix.matrix[1][2] = -axes[2][1];
|
|
|
|
Jmatrix.matrix[2][0] = axes[1][2];
|
|
Jmatrix.matrix[2][1] = axes[0][2];
|
|
Jmatrix.matrix[2][2] = -axes[2][2];
|
|
|
|
input->pRefEnt->pXFormedG2TagSurfs [input->iSurfaceNum] = Jmatrix;
|
|
input->pRefEnt->pXFormedG2TagSurfsValid [input->iSurfaceNum] = true;
|
|
|
|
// OutputDebugString(va("Tag surf %d is valid\n",input->iSurfaceNum));
|
|
}
|
|
}
|
|
glPopAttrib();
|
|
glColor3f( 1,1,1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void RB_StageIteratorGeneric( void )
|
|
{
|
|
shaderCommands_t *input;
|
|
|
|
input = &tess;
|
|
/* MODVIEWREM
|
|
RB_DeformTessGeometry();
|
|
|
|
//
|
|
// log this call
|
|
//
|
|
if ( r_logFile->integer )
|
|
{
|
|
// don't just call LogComment, or we will get
|
|
// a call to va() every frame!
|
|
GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) );
|
|
}
|
|
|
|
//
|
|
// set face culling appropriately
|
|
//
|
|
GL_Cull( input->shader->cullType );
|
|
|
|
// set polygon offset if necessary
|
|
if ( input->shader->polygonOffset )
|
|
{
|
|
qglEnable( GL_POLYGON_OFFSET_FILL );
|
|
qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value );
|
|
}
|
|
*/
|
|
|
|
/* MODVIEWREM
|
|
//
|
|
// if there is only a single pass then we can enable color
|
|
// and texture arrays before we compile, otherwise we need
|
|
// to avoid compiling those arrays since they will change
|
|
// during multipass rendering
|
|
//
|
|
if ( tess.numPasses > 1 || input->shader->multitextureEnv )
|
|
{
|
|
setArraysOnce = qfalse;
|
|
qglDisableClientState (GL_COLOR_ARRAY);
|
|
qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
// setArraysOnce = qtrue;
|
|
|
|
//MODVIEWREM glEnableClientState( GL_COLOR_ARRAY);
|
|
// glColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors );
|
|
|
|
if (!AppVars.bWireFrame)
|
|
{
|
|
glEnableClientState( GL_TEXTURE_COORD_ARRAY);
|
|
glTexCoordPointer( 2, GL_FLOAT, 16, input->texCoords );//tess.svars.texcoords[0] );
|
|
}
|
|
}
|
|
|
|
//
|
|
// lock XYZ
|
|
//
|
|
glVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD
|
|
/*MODVIEWREM
|
|
if (qglLockArraysEXT)
|
|
{
|
|
qglLockArraysEXT(0, input->numVertexes);
|
|
GLimp_LogComment( "glLockArraysEXT\n" );
|
|
}
|
|
*/
|
|
|
|
/* MODVUEWREM
|
|
//
|
|
// enable color and texcoord arrays after the lock if necessary
|
|
//
|
|
if ( !setArraysOnce )
|
|
{
|
|
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
glEnableClientState( GL_COLOR_ARRAY );
|
|
}
|
|
*/
|
|
//
|
|
// call shader function
|
|
//
|
|
glEnableClientState( GL_VERTEX_ARRAY );
|
|
RB_IterateStagesGeneric( input );
|
|
|
|
/* MODVIEWREM
|
|
//
|
|
// now do any dynamic lighting needed
|
|
//
|
|
if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE
|
|
&& !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) {
|
|
ProjectDlightTexture();
|
|
}
|
|
|
|
//
|
|
// now do fog
|
|
//
|
|
if ( tess.fogNum && tess.shader->fogPass ) {
|
|
RB_FogPass();
|
|
}
|
|
|
|
//
|
|
// unlock arrays
|
|
//
|
|
if (qglUnlockArraysEXT)
|
|
{
|
|
qglUnlockArraysEXT();
|
|
GLimp_LogComment( "glUnlockArraysEXT\n" );
|
|
}
|
|
|
|
//
|
|
// reset polygon offset
|
|
//
|
|
if ( input->shader->polygonOffset )
|
|
{
|
|
qglDisable( GL_POLYGON_OFFSET_FILL );
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Generates an orientation for an entity and viewParms
|
|
Does NOT produce any GL calls
|
|
Called by both the front end and the back end
|
|
=================
|
|
*/
|
|
/*
|
|
void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms,
|
|
orientationr_t *or ) {
|
|
float glMatrix[16];
|
|
vec3_t delta;
|
|
float axisLength;
|
|
|
|
if ( ent->e.reType != RT_MODEL ) {
|
|
*or = viewParms->world;
|
|
return;
|
|
}
|
|
|
|
VectorCopy( ent->e.origin, or->origin );
|
|
|
|
VectorCopy( ent->e.axis[0], or->axis[0] );
|
|
VectorCopy( ent->e.axis[1], or->axis[1] );
|
|
VectorCopy( ent->e.axis[2], or->axis[2] );
|
|
|
|
glMatrix[0] = or->axis[0][0];
|
|
glMatrix[4] = or->axis[1][0];
|
|
glMatrix[8] = or->axis[2][0];
|
|
glMatrix[12] = or->origin[0];
|
|
|
|
glMatrix[1] = or->axis[0][1];
|
|
glMatrix[5] = or->axis[1][1];
|
|
glMatrix[9] = or->axis[2][1];
|
|
glMatrix[13] = or->origin[1];
|
|
|
|
glMatrix[2] = or->axis[0][2];
|
|
glMatrix[6] = or->axis[1][2];
|
|
glMatrix[10] = or->axis[2][2];
|
|
glMatrix[14] = or->origin[2];
|
|
|
|
glMatrix[3] = 0;
|
|
glMatrix[7] = 0;
|
|
glMatrix[11] = 0;
|
|
glMatrix[15] = 1;
|
|
|
|
myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix );
|
|
|
|
// calculate the viewer origin in the model's space
|
|
// needed for fog, specular, and environment mapping
|
|
VectorSubtract( viewParms->or.origin, or->origin, delta );
|
|
|
|
// compensate for scale in the axes if necessary
|
|
if ( ent->e.nonNormalizedAxes ) {
|
|
axisLength = VectorLength( ent->e.axis[0] );
|
|
if ( !axisLength ) {
|
|
axisLength = 0;
|
|
} else {
|
|
axisLength = 1.0 / axisLength;
|
|
}
|
|
} else {
|
|
axisLength = 1.0;
|
|
}
|
|
|
|
or->viewOrigin[0] = DotProduct( delta, or->axis[0] ) * axisLength;
|
|
or->viewOrigin[1] = DotProduct( delta, or->axis[1] ) * axisLength;
|
|
or->viewOrigin[2] = DotProduct( delta, or->axis[2] ) * axisLength;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
|
|
void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs )
|
|
{
|
|
shader_t /**shader,*/ *oldShader;
|
|
int /*fogNum,*/ oldFogNum;
|
|
int entityNum, oldEntityNum;
|
|
int /*dlighted,*/ oldDlighted;
|
|
qboolean depthRange, oldDepthRange;
|
|
int i;
|
|
drawSurf_t *drawSurf;
|
|
int oldSort;
|
|
// float originalTime;
|
|
|
|
/*MODVIEWREM
|
|
// save original time for entity shader offsets
|
|
originalTime = backEnd.refdef.floatTime;
|
|
|
|
// clear the z buffer, set the modelview, etc
|
|
RB_BeginDrawingView ();
|
|
*/
|
|
// draw everything
|
|
oldEntityNum = -1;
|
|
//MODVIEWREM backEnd.currentEntity = &tr.worldEntity;
|
|
oldShader = NULL;
|
|
oldFogNum = -1;
|
|
oldDepthRange = qfalse;
|
|
oldDlighted = qfalse;
|
|
oldSort = -1;
|
|
depthRange = qfalse;
|
|
|
|
//MODVIEWREM backEnd.pc.c_surfaces += numDrawSurfs;
|
|
|
|
for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++)
|
|
{
|
|
/*MODVIEWREM
|
|
if ( drawSurf->sort == oldSort ) {
|
|
// fast path, same as previous sort
|
|
rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
|
|
continue;
|
|
}
|
|
oldSort = drawSurf->sort;
|
|
*/
|
|
GLuint gluiTextureBind = 0;
|
|
R_DecomposeSort( drawSurf->sort, &entityNum, &gluiTextureBind);//MODVIEWREM , &shader, &fogNum, &dlighted );
|
|
/*
|
|
//
|
|
// change the tess parameters if needed
|
|
// a "entityMergable" shader is a shader that can have surfaces from seperate
|
|
// entities merged into a single batch, like smoke and blood puff sprites
|
|
if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted
|
|
|| ( entityNum != oldEntityNum && !shader->entityMergable ) ) {
|
|
if (oldShader != NULL) {
|
|
RB_EndSurface();
|
|
}
|
|
RB_BeginSurface( shader, fogNum );
|
|
oldShader = shader;
|
|
oldFogNum = fogNum;
|
|
oldDlighted = dlighted;
|
|
}
|
|
|
|
//
|
|
// change the modelview matrix if needed
|
|
//
|
|
if ( entityNum != oldEntityNum ) {
|
|
depthRange = qfalse;
|
|
|
|
if ( entityNum != ENTITYNUM_WORLD ) {
|
|
backEnd.currentEntity = &backEnd.refdef.entities[entityNum];
|
|
backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime;
|
|
|
|
// set up the transformation matrix
|
|
*/
|
|
////////////////////////////////R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or );
|
|
/*
|
|
// set up the dynamic lighting if needed
|
|
if ( backEnd.currentEntity->needDlights ) {
|
|
R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or );
|
|
}
|
|
|
|
if ( backEnd.currentEntity->e.renderfx & RF_DEPTHHACK ) {
|
|
// hack the depth range to prevent view model from poking into walls
|
|
depthRange = qtrue;
|
|
}
|
|
} else {
|
|
backEnd.currentEntity = &tr.worldEntity;
|
|
backEnd.refdef.floatTime = originalTime;
|
|
backEnd.or = backEnd.viewParms.world;
|
|
R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or );
|
|
}
|
|
*/
|
|
//////////////////////////////////////glLoadMatrixf( backEnd.or.modelMatrix );
|
|
/*
|
|
//
|
|
// change depthrange if needed
|
|
//
|
|
if ( oldDepthRange != depthRange ) {
|
|
if ( depthRange ) {
|
|
qglDepthRange (0, 0.3);
|
|
} else {
|
|
qglDepthRange (0, 1);
|
|
}
|
|
oldDepthRange = depthRange;
|
|
}
|
|
|
|
oldEntityNum = entityNum;
|
|
}
|
|
*/
|
|
|
|
RB_BeginSurface( 0,0, gluiTextureBind);//shader, fogNum );
|
|
|
|
tess.hModel = tr.refdef.entities[entityNum].e.hModel;
|
|
tess.pRefEnt=&tr.refdef.entities[entityNum].e;
|
|
|
|
// add the triangles for this surface
|
|
rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
|
|
|
|
|
|
RB_EndSurface();
|
|
|
|
// stats...
|
|
//
|
|
if (!tess.bSurfaceIsG2Tag || AppVars.bShowTagSurfaces)
|
|
{
|
|
*tr.refdef.entities[entityNum].e.piRenderedTris += giSurfaceTrisDrawn;
|
|
*tr.refdef.entities[entityNum].e.piRenderedVerts+= giSurfaceVertsDrawn;
|
|
*tr.refdef.entities[entityNum].e.piRenderedSurfs+= 1; // NOT ++!
|
|
*tr.refdef.entities[entityNum].e.piRenderedBoneWeights += giRenderedBoneWeights;
|
|
*tr.refdef.entities[entityNum].e.piOmittedBoneWeights += giOmittedBoneWeights;
|
|
}
|
|
}
|
|
/*MODVIEWREM
|
|
// draw the contents of the last shader batch
|
|
if (oldShader != NULL) {
|
|
RB_EndSurface();
|
|
}
|
|
|
|
// go back to the world modelview matrix
|
|
qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
|
|
if ( depthRange ) {
|
|
qglDepthRange (0, 1);
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
// entry point from ModView draw function to setup all surfaces ready for actual render call later...
|
|
//
|
|
void RE_RenderDrawSurfs( void )
|
|
{
|
|
RB_RenderDrawSurfList( tr.refdef.drawSurfs, tr.refdef.numDrawSurfs );
|
|
}
|
|
|
|
|
|
////////////// eof //////////
|
|
|