6e3f69f504
fte particle scripts are disabled (classic works). I'll fix these in the new year. Redid framestate stuff again. Slightly better now, but this is the bulk of the changes here. Reworked the renderqueue to provide batches of items instead of individual items. This cleans up the particle rendering code significantly, and is a step towards multiple concurrent particle systems. fte's scripted particles are broken as I'm trying to find a way to rework them to batch types together, rather than having to restart each batch after each particle when you have two particles in a trail. I'll fix it some time. Reworked some alias model code regarding skeletal models. Added some conceptual skeletal bone control builtins available to csqc. Currently it can query the bone names and save off animation states, but can't animate - its just not complete. Added more info to glsl custom shaders. Updated surface sorting on halflife maps to properly cope with alphaed entities, rather than just texture-based blends (q2-style). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3095 fc73d0e0-1445-4013-8a0c-d673dee63da5
2474 lines
58 KiB
C
2474 lines
58 KiB
C
#include "quakedef.h"
|
|
#include "glquake.h"
|
|
#include "shader.h"
|
|
|
|
#ifdef RGLQUAKE
|
|
|
|
#define MAX_TEXTURE_UNITS 8
|
|
|
|
typedef struct {
|
|
GLenum currenttextures[MAX_TEXTURE_UNITS];
|
|
GLenum texenvmode[MAX_TEXTURE_UNITS];
|
|
|
|
int currenttmu;
|
|
|
|
qboolean in2d;
|
|
} gl_state_t;
|
|
gl_state_t gl_state;
|
|
|
|
void GL_SetShaderState2D(qboolean is2d)
|
|
{
|
|
gl_state.in2d = is2d;
|
|
}
|
|
|
|
extern int *lightmap_textures;
|
|
extern int *deluxmap_textures;
|
|
|
|
void GL_SelectTexture (GLenum target)
|
|
{
|
|
gl_state.currenttmu = target - mtexid0;
|
|
if (qglClientActiveTextureARB)
|
|
{
|
|
qglClientActiveTextureARB(target);
|
|
qglActiveTextureARB(target);
|
|
}
|
|
else
|
|
qglSelectTextureSGIS(target);
|
|
}
|
|
|
|
void GL_CheckTMUIs0(void)
|
|
{
|
|
if (gl_state.currenttmu != 0)
|
|
{
|
|
Con_Printf("TMU is not 0\n");
|
|
GL_SelectTexture(mtexid0);
|
|
}
|
|
}
|
|
|
|
void GL_MBind( GLenum target, int texnum )
|
|
{
|
|
GL_SelectTexture( target );
|
|
|
|
if ( gl_state.currenttextures[gl_state.currenttmu] == texnum )
|
|
return;
|
|
|
|
gl_state.currenttextures[gl_state.currenttmu] = texnum;
|
|
bindTexFunc (GL_TEXTURE_2D, texnum);
|
|
}
|
|
|
|
void GL_Bind (int texnum)
|
|
{
|
|
if (gl_state.currenttextures[gl_state.currenttmu] == texnum)
|
|
return;
|
|
|
|
gl_state.currenttextures[gl_state.currenttmu] = texnum;
|
|
|
|
bindTexFunc (GL_TEXTURE_2D, texnum);
|
|
}
|
|
void GL_BindType (int type, int texnum)
|
|
{
|
|
if (gl_state.currenttextures[gl_state.currenttmu] == texnum)
|
|
return;
|
|
|
|
gl_state.currenttextures[gl_state.currenttmu] = texnum;
|
|
bindTexFunc (type, texnum);
|
|
}
|
|
|
|
void GL_TexEnv( GLenum mode )
|
|
{
|
|
if ( mode != gl_state.texenvmode[gl_state.currenttmu] )
|
|
{
|
|
qglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode );
|
|
gl_state.texenvmode[gl_state.currenttmu] = mode;
|
|
}
|
|
}
|
|
|
|
//vid restarted.
|
|
void GL_FlushBackEnd(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_TEXTURE_UNITS; i++)
|
|
{
|
|
gl_state.currenttextures[i] = -1;
|
|
gl_state.texenvmode[i] = -1;
|
|
}
|
|
}
|
|
|
|
typedef vec3_t mat3_t[3];
|
|
|
|
|
|
|
|
|
|
#ifndef Q3SHADERS
|
|
|
|
qboolean varrayactive;
|
|
void R_IBrokeTheArrays(void)
|
|
{
|
|
}
|
|
|
|
void R_BackendInit(void)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
/*
|
|
Copyright (C) 2002-2003 Victor Luchits
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
#define MAX_ARRAY_VERTS 8192
|
|
#define MAX_ARRAY_INDEXES 8192
|
|
#define MAX_ARRAY_NEIGHBORS 8192
|
|
#define MAX_ARRAY_TRIANGLES (8192/3)
|
|
#define M_TWO_PI (M_PI*2)
|
|
|
|
cvar_t r_detailtextures = SCVAR("r_detailtextures", "1");
|
|
cvar_t r_showtris = SCVAR("r_showtris", "1");
|
|
cvar_t r_shownormals = SCVAR("r_shownormals", "1");
|
|
|
|
mat3_t axisDefault={{1, 0, 0},
|
|
{0, 1, 0},
|
|
{0, 0, 1}};
|
|
|
|
void Matrix3_Transpose (mat3_t in, mat3_t out)
|
|
{
|
|
out[0][0] = in[0][0];
|
|
out[1][1] = in[1][1];
|
|
out[2][2] = in[2][2];
|
|
|
|
out[0][1] = in[1][0];
|
|
out[0][2] = in[2][0];
|
|
out[1][0] = in[0][1];
|
|
out[1][2] = in[2][1];
|
|
out[2][0] = in[0][2];
|
|
out[2][1] = in[1][2];
|
|
}
|
|
void Matrix3_Multiply_Vec3 (mat3_t a, vec3_t b, vec3_t product)
|
|
{
|
|
product[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];
|
|
product[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];
|
|
product[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];
|
|
}
|
|
|
|
int Matrix3_Compare(mat3_t in, mat3_t out)
|
|
{
|
|
return memcmp(in, out, sizeof(mat3_t));
|
|
}
|
|
extern model_t *currentmodel;
|
|
|
|
#define clamp(v,min,max) (v) = (((v)<(min))?(min):(((v)>(max))?(max):(v)))
|
|
|
|
extern qbyte FloatToByte( float x );
|
|
|
|
|
|
#define FTABLE_SIZE 1024
|
|
#define FTABLE_CLAMP(x) (((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))
|
|
#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))
|
|
|
|
static float r_sintable[FTABLE_SIZE];
|
|
static float r_triangletable[FTABLE_SIZE];
|
|
static float r_squaretable[FTABLE_SIZE];
|
|
static float r_sawtoothtable[FTABLE_SIZE];
|
|
static float r_inversesawtoothtable[FTABLE_SIZE];
|
|
|
|
index_t *indexesArray;
|
|
int *neighborsArray;
|
|
vec3_t *trNormalsArray;
|
|
|
|
vec2_t *coordsArray;
|
|
vec2_t *lightmapCoordsArray;
|
|
|
|
vec3_t vertexArray[MAX_ARRAY_VERTS*2];
|
|
vec3_t normalsArray[MAX_ARRAY_VERTS];
|
|
|
|
vec3_t tempVertexArray[MAX_ARRAY_VERTS];
|
|
vec3_t tempNormalsArray[MAX_ARRAY_VERTS];
|
|
index_t tempIndexesArray[MAX_ARRAY_INDEXES];
|
|
|
|
index_t inIndexesArray[MAX_ARRAY_INDEXES];
|
|
int inNeighborsArray[MAX_ARRAY_NEIGHBORS];
|
|
vec3_t inTrNormalsArray[MAX_ARRAY_TRIANGLES];
|
|
vec2_t inCoordsArray[MAX_ARRAY_VERTS];
|
|
vec2_t inLightmapCoordsArray[MAX_ARRAY_VERTS];
|
|
byte_vec4_t inColorsArray[MAX_ARRAY_VERTS];
|
|
|
|
static vec2_t tUnitCoordsArray[MAX_TEXTURE_UNITS][MAX_ARRAY_VERTS];
|
|
static byte_vec4_t colorArray[MAX_ARRAY_VERTS];
|
|
|
|
int numVerts, numIndexes, numColors;
|
|
|
|
qboolean r_arrays_locked;
|
|
qboolean r_blocked;
|
|
|
|
int r_features;
|
|
|
|
static int r_lmtex;
|
|
|
|
static int r_texNums[SHADER_PASS_MAX];
|
|
static int r_numUnits;
|
|
|
|
index_t *currentIndex;
|
|
int *currentTrNeighbor;
|
|
float *currentTrNormal;
|
|
float *currentVertex;
|
|
float *currentNormal;
|
|
float *currentCoords;
|
|
float *currentLightmapCoords;
|
|
qbyte *currentColor;
|
|
|
|
static int r_identityLighting;
|
|
static float r_localShaderTime;
|
|
|
|
unsigned int r_numverts;
|
|
unsigned int r_numtris;
|
|
unsigned int r_numflushes;
|
|
int r_backendStart;
|
|
|
|
int r_dlighttexture;
|
|
|
|
extern qbyte *host_basepal;
|
|
extern qboolean gammaworks;
|
|
extern qbyte gammatable[256];
|
|
|
|
void R_FetchTopColour(int *retred, int *retgreen, int *retblue)
|
|
{
|
|
int i;
|
|
|
|
if (currententity->scoreboard)
|
|
{
|
|
i = currententity->scoreboard->ttopcolor;
|
|
}
|
|
else
|
|
i = TOP_RANGE>>4;
|
|
if (i > 8)
|
|
{
|
|
i<<=4;
|
|
}
|
|
else
|
|
{
|
|
i<<=4;
|
|
i+=15;
|
|
}
|
|
i*=3;
|
|
*retred = host_basepal[i+0];
|
|
*retgreen = host_basepal[i+1];
|
|
*retblue = host_basepal[i+2];
|
|
if (!gammaworks)
|
|
{
|
|
*retred = gammatable[*retred];
|
|
*retgreen = gammatable[*retgreen];
|
|
*retblue = gammatable[*retblue];
|
|
}
|
|
}
|
|
void R_FetchBottomColour(int *retred, int *retgreen, int *retblue)
|
|
{
|
|
int i;
|
|
|
|
if (currententity->scoreboard)
|
|
{
|
|
i = currententity->scoreboard->tbottomcolor;
|
|
}
|
|
else
|
|
i = BOTTOM_RANGE>>4;
|
|
if (i > 8)
|
|
{
|
|
i<<=4;
|
|
}
|
|
else
|
|
{
|
|
i<<=4;
|
|
i+=15;
|
|
}
|
|
i*=3;
|
|
*retred = host_basepal[i+0];
|
|
*retgreen = host_basepal[i+1];
|
|
*retblue = host_basepal[i+2];
|
|
if (!gammaworks)
|
|
{
|
|
*retred = gammatable[*retred];
|
|
*retgreen = gammatable[*retgreen];
|
|
*retblue = gammatable[*retblue];
|
|
}
|
|
}
|
|
|
|
void R_InitDynamicLightTexture (void)
|
|
{
|
|
int x, y;
|
|
int dx2, dy, d;
|
|
qbyte data[64*64*4];
|
|
|
|
//
|
|
// dynamic light texture
|
|
//
|
|
|
|
for (x = 0; x < 64; x++)
|
|
{
|
|
dx2 = x - 32;
|
|
dx2 = dx2 * dx2 + 8;
|
|
|
|
for (y = 0; y < 64; y++)
|
|
{
|
|
dy = y - 32;
|
|
d = (int)(65536.0f * ((1.0f / (dx2 + dy * dy + 32.0f)) - 0.0005) + 0.5f);
|
|
if ( d < 50 ) d = 0; else if ( d > 255 ) d = 255;
|
|
|
|
data[(y*64 + x) * 4 + 0] = d;
|
|
data[(y*64 + x) * 4 + 1] = d;
|
|
data[(y*64 + x) * 4 + 2] = d;
|
|
data[(y*64 + x) * 4 + 3] = 255;
|
|
}
|
|
}
|
|
|
|
r_dlighttexture = GL_LoadTexture32("", 64, 64, (unsigned int*)data, true, false);
|
|
|
|
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
}
|
|
|
|
void R_ResetTexState (void)
|
|
{
|
|
coordsArray = inCoordsArray;
|
|
lightmapCoordsArray = inLightmapCoordsArray;
|
|
|
|
currentCoords = coordsArray[0];
|
|
currentLightmapCoords = lightmapCoordsArray[0];
|
|
|
|
numColors = 0;
|
|
currentColor = inColorsArray[0];
|
|
}
|
|
|
|
|
|
void R_PushIndexes ( index_t *indexes, int *neighbors, vec3_t *trnormals, int numindexes, int features )
|
|
{
|
|
int i;
|
|
int numTris;
|
|
|
|
// this is a fast path for non-batched geometry, use carefully
|
|
// used on pics, sprites, .dpm, .md3 and .md2 models
|
|
if ( features & MF_NONBATCHED ) {
|
|
if ( numindexes > MAX_ARRAY_INDEXES ) {
|
|
numindexes = MAX_ARRAY_INDEXES;
|
|
}
|
|
|
|
// simply change indexesArray to point at indexes
|
|
numIndexes = numindexes;
|
|
indexesArray = indexes;
|
|
currentIndex = indexesArray + numIndexes;
|
|
|
|
if ( neighbors ) {
|
|
neighborsArray = neighbors;
|
|
currentTrNeighbor = neighborsArray + numIndexes;
|
|
}
|
|
|
|
if ( trnormals && (features & MF_TRNORMALS) ) {
|
|
numTris = numIndexes / 3;
|
|
|
|
trNormalsArray = trnormals;
|
|
currentTrNormal = trNormalsArray[0] + numTris;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// clamp
|
|
if ( numIndexes + numindexes > MAX_ARRAY_INDEXES ) {
|
|
numindexes = MAX_ARRAY_INDEXES - numIndexes;
|
|
}
|
|
|
|
numTris = numindexes / 3;
|
|
numIndexes += numindexes;
|
|
|
|
// the following code assumes that R_PushIndexes is fed with triangles...
|
|
for ( i=0; i<numTris; i++, indexes += 3, currentIndex += 3 )
|
|
{
|
|
currentIndex[0] = numVerts + indexes[0];
|
|
currentIndex[1] = numVerts + indexes[1];
|
|
currentIndex[2] = numVerts + indexes[2];
|
|
|
|
if ( neighbors ) {
|
|
currentTrNeighbor[0] = numTris + neighbors[0];
|
|
currentTrNeighbor[1] = numTris + neighbors[1];
|
|
currentTrNeighbor[2] = numTris + neighbors[2];
|
|
|
|
neighbors += 3;
|
|
currentTrNeighbor += 3;
|
|
}
|
|
|
|
if ( trnormals && (features & MF_TRNORMALS) ) {
|
|
VectorCopy ( trnormals[i], currentTrNormal );
|
|
currentTrNormal += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void R_PushMesh ( mesh_t *mesh, int features )
|
|
{
|
|
int numverts;
|
|
|
|
if ( !mesh->indexes || !mesh->xyz_array ) {
|
|
return;
|
|
}
|
|
|
|
r_features = features;
|
|
|
|
R_PushIndexes ( mesh->indexes, mesh->trneighbors, mesh->trnormals, mesh->numindexes, features );
|
|
|
|
numverts = mesh->numvertexes;
|
|
if ( numVerts + numverts > MAX_ARRAY_VERTS ) {
|
|
numverts = MAX_ARRAY_VERTS - numVerts;
|
|
}
|
|
|
|
memcpy ( currentVertex, mesh->xyz_array, numverts * sizeof(vec3_t) );
|
|
currentVertex += numverts * 3;
|
|
|
|
if ( mesh->normals_array && (features & MF_NORMALS) ) {
|
|
memcpy ( currentNormal, mesh->normals_array, numverts * sizeof(vec3_t) );
|
|
currentNormal += numverts * 3;
|
|
}
|
|
|
|
if ( mesh->st_array && (features & MF_STCOORDS) ) {
|
|
if ( features & MF_NONBATCHED ) {
|
|
coordsArray = mesh->st_array;
|
|
currentCoords = coordsArray[0];
|
|
} else {
|
|
memcpy ( currentCoords, mesh->st_array, numverts * sizeof(vec2_t) );
|
|
}
|
|
|
|
currentCoords += numverts * 2;
|
|
}
|
|
|
|
if ( mesh->lmst_array && (features & MF_LMCOORDS) ) {
|
|
if ( features & MF_NONBATCHED ) {
|
|
lightmapCoordsArray = mesh->lmst_array;
|
|
currentLightmapCoords = lightmapCoordsArray[0];
|
|
} else {
|
|
memcpy ( currentLightmapCoords, mesh->lmst_array, numverts * sizeof(vec2_t) );
|
|
}
|
|
|
|
currentLightmapCoords += numverts * 2;
|
|
}
|
|
|
|
if ( mesh->colors_array && (features & MF_COLORS) ) {
|
|
memcpy ( currentColor, mesh->colors_array, numverts * sizeof(byte_vec4_t) );
|
|
currentColor += numverts * 4;
|
|
}
|
|
|
|
numVerts += numverts;
|
|
r_numverts += numverts;
|
|
}
|
|
|
|
|
|
qboolean R_MeshWillExceed(mesh_t *mesh)
|
|
{
|
|
if (numVerts + mesh->numvertexes > MAX_ARRAY_VERTS)
|
|
return true;
|
|
if (numIndexes + mesh->numindexes > MAX_ARRAY_INDEXES)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
extern index_t r_quad_indexes[6];// = { 0, 1, 2, 0, 2, 3 };
|
|
|
|
void R_FinishMeshBuffer ( meshbuffer_t *mb );
|
|
|
|
static float frand(void)
|
|
{
|
|
return (rand()&32767)* (1.0/32767);
|
|
}
|
|
|
|
//static float crand(void)
|
|
//{
|
|
// return (rand()&32767)* (2.0/32767) - 1;
|
|
//}
|
|
|
|
/*
|
|
==============
|
|
R_BackendInit
|
|
==============
|
|
*/
|
|
void R_IBrokeTheArrays(void);
|
|
void R_BackendInit (void)
|
|
{
|
|
int i;
|
|
double t;
|
|
|
|
numVerts = 0;
|
|
numIndexes = 0;
|
|
numColors = 0;
|
|
|
|
indexesArray = inIndexesArray;
|
|
currentIndex = indexesArray;
|
|
neighborsArray = inNeighborsArray;
|
|
trNormalsArray = inTrNormalsArray;
|
|
coordsArray = inCoordsArray;
|
|
lightmapCoordsArray = inLightmapCoordsArray;
|
|
|
|
currentTrNeighbor = neighborsArray;
|
|
currentTrNormal = trNormalsArray[0];
|
|
|
|
currentVertex = vertexArray[0];
|
|
currentNormal = normalsArray[0];
|
|
|
|
currentCoords = coordsArray[0];
|
|
currentLightmapCoords = lightmapCoordsArray[0];
|
|
|
|
currentColor = inColorsArray[0];
|
|
|
|
r_arrays_locked = false;
|
|
r_blocked = false;
|
|
|
|
R_IBrokeTheArrays();
|
|
|
|
//FIZME: FTE already has some stuff along these lines, surly...
|
|
// if ( !r_ignorehwgamma->value )
|
|
// r_identityLighting = (int)(255.0f / pow(2, max(0, floor(r_overbrightbits->value))));
|
|
// else
|
|
r_identityLighting = 255;
|
|
|
|
for ( i = 0; i < FTABLE_SIZE; i++ ) {
|
|
t = (double)i / (double)FTABLE_SIZE;
|
|
|
|
r_sintable[i] = sin ( t * M_TWO_PI );
|
|
|
|
if (t < 0.25)
|
|
r_triangletable[i] = t * 4.0;
|
|
else if (t < 0.75)
|
|
r_triangletable[i] = 2 - 4.0 * t;
|
|
else
|
|
r_triangletable[i] = (t - 0.75) * 4.0 - 1.0;
|
|
|
|
if (t < 0.5)
|
|
r_squaretable[i] = 1.0f;
|
|
else
|
|
r_squaretable[i] = -1.0f;
|
|
|
|
r_sawtoothtable[i] = t;
|
|
r_inversesawtoothtable[i] = 1.0 - t;
|
|
}
|
|
|
|
R_InitDynamicLightTexture();
|
|
}
|
|
|
|
qboolean varrayactive;
|
|
void R_IBrokeTheArrays(void)
|
|
{
|
|
varrayactive = true;
|
|
qglVertexPointer( 3, GL_FLOAT, 0, vertexArray );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray );
|
|
|
|
qglEnableClientState( GL_VERTEX_ARRAY );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BackendShutdown
|
|
==============
|
|
*/
|
|
void R_BackendShutdown (void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_FastSin
|
|
==============
|
|
*/
|
|
float R_FastSin ( float t )
|
|
{
|
|
return r_sintable[FTABLE_CLAMP(t)];
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_TableForFunc
|
|
==============
|
|
*/
|
|
static float *R_TableForFunc ( unsigned int func )
|
|
{
|
|
switch (func)
|
|
{
|
|
case SHADER_FUNC_SIN:
|
|
return r_sintable;
|
|
|
|
case SHADER_FUNC_TRIANGLE:
|
|
return r_triangletable;
|
|
|
|
case SHADER_FUNC_SQUARE:
|
|
return r_squaretable;
|
|
|
|
case SHADER_FUNC_SAWTOOTH:
|
|
return r_sawtoothtable;
|
|
|
|
case SHADER_FUNC_INVERSESAWTOOTH:
|
|
return r_inversesawtoothtable;
|
|
}
|
|
|
|
// assume noise
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BackendStartFrame
|
|
==============
|
|
*/
|
|
void R_BackendStartFrame (void)
|
|
{
|
|
r_numverts = 0;
|
|
r_numtris = 0;
|
|
r_numflushes = 0;
|
|
// r_backendStart = Sys_Milliseconds();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BackendEndFrame
|
|
==============
|
|
*/
|
|
void R_BackendEndFrame (void)
|
|
{
|
|
if (r_speeds.value)
|
|
{
|
|
Con_Printf( "%4i wpoly %4i leafs %4i verts %4i tris %4i flushes\n",
|
|
c_brush_polys,
|
|
0/*c_world_leafs*/,
|
|
r_numverts,
|
|
r_numtris,
|
|
r_numflushes );
|
|
}
|
|
// time_backend = Sys_Milliseconds() - r_backendStart;
|
|
// r_backendStart = 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_LockArrays
|
|
==============
|
|
*/
|
|
void R_LockArrays ( int numverts )
|
|
{
|
|
if ( r_arrays_locked )
|
|
return;
|
|
|
|
if ( qglLockArraysEXT != 0 ) {
|
|
qglLockArraysEXT( 0, numverts );
|
|
r_arrays_locked = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_UnlockArrays
|
|
==============
|
|
*/
|
|
void R_UnlockArrays (void)
|
|
{
|
|
if ( !r_arrays_locked )
|
|
return;
|
|
|
|
if ( qglUnlockArraysEXT != 0 ) {
|
|
qglUnlockArraysEXT();
|
|
r_arrays_locked = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_DrawTriangleStrips
|
|
|
|
This function looks for and sends tristrips.
|
|
Original code by Stephen C. Taylor (Aftershock 3D rendering engine)
|
|
==============
|
|
*/
|
|
void R_DrawTriangleStrips (index_t *indexes, int numindexes)
|
|
{
|
|
int toggle;
|
|
index_t a, b, c, *index;
|
|
|
|
c = 0;
|
|
index = indexes;
|
|
while ( c < numindexes ) {
|
|
toggle = 1;
|
|
|
|
qglBegin( GL_TRIANGLE_STRIP );
|
|
|
|
qglArrayElement( index[0] );
|
|
qglArrayElement( b = index[1] );
|
|
qglArrayElement( a = index[2] );
|
|
|
|
c += 3;
|
|
index += 3;
|
|
|
|
while ( c < numindexes ) {
|
|
if ( a != index[0] || b != index[1] ) {
|
|
break;
|
|
}
|
|
|
|
if ( toggle ) {
|
|
qglArrayElement( b = index[2] );
|
|
} else {
|
|
qglArrayElement( a = index[2] );
|
|
}
|
|
|
|
c += 3;
|
|
index += 3;
|
|
toggle = !toggle;
|
|
}
|
|
|
|
qglEnd();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_ClearArrays
|
|
==============
|
|
*/
|
|
void R_ClearArrays (void)
|
|
{
|
|
numVerts = 0;
|
|
numIndexes = 0;
|
|
|
|
indexesArray = inIndexesArray;
|
|
currentIndex = indexesArray;
|
|
neighborsArray = inNeighborsArray;
|
|
trNormalsArray = inTrNormalsArray;
|
|
|
|
currentTrNeighbor = neighborsArray;
|
|
currentTrNormal = trNormalsArray[0];
|
|
|
|
currentVertex = vertexArray[0];
|
|
currentNormal = normalsArray[0];
|
|
|
|
R_ResetTexState ();
|
|
|
|
r_blocked = false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_FlushArrays
|
|
==============
|
|
*/
|
|
void R_FlushArrays (void)
|
|
{
|
|
if ( !numVerts || !numIndexes ) {
|
|
return;
|
|
}
|
|
|
|
if ( numColors > 1 ) {
|
|
qglEnableClientState( GL_COLOR_ARRAY );
|
|
} else if ( numColors == 1 ) {
|
|
qglColor4ubv ( colorArray[0] );
|
|
}
|
|
|
|
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
|
|
if ( !r_arrays_locked ) {
|
|
R_DrawTriangleStrips ( indexesArray, numIndexes );
|
|
} else {
|
|
qglDrawElements( GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexesArray );
|
|
}
|
|
|
|
r_numtris += numIndexes / 3;
|
|
|
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
|
|
if ( numColors > 1 ) {
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
}
|
|
|
|
r_numflushes++;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_FlushArraysMtex
|
|
==============
|
|
*/
|
|
void R_FlushArraysMtex (void)
|
|
{
|
|
int i;
|
|
|
|
if ( !numVerts || !numIndexes ) {
|
|
return;
|
|
}
|
|
|
|
if ( numColors > 1 ) {
|
|
qglEnableClientState( GL_COLOR_ARRAY );
|
|
} else if ( numColors == 1 ) {
|
|
qglColor4ubv ( colorArray[0] );
|
|
}
|
|
|
|
GL_MBind( mtexid0, r_texNums[0] );
|
|
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
for ( i = 1; i < r_numUnits; i++ )
|
|
{
|
|
GL_MBind( mtexid0 + i, r_texNums[i] );
|
|
qglEnable ( GL_TEXTURE_2D );
|
|
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
}
|
|
|
|
if ( !r_arrays_locked ) {
|
|
R_DrawTriangleStrips ( indexesArray, numIndexes );
|
|
} else {
|
|
qglDrawElements( GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexesArray );
|
|
}
|
|
|
|
r_numtris += numIndexes / 3;
|
|
|
|
for ( i = r_numUnits - 1; i >= 0; i-- )
|
|
{
|
|
GL_SelectTexture ( mtexid0 + i );
|
|
|
|
if ( i ) {
|
|
qglDisable ( GL_TEXTURE_2D );
|
|
}
|
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
}
|
|
|
|
if ( numColors > 1 ) {
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
}
|
|
|
|
r_numflushes++;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DeformVertices
|
|
================
|
|
*/
|
|
void R_DeformVertices ( meshbuffer_t *mb )
|
|
{
|
|
int i, j, k, pw, ph, p;
|
|
float args[4], deflect;
|
|
float *quad[4], *table;
|
|
shader_t *shader;
|
|
deformv_t *deformv;
|
|
vec3_t tv, rot_centre;
|
|
|
|
shader = mb->shader;
|
|
deformv = &shader->deforms[0];
|
|
|
|
for (i = 0; i < shader->numdeforms; i++, deformv++)
|
|
{
|
|
switch (deformv->type)
|
|
{
|
|
case DEFORMV_NONE:
|
|
break;
|
|
|
|
case DEFORMV_WAVE:
|
|
args[0] = deformv->func.args[0];
|
|
args[1] = deformv->func.args[1];
|
|
args[3] = deformv->func.args[2] + deformv->func.args[3] * r_localShaderTime;
|
|
table = R_TableForFunc ( deformv->func.type );
|
|
|
|
for ( j = 0; j < numVerts; j++ ) {
|
|
deflect = deformv->args[0] * (vertexArray[j][0]+vertexArray[j][1]+vertexArray[j][2]) + args[3];
|
|
deflect = FTABLE_EVALUATE ( table, deflect ) * args[1] + args[0];
|
|
|
|
// Deflect vertex along its normal by wave amount
|
|
VectorMA ( vertexArray[j], deflect, normalsArray[j], vertexArray[j] );
|
|
}
|
|
break;
|
|
|
|
case DEFORMV_NORMAL:
|
|
args[0] = deformv->args[1] * r_localShaderTime;
|
|
|
|
for ( j = 0; j < numVerts; j++ ) {
|
|
args[1] = normalsArray[j][2] * args[0];
|
|
|
|
deflect = deformv->args[0] * R_FastSin ( args[1] );
|
|
normalsArray[j][0] *= deflect;
|
|
deflect = deformv->args[0] * R_FastSin ( args[1] + 0.25 );
|
|
normalsArray[j][1] *= deflect;
|
|
VectorNormalizeFast ( normalsArray[j] );
|
|
}
|
|
break;
|
|
|
|
case DEFORMV_MOVE:
|
|
table = R_TableForFunc ( deformv->func.type );
|
|
deflect = deformv->func.args[2] + r_localShaderTime * deformv->func.args[3];
|
|
deflect = FTABLE_EVALUATE (table, deflect) * deformv->func.args[1] + deformv->func.args[0];
|
|
|
|
for ( j = 0; j < numVerts; j++ )
|
|
VectorMA ( vertexArray[j], deflect, deformv->args, vertexArray[j] );
|
|
break;
|
|
|
|
case DEFORMV_BULGE:
|
|
pw = mb->mesh->patchWidth;
|
|
ph = mb->mesh->patchHeight;
|
|
|
|
args[0] = deformv->args[0] / (float)ph;
|
|
args[1] = deformv->args[1];
|
|
args[2] = r_localShaderTime / (deformv->args[2]*pw);
|
|
|
|
for ( k = 0, p = 0; k < ph; k++ ) {
|
|
deflect = R_FastSin ( (float)k * args[0] + args[2] ) * args[1];
|
|
|
|
for ( j = 0; j < pw; j++, p++ )
|
|
VectorMA ( vertexArray[p], deflect, normalsArray[p], vertexArray[p] );
|
|
}
|
|
break;
|
|
|
|
case DEFORMV_AUTOSPRITE:
|
|
if ( numIndexes < 6 )
|
|
break;
|
|
|
|
for ( k = 0; k < numIndexes; k += 6 )
|
|
{
|
|
mat3_t m0, m1, result;
|
|
|
|
quad[0] = (float *)(vertexArray + indexesArray[k+0]);
|
|
quad[1] = (float *)(vertexArray + indexesArray[k+1]);
|
|
quad[2] = (float *)(vertexArray + indexesArray[k+2]);
|
|
|
|
for ( j = 2; j >= 0; j-- ) {
|
|
quad[3] = (float *)(vertexArray + indexesArray[k+3+j]);
|
|
if ( !VectorCompare (quad[3], quad[0]) &&
|
|
!VectorCompare (quad[3], quad[1]) &&
|
|
!VectorCompare (quad[3], quad[2]) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
VectorSubtract ( quad[0], quad[1], m0[0] );
|
|
VectorSubtract ( quad[2], quad[1], m0[1] );
|
|
CrossProduct ( m0[0], m0[1], m0[2] );
|
|
VectorNormalizeFast ( m0[2] );
|
|
VectorVectors ( m0[2], m0[1], m0[0] );
|
|
|
|
VectorCopy ( (&r_view_matrix[0]), m1[0] );
|
|
VectorCopy ( (&r_view_matrix[4]), m1[1] );
|
|
VectorCopy ( (&r_view_matrix[8]), m1[2] );
|
|
|
|
Matrix3_Multiply ( m1, m0, result );
|
|
|
|
for ( j = 0; j < 3; j++ )
|
|
rot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25 + currententity->origin[j];
|
|
|
|
for ( j = 0; j < 4; j++ ) {
|
|
VectorSubtract ( quad[j], rot_centre, tv );
|
|
Matrix3_Multiply_Vec3 ( result, tv, quad[j] );
|
|
VectorAdd ( rot_centre, quad[j], quad[j] );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DEFORMV_AUTOSPRITE2:
|
|
if ( numIndexes < 6 )
|
|
break;
|
|
|
|
for ( k = 0; k < numIndexes; k += 6 )
|
|
{
|
|
int long_axis, short_axis;
|
|
vec3_t axis;
|
|
float len[3];
|
|
mat3_t m0, m1, m2, result;
|
|
|
|
quad[0] = (float *)(vertexArray + indexesArray[k+0]);
|
|
quad[1] = (float *)(vertexArray + indexesArray[k+1]);
|
|
quad[2] = (float *)(vertexArray + indexesArray[k+2]);
|
|
|
|
for ( j = 2; j >= 0; j-- ) {
|
|
quad[3] = (float *)(vertexArray + indexesArray[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 ( DotProduct (m0[long_axis], m0[short_axis]) ) {
|
|
VectorNormalize2 ( m0[long_axis], axis );
|
|
VectorCopy ( axis, m0[1] );
|
|
|
|
if ( axis[0] || axis[1] ) {
|
|
VectorVectors ( m0[1], m0[2], m0[0] );
|
|
} else {
|
|
VectorVectors ( m0[1], m0[0], m0[2] );
|
|
}
|
|
} else {
|
|
VectorNormalize2 ( m0[long_axis], axis );
|
|
VectorNormalize2 ( m0[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 ( currententity ) {
|
|
VectorAdd ( currententity->origin, rot_centre, tv );
|
|
} else {
|
|
VectorCopy ( rot_centre, tv );
|
|
}
|
|
VectorSubtract ( r_origin, 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] );
|
|
|
|
Matrix3_Transpose ( m1, m2 );
|
|
Matrix3_Multiply ( m2, m0, result );
|
|
|
|
for ( j = 0; j < 4; j++ ) {
|
|
VectorSubtract ( quad[j], rot_centre, tv );
|
|
Matrix3_Multiply_Vec3 ( result, tv, quad[j] );
|
|
VectorAdd ( rot_centre, quad[j], quad[j] );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DEFORMV_PROJECTION_SHADOW:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RB_CalcEnvironmentTexCoords( float *st )
|
|
{
|
|
int i;
|
|
float *v, *normal;
|
|
vec3_t viewer, reflected;
|
|
float d;
|
|
|
|
vec3_t rorg;
|
|
|
|
v = vertexArray[0];
|
|
normal = normalsArray[0];
|
|
|
|
RotateLightVector(currententity->axis, currententity->origin, r_origin, rorg);
|
|
|
|
for (i = 0 ; i < numVerts ; i++, v += 3, normal += 3, st += 2 )
|
|
{
|
|
VectorSubtract (rorg, v, viewer);
|
|
VectorNormalizeFast (viewer);
|
|
|
|
d = DotProduct (normal, viewer);
|
|
|
|
reflected[0] = normal[0]*2*d - viewer[0];
|
|
reflected[1] = normal[1]*2*d - viewer[1];
|
|
reflected[2] = normal[2]*2*d - viewer[2];
|
|
|
|
st[0] = 0.5 + reflected[1] * 0.5;
|
|
st[1] = 0.5 - reflected[2] * 0.5;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_VertexTCBase
|
|
==============
|
|
*/
|
|
float *R_VertexTCBase ( int tcgen, int unit )
|
|
{
|
|
int i;
|
|
// vec3_t t, n;
|
|
float *outCoords;
|
|
// vec3_t transform;
|
|
// mat3_t inverse_axis;
|
|
// mat3_t axis;
|
|
|
|
outCoords = tUnitCoordsArray[unit][0];
|
|
|
|
if ( tcgen == TC_GEN_BASE )
|
|
{
|
|
memcpy ( outCoords, coordsArray[0], sizeof(float) * 2 * numVerts );
|
|
} else if ( tcgen == TC_GEN_LIGHTMAP )
|
|
{
|
|
memcpy ( outCoords, lightmapCoordsArray[0], sizeof(float) * 2 * numVerts );
|
|
}
|
|
else if ( tcgen == TC_GEN_ENVIRONMENT )
|
|
{
|
|
RB_CalcEnvironmentTexCoords(outCoords); //use genuine q3 code, to get it totally identical (for cell shading effects)
|
|
//plus, it looks like less overhead too
|
|
//I guess it depends on the size of the mesh
|
|
/*
|
|
//the old qfusion code
|
|
if ( !currentmodel )
|
|
{
|
|
VectorSubtract ( vec3_origin, currententity->origin, transform );
|
|
AngleVectors(currententity->angles, axis[0], axis[1], axis[2]);
|
|
Matrix3_Transpose ( axis, inverse_axis );
|
|
}
|
|
else if ( currentmodel == cl.worldmodel )
|
|
{
|
|
VectorSubtract ( vec3_origin, r_origin, transform );
|
|
}
|
|
else if ( currentmodel->type == mod_brush )
|
|
{
|
|
VectorNegate ( currententity->origin, t );
|
|
VectorSubtract ( t, r_origin, transform );
|
|
AngleVectors(currententity->angles, axis[0], axis[1], axis[2]);
|
|
Matrix3_Transpose ( axis, inverse_axis );
|
|
}
|
|
else
|
|
{
|
|
VectorSubtract ( vec3_origin, currententity->origin, transform );
|
|
AngleVectors(currententity->angles, axis[0], axis[1], axis[2]);
|
|
Matrix3_Transpose ( axis, inverse_axis );
|
|
}
|
|
|
|
for ( i = 0; i < numVerts; i++, outCoords += 2 )
|
|
{
|
|
VectorAdd ( vertexArray[i], transform, t );
|
|
|
|
// project vector
|
|
if ( currentmodel && (currentmodel == cl.worldmodel) )
|
|
{
|
|
n[0] = normalsArray[i][0];
|
|
n[1] = normalsArray[i][1];
|
|
n[2] = Q_rsqrt ( DotProduct(t,t) );
|
|
}
|
|
else
|
|
{
|
|
n[0] = DotProduct ( normalsArray[i], inverse_axis[0] );
|
|
n[1] = DotProduct ( normalsArray[i], inverse_axis[1] );
|
|
n[2] = Q_rsqrt ( DotProduct(t,t) );
|
|
}
|
|
|
|
outCoords[0] = t[0]*n[2] - n[0];
|
|
outCoords[1] = t[1]*n[2] - n[1];
|
|
}
|
|
*/
|
|
}
|
|
else if ( tcgen == TC_GEN_VECTOR )
|
|
{
|
|
for ( i = 0; i < numVerts; i++, outCoords += 2 )
|
|
{
|
|
static vec3_t tc_gen_s = { 1.0f, 0.0f, 0.0f };
|
|
static vec3_t tc_gen_t = { 0.0f, 1.0f, 0.0f };
|
|
|
|
outCoords[0] = DotProduct ( tc_gen_s, vertexArray[i] );
|
|
outCoords[1] = DotProduct ( tc_gen_t, vertexArray[i] );
|
|
}
|
|
}
|
|
|
|
return tUnitCoordsArray[unit][0];
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_ShaderpassTex
|
|
==============
|
|
*/
|
|
int R_ShaderpassTex ( shaderpass_t *pass )
|
|
{
|
|
if (pass->flags & (SHADER_PASS_ANIMMAP|SHADER_PASS_LIGHTMAP|SHADER_PASS_VIDEOMAP|SHADER_PASS_DELUXMAP))
|
|
{
|
|
if ( pass->flags & SHADER_PASS_ANIMMAP ) {
|
|
return pass->anim_frames[(int)(pass->anim_fps * r_localShaderTime) % pass->anim_numframes];
|
|
}
|
|
else if ( (pass->flags & SHADER_PASS_LIGHTMAP) && r_lmtex >= 0 )
|
|
{
|
|
return lightmap_textures[r_lmtex];
|
|
}
|
|
else if ( (pass->flags & SHADER_PASS_DELUXMAP) && r_lmtex >= 0 )
|
|
{
|
|
return lightmap_textures[r_lmtex+1];
|
|
}
|
|
else if ( (pass->flags & SHADER_PASS_VIDEOMAP))
|
|
{
|
|
return Media_UpdateForShader(pass->anim_frames[0], pass->cin);
|
|
}
|
|
}
|
|
|
|
return pass->anim_frames[0] ? pass->anim_frames[0] : 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_ModifyTextureCoords
|
|
================
|
|
*/
|
|
void R_ModifyTextureCoords ( shaderpass_t *pass, int unit )
|
|
{
|
|
int i, j;
|
|
float *table;
|
|
float t1, t2, sint, cost;
|
|
float *tcArray, *buffer;
|
|
tcmod_t *tcmod;
|
|
|
|
r_texNums[unit] = R_ShaderpassTex ( pass );
|
|
|
|
// we're smart enough not to copy data and simply switch the pointer
|
|
if ( !pass->numtcmods ) {
|
|
if ( pass->tcgen == TC_GEN_BASE ) {
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, coordsArray );
|
|
} else if ( pass->tcgen == TC_GEN_LIGHTMAP ) {
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, lightmapCoordsArray );
|
|
} else {
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, R_VertexTCBase (pass->tcgen, unit));
|
|
}
|
|
return;
|
|
}
|
|
|
|
buffer = R_VertexTCBase (pass->tcgen, unit);
|
|
qglTexCoordPointer(2, GL_FLOAT, 0, buffer);
|
|
|
|
for (i = 0, tcmod = pass->tcmods; i < pass->numtcmods; i++, tcmod++)
|
|
{
|
|
tcArray = buffer;
|
|
|
|
switch (tcmod->type)
|
|
{
|
|
case SHADER_TCMOD_ROTATE:
|
|
cost = tcmod->args[0] * r_localShaderTime;
|
|
sint = R_FastSin( cost );
|
|
cost = R_FastSin( cost + 0.25 );
|
|
|
|
for ( j = 0; j < numVerts; j++, tcArray += 2 ) {
|
|
t1 = cost * (tcArray[0] - 0.5f) - sint * (tcArray[1] - 0.5f) + 0.5f;
|
|
t2 = cost * (tcArray[1] - 0.5f) + sint * (tcArray[0] - 0.5f) + 0.5f;
|
|
tcArray[0] = t1;
|
|
tcArray[1] = t2;
|
|
}
|
|
break;
|
|
|
|
case SHADER_TCMOD_SCALE:
|
|
t1 = tcmod->args[0];
|
|
t2 = tcmod->args[1];
|
|
|
|
for ( j = 0; j < numVerts; j++, tcArray += 2 ) {
|
|
tcArray[0] = tcArray[0] * t1;
|
|
tcArray[1] = tcArray[1] * t2;
|
|
}
|
|
break;
|
|
|
|
case SHADER_TCMOD_TURB:
|
|
t1 = tcmod->args[2] + r_localShaderTime * tcmod->args[3];
|
|
t2 = tcmod->args[1];
|
|
|
|
for ( j = 0; j < numVerts; j++, tcArray += 2 ) {
|
|
tcArray[0] = tcArray[0] + R_FastSin (tcArray[0]*t2+t1) * t2;
|
|
tcArray[1] = tcArray[1] + R_FastSin (tcArray[1]*t2+t1) * t2;
|
|
}
|
|
break;
|
|
|
|
case SHADER_TCMOD_STRETCH:
|
|
table = R_TableForFunc ( tcmod->args[0] );
|
|
t2 = tcmod->args[3] + r_localShaderTime * tcmod->args[4];
|
|
t1 = FTABLE_EVALUATE ( table, t2 ) * tcmod->args[2] + tcmod->args[1];
|
|
t1 = t1 ? 1.0f / t1 : 1.0f;
|
|
t2 = 0.5f - 0.5f * t1;
|
|
|
|
for ( j = 0; j < numVerts; j++, tcArray += 2 ) {
|
|
tcArray[0] = tcArray[0] * t1 + t2;
|
|
tcArray[1] = tcArray[1] * t1 + t2;
|
|
}
|
|
break;
|
|
|
|
case SHADER_TCMOD_SCROLL:
|
|
t1 = tcmod->args[0] * r_localShaderTime;
|
|
t2 = tcmod->args[1] * r_localShaderTime;
|
|
|
|
for ( j = 0; j < numVerts; j++, tcArray += 2 ) {
|
|
tcArray[0] = tcArray[0] + t1;
|
|
tcArray[1] = tcArray[1] + t2;
|
|
}
|
|
break;
|
|
|
|
case SHADER_TCMOD_TRANSFORM:
|
|
for ( j = 0; j < numVerts; j++, tcArray += 2 ) {
|
|
t1 = tcArray[0];
|
|
t2 = tcArray[1];
|
|
tcArray[0] = t1 * tcmod->args[0] + t2 * tcmod->args[2] + tcmod->args[4];
|
|
tcArray[1] = t2 * tcmod->args[1] + t1 * tcmod->args[3] + tcmod->args[5];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)
|
|
#define VectorScalef(a, b, c) c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b
|
|
|
|
/*
|
|
================
|
|
R_ModifyColor
|
|
================
|
|
*/
|
|
void R_ModifyColor ( meshbuffer_t *mb, shaderpass_t *pass )
|
|
{
|
|
|
|
int i, b;
|
|
float *table, c, a;
|
|
vec3_t t, v;
|
|
shader_t *shader;
|
|
qbyte *bArray, *vArray;
|
|
qboolean fogged, noArray;
|
|
shaderfunc_t *rgbgenfunc, *alphagenfunc;
|
|
|
|
shader = mb->shader;
|
|
fogged = mb->fog && (shader->sort >= SHADER_SORT_UNDERWATER) &&
|
|
!(pass->flags & SHADER_PASS_DEPTHWRITE) && !shader->fog_dist;
|
|
noArray = (pass->flags & SHADER_PASS_NOCOLORARRAY) && !fogged;
|
|
rgbgenfunc = &pass->rgbgen_func;
|
|
alphagenfunc = &pass->alphagen_func;
|
|
|
|
if ( noArray ) {
|
|
numColors = 1;
|
|
} else {
|
|
numColors = numVerts;
|
|
}
|
|
|
|
bArray = colorArray[0];
|
|
vArray = inColorsArray[0];
|
|
|
|
switch (pass->rgbgen)
|
|
{
|
|
case RGB_GEN_IDENTITY:
|
|
memset ( bArray, 255, sizeof(byte_vec4_t)*numColors );
|
|
break;
|
|
|
|
case RGB_GEN_IDENTITY_LIGHTING:
|
|
memset ( bArray, r_identityLighting, sizeof(byte_vec4_t)*numColors );
|
|
break;
|
|
|
|
case RGB_GEN_CONST:
|
|
for ( i = 0; i < numColors; i++, bArray += 4 ) {
|
|
bArray[0] = FloatToByte (rgbgenfunc->args[0]);
|
|
bArray[1] = FloatToByte (rgbgenfunc->args[1]);
|
|
bArray[2] = FloatToByte (rgbgenfunc->args[2]);
|
|
}
|
|
break;
|
|
|
|
case RGB_GEN_WAVE:
|
|
table = R_TableForFunc ( rgbgenfunc->type );
|
|
c = rgbgenfunc->args[2] + r_localShaderTime * rgbgenfunc->args[3];
|
|
c = FTABLE_EVALUATE ( table, c ) * rgbgenfunc->args[1] + rgbgenfunc->args[0];
|
|
clamp ( c, 0.0f, 1.0f );
|
|
|
|
memset ( bArray, FloatToByte (c), sizeof(byte_vec4_t)*numColors );
|
|
break;
|
|
|
|
case RGB_GEN_ENTITY:
|
|
((qbyte*)&b)[0] = currententity->shaderRGBAf[0];
|
|
((qbyte*)&b)[1] = currententity->shaderRGBAf[1];
|
|
((qbyte*)&b)[2] = currententity->shaderRGBAf[2];
|
|
((qbyte*)&b)[3] = currententity->shaderRGBAf[3];
|
|
for ( i = 0; i < numColors; i++, bArray += 4 )
|
|
{
|
|
*(int *)bArray = b;
|
|
}
|
|
break;
|
|
|
|
case RGB_GEN_ONE_MINUS_ENTITY:
|
|
((qbyte*)&b)[0] = 255-currententity->shaderRGBAf[0];
|
|
((qbyte*)&b)[1] = 255-currententity->shaderRGBAf[1];
|
|
((qbyte*)&b)[2] = 255-currententity->shaderRGBAf[2];
|
|
((qbyte*)&b)[3] = 255-currententity->shaderRGBAf[3];
|
|
for ( i = 0; i < numColors; i++, bArray += 4 )
|
|
{
|
|
*(int *)bArray = b;
|
|
}
|
|
break;
|
|
|
|
case RGB_GEN_VERTEX:
|
|
case RGB_GEN_EXACT_VERTEX:
|
|
memcpy ( bArray, vArray, sizeof(byte_vec4_t)*numColors );
|
|
break;
|
|
|
|
case RGB_GEN_TOPCOLOR: //multiply vertex by topcolor (for player models)
|
|
{
|
|
int rc, gc, bc;
|
|
R_FetchTopColour(&rc, &gc, &bc);
|
|
for ( i = 0; i < numColors; i++, bArray += 4, vArray += 4 ) {
|
|
bArray[0] = (vArray[0]*rc)>>8;
|
|
bArray[1] = (vArray[1]*gc)>>8;
|
|
bArray[2] = (vArray[2]*bc)>>8;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RGB_GEN_BOTTOMCOLOR: //multiply vertex by bottomcolor (for player models)
|
|
{
|
|
int rc, gc, bc;
|
|
R_FetchBottomColour(&rc, &gc, &bc);
|
|
for ( i = 0; i < numColors; i++, bArray += 4, vArray += 4 ) {
|
|
bArray[0] = (vArray[0]*rc)>>8;
|
|
bArray[1] = (vArray[1]*gc)>>8;
|
|
bArray[2] = (vArray[2]*bc)>>8;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case RGB_GEN_ONE_MINUS_VERTEX:
|
|
for ( i = 0; i < numColors; i++, bArray += 4, vArray += 4 ) {
|
|
bArray[0] = 255 - vArray[0];
|
|
bArray[1] = 255 - vArray[1];
|
|
bArray[2] = 255 - vArray[2];
|
|
}
|
|
break;
|
|
|
|
case RGB_GEN_LIGHTING_DIFFUSE:
|
|
if ( !currententity )
|
|
{
|
|
memset ( bArray, 255, sizeof(byte_vec4_t)*numColors );
|
|
}
|
|
else
|
|
{
|
|
R_LightArrays((byte_vec4_t*)bArray, numColors, normalsArray);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bArray = colorArray[0];
|
|
vArray = inColorsArray[0];
|
|
|
|
switch (pass->alphagen)
|
|
{
|
|
case ALPHA_GEN_IDENTITY:
|
|
for ( i = 0; i < numColors; i++, bArray += 4 ) {
|
|
bArray[3] = 255;
|
|
}
|
|
break;
|
|
|
|
case ALPHA_GEN_CONST:
|
|
b = FloatToByte ( alphagenfunc->args[0] );
|
|
|
|
for ( i = 0; i < numColors; i++, bArray += 4 ) {
|
|
bArray[3] = b;
|
|
}
|
|
break;
|
|
|
|
case ALPHA_GEN_WAVE:
|
|
table = R_TableForFunc ( alphagenfunc->type );
|
|
a = alphagenfunc->args[2] + r_localShaderTime * alphagenfunc->args[3];
|
|
a = FTABLE_EVALUATE ( table, a ) * alphagenfunc->args[1] + alphagenfunc->args[0];
|
|
b = FloatToByte ( bound (0.0f, a, 1.0f) );
|
|
|
|
for ( i = 0; i < numColors; i++, bArray += 4 ) {
|
|
bArray[3] = b;
|
|
}
|
|
break;
|
|
|
|
case ALPHA_GEN_PORTAL:
|
|
VectorAdd ( vertexArray[0], currententity->origin, v );
|
|
VectorSubtract ( r_origin, v, t );
|
|
a = VectorLength ( t ) * (1.0 / 255.0);
|
|
clamp ( a, 0.0f, 1.0f );
|
|
b = FloatToByte ( a );
|
|
|
|
for ( i = 0; i < numColors; i++, bArray += 4 ) {
|
|
bArray[3] = b;
|
|
}
|
|
break;
|
|
|
|
case ALPHA_GEN_VERTEX:
|
|
for ( i = 0; i < numColors; i++, bArray += 4, vArray += 4 ) {
|
|
bArray[3] = vArray[3];
|
|
}
|
|
break;
|
|
|
|
case ALPHA_GEN_ENTITY:
|
|
if (pass->rgbgen != RGB_GEN_ENTITY)
|
|
{//rgbgenentity copies across ints rather than chars. it comes padded with the alpha too.
|
|
unsigned char value = bound(0, currententity->shaderRGBAf[3]*255, 255);
|
|
for ( i = 0; i < numColors; i++, bArray += 4 )
|
|
{
|
|
bArray[3] = value;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case ALPHA_GEN_SPECULAR:
|
|
{
|
|
mat3_t axis;
|
|
AngleVectors(currententity->angles, axis[0], axis[1], axis[2]);
|
|
VectorSubtract ( r_origin, currententity->origin, t );
|
|
|
|
if ( !Matrix3_Compare (axis, axisDefault) ) {
|
|
Matrix3_Multiply_Vec3 ( axis, t, v );
|
|
} else {
|
|
VectorCopy ( t, v );
|
|
}
|
|
|
|
for ( i = 0; i < numColors; i++, bArray += 4 ) {
|
|
VectorSubtract ( v, vertexArray[i], t );
|
|
a = DotProduct( t, normalsArray[i] ) * Q_rsqrt ( DotProduct(t,t) );
|
|
a = a * a * a * a * a;
|
|
bArray[3] = FloatToByte ( bound (0.0f, a, 1.0f) );
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
if ( fogged )
|
|
{
|
|
float dist, vdist;
|
|
mplane_t *fogplane;
|
|
vec3_t diff, viewtofog, fog_vpn;
|
|
|
|
fogplane = mb->fog->visibleplane;
|
|
if (!fogplane)
|
|
return;
|
|
dist = PlaneDiff ( r_origin, fogplane );
|
|
|
|
if ( shader->flags & SHADER_SKY )
|
|
{
|
|
if ( dist > 0 )
|
|
VectorScale( fogplane->normal, -dist, viewtofog );
|
|
else
|
|
VectorClear( viewtofog );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy ( currententity->origin, viewtofog );
|
|
}
|
|
|
|
VectorScalef ( vpn, mb->fog->shader->fog_dist, fog_vpn );
|
|
|
|
bArray = colorArray[0];
|
|
for ( i = 0; i < numColors; i++, bArray += 4 )
|
|
{
|
|
VectorAdd ( vertexArray[i], viewtofog, diff );
|
|
|
|
// camera is inside the fog
|
|
if ( dist < 0 ) {
|
|
VectorSubtract ( diff, r_origin, diff );
|
|
|
|
c = DotProduct ( diff, fog_vpn );
|
|
a = (1.0f - bound ( 0, c, 1.0f )) * (1.0 / 255.0);
|
|
} else {
|
|
vdist = PlaneDiff ( diff, fogplane );
|
|
|
|
if ( vdist < 0 ) {
|
|
VectorSubtract ( diff, r_origin, diff );
|
|
|
|
c = vdist / ( vdist - dist );
|
|
c *= DotProduct ( diff, fog_vpn );
|
|
a = (1.0f - bound ( 0, c, 1.0f )) * (1.0 / 255.0);
|
|
} else {
|
|
a = 1.0 / 255.0;
|
|
}
|
|
}
|
|
|
|
if ( pass->blendmode == GL_ADD ||
|
|
((pass->blendsrc == GL_ZERO) && (pass->blenddst == GL_ONE_MINUS_SRC_COLOR)) ) {
|
|
bArray[0] = FloatToByte ( (float)bArray[0]*a );
|
|
bArray[1] = FloatToByte ( (float)bArray[1]*a );
|
|
bArray[2] = FloatToByte ( (float)bArray[2]*a );
|
|
} else {
|
|
bArray[3] = FloatToByte ( (float)bArray[3]*a );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_SetShaderState
|
|
================
|
|
*/
|
|
void R_SetShaderState ( shader_t *shader )
|
|
{
|
|
// Face culling
|
|
if ( !gl_cull.value || (r_features & MF_NOCULL) )
|
|
{
|
|
qglDisable ( GL_CULL_FACE );
|
|
}
|
|
else
|
|
{
|
|
if ( shader->flags & SHADER_CULL_FRONT )
|
|
{
|
|
qglEnable ( GL_CULL_FACE );
|
|
qglCullFace ( GL_FRONT );
|
|
}
|
|
else if ( shader->flags & SHADER_CULL_BACK )
|
|
{
|
|
qglEnable ( GL_CULL_FACE );
|
|
qglCullFace ( GL_BACK );
|
|
}
|
|
else
|
|
{
|
|
qglDisable ( GL_CULL_FACE );
|
|
}
|
|
}
|
|
|
|
if ( shader->flags & SHADER_POLYGONOFFSET )
|
|
{
|
|
qglEnable ( GL_POLYGON_OFFSET_FILL );
|
|
}
|
|
else
|
|
{
|
|
qglDisable ( GL_POLYGON_OFFSET_FILL );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_SetShaderpassState
|
|
================
|
|
*/
|
|
void R_SetShaderpassState ( shaderpass_t *pass, qboolean mtex )
|
|
{
|
|
if ( (mtex && (pass->blendmode != GL_REPLACE)) || (pass->flags & SHADER_PASS_BLEND) )
|
|
{
|
|
qglEnable ( GL_BLEND );
|
|
qglBlendFunc ( pass->blendsrc, pass->blenddst );
|
|
}
|
|
else
|
|
{
|
|
qglDisable ( GL_BLEND );
|
|
}
|
|
|
|
if ( pass->flags & SHADER_PASS_ALPHAFUNC )
|
|
{
|
|
qglEnable ( GL_ALPHA_TEST );
|
|
|
|
if ( pass->alphafunc == SHADER_ALPHA_GT0 )
|
|
{
|
|
qglAlphaFunc ( GL_GREATER, 0 );
|
|
}
|
|
else if ( pass->alphafunc == SHADER_ALPHA_LT128 )
|
|
{
|
|
qglAlphaFunc ( GL_LESS, 0.5f );
|
|
}
|
|
else if ( pass->alphafunc == SHADER_ALPHA_GE128 )
|
|
{
|
|
qglAlphaFunc ( GL_GEQUAL, 0.5f );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qglDisable ( GL_ALPHA_TEST );
|
|
}
|
|
|
|
// nasty hack!!!
|
|
if ( !gl_state.in2d )
|
|
{
|
|
extern int gldepthfunc;
|
|
if (gldepthfunc == GL_LEQUAL)
|
|
qglDepthFunc ( pass->depthfunc );
|
|
else
|
|
{
|
|
switch(pass->depthfunc)
|
|
{
|
|
case GL_LESS:
|
|
qglDepthFunc ( GL_GREATER );
|
|
break;
|
|
case GL_LEQUAL:
|
|
qglDepthFunc ( GL_GEQUAL );
|
|
break;
|
|
case GL_GREATER:
|
|
qglDepthFunc ( GL_LESS );
|
|
break;
|
|
case GL_GEQUAL:
|
|
qglDepthFunc ( GL_LEQUAL );
|
|
break;
|
|
|
|
case GL_NEVER:
|
|
case GL_EQUAL:
|
|
case GL_ALWAYS:
|
|
case GL_NOTEQUAL:
|
|
default:
|
|
qglDepthFunc ( pass->depthfunc );
|
|
}
|
|
}
|
|
|
|
if ( pass->flags & SHADER_PASS_DEPTHWRITE )
|
|
{
|
|
qglDepthMask ( GL_TRUE );
|
|
}
|
|
else
|
|
{
|
|
qglDepthMask ( GL_FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qglDepthFunc ( GL_ALWAYS );
|
|
qglDepthMask ( GL_FALSE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_RenderMeshGeneric
|
|
================
|
|
*/
|
|
void R_RenderMeshGeneric ( meshbuffer_t *mb, shaderpass_t *pass )
|
|
{
|
|
R_SetShaderpassState ( pass, false );
|
|
R_ModifyTextureCoords ( pass, 0 );
|
|
R_ModifyColor ( mb, pass );
|
|
|
|
if ( pass->blendmode == GL_REPLACE )
|
|
GL_TexEnv( GL_REPLACE );
|
|
else
|
|
GL_TexEnv ( GL_MODULATE );
|
|
GL_Bind ( r_texNums[0] );
|
|
|
|
R_FlushArrays ();
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_RenderMeshMultitextured
|
|
================
|
|
*/
|
|
|
|
void R_RenderMeshMultitextured ( meshbuffer_t *mb, shaderpass_t *pass )
|
|
{
|
|
int i;
|
|
|
|
r_numUnits = pass->numMergedPasses;
|
|
|
|
GL_SelectTexture( mtexid0 );
|
|
GL_TexEnv( pass->blendmode );
|
|
R_SetShaderpassState ( pass, true );
|
|
R_ModifyTextureCoords ( pass, 0 );
|
|
R_ModifyColor ( mb, pass );
|
|
|
|
for ( i = 1, pass++; i < r_numUnits; i++, pass++ )
|
|
{
|
|
GL_SelectTexture( mtexid0 + i );
|
|
GL_TexEnv( pass->blendmode );
|
|
R_ModifyTextureCoords ( pass, i );
|
|
}
|
|
|
|
R_FlushArraysMtex ();
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_RenderMeshCombined
|
|
================
|
|
*/
|
|
void R_RenderMeshCombined ( meshbuffer_t *mb, shaderpass_t *pass )
|
|
{
|
|
int i;
|
|
|
|
r_numUnits = pass->numMergedPasses;
|
|
|
|
R_SetShaderpassState ( pass, true );
|
|
R_ModifyColor ( mb, pass );
|
|
|
|
GL_SelectTexture( mtexid0 );
|
|
if ( pass->blendmode == GL_REPLACE )
|
|
GL_TexEnv( GL_REPLACE );
|
|
else
|
|
GL_TexEnv( GL_MODULATE );
|
|
R_ModifyTextureCoords ( pass, 0 );
|
|
|
|
for ( i = 1, pass++; i < r_numUnits; i++, pass++ )
|
|
{
|
|
GL_SelectTexture( mtexid0 + i );
|
|
|
|
|
|
switch ( pass->blendmode )
|
|
{
|
|
case GL_DOT3_RGB_ARB:
|
|
GL_TexEnv (GL_COMBINE_EXT);
|
|
qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
|
qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, pass->blendmode);
|
|
break;
|
|
|
|
case GL_REPLACE:
|
|
case GL_MODULATE:
|
|
case GL_ADD:
|
|
// these modes are best set with TexEnv, Combine4 would need much more setup
|
|
GL_TexEnv (pass->blendmode);
|
|
break;
|
|
|
|
case GL_DECAL:
|
|
// mimics Alpha-Blending in upper texture stage, but instead of multiplying the alpha-channel, theyre added
|
|
// this way it can be possible to use GL_DECAL in both texture-units, while still looking good
|
|
// normal mutlitexturing would multiply the alpha-channel which looks ugly
|
|
GL_TexEnv (GL_COMBINE_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INTERPOLATE_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_ADD);
|
|
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA);
|
|
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA);
|
|
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA);
|
|
break;
|
|
|
|
default:
|
|
GL_TexEnv (GL_COMBINE4_NV);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_ADD);
|
|
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA);
|
|
|
|
switch ( pass->blendsrc )
|
|
{
|
|
case GL_ONE:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case GL_ZERO:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_DST_COLOR:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_ONE_MINUS_DST_COLOR:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case GL_SRC_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_ONE_MINUS_SRC_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case GL_DST_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_ONE_MINUS_DST_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
}
|
|
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_EXT, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_EXT, GL_SRC_ALPHA);
|
|
|
|
switch (pass->blenddst)
|
|
{
|
|
case GL_ONE:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case GL_ZERO:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_ZERO);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_SRC_COLOR:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_ONE_MINUS_SRC_COLOR:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case GL_SRC_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_ONE_MINUS_SRC_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_TEXTURE);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case GL_DST_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_SRC_ALPHA);
|
|
break;
|
|
case GL_ONE_MINUS_DST_ALPHA:
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_ALPHA);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_PREVIOUS_EXT);
|
|
qglTexEnvi (GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
R_ModifyTextureCoords ( pass, i );
|
|
}
|
|
|
|
R_FlushArraysMtex ();
|
|
}
|
|
|
|
void R_RenderMeshProgram ( meshbuffer_t *mb, shaderpass_t *pass )
|
|
{
|
|
shader_t *s;
|
|
int i;
|
|
vec3_t param3;
|
|
int r, g, b;
|
|
|
|
r_numUnits = pass->numMergedPasses;
|
|
|
|
R_SetShaderpassState ( pass, true );
|
|
R_ModifyColor ( mb, pass );
|
|
|
|
GL_SelectTexture( mtexid0 );
|
|
if ( pass->blendmode == GL_REPLACE )
|
|
GL_TexEnv( GL_REPLACE );
|
|
else
|
|
GL_TexEnv( GL_MODULATE );
|
|
R_ModifyTextureCoords ( pass, 0 );
|
|
|
|
for ( i = 1, pass++; i < r_numUnits; i++, pass++ )
|
|
{
|
|
GL_SelectTexture( mtexid0 + i );
|
|
|
|
R_ModifyTextureCoords ( pass, i );
|
|
}
|
|
|
|
s = mb->shader;
|
|
GLSlang_UseProgram(s->programhandle);
|
|
for (i = 0; i < s->numprogparams; i++)
|
|
{
|
|
switch(s->progparm[i].type)
|
|
{
|
|
case SP_EYEPOS:
|
|
qglUniform3fvARB(s->progparm[i].handle, 1, r_origin);
|
|
break;
|
|
case SP_TIME:
|
|
qglUniform1fARB(s->progparm[i].handle, r_localShaderTime);
|
|
break;
|
|
|
|
case SP_ENTCOLOURS:
|
|
qglUniform4fvARB(s->progparm[i].handle, 1, currententity->shaderRGBAf);
|
|
break;
|
|
case SP_TOPCOLOURS:
|
|
R_FetchTopColour(&r, &g, &b);
|
|
param3[0] = r/255;
|
|
param3[1] = g/255;
|
|
param3[2] = b/255;
|
|
qglUniform3fvARB(s->progparm[i].handle, 1, param3);
|
|
break;
|
|
case SP_BOTTOMCOLOURS:
|
|
R_FetchBottomColour(&r, &g, &b);
|
|
param3[0] = r/255;
|
|
param3[1] = g/255;
|
|
param3[2] = b/255;
|
|
qglUniform3fvARB(s->progparm[i].handle, 1, param3);
|
|
break;
|
|
|
|
default:
|
|
Host_EndGame("Bad shader program parameter type (%i)", s->progparm[i].type);
|
|
break;
|
|
}
|
|
}
|
|
R_FlushArraysMtex ();
|
|
GLSlang_UseProgram(0);
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_RenderMeshBuffer
|
|
================
|
|
*/
|
|
void R_RenderMeshBuffer ( meshbuffer_t *mb, qboolean shadowpass )
|
|
{
|
|
int i;
|
|
shader_t *shader;
|
|
shaderpass_t *pass;
|
|
|
|
if ( !numVerts )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// R_IBrokeTheArrays();
|
|
|
|
// qglVertexPointer( 3, GL_FLOAT, 16, vertexArray ); // padded for SIMD
|
|
// qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray );
|
|
|
|
// qglEnableClientState( GL_VERTEX_ARRAY );
|
|
|
|
shader = mb->shader;
|
|
r_lmtex = mb->infokey;
|
|
|
|
if ( currententity && !gl_state.in2d )
|
|
{
|
|
r_localShaderTime = r_refdef.time - currententity->shaderTime;
|
|
} else
|
|
{
|
|
r_localShaderTime = realtime;
|
|
}
|
|
|
|
R_SetShaderState ( shader );
|
|
|
|
if ( shader->numdeforms )
|
|
{
|
|
R_DeformVertices ( mb );
|
|
}
|
|
|
|
if ( !numIndexes || shadowpass )
|
|
{
|
|
return;
|
|
}
|
|
|
|
R_LockArrays ( numVerts );
|
|
|
|
for ( i = 0, pass = shader->passes; i < shader->numpasses; )
|
|
{
|
|
if ( !(pass->flags & SHADER_PASS_DETAIL) || r_detailtextures.value )
|
|
{
|
|
pass->flush ( mb, pass );
|
|
}
|
|
|
|
i += pass->numMergedPasses;
|
|
pass += pass->numMergedPasses;
|
|
}
|
|
|
|
R_FinishMeshBuffer ( mb );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_RenderFogOnMesh
|
|
================
|
|
*/
|
|
|
|
int r_fogtexture;
|
|
#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)
|
|
void R_RenderFogOnMesh ( shader_t *shader, struct mfog_s *fog )
|
|
{
|
|
#define FOG_TEXTURE_HEIGHT 32
|
|
|
|
int i;
|
|
vec3_t diff, viewtofog, fog_vpn;
|
|
float dist, vdist;
|
|
shader_t *fogshader;
|
|
mplane_t *fogplane;
|
|
|
|
if ( !fog->numplanes || !fog->shader || !fog->visibleplane )
|
|
{
|
|
return;
|
|
}
|
|
|
|
R_ResetTexState ();
|
|
|
|
fogshader = fog->shader;
|
|
fogplane = fog->visibleplane;
|
|
|
|
GL_Bind( r_fogtexture );
|
|
|
|
qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
GL_TexEnv(GL_MODULATE);
|
|
|
|
if ( !shader || !shader->numpasses || shader->fog_dist || (shader->flags & SHADER_SKY) )
|
|
{
|
|
extern int gldepthfunc;
|
|
qglDepthFunc ( gldepthfunc );
|
|
}
|
|
else
|
|
{
|
|
qglDepthFunc ( GL_EQUAL );
|
|
}
|
|
|
|
qglColor4ubv ( fogshader->fog_color );
|
|
|
|
// distance to fog
|
|
dist = PlaneDiff ( r_origin, fogplane );
|
|
|
|
if ( shader && shader->flags & SHADER_SKY )
|
|
{
|
|
if ( dist > 0 )
|
|
VectorMA( r_origin, -dist, fogplane->normal, viewtofog );
|
|
else
|
|
VectorCopy( r_origin, viewtofog );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( currententity->origin, viewtofog );
|
|
}
|
|
|
|
VectorScale ( vpn, fogshader->fog_dist, fog_vpn );
|
|
|
|
for ( i = 0; i < numVerts; i++, currentCoords += 2 )
|
|
{
|
|
VectorAdd ( viewtofog, vertexArray[i], diff );
|
|
vdist = PlaneDiff ( diff, fogplane );
|
|
VectorSubtract ( diff, r_origin, diff );
|
|
|
|
if ( dist < 0 )
|
|
{ // camera is inside the fog brush
|
|
currentCoords[0] = DotProduct ( diff, fog_vpn );
|
|
}
|
|
else
|
|
{
|
|
if ( vdist < 0 )
|
|
{
|
|
currentCoords[0] = vdist / ( vdist - dist );
|
|
currentCoords[0] *= DotProduct ( diff, fog_vpn );
|
|
}
|
|
else
|
|
{
|
|
currentCoords[0] = 0.0f;
|
|
}
|
|
}
|
|
|
|
currentCoords[1] = -vdist * fogshader->fog_dist + 1.5f/(float)FOG_TEXTURE_HEIGHT;
|
|
}
|
|
|
|
if ( shader && !shader->numpasses )
|
|
{
|
|
R_LockArrays ( numVerts );
|
|
}
|
|
|
|
R_FlushArrays ();
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTriangleOutlines
|
|
================
|
|
*/
|
|
void R_DrawTriangleOutlines (void)
|
|
{
|
|
R_ResetTexState ();
|
|
|
|
qglDisable( GL_TEXTURE_2D );
|
|
qglDisable( GL_DEPTH_TEST );
|
|
qglColor4f( 1, 1, 1, 1 );
|
|
qglDisable ( GL_BLEND );
|
|
qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
R_FlushArrays ();
|
|
|
|
qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
|
|
qglEnable( GL_DEPTH_TEST );
|
|
qglEnable( GL_TEXTURE_2D );
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawNormals
|
|
================
|
|
*/
|
|
void R_DrawNormals (void)
|
|
{
|
|
int i;
|
|
|
|
R_ResetTexState ();
|
|
|
|
qglDisable( GL_TEXTURE_2D );
|
|
qglColor4f( 1, 1, 1, 1 );
|
|
qglDisable( GL_BLEND );
|
|
|
|
if ( gl_state.in2d ) {
|
|
qglBegin ( GL_POINTS );
|
|
for ( i = 0; i < numVerts; i++ ) {
|
|
qglVertex3fv ( vertexArray[i] );
|
|
}
|
|
qglEnd ();
|
|
} else {
|
|
qglDisable( GL_DEPTH_TEST );
|
|
qglBegin ( GL_LINES );
|
|
for ( i = 0; i < numVerts; i++ ) {
|
|
qglVertex3fv ( vertexArray[i] );
|
|
qglVertex3f ( vertexArray[i][0] + normalsArray[i][0],
|
|
vertexArray[i][1] + normalsArray[i][1],
|
|
vertexArray[i][2] + normalsArray[i][2] );
|
|
}
|
|
qglEnd ();
|
|
qglEnable( GL_DEPTH_TEST );
|
|
}
|
|
|
|
qglEnable( GL_TEXTURE_2D );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddDynamicLights
|
|
=================
|
|
*/
|
|
void R_AddDynamicLights ( meshbuffer_t *mb )
|
|
{
|
|
dlight_t *light;
|
|
int i, j, lnum;
|
|
vec3_t point, tvec, dlorigin;
|
|
vec3_t vright, vup;
|
|
vec3_t dir1, dir2, normal, right, up, oldnormal;
|
|
float *v[3], dist, scale;
|
|
index_t *oldIndexesArray, index[3];
|
|
int dlightNumIndexes, oldNumIndexes;
|
|
|
|
oldNumIndexes = numIndexes;
|
|
oldIndexesArray = indexesArray;
|
|
VectorClear ( oldnormal );
|
|
|
|
GL_Bind ( r_dlighttexture );
|
|
|
|
qglDepthFunc ( GL_EQUAL );
|
|
qglBlendFunc ( GL_DST_COLOR, GL_ONE );
|
|
GL_TexEnv(GL_MODULATE);
|
|
|
|
light = cl_dlights;
|
|
for ( lnum = 0; lnum < 32; lnum++, light++ )
|
|
{
|
|
if ( !(mb->dlightbits & (1<<lnum) ) )
|
|
continue; // not lit by this light
|
|
if (!light->radius)
|
|
continue; //urm
|
|
|
|
VectorSubtract ( light->origin, currententity->origin, dlorigin );
|
|
if ( !Matrix3_Compare (currententity->axis, axisDefault) )
|
|
{
|
|
VectorCopy ( dlorigin, point );
|
|
Matrix3_Multiply_Vec3 ( currententity->axis, point, dlorigin );
|
|
}
|
|
|
|
qglColor4f (light->color[0]*2, light->color[1]*2, light->color[2]*2,
|
|
1);//light->color[3]);
|
|
|
|
R_ResetTexState ();
|
|
dlightNumIndexes = 0;
|
|
|
|
for ( i = 0; i < oldNumIndexes; i += 3 )
|
|
{
|
|
index[0] = oldIndexesArray[i+0];
|
|
index[1] = oldIndexesArray[i+1];
|
|
index[2] = oldIndexesArray[i+2];
|
|
|
|
v[0] = (float *)(vertexArray + index[0]);
|
|
v[1] = (float *)(vertexArray + index[1]);
|
|
v[2] = (float *)(vertexArray + index[2]);
|
|
|
|
// calculate two mostly perpendicular edge directions
|
|
VectorSubtract ( v[0], v[1], dir1 );
|
|
VectorSubtract ( v[2], v[1], dir2 );
|
|
|
|
// we have two edge directions, we can calculate a third vector from
|
|
// them, which is the direction of the surface normal
|
|
CrossProduct ( dir1, dir2, normal );
|
|
VectorNormalize ( normal );
|
|
|
|
VectorSubtract ( v[0], dlorigin, tvec );
|
|
dist = DotProduct ( tvec, normal );
|
|
if ( dist < 0 )
|
|
dist = -dist;
|
|
if ( dist >= light->radius ) {
|
|
continue;
|
|
}
|
|
|
|
VectorMA ( dlorigin, -dist, normal, point );
|
|
scale = 1 / (light->radius - dist);
|
|
|
|
if ( !VectorCompare (normal, oldnormal) ) {
|
|
MakeNormalVectors ( normal, right, up );
|
|
VectorCopy ( normal, oldnormal );
|
|
}
|
|
|
|
VectorScale ( right, scale, vright );
|
|
VectorScale ( up, scale, vup );
|
|
|
|
for ( j = 0; j < 3; j++ )
|
|
{
|
|
// Get our texture coordinates
|
|
// Project the light image onto the face
|
|
VectorSubtract( v[j], point, tvec );
|
|
|
|
coordsArray[index[j]][0] = DotProduct( tvec, vright ) + 0.5f;
|
|
coordsArray[index[j]][1] = DotProduct( tvec, vup ) + 0.5f;
|
|
}
|
|
|
|
tempIndexesArray[dlightNumIndexes++] = index[0];
|
|
tempIndexesArray[dlightNumIndexes++] = index[1];
|
|
tempIndexesArray[dlightNumIndexes++] = index[2];
|
|
}
|
|
|
|
if ( dlightNumIndexes ) {
|
|
R_PushIndexes ( tempIndexesArray, NULL, NULL, dlightNumIndexes, MF_NONBATCHED );
|
|
R_FlushArrays ();
|
|
dlightNumIndexes = 0;
|
|
}
|
|
}
|
|
|
|
numIndexes = oldNumIndexes;
|
|
indexesArray = oldIndexesArray;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_FinishMeshBuffer
|
|
Render dynamic lights, fog, triangle outlines, normals and clear arrays
|
|
================
|
|
*/
|
|
void R_FinishMeshBuffer ( meshbuffer_t *mb )
|
|
{
|
|
shader_t *shader;
|
|
qboolean fogged;
|
|
qboolean dlight;
|
|
|
|
shader = mb->shader;
|
|
if ((mb->dlightbits != 0) && !(shader->flags & SHADER_FLARE))
|
|
dlight = (currententity->model->type == mod_brush && currententity->model->fromgame == fg_quake3);
|
|
else
|
|
dlight = false;
|
|
|
|
fogged = mb->fog && ((shader->sort < SHADER_SORT_UNDERWATER &&
|
|
(shader->flags & (SHADER_DEPTHWRITE|SHADER_SKY))) || shader->fog_dist);
|
|
|
|
if ( dlight || fogged ) {
|
|
GL_DisableMultitexture ( );
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, inCoordsArray[0] );
|
|
|
|
qglEnable ( GL_BLEND );
|
|
qglDisable ( GL_ALPHA_TEST );
|
|
qglDepthMask ( GL_FALSE );
|
|
|
|
if (dlight) //HACK: the extra check is because we play with the lightmaps in q1/q2
|
|
{
|
|
R_AddDynamicLights ( mb );
|
|
}
|
|
|
|
if (fogged)
|
|
{
|
|
R_RenderFogOnMesh ( shader, mb->fog );
|
|
}
|
|
|
|
qglDepthMask ( GL_TRUE );
|
|
}
|
|
|
|
if ( r_showtris.value || r_shownormals.value ) {
|
|
GL_DisableMultitexture ( );
|
|
|
|
if ( r_showtris.value ) {
|
|
R_DrawTriangleOutlines ();
|
|
}
|
|
|
|
if ( r_shownormals.value ) {
|
|
R_DrawNormals ();
|
|
}
|
|
}
|
|
|
|
R_UnlockArrays ();
|
|
R_ClearArrays ();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|