/* 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 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 playertextures; // up to 16 color translated skins int skyboxtextures; // by joe int underwatertexture, detailtexture, chrometexture2; // JT added chrometexture2 #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; int DoWeaponInterpolation (void); // 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; 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 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", "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_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"}; qboolean OnChange_r_skybox (cvar_t *var, char *string); 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", "1"}; cvar_t gl_ztrick = {"gl_ztrick", "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_finish = {"gl_finish", "0"}; cvar_t gl_loadlitfiles = {"gl_loadlitfiles", "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"}; cvar_t gl_caustics = {"gl_caustics", "0"}; cvar_t gl_ringalpha = {"gl_ringalpha", "0.4"}; cvar_t gl_fb_bmodels = {"gl_fb_bmodels", "1"}; cvar_t gl_fb_models = {"gl_fb_models", "1"}; cvar_t gl_solidparticles = {"gl_solidparticles", "0"}; cvar_t gl_vertexlights = {"gl_vertexlights", "0"}; cvar_t gl_part_explosions = {"gl_part_explosions", "0"}; cvar_t gl_part_trails = {"gl_part_trails", "0"}; cvar_t gl_part_spikes = {"gl_part_spikes", "0"}; cvar_t gl_part_gunshots = {"gl_part_gunshots", "0"}; cvar_t gl_part_blood = {"gl_part_blood", "0"}; cvar_t gl_part_telesplash = {"gl_part_telesplash", "0"}; cvar_t gl_part_blobs = {"gl_part_blobs", "0"}; cvar_t gl_part_lavasplash = {"gl_part_lavasplash", "0"}; cvar_t gl_part_inferno = {"gl_part_inferno", "0"}; cvar_t gl_part_flames = {"gl_part_flames", "0"}; cvar_t gl_part_lightning = {"gl_part_lightning", "0"}; cvar_t gl_part_spiketrails = {"gl_part_spiketrails", "0"}; cvar_t gl_shinywater = {"gl_shinywater", "0"}; // JT030105 - add reflections int lightmode = 2; void R_MarkLeaves (void); void R_InitBubble (void); /* ================= 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 ============= */ 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) { float blend, timepassed; 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); 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 frame = R_GetSpriteFrame (e); psprite = currententity->model->cache.data; 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 =============================================================================== */ #define NUMVERTEXNORMALS 162 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; vec3_t shadevector; qboolean full_light; 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]; // fenix@io.com: model animation interpolation int lastposenum0; int lastposenum; float apitch, ayaw; vec3_t vertexlight; /* ============= GL_DrawAliasFrame ============= */ void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum, qboolean mtex) { float l, alpha; trivertx_t *verts; int i, *order, count; vec3_t l_v; alpha = (currententity == &cl.viewent) ? ( (cl.items & IT_INVISIBILITY) ? gl_ringalpha.value : bound(0, r_drawviewmodel.value, 1) ) : r_modelalpha; lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); if (alpha < 1) 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 if (gl_vertexlights.value && !full_light) l = R_GetVertexLightValue (verts->lightnormalindex, apitch, ayaw); else l = (shadedots[verts->lightnormalindex] * shadelight + ambientlight) / 256.0; l = min(l, 1); if (!full_light) { 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) 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; trivertx_t *verts1, *verts2; int i, *order, count, maxDistance; vec3_t d, l_v; alpha = (currententity == &cl.viewent) ? ( (cl.items & IT_INVISIBILITY) ? gl_ringalpha.value : bound(0, r_drawviewmodel.value, 1) ) : r_modelalpha; 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) 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 && !full_light) { l = R_LerpVertexLight (verts1->lightnormalindex, verts2->lightnormalindex, blend, apitch, ayaw); } else { d[0] = (shadedots[verts2->lightnormalindex] * shadelight + ambientlight) - (shadedots[verts1->lightnormalindex] * shadelight + ambientlight); l = ((shadedots[verts1->lightnormalindex] * shadelight + ambientlight) + (blend * d[0])) / 256.0; } l = min(l, 1); if (!full_light) { 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) glDisable (GL_BLEND); } /* ============= GL_DrawAliasShadow ============= */ void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) { trivertx_t *verts; int *order, count; vec3_t point; 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); 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[2] + lheight); point[1] -= shadevector[1] * (point[2] + lheight); point[2] = height; glVertex3fv (point); verts++; } while (--count); glEnd (); } } /* ============= 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; int *order, count; vec3_t point1, point2, d; float height, lheight, blend; blend = min(1, (cl.time - e->frame_start_time) / e->frame_interval); 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); 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); glVertex3f (point1[0] + (blend * d[0]), point1[1] + (blend * d[1]), height); verts1++; verts2++; } while (--count); glEnd (); } } /* ================= 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_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *ent) { int i, lnum; vec3_t dist; float add; vec3_t mins, maxs, dlight_color; aliashdr_t *paliashdr; model_t *clmodel = ent->model; int anim, skinnum, distance; qboolean noshadow = false; int texture, fb_texture = 0; float radiusmax = 0.0; if (!gl_notrans.value) // always true if not -nehahra { r_modelalpha = currententity->transparency; if (r_modelalpha == 0) r_modelalpha = 1.0; } else { r_modelalpha = 1.0; } VectorAdd (ent->origin, clmodel->mins, mins); VectorAdd (ent->origin, clmodel->maxs, maxs); if (ent->angles[0] || ent->angles[1] || ent->angles[2]) { if (R_CullSphere(ent->origin, clmodel->radius)) return; } else if (R_CullBox(mins, maxs)) { return; } VectorCopy (ent->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // get lighting information // make thunderbolt and torches full light if (clmodel->modhint == MOD_THUNDERBOLT) { ambientlight = 210; shadelight = 0; full_light = true; noshadow = true; } else if (clmodel->modhint == MOD_FLAME) { ambientlight = 255; shadelight = 0; full_light = true; noshadow = true; } else { // normal lighting full_light = false; ambientlight = shadelight = R_LightPoint (ent->origin); for (lnum = 0 ; lnum < MAX_DLIGHTS ; lnum++) { if (cl_dlights[lnum].die < cl.time || !cl_dlights[lnum].radius) continue; VectorSubtract (ent->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); } } // joe: only allow colorlight affection if dynamic lights are on if (r_dynamic.value) { 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; } } ambientlight += (add*0.75);//<--add this here shadelight += (add*0.70); } } else { ambientlight += add; } } } // calculate pitch and yaw for vertex lighting if (gl_vertexlights.value) { vec3_t dist, ang; apitch = currententity->angles[0]; ayaw = currententity->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 = min(128, ambientlight); if (ambientlight + shadelight > 192) shadelight = 192 - ambientlight; // always give the gun some light if (ent == &cl.viewent && ambientlight < 24) ambientlight = shadelight = 24; // never allow players to go totally black if (clmodel->modhint == MOD_PLAYER) { if (ambientlight < 8) ambientlight = shadelight = 8; } } if (clmodel->modhint == MOD_PLAYER && r_fullbrightskins.value) { ambientlight = shadelight = 128; full_light = true; } shadedots = r_avertexnormal_dots[((int)(ent->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; // locate the proper data paliashdr = (aliashdr_t *)Mod_Extradata (ent->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->modhint != MOD_FLAME) R_BlendedRotateForEntity (ent); else R_RotateForEntity (ent); if (clmodel->modhint == MOD_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 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 = ent->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 (ent->colormap != vid.colormap && !gl_nocolors.value) { i = ent - cl_entities; if (i > 0 && i <= cl.maxclients) { texture = playertextures - 1 + i; fb_texture = fb_skins[i-1]; } } if (full_light || !gl_fb_models.value) 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 (); GL_Bind (texture); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_EnableMultitexture (); GL_Bind (fb_texture); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value && clmodel->modhint != MOD_FLAME) { // if model's on list, use the given value if ((distance = DoWeaponInterpolation()) != -1) R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, distance, true); // else if model's not on list, but is on JQ's weapon list, use gl_interdist else if (clmodel->modhint == MOD_WEAPON) R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, (int)gl_interdist.value, true); // else use the max distance else R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, INTERP_WEAP_MAXDIST, true); } else { R_SetupAliasFrame (ent->frame, paliashdr, true); } GL_DisableMultitexture (); } else { GL_DisableMultitexture (); GL_Bind (texture); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value && clmodel->modhint != MOD_FLAME) { if ((distance = DoWeaponInterpolation()) != -1) R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, distance, false); else if (clmodel->modhint == MOD_WEAPON) R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, (int)gl_interdist.value, false); else R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, INTERP_WEAP_MAXDIST, false); } else { R_SetupAliasFrame (ent->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->modhint != MOD_FLAME) { if ((distance = DoWeaponInterpolation()) != -1) R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, distance, false); else if (clmodel->modhint == MOD_WEAPON) R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, (int)gl_interdist.value, false); else R_SetupAliasBlendedFrame (ent->frame, paliashdr, ent, INTERP_WEAP_MAXDIST, false); } else { R_SetupAliasFrame (ent->frame, paliashdr, false); } glDisable (GL_ALPHA_TEST); } } glShadeModel (GL_FLAT); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glPopMatrix (); if (r_shadows.value && !noshadow && ent != &cl.viewent) { float an; static float shadescale = 0; if (!shadescale) shadescale = 1 / sqrt(2); an = -ent->angles[1] / 180 * M_PI; VectorSet (shadevector, cos(an) * shadescale, sin(an) * shadescale, shadescale); glPushMatrix (); glTranslatef (ent->origin[0], ent->origin[1], ent->origin[2]); glRotatef (ent->angles[1], 0, 0, 1); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); // glColor4f (0, 0, 0, r_shadows.value); glColor4f (0, 0, 0, (ambientlight/200)); // fenix@io.com: model animation interpolation if (gl_interpolate_animation.value) GL_DrawAliasBlendedShadow (paliashdr, lastposenum0, lastposenum, ent); else GL_DrawAliasShadow (paliashdr, lastposenum); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glPopMatrix (); } glColor3f (1, 1, 1); } // joe: from FuhQuake, but this is less configurable void Set_Interpolated_Weapon_f (void) { int i; 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 ; 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; } //================================================================================== // 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->modhint == 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->modhint == MOD_SPR32) { glDisable (GL_BLEND); glDepthMask (GL_TRUE); // enable zbuffer updates } else { glDisable (GL_ALPHA_TEST); } } } /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList (void) { int i; vec3_t liteorg; if (!r_drawentities.value) return; // draw sprites seperately, because of alpha blending for (i=0 ; itransparency != 1 && currententity->transparency != 0 && !gl_notrans.value) { currententity->transignore = false; continue; } switch (currententity->model->type) { case mod_alias: if (qmb_initialized) { if (!gl_part_flames.value && !strcmp(currententity->model->name, "progs/flame0.mdl")) { currententity->model = cl.model_precache[cl_modelindex[mi_flame1]]; } else if (gl_part_flames.value) { VectorCopy (currententity->origin, liteorg); if (currententity->baseline.modelindex == cl_modelindex[mi_flame0]) { liteorg[2] += 5.5; QMB_TorchFlame (liteorg, 8, 0.8); // changed from 7 to 8 } else if (currententity->baseline.modelindex == cl_modelindex[mi_flame1]) { liteorg[2] += 5.5; QMB_TorchFlame (liteorg, 8, 0.8); // changed from 7 to 8 currententity->model = cl.model_precache[cl_modelindex[mi_flame0]]; } else if (currententity->baseline.modelindex == cl_modelindex[mi_flame2]) { liteorg[2] -= 1; QMB_TorchFlame (liteorg, 15, 1); // changed from 12 to 15 continue; } } } R_DrawAliasModel (currententity); break; case mod_brush: R_DrawBrushModel (currententity); break; case mod_md3: R_DrawQ3Model (currententity); break; default: break; } } 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; transgetent: bestdist = 0; for (i=0 ; itransignore || 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: R_DrawAliasModel (currententity); break; case mod_brush: R_DrawBrushModel (currententity); break; default: break; } goto transgetent; } /* ============= R_DrawViewModel ============= */ void R_DrawViewModel (void) { // fenix@io.com: model transform interpolation float old_interpolate_transform; currententity = &cl.viewent; if (!r_drawviewmodel.value || chase_active.value || !r_drawentities.value || (cl.stats[STAT_HEALTH] <= 0) || !currententity->model) return; // LordHavoc: if the player is transparent, so is his gun currententity->transparency = r_modelalpha = 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_DrawAliasModel (currententity); switch (currententity->model->type) { case mod_alias: R_DrawAliasModel (currententity); break; case mod_md3: R_DrawQ3Model (currententity); break; } gl_interpolate_transform.value = old_interpolate_transform; glDepthRange (gldepthmin, gldepthmax); } /* ============ R_PolyBlend ============ */ void R_PolyBlend (void) { extern cvar_t gl_hwblend; 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) { extern float vid_gamma; float f; if (vid_hwgamma_enabled || v_contrast.value <= 1.0) return; f = min(v_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); } 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_AddWaterfog (r_viewleaf->contents); if (nehahra) Neh_SetupFrame (); 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; int x, x2, y2, y, w, h, farclip; // 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; w = x2 - x; h = y - y2; glViewport (glx + x, gly + y2, w, h); screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; farclip = max((int)r_farclip.value, 4096); MYgluPerspective (r_refdef.fov_y, screenaspect, 4, farclip); 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_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", Set_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_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); // 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_polyblend); Cvar_RegisterVariable (&gl_flashblend); Cvar_RegisterVariable (&gl_playermip); Cvar_RegisterVariable (&gl_nocolors); Cvar_RegisterVariable (&gl_loadlitfiles); 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_ringalpha); Cvar_RegisterVariable (&gl_fb_bmodels); Cvar_RegisterVariable (&gl_fb_models); Cvar_RegisterVariable (&gl_solidparticles); Cvar_RegisterVariable (&gl_vertexlights); 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_part_spiketrails); Cvar_RegisterVariable (&gl_shinywater); // JT030105 - reflections Cmd_AddLegacyCommand ("loadsky", "r_skybox"); if (!strcmp(gl_vendor, "METABYTE/WICKED3D")) Cvar_Set ("gl_solidparticles", "1"); R_InitTextures (); R_InitBubble (); R_InitParticles (); R_InitVertexLights (); playertextures = texture_extension_number; texture_extension_number += 16; // fullbright skins texture_extension_number += 16; // by joe skyboxtextures = texture_extension_number; texture_extension_number += 6; R_InitOtherTextures (); } /* ================ 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 && v_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); } glDepthRange (gldepthmin, gldepthmax); } /* ================ R_RenderView r_refdef must be set before the first call ================ */ void R_RenderView (void) { double time1 = 0, time2; 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 (); R_Clear (); // render normal view R_RenderScene (); R_RenderDlights (); R_DrawParticles (); R_DrawViewModel (); if (!gl_notrans.value) // always true if not -nehahra R_DrawTransEntities (); 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); } }