quake2forge/ref_gl/gl_mesh.c
2001-12-28 19:56:33 +00:00

861 lines
19 KiB
C

/*
Copyright (C) 1997-2001 Id Software, Inc.
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.
*/
// gl_mesh.c: triangle model functions
#include "gl_local.h"
/*
=============================================================
ALIAS MODELS
=============================================================
*/
#define NUMVERTEXNORMALS 162
float r_avertexnormals[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};
typedef float vec4_t[4];
static vec4_t s_lerped[MAX_VERTS];
//static vec3_t lerped[MAX_VERTS];
vec3_t shadevector;
float shadelight[3];
// precalculated dot products for quantized angles
#define SHADEDOT_QUANT 16
float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
#include "anormtab.h"
;
float *shadedots = r_avertexnormal_dots[0];
void GL_LerpVerts( int nverts, dtrivertx_t *v, dtrivertx_t *ov, dtrivertx_t *verts, float *lerp, float move[3], float frontv[3], float backv[3] )
{
int i;
//PMM -- added RF_SHELL_DOUBLE, RF_SHELL_HALF_DAM
if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) )
{
for (i=0 ; i < nverts; i++, v++, ov++, lerp+=4 )
{
float *normal = r_avertexnormals[verts[i].lightnormalindex];
lerp[0] = move[0] + ov->v[0]*backv[0] + v->v[0]*frontv[0] + normal[0] * POWERSUIT_SCALE;
lerp[1] = move[1] + ov->v[1]*backv[1] + v->v[1]*frontv[1] + normal[1] * POWERSUIT_SCALE;
lerp[2] = move[2] + ov->v[2]*backv[2] + v->v[2]*frontv[2] + normal[2] * POWERSUIT_SCALE;
}
}
else
{
for (i=0 ; i < nverts; i++, v++, ov++, lerp+=4)
{
lerp[0] = move[0] + ov->v[0]*backv[0] + v->v[0]*frontv[0];
lerp[1] = move[1] + ov->v[1]*backv[1] + v->v[1]*frontv[1];
lerp[2] = move[2] + ov->v[2]*backv[2] + v->v[2]*frontv[2];
}
}
}
/*
=============
GL_DrawAliasFrameLerp
interpolates between two frames and origins
FIXME: batch lerp all vertexes
=============
*/
void GL_DrawAliasFrameLerp (dmdl_t *paliashdr, float backlerp)
{
float l;
daliasframe_t *frame, *oldframe;
dtrivertx_t *v, *ov, *verts;
int *order;
int count;
float frontlerp;
float alpha;
vec3_t move, delta, vectors[3];
vec3_t frontv, backv;
int i;
int index_xyz;
float *lerp;
frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames
+ currententity->frame * paliashdr->framesize);
verts = v = frame->verts;
oldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames
+ currententity->oldframe * paliashdr->framesize);
ov = oldframe->verts;
order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds);
// glTranslatef (frame->translate[0], frame->translate[1], frame->translate[2]);
// glScalef (frame->scale[0], frame->scale[1], frame->scale[2]);
if (currententity->flags & RF_TRANSLUCENT)
alpha = currententity->alpha;
else
alpha = 1.0;
// PMM - added double shell
if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) )
qglDisable( GL_TEXTURE_2D );
frontlerp = 1.0 - backlerp;
// move should be the delta back to the previous frame * backlerp
VectorSubtract (currententity->oldorigin, currententity->origin, delta);
AngleVectors (currententity->angles, vectors[0], vectors[1], vectors[2]);
move[0] = DotProduct (delta, vectors[0]); // forward
move[1] = -DotProduct (delta, vectors[1]); // left
move[2] = DotProduct (delta, vectors[2]); // up
VectorAdd (move, oldframe->translate, move);
for (i=0 ; i<3 ; i++)
{
move[i] = backlerp*move[i] + frontlerp*frame->translate[i];
}
for (i=0 ; i<3 ; i++)
{
frontv[i] = frontlerp*frame->scale[i];
backv[i] = backlerp*oldframe->scale[i];
}
lerp = s_lerped[0];
GL_LerpVerts( paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv );
if ( gl_vertex_arrays->value )
{
float colorArray[MAX_VERTS*4];
qglEnableClientState( GL_VERTEX_ARRAY );
qglVertexPointer( 3, GL_FLOAT, 16, s_lerped ); // padded for SIMD
// if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE ) )
// PMM - added double damage shell
if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) )
{
qglColor4f( shadelight[0], shadelight[1], shadelight[2], alpha );
}
else
{
qglEnableClientState( GL_COLOR_ARRAY );
qglColorPointer( 3, GL_FLOAT, 0, colorArray );
//
// pre light everything
//
for ( i = 0; i < paliashdr->num_xyz; i++ )
{
float l = shadedots[verts[i].lightnormalindex];
colorArray[i*3+0] = l * shadelight[0];
colorArray[i*3+1] = l * shadelight[1];
colorArray[i*3+2] = l * shadelight[2];
}
}
if ( qglLockArraysEXT != 0 )
qglLockArraysEXT( 0, paliashdr->num_xyz );
while (1)
{
// get the vertex count and primitive type
count = *order++;
if (!count)
break; // done
if (count < 0)
{
count = -count;
qglBegin (GL_TRIANGLE_FAN);
}
else
{
qglBegin (GL_TRIANGLE_STRIP);
}
// PMM - added double damage shell
if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) )
{
do
{
index_xyz = order[2];
order += 3;
qglVertex3fv( s_lerped[index_xyz] );
} while (--count);
}
else
{
do
{
// texture coordinates come from the draw list
qglTexCoord2f (((float *)order)[0], ((float *)order)[1]);
index_xyz = order[2];
order += 3;
// normals and vertexes come from the frame list
// l = shadedots[verts[index_xyz].lightnormalindex];
// qglColor4f (l* shadelight[0], l*shadelight[1], l*shadelight[2], alpha);
qglArrayElement( index_xyz );
} while (--count);
}
qglEnd ();
}
if ( qglUnlockArraysEXT != 0 )
qglUnlockArraysEXT();
}
else
{
while (1)
{
// get the vertex count and primitive type
count = *order++;
if (!count)
break; // done
if (count < 0)
{
count = -count;
qglBegin (GL_TRIANGLE_FAN);
}
else
{
qglBegin (GL_TRIANGLE_STRIP);
}
if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE ) )
{
do
{
index_xyz = order[2];
order += 3;
qglColor4f( shadelight[0], shadelight[1], shadelight[2], alpha);
qglVertex3fv (s_lerped[index_xyz]);
} while (--count);
}
else
{
do
{
// texture coordinates come from the draw list
qglTexCoord2f (((float *)order)[0], ((float *)order)[1]);
index_xyz = order[2];
order += 3;
// normals and vertexes come from the frame list
l = shadedots[verts[index_xyz].lightnormalindex];
qglColor4f (l* shadelight[0], l*shadelight[1], l*shadelight[2], alpha);
qglVertex3fv (s_lerped[index_xyz]);
} while (--count);
}
qglEnd ();
}
}
// if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE ) )
// PMM - added double damage shell
if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) )
qglEnable( GL_TEXTURE_2D );
}
#if 1
/*
=============
GL_DrawAliasShadow
=============
*/
extern vec3_t lightspot;
void GL_DrawAliasShadow (dmdl_t *paliashdr, int posenum)
{
dtrivertx_t *verts;
int *order;
vec3_t point;
float height, lheight;
int count;
daliasframe_t *frame;
lheight = currententity->origin[2] - lightspot[2];
frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames
+ currententity->frame * paliashdr->framesize);
verts = frame->verts;
height = 0;
order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds);
height = -lheight + 1.0;
while (1)
{
// get the vertex count and primitive type
count = *order++;
if (!count)
break; // done
if (count < 0)
{
count = -count;
qglBegin (GL_TRIANGLE_FAN);
}
else
qglBegin (GL_TRIANGLE_STRIP);
do
{
// normals and vertexes come from the frame list
/*
point[0] = verts[order[2]].v[0] * frame->scale[0] + frame->translate[0];
point[1] = verts[order[2]].v[1] * frame->scale[1] + frame->translate[1];
point[2] = verts[order[2]].v[2] * frame->scale[2] + frame->translate[2];
*/
memcpy( point, s_lerped[order[2]], sizeof( point ) );
point[0] -= shadevector[0]*(point[2]+lheight);
point[1] -= shadevector[1]*(point[2]+lheight);
point[2] = height;
// height -= 0.001;
qglVertex3fv (point);
order += 3;
// verts++;
} while (--count);
qglEnd ();
}
}
#endif
/*
** R_CullAliasModel
*/
static qboolean R_CullAliasModel( vec3_t bbox[8], entity_t *e )
{
int i;
vec3_t mins, maxs;
dmdl_t *paliashdr;
vec3_t vectors[3];
vec3_t thismins, oldmins, thismaxs, oldmaxs;
daliasframe_t *pframe, *poldframe;
vec3_t angles;
paliashdr = (dmdl_t *)currentmodel->extradata;
if ( ( e->frame >= paliashdr->num_frames ) || ( e->frame < 0 ) )
{
ri.Con_Printf (PRINT_ALL, "R_CullAliasModel %s: no such frame %d\n",
currentmodel->name, e->frame);
e->frame = 0;
}
if ( ( e->oldframe >= paliashdr->num_frames ) || ( e->oldframe < 0 ) )
{
ri.Con_Printf (PRINT_ALL, "R_CullAliasModel %s: no such oldframe %d\n",
currentmodel->name, e->oldframe);
e->oldframe = 0;
}
pframe = ( daliasframe_t * ) ( ( byte * ) paliashdr +
paliashdr->ofs_frames +
e->frame * paliashdr->framesize);
poldframe = ( daliasframe_t * ) ( ( byte * ) paliashdr +
paliashdr->ofs_frames +
e->oldframe * paliashdr->framesize);
/*
** compute axially aligned mins and maxs
*/
if ( pframe == poldframe )
{
for ( i = 0; i < 3; i++ )
{
mins[i] = pframe->translate[i];
maxs[i] = mins[i] + pframe->scale[i]*255;
}
}
else
{
for ( i = 0; i < 3; i++ )
{
thismins[i] = pframe->translate[i];
thismaxs[i] = thismins[i] + pframe->scale[i]*255;
oldmins[i] = poldframe->translate[i];
oldmaxs[i] = oldmins[i] + poldframe->scale[i]*255;
if ( thismins[i] < oldmins[i] )
mins[i] = thismins[i];
else
mins[i] = oldmins[i];
if ( thismaxs[i] > oldmaxs[i] )
maxs[i] = thismaxs[i];
else
maxs[i] = oldmaxs[i];
}
}
/*
** compute a full bounding box
*/
for ( i = 0; i < 8; i++ )
{
vec3_t tmp;
if ( i & 1 )
tmp[0] = mins[0];
else
tmp[0] = maxs[0];
if ( i & 2 )
tmp[1] = mins[1];
else
tmp[1] = maxs[1];
if ( i & 4 )
tmp[2] = mins[2];
else
tmp[2] = maxs[2];
VectorCopy( tmp, bbox[i] );
}
/*
** rotate the bounding box
*/
VectorCopy( e->angles, angles );
angles[YAW] = -angles[YAW];
AngleVectors( angles, vectors[0], vectors[1], vectors[2] );
for ( i = 0; i < 8; i++ )
{
vec3_t tmp;
VectorCopy( bbox[i], tmp );
bbox[i][0] = DotProduct( vectors[0], tmp );
bbox[i][1] = -DotProduct( vectors[1], tmp );
bbox[i][2] = DotProduct( vectors[2], tmp );
VectorAdd( e->origin, bbox[i], bbox[i] );
}
{
int p, f, aggregatemask = ~0;
for ( p = 0; p < 8; p++ )
{
int mask = 0;
for ( f = 0; f < 4; f++ )
{
float dp = DotProduct( frustum[f].normal, bbox[p] );
if ( ( dp - frustum[f].dist ) < 0 )
{
mask |= ( 1 << f );
}
}
aggregatemask &= mask;
}
if ( aggregatemask )
{
return true;
}
return false;
}
}
/*
=================
R_DrawAliasModel
=================
*/
void R_DrawAliasModel (entity_t *e)
{
int i;
dmdl_t *paliashdr;
float an;
vec3_t bbox[8];
image_t *skin;
if ( !( e->flags & RF_WEAPONMODEL ) )
{
if ( R_CullAliasModel( bbox, e ) )
return;
}
if ( e->flags & RF_WEAPONMODEL )
{
if ( r_lefthand->value == 2 )
return;
}
paliashdr = (dmdl_t *)currentmodel->extradata;
//
// get lighting information
//
// PMM - rewrote, reordered to handle new shells & mixing
// PMM - 3.20 code .. replaced with original way of doing it to keep mod authors happy
//
if ( currententity->flags & ( RF_SHELL_HALF_DAM | RF_SHELL_GREEN | RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE ) )
{
VectorClear (shadelight);
if (currententity->flags & RF_SHELL_HALF_DAM)
{
shadelight[0] = 0.56;
shadelight[1] = 0.59;
shadelight[2] = 0.45;
}
if ( currententity->flags & RF_SHELL_DOUBLE )
{
shadelight[0] = 0.9;
shadelight[1] = 0.7;
}
if ( currententity->flags & RF_SHELL_RED )
shadelight[0] = 1.0;
if ( currententity->flags & RF_SHELL_GREEN )
shadelight[1] = 1.0;
if ( currententity->flags & RF_SHELL_BLUE )
shadelight[2] = 1.0;
}
/*
// PMM -special case for godmode
if ( (currententity->flags & RF_SHELL_RED) &&
(currententity->flags & RF_SHELL_BLUE) &&
(currententity->flags & RF_SHELL_GREEN) )
{
for (i=0 ; i<3 ; i++)
shadelight[i] = 1.0;
}
else if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE ) )
{
VectorClear (shadelight);
if ( currententity->flags & RF_SHELL_RED )
{
shadelight[0] = 1.0;
if (currententity->flags & (RF_SHELL_BLUE|RF_SHELL_DOUBLE) )
shadelight[2] = 1.0;
}
else if ( currententity->flags & RF_SHELL_BLUE )
{
if ( currententity->flags & RF_SHELL_DOUBLE )
{
shadelight[1] = 1.0;
shadelight[2] = 1.0;
}
else
{
shadelight[2] = 1.0;
}
}
else if ( currententity->flags & RF_SHELL_DOUBLE )
{
shadelight[0] = 0.9;
shadelight[1] = 0.7;
}
}
else if ( currententity->flags & ( RF_SHELL_HALF_DAM | RF_SHELL_GREEN ) )
{
VectorClear (shadelight);
// PMM - new colors
if ( currententity->flags & RF_SHELL_HALF_DAM )
{
shadelight[0] = 0.56;
shadelight[1] = 0.59;
shadelight[2] = 0.45;
}
if ( currententity->flags & RF_SHELL_GREEN )
{
shadelight[1] = 1.0;
}
}
}
//PMM - ok, now flatten these down to range from 0 to 1.0.
// max_shell_val = max(shadelight[0], max(shadelight[1], shadelight[2]));
// if (max_shell_val > 0)
// {
// for (i=0; i<3; i++)
// {
// shadelight[i] = shadelight[i] / max_shell_val;
// }
// }
// pmm
*/
else if ( currententity->flags & RF_FULLBRIGHT )
{
for (i=0 ; i<3 ; i++)
shadelight[i] = 1.0;
}
else
{
R_LightPoint (currententity->origin, shadelight);
// player lighting hack for communication back to server
// big hack!
if ( currententity->flags & RF_WEAPONMODEL )
{
// pick the greatest component, which should be the same
// as the mono value returned by software
if (shadelight[0] > shadelight[1])
{
if (shadelight[0] > shadelight[2])
r_lightlevel->value = 150*shadelight[0];
else
r_lightlevel->value = 150*shadelight[2];
}
else
{
if (shadelight[1] > shadelight[2])
r_lightlevel->value = 150*shadelight[1];
else
r_lightlevel->value = 150*shadelight[2];
}
}
if ( gl_monolightmap->string[0] != '0' )
{
float s = shadelight[0];
if ( s < shadelight[1] )
s = shadelight[1];
if ( s < shadelight[2] )
s = shadelight[2];
shadelight[0] = s;
shadelight[1] = s;
shadelight[2] = s;
}
}
if ( currententity->flags & RF_MINLIGHT )
{
for (i=0 ; i<3 ; i++)
if (shadelight[i] > 0.1)
break;
if (i == 3)
{
shadelight[0] = 0.1;
shadelight[1] = 0.1;
shadelight[2] = 0.1;
}
}
if ( currententity->flags & RF_GLOW )
{ // bonus items will pulse with time
float scale;
float min;
scale = 0.1 * sin(r_newrefdef.time*7);
for (i=0 ; i<3 ; i++)
{
min = shadelight[i] * 0.8;
shadelight[i] += scale;
if (shadelight[i] < min)
shadelight[i] = min;
}
}
// =================
// PGM ir goggles color override
if ( r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE)
{
shadelight[0] = 1.0;
shadelight[1] = 0.0;
shadelight[2] = 0.0;
}
// PGM
// =================
shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
an = currententity->angles[1]/180*M_PI;
shadevector[0] = cos(-an);
shadevector[1] = sin(-an);
shadevector[2] = 1;
VectorNormalize (shadevector);
//
// locate the proper data
//
c_alias_polys += paliashdr->num_tris;
//
// draw all the triangles
//
if (currententity->flags & RF_DEPTHHACK) // hack the depth range to prevent view model from poking into walls
qglDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
if ( ( currententity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) )
{
extern void MYgluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar );
qglMatrixMode( GL_PROJECTION );
qglPushMatrix();
qglLoadIdentity();
qglScalef( -1, 1, 1 );
MYgluPerspective( r_newrefdef.fov_y, ( float ) r_newrefdef.width / r_newrefdef.height, 4, 4096);
qglMatrixMode( GL_MODELVIEW );
qglCullFace( GL_BACK );
}
qglPushMatrix ();
e->angles[PITCH] = -e->angles[PITCH]; // sigh.
R_RotateForEntity (e);
e->angles[PITCH] = -e->angles[PITCH]; // sigh.
// select skin
if (currententity->skin)
skin = currententity->skin; // custom player skin
else
{
if (currententity->skinnum >= MAX_MD2SKINS)
skin = currentmodel->skins[0];
else
{
skin = currentmodel->skins[currententity->skinnum];
if (!skin)
skin = currentmodel->skins[0];
}
}
if (!skin)
skin = r_notexture; // fallback...
GL_Bind(skin->texnum);
// draw it
qglShadeModel (GL_SMOOTH);
GL_TexEnv( GL_MODULATE );
if ( currententity->flags & RF_TRANSLUCENT )
{
qglEnable (GL_BLEND);
}
if ( (currententity->frame >= paliashdr->num_frames)
|| (currententity->frame < 0) )
{
ri.Con_Printf (PRINT_ALL, "R_DrawAliasModel %s: no such frame %d\n",
currentmodel->name, currententity->frame);
currententity->frame = 0;
currententity->oldframe = 0;
}
if ( (currententity->oldframe >= paliashdr->num_frames)
|| (currententity->oldframe < 0))
{
ri.Con_Printf (PRINT_ALL, "R_DrawAliasModel %s: no such oldframe %d\n",
currentmodel->name, currententity->oldframe);
currententity->frame = 0;
currententity->oldframe = 0;
}
if ( !r_lerpmodels->value )
currententity->backlerp = 0;
GL_DrawAliasFrameLerp (paliashdr, currententity->backlerp);
GL_TexEnv( GL_REPLACE );
qglShadeModel (GL_FLAT);
qglPopMatrix ();
#if 0
qglDisable( GL_CULL_FACE );
qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
qglDisable( GL_TEXTURE_2D );
qglBegin( GL_TRIANGLE_STRIP );
for ( i = 0; i < 8; i++ )
{
qglVertex3fv( bbox[i] );
}
qglEnd();
qglEnable( GL_TEXTURE_2D );
qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
qglEnable( GL_CULL_FACE );
#endif
if ( ( currententity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) )
{
qglMatrixMode( GL_PROJECTION );
qglPopMatrix();
qglMatrixMode( GL_MODELVIEW );
qglCullFace( GL_FRONT );
}
if ( currententity->flags & RF_TRANSLUCENT )
{
qglDisable (GL_BLEND);
}
if (currententity->flags & RF_DEPTHHACK)
qglDepthRange (gldepthmin, gldepthmax);
#if 1
if (gl_shadows->value && !(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL)))
{
qglPushMatrix ();
R_RotateForEntity (e);
qglDisable (GL_TEXTURE_2D);
qglEnable (GL_BLEND);
qglColor4f (0,0,0,0.5);
GL_DrawAliasShadow (paliashdr, currententity->frame );
qglEnable (GL_TEXTURE_2D);
qglDisable (GL_BLEND);
qglPopMatrix ();
}
#endif
qglColor4f (1,1,1,1);
}