/* 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. */ // gl_rmain.c #include "quakedef.h" entity_t r_worldentity; qboolean r_cache_thrash; // compatibility qboolean r_fullbright_draw; qboolean r_noshadow_draw; 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; int particletexture; // little dot for particles int skyboxtextures; // by joe int underwatertexture; int detailtexture; int chrometexture; int glasstexture; int playertextures; int playerfbtextures; float frametime; float slowpulse = 0; float fastpulse = 0; float killpulse = 0; int slowpulsedir = 1; int fastpulsedir = 1; int killpulsedir = 1; float slowcycle = 0; float fastcycle = 0; float killcycle = 0; #define INTERP_WEAP_MAXNUM 24 #define INTERP_WEAP_MINDIST 5000 #define INTERP_WEAP_MAXDIST 95000 typedef struct interpolated_weapon { char name[MAX_QPATH]; int maxDistance; } interp_weapon_t; static interp_weapon_t interpolated_weapons[INTERP_WEAP_MAXNUM]; static int interp_weap_num = 0; // view origin vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_world_matrix[16]; // screen size info refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; mleaf_t *r_viewleaf2, *r_oldviewleaf2; // for watervis hack texture_t *r_notexture_mip; int d_lightstylevalue[256]; // 8.8 fraction of base light value int lightmode = 2; cvar_t r_drawentities = {"r_drawentities", "1"}; cvar_t r_drawviewmodel = {"r_drawviewmodel", "1"}; cvar_t r_viewmodelsize = {"r_viewmodelsize", "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_wateralpha = {"r_wateralpha", "0.6", true}; cvar_t r_liquidshade = {"r_liquidshade", "1", true}; cvar_t r_dynamic = {"r_dynamic", "1"}; cvar_t r_novis = {"r_novis", "0", true}; cvar_t r_fullbrightskins = {"r_fullbrightskins", "0"}; cvar_t r_fastsky = {"r_fastsky", "0"}; cvar_t r_skycolor = {"r_skycolor", "4"}; cvar_t r_farclip = {"r_farclip", "4096"}; cvar_t r_waterripple = {"r_waterripple", "0", true}; cvar_t r_skybox = {"r_skybox", "", false, false, OnChange_r_skybox}; // fenix@io.com: model interpolation cvar_t gl_interpolate_animation = {"gl_interpolate_animation", "1"}; cvar_t gl_interpolate_transform = {"gl_interpolate_transform", "1"}; cvar_t gl_clear = {"gl_clear", "0"}; cvar_t gl_cull = {"gl_cull", "0"}; cvar_t gl_ztrick = {"gl_ztrick", "0"}; cvar_t gl_smoothmodels = {"gl_smoothmodels", "1"}; cvar_t gl_affinemodels = {"gl_affinemodels", "0"}; cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", true}; 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_finish = {"gl_finish", "0"}; cvar_t gl_loadlitfiles = {"gl_loadlitfiles", "1"}; cvar_t gl_lerpimages = {"gl_lerpimages", "1"}; cvar_t gl_doubleeyes = {"gl_doubleeyes", "1"}; cvar_t gl_interdist = {"gl_interpolate_distance", "17000"}; cvar_t gl_waterfog = {"gl_waterfog", "1"}; cvar_t gl_waterfog_density = {"gl_waterfog_density", "1"}; cvar_t gl_detail = {"gl_detail", "0", true}; cvar_t gl_caustics = {"gl_caustics", "1", true}; cvar_t gl_shiny = {"gl_shiny", "0", true}; cvar_t gl_ringalpha = {"gl_ringalpha", "0.4", true}; cvar_t gl_fb_bmodels = {"gl_fb_bmodels", "1"}; cvar_t gl_fb_models = {"gl_fb_models", "1"}; cvar_t gl_vertexlights = {"gl_vertexlights", "1"}; cvar_t gl_coronas = {"gl_coronas", "1", true}; cvar_t gl_triplebuffer = {"gl_triplebuffer", "1", true}; cvar_t gl_part_explosions = {"gl_part_explosions", "1", true}; cvar_t gl_part_trails = {"gl_part_trails", "1", true}; cvar_t gl_part_spikes = {"gl_part_spikes", "1", true}; cvar_t gl_part_gunshots = {"gl_part_gunshots", "1", true}; cvar_t gl_part_blood = {"gl_part_blood", "1", true}; cvar_t gl_part_telesplash = {"gl_part_telesplash", "1", true}; cvar_t gl_part_blobs = {"gl_part_blobs", "1", true}; cvar_t gl_part_lavasplash = {"gl_part_lavasplash", "1", true}; cvar_t gl_part_inferno = {"gl_part_inferno", "1", true}; cvar_t gl_part_flames = {"gl_part_flames", "1", true}; cvar_t gl_part_lightning = {"gl_part_lightning", "1", true}; cvar_t gl_fogenable = {"gl_fogenable", "0", true}; cvar_t gl_fogstart = {"gl_fogstart", "50.0", true}; cvar_t gl_fogend = {"gl_fogend", "1500.0", true}; cvar_t gl_fogred = {"gl_fogred","0.4", true}; cvar_t gl_fogblue = {"gl_fogblue","0.4", true}; cvar_t gl_foggreen = {"gl_foggreen","0.4", true}; cvar_t gl_fogdensity = {"gl_fogdensity", "0.8", true}; cvar_t gl_fogalpha = {"gl_fogalpha", "0.5", true}; /* ============= R_CullBox Returns true if the box is completely outside the frustum ============= */ qboolean R_CullBox (vec3_t mins, vec3_t maxs) { int i; for (i = 0; i < 4; i++) { if (BOX_ON_PLANE_SIDE (mins, maxs, &frustum[i]) == 2) { return true; } } return false; } /* ============= R_CullSphere Returns true if the sphere is completely outside the frustum ============= */ qboolean R_CullSphere (vec3_t centre, float radius) { int i; mplane_t *p; for (i = 0, p = frustum; i < 4; i++, p++) { if (PlaneDiff(centre, p) <= -radius) { return true; } } return false; } /* ============= R_RotateForEntity fenix@io.com: model transform interpolation ============= */ 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); } /* ============= R_BlendedRotateForEntity fenix@io.com: model transform interpolation ============= */ void R_BlendedRotateForEntity (entity_t *e, int shadow) { float timepassed; float blend; vec3_t d; int i; // positional interpolation timepassed = cl.time - e->translate_start_time; if (e->translate_start_time == 0 || timepassed > 1) { e->translate_start_time = cl.time; VectorCopy (e->origin, e->origin1); VectorCopy (e->origin, e->origin2); } if (!VectorCompare (e->origin, e->origin2)) { e->translate_start_time = cl.time; 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 = cl.time - e->rotate_start_time; if (e->rotate_start_time == 0 || timepassed > 1) { e->rotate_start_time = cl.time; VectorCopy (e->angles, e->angles1); VectorCopy (e->angles, e->angles2); } if (!VectorCompare (e->angles, e->angles2)) { e->rotate_start_time = cl.time; 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); if (!shadow) { glRotatef (-e->angles1[0] + (-blend * d[0]), 0, 1, 0); glRotatef ( e->angles1[2] + ( blend * d[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; psprite = currententity->model->cache.data; frame = currententity->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_Printf ("R_DrawSprite: no such frame %d\n", frame); 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, right, up; mspriteframe_t *frame; msprite_t *psprite; // don't even bother culling, because it's just a single // polygon without a surface cache psprite = currententity->model->cache.data; frame = R_GetSpriteFrame (e); // standard quake sprites are handled differently. other types use the normal sprite code if (psprite->drawType == SPR_LIGHT) { // add a glow for this R_AddGlowEffect (0.89, 0.59, 0.31, 15, e->origin); return; } else if (psprite->drawType == SPR_BUBBLE) { // add a glow for this R_AddGlowEffect (0.5, 0.75, 1.0, 5, e->origin); return; } else if (psprite->drawType == SPR_RAIN) { // add a glow for this "very faint" R_SpriteRain (e->origin); R_AddGlowEffect (0.5, 0.75, 1.0, 1, e->origin); return; } else if (psprite->drawType != SPR_DRAW) { // handles sprites that have not been given a drawing routine yet return; } if (psprite->type == SPR_ORIENTED) { // bullet marks on walls AngleVectors (currententity->angles, NULL, right, up); } else if (psprite->type == SPR_FACING_UPRIGHT) { VectorSet (up, 0, 0, 1); right[0] = e->origin[1] - r_origin[1]; right[1] = -(e->origin[0] - r_origin[0]); right[2] = 0; VectorNormalize (right); } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { VectorSet (up, 0, 0, 1); VectorCopy (vright, right); } else { // normal sprite VectorCopy (vup, up); VectorCopy (vright, right); } GL_Bind (frame->gl_texturenum); 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 (); } /* =============================================================================== ALIAS MODELS =============================================================================== */ // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 float r_avertexnormal_dots[SHADEDOT_QUANT][256] = #include "anorm_dots.h" ; // light lerping vec3_t shadevector; vec3_t vertexlight; float shadelight, ambientlight; float apitch, ayaw; float lightlerpoffset; float *shadedots = r_avertexnormal_dots[0]; float *shadedots2 = r_avertexnormal_dots[0]; // fenix@io.com: model animation interpolation int lastposenum0; int lastposenum; /* ============= Calc_LightLerp Calculates lightning between two angles ============= */ float Calc_LightLerp (float l1, float l2) { float l, diff; if (l1 != l2) { if (l1 > l2) { diff = (l1 - l2) * -1; } else { diff = l2 - l1; } diff *= lightlerpoffset; l = l1 + diff; } else { l = l1; } // add contrast - this is a nasty hack from Pox's tut which simulates specular lighting // mainly introduced cos at the time he didn't know the norms. however, my engine does // calculate the norms at model load time (although it doesn't really use them) so i // should be able to do proper specular lighting... if only i knew how :-( l *= l; return l; } /* ============= GL_DrawAliasFrame ============= */ void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum, qboolean mtex) { float l, alpha; trivertx_t *verts; int i, *order, count; float l1, l2; vec3_t l_v; alpha = (currententity == &cl.viewent && gl_mtexable) ? ((cl.items & IT_INVISIBILITY) ? gl_ringalpha.value : bound(0, r_drawviewmodel.value, 1)) : model_alpha; lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); if (alpha < 1.0) { glEnable (GL_BLEND); } while ((count = *order++)) { // get the vertex count and primitive type if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else { glBegin (GL_TRIANGLE_STRIP); } do { // texture coordinates come from the draw list if (mtex) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, ((float *)order)[0], ((float *)order)[1]); qglMultiTexCoord2f (GL_TEXTURE1_ARB, ((float *)order)[0], ((float *)order)[1]); } else { glTexCoord2f (((float *)order)[0], ((float *)order)[1]); } order += 2; if (gl_vertexlights.value && !r_fullbright_draw) { l = R_VertexLight (verts->lightnormalindex, apitch, ayaw); l = min(1, l); } else { // normals and vertexes come from the frame list l1 = min(1, (shadedots[verts->lightnormalindex] * shadelight + ambientlight) / 256); l2 = min(1, (shadedots2[verts->lightnormalindex] * shadelight + ambientlight) / 256); l = Calc_LightLerp (l1, l2); } if (!r_fullbright_draw) { for (i=0 ; i<3 ; i++) { l_v[i] = lightcolor[i] / 256 + l; } glColor4f (l_v[0], l_v[1], l_v[2], alpha); } else { glColor4f (l, l, l, alpha); } glVertex3f (verts->v[0], verts->v[1], verts->v[2]); verts++; } while (--count); glEnd (); } if (alpha < 1.0) { glDisable (GL_BLEND); } } /* ============= GL_DrawAliasBlendedFrame fenix@io.com: model animation interpolation ============= */ void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend, int distance, qboolean mtex) { float l, alpha; float d2, l1, l2; trivertx_t *verts1, *verts2; int i, *order, count, maxDistance; vec3_t d, l_v; alpha = (currententity == &cl.viewent && gl_mtexable) ? ((cl.items & IT_INVISIBILITY) ? gl_ringalpha.value : bound(0, r_drawviewmodel.value, 1)) : model_alpha; maxDistance = bound(INTERP_WEAP_MINDIST, distance, INTERP_WEAP_MAXDIST); 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); if (alpha < 1.0) { glEnable (GL_BLEND); } while ((count = *order++)) { // get the vertex count and primitive type if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else { glBegin (GL_TRIANGLE_STRIP); } do { // texture coordinates come from the draw list if (mtex) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, ((float *)order)[0], ((float *)order)[1]); qglMultiTexCoord2f (GL_TEXTURE1_ARB, ((float *)order)[0], ((float *)order)[1]); } else { 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 if (gl_vertexlights.value && !r_fullbright_draw) { l = R_LerpVertexLight (verts1->lightnormalindex, verts2->lightnormalindex, blend, apitch, ayaw); l = min(1, l); } else { // normals and vertexes come from the frame list // blend the light intensity from the two frames together d[0] = (shadedots[verts2->lightnormalindex] * shadelight + ambientlight) - (shadedots[verts1->lightnormalindex] * shadelight + ambientlight); d2 = (shadedots2[verts2->lightnormalindex] * shadelight + ambientlight) - (shadedots2[verts1->lightnormalindex] * shadelight + ambientlight); l1 = min(1, (shadedots[verts1->lightnormalindex] * shadelight + ambientlight) + (blend * d[0]) / 256); l2 = min(1, (shadedots2[verts1->lightnormalindex] * shadelight + ambientlight) + (blend * d2) / 256); l = Calc_LightLerp(l1, l2); } if (!r_fullbright_draw) { for (i=0 ; i<3 ; i++) { l_v[i] = lightcolor[i] / 256 + l; } glColor4f (l_v[0], l_v[1], l_v[2], alpha); } else { glColor4f (l, l, l, alpha); } VectorSubtract (verts2->v, verts1->v, d); // blend the vertex positions from each frame together if (currententity == &cl.viewent) { if (d[0]*d[0] + d[1]*d[1] + d[2]*d[2] < maxDistance) { glVertex3f (verts1->v[0] + (blend * d[0]), verts1->v[1] + (blend * d[1]), verts1->v[2] + (blend * d[2])); } else { glVertex3f (verts2->v[0], verts2->v[1], verts2->v[2]); } } else { glVertex3f (verts1->v[0] + (blend * d[0]), verts1->v[1] + (blend * d[1]), verts1->v[2] + (blend * d[2])); } verts1++; verts2++; } while (--count); glEnd (); } if (alpha < 1.0) { glDisable (GL_BLEND); } } /* ============= GL_DrawAliasShadow ============= */ void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) { trivertx_t *verts; trace_t downtrace; int *order, count; vec3_t point; vec3_t downmove; float s1, c1; float height, lheight; lheight = currententity->origin[2] - lightspot[2]; height = 1 - lheight; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); VectorCopy (currententity->origin, downmove); downmove[2] = downmove[2] - 4096; memset (&downtrace, 0, sizeof(downtrace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, currententity->origin, downmove, &downtrace); s1 = sin( currententity->angles[1] / 180*M_PI); c1 = cos( currententity->angles[1] / 180*M_PI); if (stencildraw == true) { glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, 2); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); } while ((count = *order++)) { // get the vertex count and primitive type 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[0]); point[1] -= shadevector[1]*(point[1]); point[2] -= shadevector[2]*(point[2]); point[2] = point[2] - (currententity->origin[2] - downtrace.endpos[2]); point[2] += ((point[1] * (s1 * downtrace.plane.normal[0])) - (point[0] * (c1 * downtrace.plane.normal[0])) - (point[0] * (s1 * downtrace.plane.normal[1])) - (point[1] * (c1 * downtrace.plane.normal[1]))) + ((1.0 - downtrace.plane.normal[2])*20) + 0.2; glVertex3fv (point); verts++; } while (--count); glEnd (); } if (stencildraw == true) { glDisable(GL_STENCIL_TEST); } } /* ============= GL_DrawAliasBlendedShadow fenix@io.com: model animation interpolation ============= */ void GL_DrawAliasBlendedShadow (aliashdr_t *paliashdr, int pose1, int pose2, entity_t *e) { trivertx_t *verts1, *verts2; trace_t downtrace; int *order, count; vec3_t point1, point2, d; vec3_t downmove; float s1, c1; float height, lheight, blend; blend = min(1, (cl.time - e->frame_start_time) / e->frame_interval); if (cl.paused || blend > 1) blend = 1; lheight = e->origin[2] - lightspot[2]; height = 1 - lheight; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += pose1 * paliashdr->poseverts; verts2 += pose2 * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); VectorCopy (currententity->origin, downmove); downmove[2] = downmove[2] - 4096; memset (&downtrace, 0, sizeof(downtrace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, currententity->origin, downmove, &downtrace); // calculate the all important angles s1 = sin( currententity->angles[1]/180*M_PI); c1 = cos( currententity->angles[1]/180*M_PI); if (stencildraw == true) { glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, 2); glStencilOp(GL_KEEP,GL_KEEP,GL_INCR); } while ((count = *order++)) { // get the vertex count and primitive type 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); point1[0] = point1[0] + (blend * d[0]); point1[1] = point1[1] + (blend * d[1]); point1[2] = point1[2] + (blend * d[2]); point1[2] = - (currententity->origin[2] - downtrace.endpos[2]) ; point1[2] += ((point1[1] * (s1 * downtrace.plane.normal[0])) - (point1[0] * (c1 * downtrace.plane.normal[0])) - (point1[0] * (s1 * downtrace.plane.normal[1])) - (point1[1] * (c1 * downtrace.plane.normal[1]))) + ((1.0 - downtrace.plane.normal[2])*20) + 0.2 ; glVertex3fv (point1); verts1++; verts2++; } while (--count); glEnd (); } if (stencildraw == true) { glDisable(GL_STENCIL_TEST); } } /* ================= R_SetupAliasFrame ================= */ void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr, qboolean mtex) { int pose, numposes; float interval; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / interval) % numposes; } GL_DrawAliasFrame (paliashdr, pose, mtex); } /* ================= R_SetupAliasBlendedFrame fenix@io.com: model animation interpolation ================= */ void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t *e, int distance, qboolean mtex) { int pose, numposes; float blend; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { e->frame_interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / e->frame_interval) % 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 = cl.time; e->pose1 = e->pose2; e->pose2 = pose; blend = 0; } else { blend = (cl.time - e->frame_start_time) / e->frame_interval; } // weird things start happening if blend passes 1 if (cl.paused || blend > 1) { blend = 1; } GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend, distance, mtex); } /* ================= R_Interpolated_Weapon_f joe: from FuhQuake, but this is less configurable ================= */ void R_Interpolated_Weapon_f (void) { int i, k; char str[MAX_QPATH]; if (cmd_source != src_command) return; if (Cmd_Argc() == 2) { for (i=0 ; i \n"); return; } Q_strcpy (str, Cmd_Argv(1)); for (i=0, k=-1 ; imodel->name, va("%s.mdl", interpolated_weapons[i].name)) || !Q_strcasecmp(currententity->model->name, va("progs/%s.mdl", interpolated_weapons[i].name))) return interpolated_weapons[i].maxDistance; } return -1; } /* ================= R_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *e) { vec3_t dist, dlight_color; vec3_t mins, maxs; float add; float ang_ceil, ang_floor; float radiusmax = 0.0; aliashdr_t *paliashdr; model_t *clmodel = e->model; int i, lnum; int anim, skinnum, distance; int texture, fb_texture; if (!gl_notrans.value) { // allways true if not -nehahra model_alpha = currententity->transparency; if (model_alpha == 0) { model_alpha = 1.0; } } else { model_alpha = 1.0; } VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); if (e->angles[0] || e->angles[1] || e->angles[2]) { if (R_CullSphere(e->origin, clmodel->radius)) { return; } } else if (R_CullBox(mins, maxs)) { return; } VectorCopy (e->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); if (clmodel->mflags & MOD_BOLT) { ambientlight = 210; shadelight = 0; r_fullbright_draw = true; r_noshadow_draw = true; } else if (clmodel->mflags & MOD_FLAME) { ambientlight = 255; shadelight = 0; r_fullbright_draw = true; r_noshadow_draw = true; } else { // normal lighting r_fullbright_draw = false; r_noshadow_draw = false; ambientlight = shadelight = R_LightPoint (e->origin); for (lnum = 0 ; lnum < MAX_DLIGHTS ; lnum++) { if (cl_dlights[lnum].die < cl.time || !cl_dlights[lnum].radius) continue; VectorSubtract (e->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - VectorLength (dist); if (add > 0) { if (gl_vertexlights.value) { if (!radiusmax || cl_dlights[lnum].radius > radiusmax) { radiusmax = cl_dlights[lnum].radius; VectorCopy(cl_dlights[lnum].origin, vertexlight); } } VectorCopy(bubblecolor[cl_dlights[lnum].type], dlight_color); for (i=0 ; i<3 ; i++) { lightcolor[i] = lightcolor[i] + (dlight_color[i] * add) * 2; if (lightcolor[i] > 256) { switch (i) { case 0: lightcolor[1] = lightcolor[1] - (1 * lightcolor[1]/3); lightcolor[2] = lightcolor[2] - (1 * lightcolor[2]/3); break; case 1: lightcolor[0] = lightcolor[0] - (1 * lightcolor[0]/3); lightcolor[2] = lightcolor[2] - (1 * lightcolor[2]/3); break; case 2: lightcolor[1] = lightcolor[1] - (1 * lightcolor[1]/3); lightcolor[0] = lightcolor[0] - (1 * lightcolor[0]/3); break; } } } } } // calculate pitch and yaw for vertex lighting if (gl_vertexlights.value) { vec3_t dist, ang; apitch = e->angles[0]; ayaw = e->angles[1]; if (!radiusmax) { vlight_pitch = 45; vlight_yaw = 45; } else { VectorSubtract(vertexlight, currententity->origin, dist); vectoangles(dist, ang); vlight_pitch = ang[0]; vlight_yaw = ang[1]; } } // clamp lighting so it doesn't overbright as much ambientlight = bound(128, ambientlight, 192); if (ambientlight + shadelight > 192) { shadelight = 192 - ambientlight; } // always give the gun some light if (e == &cl.viewent && ambientlight < 24) { ambientlight = shadelight = 24; } // never allow players to go totally black if (clmodel->mflags & MOD_PLAYER) { if (ambientlight < 8) { ambientlight = shadelight = 8; r_fullbright_draw = false; r_noshadow_draw = true; } } } if (clmodel->mflags & MOD_PLAYER && r_fullbrightskins.value) { ambientlight = shadelight = 128; r_fullbright_draw = true; r_noshadow_draw = true; } if (gl_vertexlights.value) { shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; } else { // add pitch angle so lighting changes when looking up/down (mainly for viewmodel) lightlerpoffset = (e->angles[1]+e->angles[0]) * (SHADEDOT_QUANT / 360.0); ang_ceil = ceil(lightlerpoffset); ang_floor = floor(lightlerpoffset); lightlerpoffset = ang_ceil - lightlerpoffset; shadedots = r_avertexnormal_dots[(int)ang_ceil & (SHADEDOT_QUANT - 1)]; shadedots2 = r_avertexnormal_dots[(int)ang_floor & (SHADEDOT_QUANT - 1)]; if (currententity->lastShadeLight) { shadelight = (shadelight + e->lastShadeLight) / 2; } if (shadelight) { e->lastShadeLight = shadelight; } else { e->lastShadeLight = 1; } } // locate the proper data paliashdr = (aliashdr_t *)Mod_Extradata (e->model); c_alias_polys += paliashdr->numtris; // draw all the triangles glPushMatrix (); // fenix@io.com: model transform interpolation // joe: don't blend flame/fire model frames if (gl_interpolate_transform.value && !(clmodel->mflags & MOD_FLAME)) { R_BlendedRotateForEntity (e, 0); } else { R_RotateForEntity (e); } if (gl_doubleeyes.value && (clmodel->mflags & MOD_EYES)) { 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 if (currententity == &cl.viewent) { float scale = 0.5 + bound(0, r_viewmodelsize.value, 1) / 2; glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0] * scale, paliashdr->scale[1], paliashdr->scale[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; skinnum = currententity->skinnum; if ((skinnum >= paliashdr->numskins) || (skinnum < 0)) { Con_DPrintf ("R_DrawAliasModel: no such skin # %d\n", skinnum); skinnum = 0; } texture = paliashdr->gl_texturenum[skinnum][anim]; fb_texture = paliashdr->fb_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 = e - cl_entities; if (i >= 1 && i <= cl.maxclients) { texture = playertextures + i; fb_texture = playerfbtextures + i; } } if (r_fullbright_draw) { fb_texture = 0; } if (gl_smoothmodels.value) { glShadeModel (GL_SMOOTH); } if (gl_affinemodels.value) { glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); } if (fb_texture && gl_mtexable) { GL_DisableMultitexture (); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_Bind (texture); GL_EnableMultitexture (); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); GL_Bind (fb_texture); // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value && !(clmodel->mflags & MOD_FLAME)) { // if model's on list, use the given value if ((distance = R_DoWeaponInterpolation()) != -1) { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, distance, true); // else if model's not on list, but is on JQ's weapon list, use gl_interdist } else if (clmodel->mflags & MOD_WEAPON) { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, (int)gl_interdist.value, true); // else use the max distance } else { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, INTERP_WEAP_MAXDIST, true); } } else { R_SetupAliasFrame (e->frame, paliashdr, true); } GL_DisableMultitexture (); } else { GL_DisableMultitexture (); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_Bind (texture); // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value && !(clmodel->mflags & MOD_FLAME)) { if ((distance = R_DoWeaponInterpolation()) != -1) { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, distance, false); } else if (clmodel->mflags & MOD_WEAPON) { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, (int)gl_interdist.value, false); } else { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, INTERP_WEAP_MAXDIST, false); } } else { R_SetupAliasFrame (e->frame, paliashdr, false); } if (fb_texture) { glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable (GL_ALPHA_TEST); GL_Bind (fb_texture); // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value && !(clmodel->mflags & MOD_FLAME)) { if ((distance = R_DoWeaponInterpolation()) != -1) { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, distance, false); } else if (clmodel->mflags & MOD_WEAPON) { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, (int)gl_interdist.value, false); } else { R_SetupAliasBlendedFrame (e->frame, paliashdr, e, INTERP_WEAP_MAXDIST, false); } } else { R_SetupAliasFrame (e->frame, paliashdr, false); } glDisable (GL_ALPHA_TEST); } } glShadeModel (GL_FLAT); if (gl_affinemodels.value) { glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } glPopMatrix (); // ahhh yeesss if (gl_coronas.value) { if (!ISUNDERWATER(r_viewleaf->contents)) { // somewhat redone this function // now uses enumerators if (clmodel->glowtype != FX_NORMAL) { vec3_t lightorigin; float radius; // set up for drawing later VectorCopy (e->origin, lightorigin); radius = clmodel->glowRadius; if (clmodel->glowtype == FX_TORCH && qmb_initialized) { lightorigin[2] += 8.0; radius += (float) (rand () & 3); } else if (clmodel->glowtype == FX_ENTITY && qmb_initialized) { lightorigin[2] += 1.5; radius += (fastcycle * 2.5); } else if (clmodel->glowtype == FX_BOLT && !qmb_initialized) { // disable normal glow here if using qmb lightning radius += (float) (rand () & 7); } else if (clmodel->glowtype == FX_POWERUP && qmb_initialized) { lightorigin[2] += 20.0; radius += (slowcycle * 40.0); } else if (clmodel->glowtype == FX_MISSILE && qmb_initialized) { lightorigin[0] += cos (e->angles[1] / 180 * M_PI) * (-20.0f); lightorigin[1] += sin (e->angles[1] / 180 * M_PI) * (-20.0f); lightorigin[2] += sin (e->angles[0] / 180 * M_PI) * (-20.0f); } else if (clmodel->glowtype == FX_SHAMBLER && !qmb_initialized) { // disable normal glow here if using qmb lightning lightorigin[2] += 75.0; radius += (float) (rand () & 7); } R_AddGlowEffect (clmodel->glowColours[0], clmodel->glowColours[1], clmodel->glowColours[2], radius, lightorigin); } } } if (r_shadows.value && r_noshadow_draw && e != &cl.viewent && r_viewleaf->contents != CONTENTS_EMPTY) { float an; static float shadescale = 0; trace_t downtrace; vec3_t downmove; if (!shadescale) { shadescale = 1 / sqrt(2); } an = -e->angles[1] / 180 * M_PI; VectorSet (shadevector, cos(an) * shadescale, sin(an) * shadescale, shadescale); VectorNormalize (shadevector); glPushMatrix (); if (gl_interpolate_animation.value) { R_BlendedRotateForEntity (e, 1); } else { glTranslatef (e->origin[0], e->origin[1], e->origin[2]); glRotatef (e->angles[1], 0, 0, 1); } VectorCopy (e->origin, downmove); downmove[2] = downmove[2] - 4096; memset (&downtrace, 0, sizeof(downtrace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, downmove, &downtrace); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glColor4f (0, 0, 0, r_shadows.value- ((mins[2]-downtrace.endpos[2])/60)); if (!(clmodel->mflags & (MOD_WEAPON|MOD_SPIKE|MOD_LAVABALL))) { // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value) { GL_DrawAliasBlendedShadow (paliashdr, lastposenum0, lastposenum, e); } else { GL_DrawAliasShadow (paliashdr, lastposenum); } } glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glPopMatrix (); } glColor3f (1, 1, 1); } //================================================================================== // joe: from FuhQuake void R_SetSpritesState (qboolean state) { static qboolean r_state = false; if (r_state == state) return; r_state = state; if (state) { if (currententity->model->sprtype == MOD_SPR32) { glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); glDepthMask (GL_FALSE); // disable zbuffer updates } else { GL_DisableMultitexture (); glEnable (GL_ALPHA_TEST); } } else { if (currententity->model->sprtype == MOD_SPR32) { glDisable (GL_BLEND); glDepthMask (GL_TRUE); // enable zbuffer updates } else { glDisable (GL_ALPHA_TEST); } } } /* ============= R_DrawCurrentEntity ============= */ void R_DrawCurrentEntity (void) { vec3_t tmporg; switch (currententity->model->type) { case mod_alias: if (!gl_part_flames.value && !strcmp(currententity->model->name, "progs/flame0.mdl")) { currententity->model = cl.model_precache[cl_modelindex[i_flame1]]; } else if (qmb_initialized && gl_part_flames.value) { VectorCopy (currententity->origin, tmporg); if (currententity->baseline.modelindex == cl_modelindex[i_flame0]) { tmporg[2] += 5.5; QMB_TorchFlame (tmporg); } else if (currententity->baseline.modelindex == cl_modelindex[i_flame1]) { tmporg[2] += 5.5; QMB_TorchFlame (tmporg); currententity->model = cl.model_precache[cl_modelindex[i_flame0]]; } else if (currententity->baseline.modelindex == cl_modelindex[i_flame2]) { tmporg[2] -= 1; QMB_BigTorchFlame (tmporg); } } R_DrawAliasModel (currententity); break; case mod_md3: R_DrawQ3Model (currententity); break; case mod_brush: R_DrawBrushModel (currententity); break; default: break; } } /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList (void) { int i; if (!r_drawentities.value) return; for (i=0 ; itransparency != 1 && currententity->transparency != 0 && !gl_notrans.value) { currententity->transignore = false; continue; } R_DrawCurrentEntity(); } // draw sprites seperately, because of alpha blending for (i=0 ; imodel->type) { case mod_sprite: R_SetSpritesState (true); R_DrawSpriteModel (currententity); break; default: break; } } R_SetSpritesState (false); } /* ============= 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; if (!gl_notrans.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; R_DrawCurrentEntity(); goto transgetent; } /* =========== R_ShouldDrawViewModel Returns true if we should draw the view model =========== */ qboolean R_ShouldDrawViewModel (void) { currententity = &cl.viewent; if (!r_drawviewmodel.value) { return false; } if (chase_active.value) { return false; } if (!r_drawentities.value) { return false; } if (cl.stats[STAT_HEALTH] <= 0) { return false; } if (!currententity->model) { return false; } return true; } /* ============= R_DrawViewModel ============= */ void R_DrawViewModel (void) { // fenix@io.com: model transform interpolation float old_interpolate_transform; if (!R_ShouldDrawViewModel()) return; // LordHavoc: if the player is transparent, so is his gun currententity->transparency = model_alpha = cl_entities[cl.viewentity].transparency; // 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_transform = gl_interpolate_transform.value; gl_interpolate_transform.value = false; R_DrawCurrentEntity(); gl_interpolate_transform.value = old_interpolate_transform; glDepthRange (gldepthmin, gldepthmax); } /* ============ R_PolyBlend ============ */ void R_PolyBlend (void) { if ((vid_hwgamma_enabled && gl_hwblend.value) || !v_blend[3]) return; glDisable (GL_ALPHA_TEST); glEnable (GL_BLEND); glDisable (GL_TEXTURE_2D); glColor4fv (v_blend); glBegin (GL_QUADS); glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y); glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y); glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y + r_refdef.vrect.height); glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height); glEnd (); glDisable (GL_BLEND); glEnable (GL_TEXTURE_2D); glEnable (GL_ALPHA_TEST); glColor3f (1, 1, 1); } /* ================ R_BrightenScreen ================ */ void R_BrightenScreen (void) { float f; if (vid_hwgamma_enabled || gl_contrast.value <= 1.0) return; f = min(gl_contrast.value, 3); f = pow (f, vid_gamma); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glBlendFunc (GL_DST_COLOR, GL_ONE); glBegin (GL_QUADS); while (f > 1) { if (f >= 2) glColor3f (1, 1, 1); else glColor3f (f - 1, f - 1, f - 1); glVertex2f (0, 0); glVertex2f (vid.width, 0); glVertex2f (vid.width, vid.height); glVertex2f (0, vid.height); f *= 0.5; } glEnd (); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glColor3f (1, 1, 1); } /* =============== SignbitsForPlane =============== */ 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<contents <= CONTENTS_WATER && r_viewleaf->contents >= CONTENTS_LAVA) { // look up a bit VectorCopy (r_origin, testorigin); testorigin[2] += 10; leaf = Mod_PointInLeaf (testorigin, cl.worldmodel); if (leaf->contents == CONTENTS_EMPTY) { r_viewleaf2 = leaf; } } else if (r_viewleaf->contents == CONTENTS_EMPTY) { // look down a bit VectorCopy (r_origin, testorigin); testorigin[2] -= 10; leaf = Mod_PointInLeaf (testorigin, cl.worldmodel); if (leaf->contents <= CONTENTS_WATER && leaf->contents >= CONTENTS_LAVA) { r_viewleaf2 = leaf; } } V_SetContentsColor (r_viewleaf->contents); V_Setfog (r_viewleaf->contents); V_CalcBlend (); r_cache_thrash = false; c_brush_polys = 0; c_alias_polys = 0; } __inline void GL_Perspective (GLdouble fov, GLdouble aspectr, GLdouble zNear) { static const GLdouble nudge = 1.0 - 1.0 / ((GLdouble) (1<<23)); static GLdouble p[4][4] = { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0, -1.0 }, { 0.0, 0.0, 0.0, 0.0 } }; p[0][0] = 1.0 / (aspectr * tan (fov)); p[1][1] = 1.0 / tan (fov); p[2][2] = -nudge; p[3][2] = -2.0 * zNear * nudge; glLoadMatrixd (&p[0][0]); } /* ============= R_SetupGL ============= */ void R_SetupGL (void) { float screenaspect; 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; glViewport (glx + x, gly + y2, w, h); screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; GL_Perspective (atan((GLdouble)r_refdef.vrect.height / (GLdouble)r_refdef.vrect.width), (GLdouble)screenaspect, 5.0); glCullFace(GL_FRONT); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); // put Z going up and Y going deep glRotatef (-90, 1, 0, 0); glRotatef (90, 0, 0, 1); // set the view according to where the player actually is in the map!!! 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_Init =============== */ void R_Init (void) { void R_ToggleParticles_f (void); Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cmd_AddCommand ("toggleparticles", R_ToggleParticles_f); Cmd_AddCommand ("set_interpolated_weapon", R_Interpolated_Weapon_f); Cvar_RegisterVariable (&r_lightmap); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_viewmodelsize); Cvar_RegisterVariable (&r_shadows); Cvar_RegisterVariable (&r_wateralpha); Cvar_RegisterVariable (&r_liquidshade); Cvar_RegisterVariable (&r_dynamic); Cvar_RegisterVariable (&r_novis); Cvar_RegisterVariable (&r_speeds); Cvar_RegisterVariable (&r_fullbrightskins); Cvar_RegisterVariable (&r_fastsky); Cvar_RegisterVariable (&r_skycolor); Cvar_RegisterVariable (&r_skybox); Cvar_RegisterVariable (&r_farclip); Cvar_RegisterVariable (&r_waterripple); // fenix@io.com: register new cvar for model interpolation Cvar_RegisterVariable (&gl_interpolate_animation); Cvar_RegisterVariable (&gl_interpolate_transform); Cvar_RegisterVariable (&gl_finish); Cvar_RegisterVariable (&gl_clear); Cvar_RegisterVariable (&gl_cull); Cvar_RegisterVariable (&gl_ztrick); Cvar_RegisterVariable (&gl_smoothmodels); Cvar_RegisterVariable (&gl_affinemodels); Cvar_RegisterVariable (&gl_subdivide_size); Cvar_RegisterVariable (&gl_polyblend); Cvar_RegisterVariable (&gl_flashblend); Cvar_RegisterVariable (&gl_playermip); Cvar_RegisterVariable (&gl_nocolors); Cvar_RegisterVariable (&gl_loadlitfiles); Cvar_RegisterVariable (&gl_lerpimages); Cvar_RegisterVariable (&gl_doubleeyes); Cvar_RegisterVariable (&gl_interdist); Cvar_RegisterVariable (&gl_waterfog); Cvar_RegisterVariable (&gl_waterfog_density); Cvar_RegisterVariable (&gl_detail); Cvar_RegisterVariable (&gl_caustics); Cvar_RegisterVariable (&gl_shiny); Cvar_RegisterVariable (&gl_ringalpha); Cvar_RegisterVariable (&gl_fb_bmodels); Cvar_RegisterVariable (&gl_fb_models); Cvar_RegisterVariable (&gl_vertexlights); Cvar_RegisterVariable (&gl_coronas); Cvar_RegisterVariable (&gl_triplebuffer); Cvar_RegisterVariable (&gl_part_explosions); Cvar_RegisterVariable (&gl_part_trails); Cvar_RegisterVariable (&gl_part_spikes); Cvar_RegisterVariable (&gl_part_gunshots); Cvar_RegisterVariable (&gl_part_blood); Cvar_RegisterVariable (&gl_part_telesplash); Cvar_RegisterVariable (&gl_part_blobs); Cvar_RegisterVariable (&gl_part_lavasplash); Cvar_RegisterVariable (&gl_part_inferno); Cvar_RegisterVariable (&gl_part_flames); Cvar_RegisterVariable (&gl_part_lightning); Cvar_RegisterVariable (&gl_fogenable); Cvar_RegisterVariable (&gl_fogstart); Cvar_RegisterVariable (&gl_fogend); Cvar_RegisterVariable (&gl_fogred); Cvar_RegisterVariable (&gl_fogblue); Cvar_RegisterVariable (&gl_foggreen); Cvar_RegisterVariable (&gl_fogdensity); Cvar_RegisterVariable (&gl_fogalpha); Cmd_AddLegacyCommand ("loadsky", "r_skybox"); if (gl_fogenable.value) { glDisable (GL_FOG); } R_InitTextures (); R_InitBubble (); R_InitParticles (); R_InitVertexLights (); // skins playertextures = texture_extension_number; texture_extension_number += MAX_SCOREBOARD+1; // fullbright skins playerfbtextures = texture_extension_number; texture_extension_number += MAX_SCOREBOARD+1; // by joe skyboxtextures = texture_extension_number; texture_extension_number += 6; R_InitOtherTextures (); if (gl_fogenable.value) { glEnable (GL_FOG); } } /* ================ R_RenderScene r_refdef must be set before the first call ================ */ void R_RenderScene (void) { 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_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawEntitiesOnList (); R_DrawWaterSurfaces (); GL_DisableMultitexture (); } /* ============= R_Clear ============= */ int gl_ztrickframe = 0; void R_Clear (void) { static qboolean cleartogray; qboolean clear = false; if (gl_clear.value) { clear = true; if (cleartogray) { glClearColor (1, 0, 0, 0); cleartogray = false; } } else if (!vid_hwgamma_enabled && gl_contrast.value > 1) { clear = true; if (!cleartogray) { glClearColor (0.1, 0.1, 0.1, 0); cleartogray = true; } } if (gl_ztrick.value) { if (clear) { glClear (GL_COLOR_BUFFER_BIT); } gl_ztrickframe = !gl_ztrickframe; if (gl_ztrickframe) { gldepthmin = 0; gldepthmax = 0.49999; glDepthFunc (GL_LEQUAL); } else { gldepthmin = 1; gldepthmax = 0.5; glDepthFunc (GL_GEQUAL); } } else { if (clear) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { glClear (GL_DEPTH_BUFFER_BIT); } gldepthmin = 0; gldepthmax = 1; glDepthFunc (GL_LEQUAL); } if (stencildraw == true && r_shadows.value > 0) { glClearStencil(true); glClear(GL_STENCIL_BUFFER_BIT); } glDepthRange (gldepthmin, gldepthmax); } /* ================ R_RenderView r_refdef must be set before the first call ================ */ void R_RenderView (void) { double time1 = 0.0, time2; GLfloat colors[4] = {(GLfloat) 0.0, (GLfloat) 0.0, (GLfloat) 1, (GLfloat) 0.20}; if (!r_worldentity.model || !cl.worldmodel) { Sys_Error ("R_RenderView: NULL worldmodel"); } if (r_speeds.value) { glFinish (); time1 = Sys_DoubleTime (); c_brush_polys = 0; c_alias_polys = 0; } if (gl_finish.value) { glFinish (); } // we want this every frame whether we draw or not frametime = cl.time - cl.oldtime; // one pulse cycle (down to up and back again) every second slowpulse = slowpulse + (frametime * slowpulsedir); if (slowpulse > 0.5) { slowpulse = 0.5; slowpulsedir = -1; } if (slowpulse < 0.0) { slowpulse = 0.0; slowpulsedir = 1; } // one pulse cycle (down to up and back again) every tenth of a second fastpulse = fastpulse + (frametime * fastpulsedir); if (fastpulse > 0.05) { fastpulse = 0.05; fastpulsedir = -1; } if (fastpulse < 0.0) { fastpulse = 0.0; fastpulsedir = 1; } // one pulse cycle (down to up and back again) every 10 seconds killpulse = killpulse + (frametime * killpulsedir); if (killpulse > 5.0) { killpulse = 5.0; killpulsedir = -1; } if (killpulse < 0.0) { killpulse = 0.0; killpulsedir = 1; } fastcycle = fastpulse * 10; slowcycle = slowpulse; killcycle = killpulse / 10; R_Clear (); // render normal view R_RenderScene (); R_RenderDlights (); if (gl_fogenable.value) { glDisable (GL_FOG); } R_DrawParticles (); if (gl_fogenable.value) { glFogi(GL_FOG_MODE, GL_LINEAR); colors[0] = gl_fogred.value; colors[1] = gl_foggreen.value; colors[2] = gl_fogblue.value; glFogfv(GL_FOG_COLOR, colors); glFogf(GL_FOG_START, gl_fogstart.value); glFogf(GL_FOG_END, gl_fogend.value); glFogf(GL_FOG_DENSITY, gl_fogdensity.value); glEnable (GL_FOG); } else { glDisable(GL_FOG); } R_DrawViewModel (); // always true if not -nehahra R_DrawTransEntities (); R_RenderGlowEffects (); if (r_speeds.value) { time2 = Sys_DoubleTime (); Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); } }