/* 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. */ // r_mesh.c: triangle model functions #include "r_local.h" #include "vlights.h" /* ============================================================= ALIAS MODELS ============================================================= */ #define NUMVERTEXNORMALS 162 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; #ifndef __unix__ typedef float vec4_t[4]; #endif static vec4_t s_lerped[MAX_VERTS]; //static vec3_t lerped[MAX_VERTS]; extern vec3_t lightspot; vec3_t viewdir; vec3_t lightdir; vec3_t shadevector; vec3_t shadelight; #define MAX_MODEL_DLIGHTS 32 // was 8 m_dlight_t model_dlights[MAX_MODEL_DLIGHTS]; int model_dlights_num; // 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 vectoangles (vec3_t value1, vec3_t angles); #if 1 void R_LerpVerts( int nverts, dtrivertx_t *v, dtrivertx_t *ov, dtrivertx_t *verts, float *lerp, float move[3], float frontv[3], float backv[3], float normalscale ) { int i; 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] * normalscale; lerp[1] = move[1] + ov->v[1]*backv[1] + v->v[1]*frontv[1] + normal[1] * normalscale; lerp[2] = move[2] + ov->v[2]*backv[2] + v->v[2]*frontv[2] + normal[2] * normalscale; } } #else void R_LerpVerts( int nverts, dtrivertx_t *v, dtrivertx_t *ov, dtrivertx_t *verts, float *lerp, float move[3], float frontv[3], float backv[3], float normalscale ) { 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] * normalscale; lerp[1] = move[1] + ov->v[1]*backv[1] + v->v[1]*frontv[1] + normal[1] * normalscale; lerp[2] = move[2] + ov->v[2]*backv[2] + v->v[2]*frontv[2] + normal[2] * normalscale; } } 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]; } } } #endif float mirrorValue (float value, qboolean mirrormodel) { if (mirrormodel) { if (value>1) return 0; else if (value<0) return 1; else return 1-value; } return value; } float calcEntAlpha (float alpha, vec3_t point) { float newAlpha; vec3_t vert_len; newAlpha = alpha; if (!(currententity->renderfx&RF2_CAMERAMODEL) || !(currententity->flags&RF_TRANSLUCENT)) { if (newAlpha<0) newAlpha = 0; if (newAlpha>1) newAlpha = 1; return newAlpha; } VectorSubtract(r_newrefdef.vieworg, point, vert_len); newAlpha *= VectorLength(vert_len)/cl_3dcam_dist->value; if (newAlpha>alpha) newAlpha = alpha; if (newAlpha<0) newAlpha = 0; if (newAlpha>1) newAlpha = 1; return newAlpha; } void capColorVec(vec3_t color) { int i; for (i=0;i<3;i++) { if (color[i]>1) color[i] = 1; if (color[i]<0) color[i] = 0; } } void SetVertexOverbrights (qboolean toggle) { if (!r_overbrightbits->value || !gl_config.mtexcombine) return; if (toggle) //turn on { qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, r_overbrightbits->value); qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE); GL_TexEnv( GL_COMBINE_EXT ); } else //turn off { GL_TexEnv( GL_MODULATE ); qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1); } } qboolean EnvMapShell (void) { return ( (r_shelltype->value == 2) || (r_shelltype->value == 1 && currententity->alpha == 1.0f) ); } qboolean FlowingShell (void) { return (r_shelltype->value == 1 && currententity->alpha != 1.0f); } void SetShellBlend (qboolean toggle) { // shells only if ( !(currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) ) return; if (toggle) //turn on { // Knightmare- added Psychospaz's envmapping //if (gl_envmaps->value) if (EnvMapShell()) { qglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); qglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); GL_Bind(r_spheremappic->texnum); qglEnable(GL_TEXTURE_GEN_S); qglEnable(GL_TEXTURE_GEN_T); } else if (FlowingShell()) GL_Bind(r_shelltexture->texnum); else qglDisable(GL_TEXTURE_2D); if (gl_stencil->value) GL_Stencil(true, true); } else //turn off { // Knightmare- added Psychospaz's envmapping //if (gl_envmaps->value) if (EnvMapShell()) { qglDisable(GL_TEXTURE_GEN_S); qglDisable(GL_TEXTURE_GEN_T); } else if (FlowingShell()) {} else qglEnable( GL_TEXTURE_2D ); if (gl_stencil->value) GL_Stencil(false, true); } } void lightAliasModel (vec3_t baselight, dtrivertx_t *verts, dtrivertx_t *ov, float backlerp, vec3_t lightOut) { int i; float l; if (r_model_shading->value) { //l = 2.0 * VLight_LerpLight (verts->lightnormalindex, ov->lightnormalindex, // backlerp, lightdir, currententity->angles, false); if (r_model_shading->value == 2) l = 2.0 * shadedots[verts->lightnormalindex] - 1; else l = shadedots[verts->lightnormalindex]; VectorScale(baselight, l, lightOut); if (model_dlights_num) for (i=0; ilightnormalindex, ov->lightnormalindex, backlerp, model_dlights[i].direction, currententity->angles, true ); VectorMA(lightOut, l, model_dlights[i].color, lightOut); } } else { l = 2.0 * VLight_LerpLight (verts->lightnormalindex, ov->lightnormalindex, backlerp, lightdir, currententity->angles, false); //l = shadedots[verts->lightnormalindex]; VectorScale(baselight, l, lightOut); } for (i=0; i<3; i++) { if (lightOut[i]<0) lightOut[i] = 0; if (lightOut[i]>1) lightOut[i] = 1; } } void R_DrawAliasFrameLerp (dmdl_t *paliashdr, float backlerp) { 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; vec3_t lightcolor; int i; int index_xyz; float *lerp; int va = 0; float mode; //float l; //qboolean envmap_pass = false; if (currententity->flags & RF_VIEWERMODEL) return; 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; SetShellBlend (true); 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]; if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) ) { if (currententity->flags & RF_WEAPONMODEL) R_LerpVerts( paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv, WEAPON_SHELL_SCALE ); else R_LerpVerts( paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv, POWERSUIT_SCALE ); if (FlowingShell()) alpha = 0.7; } else R_LerpVerts( paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv, 0); SetVertexOverbrights (true); // added //set base light color VectorCopy(shadelight, lightcolor); for (i=0;iflags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM ) ) { do { index_xyz = order[2]; order += 3; if (FlowingShell()) VA_SetElem2(tex_array[va], (s_lerped[index_xyz][1] + s_lerped[index_xyz][0]) / 40.0, s_lerped[index_xyz][2] / 40.0 - r_newrefdef.time / 2.0); VA_SetElem3(vert_array[va],s_lerped[index_xyz][0],s_lerped[index_xyz][1],s_lerped[index_xyz][2]); VA_SetElem4(col_array[va], shadelight[0], shadelight[1], shadelight[2], alpha); va++; } while (--count); } else { do { float thisalpha; // texture coordinates come from the draw list // normals and vertexes come from the frame list index_xyz = order[2]; //l = shadedots[verts[index_xyz].lightnormalindex]; //VectorScale(shadelight, l, lightcolor); lightAliasModel (shadelight, &verts[index_xyz], &ov[index_xyz], backlerp, lightcolor); thisalpha = calcEntAlpha(alpha, s_lerped[index_xyz]); VA_SetElem2(tex_array[va],((float *)order)[0], ((float *)order)[1]); VA_SetElem3(vert_array[va],s_lerped[index_xyz][0],s_lerped[index_xyz][1],s_lerped[index_xyz][2]); VA_SetElem4(col_array[va],lightcolor[0], lightcolor[1], lightcolor[2], thisalpha); va++; order += 3; } while (--count); } qglDrawArrays(mode,0,va); } qglDisableClientState( GL_COLOR_ARRAY ); #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 | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM ) ) { do { index_xyz = order[2]; order += 3; qglColor4f( shadelight[0], shadelight[1], shadelight[2], alpha); if (FlowingShell()) qglTexCoord2f ((s_lerped[index_xyz][1] + s_lerped[index_xyz][0]) / 40.0, s_lerped[index_xyz][2] / 40.0 - r_newrefdef.time / 2.0); qglVertex3fv (s_lerped[index_xyz]); } while (--count); } else { do { float thisalpha; // texture coordinates come from the draw list // normals and vertexes come from the frame list index_xyz = order[2]; //l = shadedots[verts[index_xyz].lightnormalindex]; //VectorScale(shadelight, l, lightcolor); lightAliasModel (shadelight, &verts[index_xyz], &ov[index_xyz], backlerp, lightcolor); thisalpha = calcEntAlpha(alpha, s_lerped[index_xyz]); qglColor4f (lightcolor[0], lightcolor[1], lightcolor[2], thisalpha); qglTexCoord2f (((float *)order)[0], ((float *)order)[1]); qglVertex3fv (s_lerped[index_xyz]); order += 3; } while (--count); } qglEnd (); } #endif // PMM - added double damage shell SetShellBlend (false); SetVertexOverbrights (false); // added } #ifdef PROJECTION_SHADOWS /* ============= CastVolumeShadow projection shadows from BeefQuake R6 ============= */ #ifdef __unix__ #define BOOL int #endif void CastVolumeShadow (dmdl_t *hdr, vec3_t light, float projectdistance) { dtriangle_t *ot, *tris; int i, j;//, k; BOOL trianglefacinglight[MAX_TRIANGLES]; vec3_t v0,v1,v2,v3; //float norm[3], dist, dot1; //float tnorm[3], tdist, dot2, blah[3]; //vec3_t normvec; daliasframe_t *frame; //, *oldframe dtrivertx_t *verts; //, *ov frame = (daliasframe_t *)((byte *)hdr + hdr->ofs_frames + currententity->frame * hdr->framesize); verts = frame->verts; ot = tris = (dtriangle_t *)((unsigned char*)hdr + hdr->ofs_tris); for (i=0; inum_tris; i++, tris++) { for (j=0; j<3; j++) { v0[j] = s_lerped[tris->index_xyz[0]][j]; v1[j] = s_lerped[tris->index_xyz[1]][j]; v2[j] = s_lerped[tris->index_xyz[2]][j]; } trianglefacinglight[i] = (light[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1])) + (light[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2])) + (light[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0; } qglBegin(GL_QUADS); for (i=0, tris=ot; inum_tris; i++, tris++) { if (!trianglefacinglight[i]) continue; if (!trianglefacinglight[currentmodel->edge_tri[i][0]]) { for (j=0; j<3; j++) { v0[j]=s_lerped[tris->index_xyz[1]][j]; v1[j]=s_lerped[tris->index_xyz[0]][j]; v2[j]=v1[j]+((v1[j]-light[j]) * projectdistance); v3[j]=v0[j]+((v0[j]-light[j]) * projectdistance); } qglVertex3fv(v0); qglVertex3fv(v1); qglVertex3fv(v2); qglVertex3fv(v3); } if (!trianglefacinglight[currentmodel->edge_tri[i][1]]) { for (j=0; j<3; j++) { v0[j]=s_lerped[tris->index_xyz[2]][j]; v1[j]=s_lerped[tris->index_xyz[1]][j]; v2[j]=v1[j]+((v1[j]-light[j]) * projectdistance); v3[j]=v0[j]+((v0[j]-light[j]) * projectdistance); } qglVertex3fv(v0); qglVertex3fv(v1); qglVertex3fv(v2); qglVertex3fv(v3); } if (!trianglefacinglight[currentmodel->edge_tri[i][2]]) { for (j=0; j<3; j++) { v0[j]=s_lerped[tris->index_xyz[0]][j]; v1[j]=s_lerped[tris->index_xyz[2]][j]; v2[j]=v1[j]+((v1[j]-light[j]) * projectdistance); v3[j]=v0[j]+((v0[j]-light[j]) * projectdistance); } qglVertex3fv(v0); qglVertex3fv(v1); qglVertex3fv(v2); qglVertex3fv(v3); } } qglEnd(); // cap the volume qglBegin(GL_TRIANGLES); for (i=0, tris=ot; inum_tris; i++, tris++) { if (trianglefacinglight[i]) { for (j=0; j<3; j++) { v0[j]=s_lerped[tris->index_xyz[0]][j]; v1[j]=s_lerped[tris->index_xyz[1]][j]; v2[j]=s_lerped[tris->index_xyz[2]][j]; } qglVertex3fv(v0); qglVertex3fv(v1); qglVertex3fv(v2); continue; } for (j=0; j<3; j++) { v0[j]=s_lerped[tris->index_xyz[0]][j]; v1[j]=s_lerped[tris->index_xyz[1]][j]; v2[j]=s_lerped[tris->index_xyz[2]][j]; v0[j]=v0[j]+((v0[j]-light[j]) * projectdistance); v1[j]=v1[j]+((v1[j]-light[j]) * projectdistance); v2[j]=v2[j]+((v2[j]-light[j]) * projectdistance); } qglVertex3fv(v0); qglVertex3fv(v1); qglVertex3fv(v2); } qglEnd(); } /* ============= R_DrawAliasShadowVolume projection shadows from BeefQuake R6 ============= */ void R_ShadowBlend (float alpha); void R_DrawAliasShadowVolume (dmdl_t *paliashdr, int posenum) { int *order; vec3_t light, temp, vecAdd; // point, proj, norm, vec3_t static_offset; //l_origin; // Knightmare added //float height, lheight; //int count; int /*va = 0,*/ i, lnum; //, o; float projected_distance = 4; //mode, dtriangle_t *t, *tris; daliasframe_t *frame, *oldframe; dtrivertx_t *ov, *verts; dlight_t *dl; float cost, sint; float is, it, dist; //, add; // int pass = 0; if (currentmodel->noshadow) return; dl = r_newrefdef.dlights; t = tris = (dtriangle_t *)((unsigned char*)paliashdr + paliashdr->ofs_tris); frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + currententity->frame * paliashdr->framesize); verts = frame->verts; oldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + currententity->oldframe * paliashdr->framesize); ov = oldframe->verts; order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); cost = cos(-currententity->angles[1] / 180 * M_PI), sint = sin(-currententity->angles[1] / 180 * M_PI); //cost = cos(currententity->angles[1] / 180 * M_PI), sint = sin(currententity->angles[1] / 180 * M_PI); VectorSet(static_offset, 576,0,1024); // Knightmare- set static vector, was 144,0,256 qglColorMask(0,0,0,0); qglClear(GL_STENCIL_BUFFER_BIT); qglEnable(GL_STENCIL_TEST); qglDepthMask(0); qglDepthFunc( GL_LESS ); qglStencilFunc( GL_ALWAYS, 0, 0xFF); VectorClear(vecAdd); for (i=0, lnum=0; iorigin, currententity->origin)) continue; VectorSubtract(dl->origin, currententity->origin, temp); dist = sqrt(DotProduct(temp,temp)); if (dist > 384) continue; lnum++; // Factor in the intensity of a dlight? //VectorScale(vecAdd, (dl->intensity * (DIV256)), vecAdd); VectorAdd (vecAdd, temp, vecAdd); } if (lnum > 0) VectorCopy(vecAdd, light); else VectorCopy(static_offset, light); is = light[0], it = light[1]; light[0] = (cost * (is - 0) + sint * (0 - it) + 0); light[1] = (cost * (it - 0) + sint * (is - 0) + 0); light[2] += 8; // increment stencil if backface is behind depthbuffer qglCullFace(GL_BACK); // quake is backwards, this culls front faces qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP); CastVolumeShadow(paliashdr, light, projected_distance); // decrement stencil if frontface is behind depthbuffer qglCullFace(GL_FRONT); // quake is backwards, this culls back faces qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP); CastVolumeShadow(paliashdr, light, projected_distance); /*for (i=-1; iorigin[0] == currententity->origin[0]) && (dl->origin[1] == currententity->origin[1]) && (dl->origin[2] == currententity->origin[2])) continue; // -1 pass only for no dynamic lights if (i == -1 && r_newrefdef.num_dlights > 0) continue; // VectorSubtract(a, b, temp); // dist = sqrt(DotProduct(temp, temp)); if (i == -1) // static vector VectorAdd(currententity->origin, static_offset, l_origin); else // light origin VectorCopy(dl->origin, l_origin); if (i >= 0) { VectorSubtract(currententity->origin, l_origin, temp); // was l->origin dist = sqrt(DotProduct(temp,temp)); if (dist > 384) continue; } // projected_distance = (384 - dist) / (384 / 4); for (o=0; o<3; o++) light[o] = -currententity->origin[o] + l_origin[o]; // lights origin in relation to the entity, was l->origin is = light[0], it = light[1]; light[0] = (cost * (is - 0) + sint * (0 - it) + 0); light[1] = (cost * (it - 0) + sint * (is - 0) + 0); light[2] += 8; // increment stencil if backface is behind depthbuffer qglCullFace(GL_BACK); // quake is backwards, this culls front faces qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP); CastVolumeShadow(paliashdr, light, projected_distance); // decrement stencil if frontface is behind depthbuffer qglCullFace(GL_FRONT); // quake is backwards, this culls back faces qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP); CastVolumeShadow(paliashdr, light, projected_distance); dl++; // increment dl }*/ qglDisable(GL_STENCIL_TEST); qglColorMask(1,1,1,1); qglDepthMask(1); qglDepthFunc(GL_LEQUAL); // draw shadows for this model now R_ShadowBlend (gl_shadowalpha->value * currententity->alpha); } #endif /* ============= R_DrawAliasShadow ============= */ void R_DrawAliasShadow (dmdl_t *paliashdr, int posenum, qboolean mirrored) { dtrivertx_t *verts; int *order; vec3_t point; float height, lheight; int count; daliasframe_t *frame; #ifdef PROJECTION_SHADOWS // projection shadows from BeefQuake R6 if (gl_shadows->value == 3) { R_DrawAliasShadowVolume (paliashdr, posenum); return; } #endif // end projection shadows from BeefQuake R6 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 + 0.1f; //Knightare- 12/24/2001- lowered shadows to ground // if above entity's origin, skip if ((currententity->origin[2]+height) > currententity->origin[2]) return; // Knightmare- don't draw shadows above view origin, thnx to MrG if (r_newrefdef.vieworg[2] < (currententity->origin[2] + height)) return; GL_Stencil(true, false); GL_BlendFunction (GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA); 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 (); } //Knightmare- 12/24/2001- stencil buffer GL_Stencil(false, false); } /* ** 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 ) ) { VID_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 ) ) { VID_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; } } #if 0 static void R_FindBBox( 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 ) ) { VID_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 ) ) { VID_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] ); } } #endif /* ================= R_FlipModel ================= */ void R_FlipModel (qboolean on) { extern void MYgluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar ); if (on) { 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 ); } else { qglMatrixMode( GL_PROJECTION ); qglPopMatrix(); qglMatrixMode( GL_MODELVIEW ); qglCullFace( GL_FRONT ); } } /* ================= setShadeLight ================= */ void R_ShadowLight (vec3_t pos, vec3_t lightAdd); void R_LightPointDynamics (vec3_t p, vec3_t color, m_dlight_t *list, int *amount, int max); void setShadeLight (void) { int i; 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; } else if ( currententity->flags & RF_FULLBRIGHT ) { for (i=0 ; i<3 ; i++) shadelight[i] = 1.0; } else { //Set up basic lighting... if (r_model_shading->value && r_model_dlights->value) { int max = r_model_dlights->value; if (max<0) max=0; if (max>MAX_MODEL_DLIGHTS) max=MAX_MODEL_DLIGHTS; R_LightPointDynamics (currententity->origin, shadelight, model_dlights, &model_dlights_num, max); } else { R_LightPoint (currententity->origin, shadelight, false);//true); model_dlights_num = 0; } // 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.2 * 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) { if (currententity->flags & RF_IR_VISIBLE) { shadelight[0] = 1.0; shadelight[1] = 0.0; shadelight[2] = 0.0; } /*else { shadelight[0] = 0.0; shadelight[1] = 0.0; shadelight[2] = 0.5; }*/ } /* if (r_newrefdef.rdflags & RDF_UVGOGGLES) { if (currententity->flags & RF_IR_VISIBLE) { shadelight[0] = 0.5; shadelight[1] = 1.0; shadelight[2] = 0.5; } else { shadelight[0] = 0.0; shadelight[1] = 1.0; shadelight[2] = 0.0; } }*/ // PGM // ================= shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; } void setBlendModeOn (image_t *skin) { GL_TexEnv( GL_MODULATE ); if (skin) GL_Bind(skin->texnum); GL_ShadeModel (GL_SMOOTH); if (currententity->flags & RF_TRANSLUCENT) { qglDepthMask (false); if (currententity->flags & RF_TRANS_ADDITIVE) { GL_BlendFunction (GL_SRC_ALPHA, GL_ONE); qglColor4ub(255, 255, 255, 255); GL_ShadeModel (GL_FLAT); } else GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLSTATE_ENABLE_BLEND } } void setBlendModeOff () { if ( currententity->flags & RF_TRANSLUCENT ) { qglDepthMask (true); GLSTATE_DISABLE_BLEND GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } /* ================= R_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *e) { dmdl_t *paliashdr; vec3_t bbox[8]; image_t *skin; qboolean mirrormodel = false; // also skip this for viewermodels and cameramodels if ( !(e->flags & RF_WEAPONMODEL || e->flags & RF_VIEWERMODEL || e->renderfx & RF2_CAMERAMODEL) ) { if ( R_CullAliasModel( bbox, e ) ) return; } if (e->flags & RF_WEAPONMODEL) { if (r_lefthand->value == 2) return; else if (r_lefthand->value == 1) mirrormodel = true; } else if (e->renderfx & RF2_CAMERAMODEL) { if (r_lefthand->value==1) mirrormodel = true; } else if (currententity->flags & RF_MIRRORMODEL) mirrormodel = true; 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 // setShadeLight(); // // 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 { if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) qglDepthRange (gldepthmin, gldepthmin + 0.01*(gldepthmax-gldepthmin)); else qglDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); } if (mirrormodel) R_FlipModel(true); qglPushMatrix (); e->angles[PITCH] = -e->angles[PITCH]; // sigh. R_RotateForEntity (e, true); 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][0]; else { skin = currentmodel->skins[0][currententity->skinnum]; if (!skin) skin = currentmodel->skins[0][0]; } } if (!skin) skin = r_notexture; // fallback... if ( (currententity->frame >= paliashdr->num_frames) || (currententity->frame < 0) ) { VID_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)) { VID_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; setBlendModeOn(skin); // Q2max add R_DrawAliasFrameLerp (paliashdr, currententity->backlerp); GL_TexEnv( GL_REPLACE ); GL_ShadeModel (GL_FLAT); qglPopMatrix (); if (mirrormodel) R_FlipModel(false); //Knightmare- show model bounding box if (gl_showbbox->value && !(currententity->flags & RF_WEAPONMODEL) && !(currententity->flags & RF_BEAM)) { qglDisable( GL_CULL_FACE ); qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); qglDisable( GL_TEXTURE_2D ); // Draw top and sides qglBegin( GL_TRIANGLE_STRIP ); qglVertex3fv( bbox[2] ); qglVertex3fv( bbox[1] ); qglVertex3fv( bbox[0] ); qglVertex3fv( bbox[1] ); qglVertex3fv( bbox[4] ); qglVertex3fv( bbox[5] ); qglVertex3fv( bbox[1] ); qglVertex3fv( bbox[7] ); qglVertex3fv( bbox[3] ); qglVertex3fv( bbox[2] ); qglVertex3fv( bbox[7] ); qglVertex3fv( bbox[6] ); qglVertex3fv( bbox[2] ); qglVertex3fv( bbox[4] ); qglVertex3fv( bbox[0] ); qglEnd(); // Draw bottom qglBegin( GL_TRIANGLE_STRIP ); qglVertex3fv( bbox[4] ); qglVertex3fv( bbox[6] ); qglVertex3fv( bbox[7] ); qglEnd(); qglEnable( GL_TEXTURE_2D ); qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); qglEnable( GL_CULL_FACE ); } //end Knightmare setBlendModeOff(); // Q2max add if (currententity->flags & RF_DEPTHHACK) qglDepthRange (gldepthmin, gldepthmax); // Knightmare- added noshadow flag if (!(currententity->flags & (RF_WEAPONMODEL | RF_NOSHADOW)) // no shadows from shells && !( (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) && (currententity->flags & RF_TRANSLUCENT) ) ) { float an = currententity->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); switch ((int)(gl_shadows->value)) { case 0: break; case 2: //dynamic lighted shadows - psychospaz R_ShadowLight (currententity->origin, shadevector); default: { qglPushMatrix (); //Knightmare- 12/24/2001- don't rotate shadows on bad axis //R_RotateForEntity (e); qglTranslatef (e->origin[0], e->origin[1], e->origin[2]); qglRotatef (e->angles[1], 0, 0, 1); //end Knightmare qglDisable (GL_TEXTURE_2D); qglEnable (GL_BLEND); if (currententity->flags & RF_TRANSLUCENT) qglColor4f (0,0,0,gl_shadowalpha->value * currententity->alpha); //Knightmare- variable alpha else qglColor4f (0,0,0,gl_shadowalpha->value); R_DrawAliasShadow (paliashdr, currententity->frame, mirrormodel); qglEnable (GL_TEXTURE_2D); qglDisable (GL_BLEND); qglPopMatrix (); } break; } } qglColor4f (1,1,1,1); } #if 0 /* ================= R_DrawAliasModelShadow Just draws the shadow for a model. ================= */ void R_DrawAliasModelShadow (entity_t *e) { dmdl_t *paliashdr; vec3_t bbox[8]; qboolean mirrormodel = false; float an; if (currententity->flags & (RF_WEAPONMODEL | RF_NOSHADOW)) return; // no shadows from shells if ( (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) && (currententity->flags & RF_TRANSLUCENT) ) return; // also skip this for viewermodels and cameramodels if ( !(e->flags & RF_WEAPONMODEL || e->flags & RF_VIEWERMODEL || e->renderfx & RF2_CAMERAMODEL) ) { if ( R_CullAliasModel( bbox, e ) ) return; } if (e->renderfx & RF2_CAMERAMODEL) { if (r_lefthand->value==1) mirrormodel = true; } else if (currententity->flags & RF_MIRRORMODEL) mirrormodel = true; paliashdr = (dmdl_t *)currentmodel->extradata; // if (mirrormodel) // R_FlipModel(true); if ( (currententity->frame >= paliashdr->num_frames) || (currententity->frame < 0) ) { currententity->frame = 0; currententity->oldframe = 0; } if ( (currententity->oldframe >= paliashdr->num_frames) || (currententity->oldframe < 0)) { currententity->frame = 0; currententity->oldframe = 0; } //if ( !r_lerpmodels->value ) // currententity->backlerp = 0; an = currententity->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); switch ((int)(gl_shadows->value)) { case 0: break; case 2: //dynamic lighted shadows - psychospaz R_ShadowLight (currententity->origin, shadevector); default: { qglPushMatrix (); //Knightmare- 12/24/2001- don't rotate shadows on bad axis //R_RotateForEntity (e); qglTranslatef (e->origin[0], e->origin[1], e->origin[2]); qglRotatef (e->angles[1], 0, 0, 1); //end Knightmare qglDisable (GL_TEXTURE_2D); qglEnable (GL_BLEND); if (currententity->flags & RF_TRANSLUCENT) qglColor4f (0,0,0,gl_shadowalpha->value * currententity->alpha); //Knightmare- variable alpha else qglColor4f (0,0,0,gl_shadowalpha->value); R_DrawAliasShadow (paliashdr, currententity->frame, mirrormodel); qglEnable (GL_TEXTURE_2D); qglDisable (GL_BLEND); qglPopMatrix (); } break; } // if (mirrormodel) // R_FlipModel(false); qglColor4f (1,1,1,1); } #endif