/* Copyright (C) 1996-1997 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_main.c #include "quakedef.h" #include "neh.h" entity_t r_worldentity; qboolean r_cache_thrash; // compatability vec3_t modelorg, r_entorigin; entity_t *currententity; int r_visframecount; // bumped when going to a new PVS int r_framecount; // used for dlight push checking mplane_t frustum[4]; int c_brush_polys, c_alias_polys; qboolean envmap; // true during envmap command capture int currenttexture = -1; // to avoid unnecessary texture sets int cnttextures[2] = {-1, -1}; // cached int particletexture; // little dot for particles int playertextures; // up to 16 color translated skins int skyboxtextures; int mirrortexturenum; // quake texturenum, not gltexturenum qboolean mirror; mplane_t *mirror_plane; // // view origin // vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_world_matrix[16]; float r_base_world_matrix[16]; // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; int d_lightstylevalue[256]; // 8.8 fraction of base light value void R_MarkLeaves (void); // Nehahra - Interpolation void GL_DrawAliasBlendedShadow (aliashdr_t *paliashdr, int pose1, int pose2, entity_t* e); void R_BlendedRotateForEntity (entity_t *e); void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t* e); void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend); // Nehahra - End cvar_t r_norefresh = {"r_norefresh","0"}; cvar_t r_drawentities = {"r_drawentities","1"}; cvar_t r_drawviewmodel = {"r_drawviewmodel","1"}; cvar_t r_speeds = {"r_speeds","0"}; cvar_t r_fullbright = {"r_fullbright","0"}; cvar_t r_lightmap = {"r_lightmap","0"}; cvar_t r_shadows = {"r_shadows","0"}; cvar_t r_mirroralpha = {"r_mirroralpha","1"}; cvar_t r_wateralpha = {"r_wateralpha","1"}; cvar_t r_dynamic = {"r_dynamic","1"}; cvar_t r_novis = {"r_novis","0"}; cvar_t r_flatlightstyles = {"r_flatlightstyles", "0"}; cvar_t r_clearcolor = {"r_clearcolor", "251"}; // Closest to the original cvar_t r_oldsky = {"r_oldsky", "1"}; cvar_t gl_finish = {"gl_finish","0"}; cvar_t gl_clear = {"gl_clear","0"}; cvar_t gl_cull = {"gl_cull","1"}; cvar_t gl_texsort = {"gl_texsort","1"}; cvar_t gl_smoothmodels = {"gl_smoothmodels","1"}; cvar_t gl_affinemodels = {"gl_affinemodels","0"}; cvar_t gl_polyblend = {"gl_polyblend","1"}; cvar_t gl_flashblend = {"gl_flashblend","0"}; cvar_t gl_playermip = {"gl_playermip","0"}; cvar_t gl_nocolors = {"gl_nocolors","0"}; cvar_t gl_keeptjunctions = {"gl_keeptjunctions","1"}; cvar_t gl_doubleeyes = {"gl_doubleeys", "1"}; extern cvar_t gl_ztrick; // Nehahra - ender: Disabled. Now a define in neh.h /* ================= R_CullBox Returns true if the box is completely outside the frustom ================= */ qboolean R_CullBox (vec3_t mins, vec3_t maxs) { int i; for (i=0 ; i<4 ; i++) if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) return true; return false; } void R_RotateForEntity (entity_t *e) { glTranslatef (e->origin[0], e->origin[1], e->origin[2]); glRotatef (e->angles[1], 0, 0, 1); glRotatef (-e->angles[0], 0, 1, 0); glRotatef (e->angles[2], 1, 0, 0); } /* ============================================================= SPRITE MODELS ============================================================= */ /* ================ R_GetSpriteFrame ================ */ mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) { msprite_t *psprite; mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; static float lastmsg = 0; psprite = currententity->model->cache.data; frame = currententity->frame; if (frame >= psprite->numframes || frame < 0) { if (IsTimeout (&lastmsg, 2)) Con_Printf ("R_GetSpriteFrame: no such frame %d (%d frames) in %s\n", frame, psprite->numframes, currententity->model->name); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currententity->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i=0 ; i<(numframes-1) ; i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================= R_DrawSpriteModel ================= */ void R_DrawSpriteModel (entity_t *e) { vec3_t point; mspriteframe_t *frame; float *up, *right; vec3_t v_forward, v_right, v_up; msprite_t *psprite; // if (currententity->effects & EF_ADDITIVE) // glBlendFunc(GL_SRC_ALPHA, GL_ONE); // additive rendering // else // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glEnable(GL_BLEND); // glDepthMask(0); // disable zbuffer updates // don't even bother culling, because it's just a single // polygon without a surface cache frame = R_GetSpriteFrame (e); psprite = currententity->model->cache.data; if (psprite->type == SPR_ORIENTED) { // bullet marks on walls AngleVectors (currententity->angles, v_forward, v_right, v_up); up = v_up; right = v_right; } else { // normal sprite up = vup; right = vright; } glColor3f (1,1,1); GL_DisableMultitexture(); GL_Bind(frame->gl_texturenum); glEnable (GL_ALPHA_TEST); glBegin (GL_QUADS); glTexCoord2f (0, 1); VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->left, right, point); glVertex3fv (point); glTexCoord2f (0, 0); VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->left, right, point); glVertex3fv (point); glTexCoord2f (1, 0); VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->right, right, point); glVertex3fv (point); glTexCoord2f (1, 1); VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->right, right, point); glVertex3fv (point); glEnd (); glDisable (GL_ALPHA_TEST); // if (currententity->effects & EF_ADDITIVE) // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // normal alpha blend // glDisable(GL_BLEND); // glDepthMask(1); // enable zbuffer updates } /* ============================================================= ALIAS MODELS ============================================================= */ #define NUMVERTEXNORMALS 162 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; vec3_t shadevector; float shadelight, ambientlight; // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 float r_avertexnormal_dots[SHADEDOT_QUANT][256] = #include "anorm_dots.h" ; float *shadedots = r_avertexnormal_dots[0]; int lastposenum; int lastposenum0; // Nehahra - Interpolation /* ============= GL_DrawAliasFrame ============= */ void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) { float s, t; float l; int i, j; int index; trivertx_t *v, *verts; int list; int *order; vec3_t point; float *normal; int count; lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list glTexCoord2f (((float *)order)[0], ((float *)order)[1]); order += 2; if (r_fullbright.value || !cl.worldmodel->lightdata) l = 1; else // normals and vertexes come from the frame list l = shadedots[verts->lightnormalindex] * shadelight; // Nehahra - Model_alpha if (model_alpha != 1.0) glColor4f (l, l, l, model_alpha); else glColor3f (l, l, l); // Nehahra - End glVertex3f (verts->v[0], verts->v[1], verts->v[2]); verts++; } while (--count); glEnd (); } } /* ============= GL_DrawAliasShadow ============= */ extern vec3_t lightspot; void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) { float s, t, l; int i, j; int index; trivertx_t *v, *verts; int list; int *order; vec3_t point; float *normal; float height, lheight; int count; lheight = currententity->origin[2] - lightspot[2]; height = 0; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); height = -lheight + 1.0; while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list // (skipped for shadows) glTexCoord2fv ((float *)order); order += 2; // normals and vertexes come from the frame list point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point[0] -= shadevector[0]*(point[2]+lheight); point[1] -= shadevector[1]*(point[2]+lheight); point[2] = height; // height -= 0.001; glVertex3fv (point); verts++; } while (--count); glEnd (); } } /* ================= R_ChkFrame ================= */ int R_ChkFrame (char *function, int frame, aliashdr_t *pahdr, float *plastmsg) { if (frame >= pahdr->numframes || frame < 0) { if (IsTimeout (plastmsg, 2)) { Con_DPrintf ("%s: no such frame ", function); // Single frame? if (pahdr->frames[0].name[0]) Con_DPrintf ("%d ('%s', %d frames)", frame, pahdr->frames[0].name, pahdr->numframes); else Con_DPrintf ("group %d (%d groups)", frame, pahdr->numframes); Con_DPrintf (" in %s\n", currententity->model->name); } frame = 0; } return frame; } /* ================= R_SetupAliasFrame ================= */ void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr) { int pose, numposes; float interval; static float lastmsg = 0; frame = R_ChkFrame ("R_SetupAliasFrame", frame, paliashdr, &lastmsg); pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { int firstpose = pose; pose = numposes * currententity->syncbase; // Hack to make flames unsynchronized interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / interval) % numposes; pose = firstpose + pose % numposes; } GL_DrawAliasFrame (paliashdr, pose); } /* ================= R_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *e, int cull) { int i, j; int lnum; vec3_t dist; float add; model_t *clmodel; vec3_t mins, maxs; aliashdr_t *paliashdr; trivertx_t *verts, *v; int index; float s, t, an; int anim; qboolean torch = false; int skinnum; static float lastmsg = 0; clmodel = currententity->model; // Nehahra - Model_Alpha if (!gl_notrans.value) { model_alpha = currententity->transparency; if (model_alpha == 0) model_alpha = 1; } else model_alpha = 1; // Nehahra - End if (e == &cl.viewent) if (cl.items & IT_INVISIBILITY) model_alpha = 0.2; VectorAdd (currententity->origin, clmodel->mins, mins); VectorAdd (currententity->origin, clmodel->maxs, maxs); if (cull && R_CullBox (mins, maxs)) return; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // // get lighting information // ambientlight = shadelight = R_LightPoint (currententity->origin); // allways give the gun some light if (e == &cl.viewent && ambientlight < 24) ambientlight = shadelight = 24; for (lnum=0 ; lnum= cl.time) { VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - Length(dist); if (add > 0) { ambientlight += add; //ZOID models should be affected by dlights as well shadelight += add; } } } // clamp lighting so it doesn't overbright as much if (ambientlight > 128) ambientlight = 128; if (ambientlight + shadelight > 192) shadelight = 192 - ambientlight; // ZOID: never allow players to go totally black i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients) if (ambientlight < 8) ambientlight = shadelight = 8; if (clmodel->fullbright) { ambientlight = shadelight = 255; torch = true; } else if (gl_autobright.value) { int autobright = gl_autobright.value - 1; // Clamp if (autobright < 0) autobright = 0; else if (autobright > AUTOBRIGHTS - 1) autobright = AUTOBRIGHTS - 1; autobright = clmodel->autobright[autobright]; if (autobright) { if (autobright == 255) torch = true; if (gl_autobright.value > AUTOBRIGHTS) { // High intensity autobright += 50; if (autobright > 255) autobright = 255; } ambientlight = shadelight = autobright; } } shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; shadelight = shadelight / 200.0; an = e->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); c_alias_polys += paliashdr->numtris; skinnum = currententity->skinnum; // check skin bounds if (skinnum >= paliashdr->numskins || skinnum < 0) { if (IsTimeout (&lastmsg, 2)) Con_DPrintf ("R_DrawAliasModel: no such skin %d (%d skins) in %s\n", skinnum, paliashdr->numskins, clmodel->name); skinnum = 0; } // // draw all the triangles // GL_DisableMultitexture(); glPushMatrix (); // fenix@io.com: model transform interpolation if (r_interpolate_model_transform.value && !torch) { R_BlendedRotateForEntity (e); } else { R_RotateForEntity (e); } if (clmodel->eyes && gl_doubleeyes.value) { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); // double size of eyes, since they are really hard to see in gl glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); } else { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); } anim = (int)(cl.time*10) & 3; GL_Bind(paliashdr->gl_texturenum[skinnum][anim]); // we can't dynamically colormap textures, so they are cached // seperately for the players. Heads are just uncolored. if (currententity->colormap != vid.colormap && !gl_nocolors.value) { i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) GL_Bind(playertextures - 1 + i); } if (gl_smoothmodels.value) glShadeModel (GL_SMOOTH); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); // fenix@io.com: model animation interpolation if (r_interpolate_model_animation.value) { R_SetupAliasBlendedFrame (currententity->frame, paliashdr, currententity); } else { R_SetupAliasFrame (currententity->frame, paliashdr); } glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glShadeModel (GL_FLAT); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glPopMatrix (); // Nehahra - Model_Alpha if (model_alpha != 1.0) { glDisable (GL_BLEND); glColor4f (1,1,1,1); } // Nehahra - End if (!gl_glows.value) return; if (torch || (clmodel->candle || clmodel->lantern) && gl_autobright.value || clmodel->bolt || clmodel->missile || clmodel->quaddama || clmodel->invulner) { const int TORCH_STYLE = 1; // Flicker. vec3_t lightorigin; // Origin of glow. vec3_t v; // Vector to torch. float radius; // Radius of torch flare. float distance; // Vector distance to torch. float intensity; // Intensity of torch flare. VectorCopy(currententity->origin, lightorigin); //set radius based on what model we are doing here //jf 00-05-28 set the radius large when missile first appears if (torch) radius = 30.0; else if (clmodel->candle) radius = 15.0; else if (clmodel->lantern) radius = 25.0; else if (clmodel->missile) { if (currententity->forcelink) radius = 80.0f; else radius = 20.0f; } else if (clmodel->bolt) radius = 30.0f; else if (clmodel->quaddama || clmodel->invulner) radius = 50.0f; VectorSubtract(lightorigin, r_origin, v); // See if view is outside the light. distance = Length(v); if (distance > radius) { glDepthMask (0); glDisable (GL_TEXTURE_2D); glShadeModel (GL_SMOOTH); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); //2000-04-29 Translate the glow to coincide with the flame. KH // or be at the tail of the missile - muff // or the Quad or Pent - muff glPushMatrix(); if (torch) glTranslatef(0.0f, 0.0f, 10.0f); //jf 2000-04-10 was 0,0,8... up a tad. else if (clmodel->candle) glTranslatef(0.0f, 0.0f, clmodel->maxs[2] - 5.0f); // Adapt to model else if (clmodel->lantern) glTranslatef (0.0f, 0.0f, clmodel->maxs[2] - 35.0f); else if (clmodel->missile) { //move the glow behind the rocket glTranslatef (cos( e->angles[1]/180*M_PI)*(-20.0f), sin( e->angles[1]/180*M_PI)*(-20.0f), sin( e->angles[0]/180*M_PI)*(-20.0f)); } else if (clmodel->quaddama || clmodel->invulner) glTranslatef(0.0f, 0.0f, 20.0f); glBegin(GL_TRIANGLE_FAN); // Invert (fades as you approach) - if we are a torch if (torch || clmodel->candle || clmodel->lantern) { // Diminish torch flare inversely with distance. intensity = (1024.0f - distance) / 1024.0f; intensity = (1.0f - intensity); } // or fix settings if lightning or missile else if (clmodel->bolt) intensity = 0.2f; else if (clmodel->missile) intensity = 0.4f; else if (clmodel->quaddama || clmodel->invulner) intensity = 0.3f; // Clamp, but don't let the flare disappear. if (intensity > 1.0f) intensity = 1.0f; if (intensity < 0.0f) intensity = 0.0f; // Now modulate with flicker. i = (int)(cl.time*10); if (!cl_lightstyle[TORCH_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[TORCH_STYLE].length; j = cl_lightstyle[TORCH_STYLE].map[j] - 'a'; j = j*22; } intensity *= ((float)j / 255.0f); // Set yellow intensity //2000-04-10 set the colour of the glow - muff if (torch || clmodel->candle || clmodel->lantern) glColor3f(0.4f*intensity, 0.2f*intensity, 0.05f); else if (clmodel->missile) glColor3f(0.9f*intensity, 0.2f*intensity, 0.1f*intensity); else if (clmodel->bolt) glColor3f(0.2f*intensity, 0.2f*intensity, 0.8f*intensity); else if (clmodel->quaddama) glColor3f(0.8f*intensity, 0.8f*intensity, 0.1f*intensity); else if (clmodel->invulner) glColor3f(0.8f*intensity, 0.1f*intensity, 0.1f*intensity); for (i=0 ; i<3 ; i++) v[i] = lightorigin[i] - vpn[i]*radius; glVertex3fv(v); glColor3f(0.0f, 0.0f, 0.0f); for (i=16; i>=0; i--) { float a = i/16.0f * M_PI*2; for (j=0; j<3; j++) v[j] = lightorigin[j] + vright[j]*cos(a)*radius + vup[j]*sin(a)*radius; glVertex3fv(v); } glEnd(); // Restore previous matrix! KH glPopMatrix(); glColor3f (1,1,1); glDisable (GL_BLEND); glEnable (GL_TEXTURE_2D); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask (1); } } } //================================================================================== /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList (void) { int i; if (!r_drawentities.value) return; // draw sprites seperately, because of alpha blending for (i=0 ; iangles[0] *= 0.3; } // Nehahra - Model_Alpha if (currententity->transparency != 1 && currententity->transparency != 0 && !gl_notrans.value) { currententity->transignore = false;//model_alpha continue; // draw transparent entities last (?) } // Nehahra - End switch (currententity->model->type) { case mod_alias: #ifdef _WIN32 R_DrawAliasModel (currententity, TRUE); #else R_DrawAliasModel (currententity, true); #endif break; case mod_brush: R_DrawBrushModel (currententity); break; default: break; } } for (i=0 ; imodel->type) { case mod_sprite: R_DrawSpriteModel (currententity); break; } } } // Nehahra - Model_Alpha (Function by FrikaC) /* ============= R_DrawTransEntities ============= */ void R_DrawTransEntities (void) { // need to draw back to front // fixme: this isn't my favorite option int i; float bestdist, dist; entity_t *bestent; vec3_t start, test; VectorCopy(r_refdef.vieworg, start); if (!r_drawentities.value) return; transgetent: bestdist = 0; for (i=0 ; itransignore) continue; if (currententity->transparency == 1 || currententity->transparency ==0) continue; VectorCopy(currententity->origin, test); if (currententity->model->type == mod_brush) { test[0] += currententity->model->mins[0]; test[1] += currententity->model->mins[1]; test[2] += currententity->model->mins[2]; } dist = (((test[0] - start[0]) * (test[0] - start[0])) + ((test[1] - start[1]) * (test[1] - start[1])) + ((test[2] - start[2]) * (test[2] - start[2]))); if (dist > bestdist) { bestdist = dist; bestent = currententity; } } if (bestdist == 0) return; bestent->transignore = true; currententity = bestent; switch (currententity->model->type) { case mod_alias: #ifdef _WIN32 R_DrawAliasModel (currententity, TRUE); #else R_DrawAliasModel (currententity, true); #endif break; case mod_brush: R_DrawBrushModel (currententity); break; default: break; } goto transgetent; } /* ============= R_DrawViewModel ============= */ void R_DrawViewModel (void) { float old_interpolate_model_transform; if (!r_drawviewmodel.value) return; if (chase_active.value) return; if (envmap) return; if (!r_drawentities.value) return; if (cl.items & IT_INVISIBILITY) return; if (cl.stats[STAT_HEALTH] <= 0) return; currententity = &cl.viewent; if (!currententity->model) return; // Prevent weapon model error if (currententity->model->name[0] == '*') { Con_Printf ("\x02R_DrawViewModel: "); Con_Printf ("viewmodel %s invalid\n", currententity->model->name); Cvar_Set ("r_drawviewmodel", "0"); return; } currententity->transparency = model_alpha = cl_entities[cl.viewentity].transparency; // LordHavoc: if the player is transparent, so is his gun currententity->effects = cl_entities[cl.viewentity].effects; // hack the depth range to prevent view model from poking into walls glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); // fenix@io.com: model transform interpolation old_interpolate_model_transform = r_interpolate_model_transform.value; r_interpolate_model_transform.value = false; #ifdef _WIN32 R_DrawAliasModel (currententity, FALSE); #else R_DrawAliasModel (currententity, false); #endif r_interpolate_model_transform.value = old_interpolate_model_transform; glDepthRange (gldepthmin, gldepthmax); } /* ============ R_PolyBlend ============ */ void R_PolyBlend (void) { if (!gl_polyblend.value) return; if (!v_blend[3]) return; GL_DisableMultitexture(); glDisable (GL_ALPHA_TEST); glEnable (GL_BLEND); glDisable (GL_DEPTH_TEST); glDisable (GL_TEXTURE_2D); glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up glColor4fv (v_blend); glBegin (GL_QUADS); glVertex3f (10, 100, 100); glVertex3f (10, -100, 100); glVertex3f (10, -100, -100); glVertex3f (10, 100, -100); glEnd (); glDisable (GL_BLEND); glEnable (GL_TEXTURE_2D); glEnable (GL_ALPHA_TEST); } int SignbitsForPlane (mplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) bits |= 1< 1) Cvar_Set ("r_fullbright", "0"); R_CheckVariables (); R_AnimateLight (); r_framecount++; // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); V_SetContentsColor (r_viewleaf->contents); V_CalcBlend (); r_cache_thrash = false; c_brush_polys = 0; c_alias_polys = 0; } void MYgluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar ) { GLdouble xmin, xmax, ymin, ymax; ymax = zNear * tan( fovy * M_PI / 360.0 ); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); } /* ============= R_SetupGL ============= */ void R_SetupGL (void) { float screenaspect; float yfov; int i; extern int glwidth, glheight; int x, x2, y2, y, w, h; // // set up viewpoint // glMatrixMode(GL_PROJECTION); glLoadIdentity (); x = r_refdef.vrect.x * glwidth/vid.width; x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; // fudge around because of frac screen scale if (x > 0) x--; if (x2 < glwidth) x2++; if (y2 < 0) y2--; if (y < glheight) y++; w = x2 - x; h = y - y2; if (envmap) { x = y2 = 0; w = h = 256; } glViewport (glx + x, gly + y2, w, h); screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; // yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; MYgluPerspective (r_refdef.fov_y, screenaspect, 4, GL_FARCLIP); //6144 if (mirror) { if (mirror_plane->normal[2]) glScalef (1, -1, 1); else glScalef (-1, 1, 1); glCullFace(GL_BACK); } else glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up glRotatef (-r_refdef.viewangles[2], 1, 0, 0); glRotatef (-r_refdef.viewangles[0], 0, 1, 0); glRotatef (-r_refdef.viewangles[1], 0, 0, 1); glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); // // set drawing parms // if (gl_cull.value) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); } /* ================ R_RenderScene r_refdef must be set before the first call ================ */ void R_RenderScene (void) { S_ExtraUpdateTime (); // don't let sound get messed up if going slow R_SetupFrame (); R_SetFrustum (); R_SetupGL (); R_MarkLeaves (); // done here so we know if we're in water R_DrawWorld (); // adds static entities to the list S_ExtraUpdateTime (); // don't let sound get messed up if going slow R_DrawEntitiesOnList (); GL_DisableMultitexture(); R_RenderDlights (); // EnderTest R_DrawParticles (); S_ExtraUpdateTime (); // don't let sound get messed up if going slow #ifdef GLTEST Test_Draw (); #endif } /* ============= R_Clear ============= */ void R_Clear (void) { if (r_mirroralpha.value != 1.0) { if (gl_clear.value) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 0.5; glDepthFunc (GL_LEQUAL); } else if (gl_ztrick.value) { static int trickframe; if (gl_clear.value) glClear (GL_COLOR_BUFFER_BIT); trickframe++; if (trickframe & 1) { gldepthmin = 0; gldepthmax = 0.49999; glDepthFunc (GL_LEQUAL); } else { gldepthmin = 1; gldepthmax = 0.5; glDepthFunc (GL_GEQUAL); } } else { if (gl_clear.value) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 1; glDepthFunc (GL_LEQUAL); } glDepthRange (gldepthmin, gldepthmax); } /* ============= R_Mirror ============= */ void R_Mirror (void) { float d; msurface_t *s; entity_t *ent; if (!mirror) return; memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); d = DotProduct (vpn, mirror_plane->normal); VectorMA (vpn, -2*d, mirror_plane->normal, vpn); r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; r_refdef.viewangles[2] = -r_refdef.viewangles[2]; ent = &cl_entities[cl.viewentity]; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } gldepthmin = 0.5; gldepthmax = 1; glDepthRange (gldepthmin, gldepthmax); glDepthFunc (GL_LEQUAL); R_RenderScene (); R_DrawWaterSurfaces (); gldepthmin = 0; gldepthmax = 0.5; glDepthRange (gldepthmin, gldepthmax); glDepthFunc (GL_LEQUAL); // blend on top glEnable (GL_BLEND); glMatrixMode(GL_PROJECTION); if (mirror_plane->normal[2]) glScalef (1,-1,1); else glScalef (-1,1,1); glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); glLoadMatrixf (r_base_world_matrix); glColor4f (1,1,1,r_mirroralpha.value); s = cl.worldmodel->textures[mirrortexturenum]->texturechain; for ( ; s ; s=s->texturechain) R_RenderBrushPoly (s); cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; glDisable (GL_BLEND); glColor4f (1,1,1,1); } /* ================ R_RenderView r_refdef must be set before the first call ================ */ // Nehahra: Altered significantly. Move glFinish etc to SCR_UpdateScreen void R_RenderView (void) { if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("R_RenderView: NULL worldmodel"); R_Clear (); Neh_BeginFrame(); R_RenderScene (); R_DrawViewModel (); R_DrawWaterSurfaces (); if (!gl_notrans.value) // Nehahra - Model_Alpha R_DrawTransEntities(); Neh_EndFrame(); R_DrawParticles (); // Nehahra - Model_Alpha R_PolyBlend (); } /* ============= GL_DrawAliasBlendedFrame fenix@io.com: model animation interpolation ============= */ void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend) { float l; trivertx_t* verts1; trivertx_t* verts2; int* order; int count; vec3_t d; #ifdef D3DQuake if ( gNoAlias ) return; // D3DQuake #endif if (model_alpha != 1.0) glEnable (GL_BLEND); lastposenum0 = pose1; lastposenum = pose2; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += pose1 * paliashdr->poseverts; verts2 += pose2 * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); for (;;) { // get the vertex count and primitive type count = *order++; if (!count) break; if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list glTexCoord2f (((float *)order)[0], ((float *)order)[1]); order += 2; // normals and vertexes come from the frame list // blend the light intensity from the two frames together d[0] = shadedots[verts2->lightnormalindex] - shadedots[verts1->lightnormalindex]; if (r_fullbright.value || !cl.worldmodel->lightdata) l = 1; else l = shadelight * (shadedots[verts1->lightnormalindex] + (blend * d[0])); if (model_alpha != 1.0) glColor4f (l, l, l, model_alpha); else glColor3f (l, l, l); VectorSubtract(verts2->v, verts1->v, d); // blend the vertex positions from each frame together glVertex3f (verts1->v[0] + (blend * d[0]), verts1->v[1] + (blend * d[1]), verts1->v[2] + (blend * d[2])); verts1++; verts2++; } while (--count); glEnd (); } } /* ================= R_SetupAliasBlendedFrame fenix@io.com: model animation interpolation ================= */ void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t* e) { int pose; int numposes; float blend; static float lastmsg = 0; frame = R_ChkFrame ("R_SetupAliasBlendedFrame", frame, paliashdr, &lastmsg); pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { int firstpose = pose; pose = numposes * currententity->syncbase; // Hack to make flames unsynchronized e->frame_interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / e->frame_interval) % numposes; pose = firstpose + pose % numposes; } else { /* One tenth of a second is a good for most Quake animations. If the nextthink is longer then the animation is usually meant to pause (e.g. check out the shambler magic animation in shambler.qc). If its shorter then things will still be smoothed partly, and the jumps will be less noticable because of the shorter time. So, this is probably a good assumption. */ e->frame_interval = 0.1; } if (e->pose2 != pose) { e->frame_start_time = realtime; e->pose1 = e->pose2; e->pose2 = pose; blend = 0; } else blend = (realtime - e->frame_start_time) / e->frame_interval; // wierd things start happening if blend passes 1 if (cl.paused || blend > 1) blend = 1; GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend); } /* ============= R_BlendedRotateForEntity fenix@io.com: model transform interpolation ============= */ void R_BlendedRotateForEntity (entity_t *e) { float timepassed; float blend; vec3_t d; int i; // positional interpolation timepassed = realtime - e->translate_start_time; if (e->translate_start_time == 0 || timepassed > 1) { e->translate_start_time = realtime; VectorCopy (e->origin, e->origin1); VectorCopy (e->origin, e->origin2); } if (!VectorCompare (e->origin, e->origin2)) { e->translate_start_time = realtime; VectorCopy (e->origin2, e->origin1); VectorCopy (e->origin, e->origin2); blend = 0; } else { blend = timepassed / 0.1; if (cl.paused || blend > 1) blend = 1; } VectorSubtract (e->origin2, e->origin1, d); glTranslatef (e->origin1[0] + (blend * d[0]), e->origin1[1] + (blend * d[1]), e->origin1[2] + (blend * d[2])); // orientation interpolation (Euler angles, yuck!) timepassed = realtime - e->rotate_start_time; if (e->rotate_start_time == 0 || timepassed > 1) { e->rotate_start_time = realtime; VectorCopy (e->angles, e->angles1); VectorCopy (e->angles, e->angles2); } if (!VectorCompare (e->angles, e->angles2)) { e->rotate_start_time = realtime; VectorCopy (e->angles2, e->angles1); VectorCopy (e->angles, e->angles2); blend = 0; } else { blend = timepassed / 0.1; if (cl.paused || blend > 1) blend = 1; } VectorSubtract (e->angles2, e->angles1, d); // always interpolate along the shortest path for (i = 0; i < 3; i++) { if (d[i] > 180) d[i] -= 360; else if (d[i] < -180) d[i] += 360; } glRotatef ( e->angles1[1] + ( blend * d[1]), 0, 0, 1); glRotatef (-e->angles1[0] + (-blend * d[0]), 0, 1, 0); glRotatef ( e->angles1[2] + ( blend * d[2]), 1, 0, 0); } /* ============= GL_DrawAliasBlendedShadow fenix@io.com: model animation interpolation ============= */ void GL_DrawAliasBlendedShadow (aliashdr_t *paliashdr, int pose1, int pose2, entity_t* e) { trivertx_t* verts1; trivertx_t* verts2; int* order; vec3_t point1; vec3_t point2; vec3_t d; float height; float lheight; int count; float blend; blend = (realtime - e->frame_start_time) / e->frame_interval; if (blend > 1) blend = 1; lheight = e->origin[2] - lightspot[2]; height = -lheight + 1.0; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += pose1 * paliashdr->poseverts; verts2 += pose2 * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); for (;;) { // get the vertex count and primitive type count = *order++; if (!count) break; if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { order += 2; point1[0] = verts1->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point1[1] = verts1->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point1[2] = verts1->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point1[0] -= shadevector[0]*(point1[2]+lheight); point1[1] -= shadevector[1]*(point1[2]+lheight); point2[0] = verts2->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point2[1] = verts2->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point2[2] = verts2->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point2[0] -= shadevector[0]*(point2[2]+lheight); point2[1] -= shadevector[1]*(point2[2]+lheight); VectorSubtract(point2, point1, d); glVertex3f (point1[0] + (blend * d[0]), point1[1] + (blend * d[1]), height); verts1++; verts2++; } while (--count); glEnd (); } }