/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_main.c #include "q_defs.h" bool r_cache_thrash; // compatability vec3_t modelorg, r_entorigin; entity_t *currententity; int32_t r_visframecount; // bumped when going to a new PVS int32_t r_framecount; // used for dlight push checking mplane_t frustum[4]; //johnfitz -- rendering statistics int32_t rs_brushpolys, rs_aliaspolys, rs_skypolys, rs_particles, rs_fogpolys; int32_t rs_dynamiclightmaps, rs_brushpasses, rs_aliaspasses, rs_skypasses; float rs_megatexels; // // view origin // vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_fovx, r_fovy; //johnfitz -- rendering fov may be different becuase of r_waterwarp and r_stereo // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; int32_t d_lightstylevalue[256]; // 8.8 fraction of base light value cvar_t r_norefresh = {"r_norefresh", "0", CVAR_NONE}; cvar_t r_drawentities = {"r_drawentities", "1", CVAR_NONE}; cvar_t r_drawviewmodel = {"r_drawviewmodel", "1", CVAR_NONE}; cvar_t r_speeds = {"r_speeds", "0", CVAR_NONE}; cvar_t r_pos = {"r_pos", "0", CVAR_NONE}; cvar_t r_fullbright = {"r_fullbright", "0", CVAR_NONE}; cvar_t r_lightmap = {"r_lightmap", "0", CVAR_NONE}; cvar_t r_shadows = {"r_shadows", "0", CVAR_ARCHIVE}; cvar_t r_wateralpha = {"r_wateralpha", "1", CVAR_ARCHIVE}; cvar_t r_dynamic = {"r_dynamic", "1", CVAR_ARCHIVE}; cvar_t r_novis = {"r_novis", "0", CVAR_ARCHIVE}; cvar_t gl_finish = {"gl_finish", "0", CVAR_NONE}; cvar_t gl_clear = {"gl_clear", "1", CVAR_NONE}; cvar_t gl_cull = {"gl_cull", "1", CVAR_NONE}; cvar_t gl_smoothmodels = {"gl_smoothmodels", "1", CVAR_NONE}; cvar_t gl_affinemodels = {"gl_affinemodels", "0", CVAR_NONE}; cvar_t gl_polyblend = {"gl_polyblend", "1", CVAR_NONE}; cvar_t gl_flashblend = {"gl_flashblend", "0", CVAR_ARCHIVE}; cvar_t gl_playermip = {"gl_playermip", "0", CVAR_NONE}; cvar_t gl_nocolors = {"gl_nocolors", "0", CVAR_NONE}; //johnfitz -- new cvars cvar_t r_stereo = {"r_stereo", "0", CVAR_NONE}; cvar_t r_stereodepth = {"r_stereodepth", "128", CVAR_NONE}; cvar_t r_clearcolor = {"r_clearcolor", "2", CVAR_ARCHIVE}; cvar_t r_drawflat = {"r_drawflat", "0", CVAR_NONE}; cvar_t r_flatlightstyles = {"r_flatlightstyles", "0", CVAR_NONE}; cvar_t gl_fullbrights = {"gl_fullbrights", "1", CVAR_ARCHIVE}; cvar_t gl_farclip = {"gl_farclip", "16384", CVAR_ARCHIVE}; cvar_t gl_overbright = {"gl_overbright", "1", CVAR_ARCHIVE}; cvar_t gl_overbright_models = {"gl_overbright_models", "1", CVAR_ARCHIVE}; cvar_t r_oldskyleaf = {"r_oldskyleaf", "0", CVAR_NONE}; cvar_t r_drawworld = {"r_drawworld", "1", CVAR_NONE}; cvar_t r_showtris = {"r_showtris", "0", CVAR_NONE}; cvar_t r_showbboxes = {"r_showbboxes", "0", CVAR_NONE}; cvar_t r_lerpmodels = {"r_lerpmodels", "1", CVAR_NONE}; cvar_t r_lerpmove = {"r_lerpmove", "1", CVAR_NONE}; cvar_t r_nolerp_list = {"r_nolerp_list", "progs/flame.mdl,progs/flame2.mdl,progs/braztall.mdl,progs/brazshrt.mdl,progs/longtrch.mdl,progs/flame_pyre.mdl,progs/v_saw.mdl,progs/v_xfist.mdl,progs/h2stuff/newfire.mdl", CVAR_NONE}; cvar_t r_noshadow_list = {"r_noshadow_list", "progs/flame2.mdl,progs/flame.mdl,progs/bolt1.mdl,progs/bolt2.mdl,progs/bolt3.mdl,progs/laser.mdl", CVAR_NONE}; extern cvar_t r_vfog; //johnfitz cvar_t gl_zfix = {"gl_zfix", "0", CVAR_NONE}; // QuakeSpasm z-fighting fix cvar_t r_lavaalpha = {"r_lavaalpha", "0", CVAR_NONE}; cvar_t r_telealpha = {"r_telealpha", "0", CVAR_NONE}; cvar_t r_slimealpha = {"r_slimealpha", "0", CVAR_NONE}; float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha; bool r_drawflat_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; //johnfitz cvar_t r_scale = {"r_scale", "1", CVAR_ARCHIVE}; //============================================================================== // // GLSL GAMMA CORRECTION // //============================================================================== static GLuint r_gamma_texture; static GLuint r_gamma_program; static int32_t r_gamma_texture_width, r_gamma_texture_height; // uniforms used in gamma shader static GLuint gammaLoc; static GLuint contrastLoc; static GLuint textureLoc; /* ============= GLSLGamma_DeleteTexture ============= */ void GLSLGamma_DeleteTexture(void) { glDeleteTextures(1, &r_gamma_texture); r_gamma_texture = 0; r_gamma_program = 0; // deleted in R_DeleteShaders } /* ============= GLSLGamma_CreateShaders ============= */ static void GLSLGamma_CreateShaders(void) { const GLchar *vertSource = \ "#version 110\n" "\n" "void main(void) {\n" " gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0);\n" " gl_TexCoord[0] = gl_MultiTexCoord0;\n" "}\n"; const GLchar *fragSource = \ "#version 110\n" "\n" "uniform sampler2D GammaTexture;\n" "uniform float GammaValue;\n" "uniform float ContrastValue;\n" "\n" "void main(void) {\n" " vec4 frag = texture2D(GammaTexture, gl_TexCoord[0].xy);\n" " frag.rgb = frag.rgb * ContrastValue;\n" " gl_FragColor = vec4(pow(frag.rgb, vec3(GammaValue)), 1.0);\n" "}\n"; if(!gl_glsl_gamma_able) return; r_gamma_program = GL_CreateProgram(vertSource, fragSource, 0, NULL); // get uniform locations gammaLoc = GL_GetUniformLocation(&r_gamma_program, "GammaValue"); contrastLoc = GL_GetUniformLocation(&r_gamma_program, "ContrastValue"); textureLoc = GL_GetUniformLocation(&r_gamma_program, "GammaTexture"); } /* ============= GLSLGamma_GammaCorrect ============= */ void GLSLGamma_GammaCorrect(void) { float smax, tmax; if(!gl_glsl_gamma_able) return; if(vid_gamma.value == 1 && vid_contrast.value == 1) return; // create render-to-texture texture if needed if(!r_gamma_texture) { glGenTextures(1, &r_gamma_texture); glBindTexture(GL_TEXTURE_2D, r_gamma_texture); r_gamma_texture_width = glwidth; r_gamma_texture_height = glheight; if(!gl_texture_NPOT) { r_gamma_texture_width = TexMgr_Pad(r_gamma_texture_width); r_gamma_texture_height = TexMgr_Pad(r_gamma_texture_height); } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, r_gamma_texture_width, r_gamma_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } // create shader if needed if(!r_gamma_program) { GLSLGamma_CreateShaders(); if(!r_gamma_program) { Sys_Error("GLSLGamma_CreateShaders failed"); } } // copy the framebuffer to the texture GL_DisableMultitexture(); glBindTexture(GL_TEXTURE_2D, r_gamma_texture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, glx, gly, glwidth, glheight); // draw the texture back to the framebuffer with a fragment shader GL_UseProgramFunc(r_gamma_program); GL_Uniform1fFunc(gammaLoc, vid_gamma.value); GL_Uniform1fFunc(contrastLoc, q_min(2.0, q_max(1.0, vid_contrast.value))); GL_Uniform1iFunc(textureLoc, 0); // use texture unit 0 glDisable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glViewport(glx, gly, glwidth, glheight); smax = glwidth / (float)r_gamma_texture_width; tmax = glheight / (float)r_gamma_texture_height; glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(-1, -1); glTexCoord2f(smax, 0); glVertex2f(1, -1); glTexCoord2f(smax, tmax); glVertex2f(1, 1); glTexCoord2f(0, tmax); glVertex2f(-1, 1); glEnd(); GL_UseProgramFunc(0); // clear cached binding GL_ClearBindings(); } /* ================= R_CullBox -- johnfitz -- replaced with new function from lordhavoc Returns true if the box is completely outside the frustum ================= */ bool R_CullBox(vec3_t emins, vec3_t emaxs) { int32_t i; mplane_t *p; for(i = 0; i < 4; i++) { p = frustum + i; switch(p->signbits) { default: case 0: if(p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 1: if(p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 2: if(p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 3: if(p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 4: if(p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist) return true; break; case 5: if(p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist) return true; break; case 6: if(p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist) return true; break; case 7: if(p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist) return true; break; } } return false; } /* =============== R_CullModelForEntity -- johnfitz -- uses correct bounds based on rotation =============== */ bool R_CullModelForEntity(entity_t *e) { vec3_t mins, maxs; if(e->angles[0] || e->angles[2]) //pitch or roll { VectorAdd(e->origin, e->model->rmins, mins); VectorAdd(e->origin, e->model->rmaxs, maxs); } else if(e->angles[1]) //yaw { VectorAdd(e->origin, e->model->ymins, mins); VectorAdd(e->origin, e->model->ymaxs, maxs); } else //no rotation { VectorAdd(e->origin, e->model->mins, mins); VectorAdd(e->origin, e->model->maxs, maxs); } return R_CullBox(mins, maxs); } /* =============== R_RotateForEntity -- johnfitz -- modified to take origin and angles instead of pointer to entity =============== */ void R_RotateForEntity(vec3_t origin, vec3_t angles) { glTranslatef(origin[0], origin[1], origin[2]); glRotatef(angles[1], 0, 0, 1); glRotatef(-angles[0], 0, 1, 0); glRotatef(angles[2], 1, 0, 0); } /* ============= GL_PolygonOffset -- johnfitz negative offset moves polygon closer to camera ============= */ void GL_PolygonOffset(int32_t offset) { if(offset > 0) { glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_LINE); glPolygonOffset(1, offset); } else if(offset < 0) { glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_LINE); glPolygonOffset(-1, offset); } else { glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_POLYGON_OFFSET_LINE); } } //============================================================================== // // SETUP FRAME // //============================================================================== int32_t SignbitsForPlane(mplane_t *out) { int32_t bits, j; // for fast box on planeside test bits = 0; for(j = 0 ; j < 3 ; j++) { if(out->normal[j] < 0) bits |= 1 << j; } return bits; } /* =============== TurnVector -- johnfitz turn forward towards side on the plane defined by forward and side if angle = 90, the result will be equal to side assumes side and forward are perpendicular, and normalized to turn away from side, use a negative angle =============== */ #define DEG2RAD( a ) ( (a) * PI_DIV_180 ) void TurnVector(vec3_t out, const vec3_t forward, const vec3_t side, float angle) { float scale_forward, scale_side; scale_forward = cos(DEG2RAD(angle)); scale_side = sin(DEG2RAD(angle)); out[0] = scale_forward * forward[0] + scale_side * side[0]; out[1] = scale_forward * forward[1] + scale_side * side[1]; out[2] = scale_forward * forward[2] + scale_side * side[2]; } /* =============== R_SetFrustum -- johnfitz -- rewritten =============== */ void R_SetFrustum(float fovx, float fovy) { int32_t i; if(r_stereo.value) fovx += 10; //silly hack so that polygons don't drop out becuase of stereo skew TurnVector(frustum[0].normal, vpn, vright, fovx / 2 - 90); //left plane TurnVector(frustum[1].normal, vpn, vright, 90 - fovx / 2); //right plane TurnVector(frustum[2].normal, vpn, vup, 90 - fovy / 2); //bottom plane TurnVector(frustum[3].normal, vpn, vup, fovy / 2 - 90); //top plane for(i = 0 ; i < 4 ; i++) { frustum[i].type = PLANE_ANYZ; frustum[i].dist = DotProduct(r_origin, frustum[i].normal); //FIXME: shouldn't this always be zero? frustum[i].signbits = SignbitsForPlane(&frustum[i]); } } /* ============= GL_SetFrustum -- johnfitz -- written to replace MYgluPerspective ============= */ #define NEARCLIP 4 float frustum_skew = 0.0; //used by r_stereo void GL_SetFrustum(float fovx, float fovy) { float xmax, ymax; xmax = NEARCLIP * tan(fovx * PI / 360.0); ymax = NEARCLIP * tan(fovy * PI / 360.0); glFrustum(-xmax + frustum_skew, xmax + frustum_skew, -ymax, ymax, NEARCLIP, gl_farclip.value); } /* ============= R_SetupGL ============= */ void R_SetupGL(void) { int32_t scale; //johnfitz -- rewrote this section glMatrixMode(GL_PROJECTION); glLoadIdentity(); scale = CLAMP(1, (int32_t)r_scale.value, 4); // ericw -- see R_ScaleView glViewport(glx + r_refdef.vrect.x, gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width / scale, r_refdef.vrect.height / scale); //johnfitz GL_SetFrustum(r_fovx, r_fovy); //johnfitz -- use r_fov* vars 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]); // // 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_Clear -- johnfitz -- rewritten and gutted ============= */ void R_Clear(void) { uint32_t clearbits; clearbits = GL_DEPTH_BUFFER_BIT; // from mh -- if we get a stencil buffer, we should clear it, even though we don't use it if(gl_stencilbits) clearbits |= GL_STENCIL_BUFFER_BIT; if(gl_clear.value) clearbits |= GL_COLOR_BUFFER_BIT; glClear(clearbits); } /* =============== R_SetupScene -- johnfitz -- this is the stuff that needs to be done once per eye in stereo mode =============== */ void R_SetupScene(void) { R_PushDlights(); R_AnimateLight(); r_framecount++; R_SetupGL(); } /* =============== R_SetupView -- johnfitz -- this is the stuff that needs to be done once per frame, even in stereo mode =============== */ void R_SetupView(void) { Fog_SetupFrame(); //johnfitz // build the transformation matrix for the given view angles VectorCopy(r_refdef.vieworg, r_origin); AngleVectors(r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf(r_origin, cl.worldmodel); V_SetContentsColor(r_viewleaf->contents); V_CalcBlend(); r_cache_thrash = false; //johnfitz -- calculate r_fovx and r_fovy here r_fovx = r_refdef.fov_x; r_fovy = r_refdef.fov_y; if(r_waterwarp.value) { int32_t contents = Mod_PointInLeaf(r_origin, cl.worldmodel)->contents; if(contents == CONTENTS_WATER || contents == CONTENTS_SLIME || contents == CONTENTS_LAVA) { //variance is a percentage of width, where width = 2 * tan(fov / 2) otherwise the effect is too dramatic at high FOV and too subtle at low FOV. what a mess! r_fovx = atan(tan(DEG2RAD(r_refdef.fov_x) / 2) * (0.97 + sin(cl.time * 1.5) * 0.03)) * 2 / PI_DIV_180; r_fovy = atan(tan(DEG2RAD(r_refdef.fov_y) / 2) * (1.03 - sin(cl.time * 1.5) * 0.03)) * 2 / PI_DIV_180; } } //johnfitz R_SetFrustum(r_fovx, r_fovy); //johnfitz -- use r_fov* vars R_MarkSurfaces(); //johnfitz -- create texture chains from PVS R_CullSurfaces(); //johnfitz -- do after R_SetFrustum and R_MarkSurfaces R_UpdateWarpTextures(); //johnfitz -- do this before R_Clear R_Clear(); //johnfitz -- cheat-protect some draw modes r_drawflat_cheatsafe = r_fullbright_cheatsafe = r_lightmap_cheatsafe = false; r_drawworld_cheatsafe = true; if(cl.maxclients == 1) { if(!r_drawworld.value) r_drawworld_cheatsafe = false; if(r_drawflat.value) r_drawflat_cheatsafe = true; else if(r_fullbright.value || !cl.worldmodel->lightdata) r_fullbright_cheatsafe = true; else if(r_lightmap.value) r_lightmap_cheatsafe = true; } //johnfitz } //============================================================================== // // RENDER VIEW // //============================================================================== /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList(bool alphapass) //johnfitz -- added parameter { int32_t i; if(!r_drawentities.value) return; //johnfitz -- sprites are not a special case for(i = 0 ; i < cl_numvisedicts ; i++) { currententity = cl_visedicts[i]; //johnfitz -- if alphapass is true, draw only alpha entites this time //if alphapass is false, draw only nonalpha entities this time if((ENTALPHA_DECODE(currententity->alpha) < 1 && !alphapass) || (ENTALPHA_DECODE(currententity->alpha) == 1 && alphapass)) continue; //johnfitz -- chasecam if(currententity == &cl_entities[cl.viewentity]) currententity->angles[0] *= 0.3; //johnfitz switch(currententity->model->type) { case mod_alias: R_DrawAliasModel(currententity); break; case mod_brush: R_DrawBrushModel(currententity); break; case mod_sprite: R_DrawSpriteModel(currententity); break; } } } /* ============= R_DrawViewModel -- johnfitz -- gutted ============= */ void R_DrawViewModel(void) { if(!r_drawviewmodel.value || !r_drawentities.value || chase_active.value) return; if(cl.items & IT_INVISIBILITY || cl.stats[STAT_HEALTH] <= 0) return; currententity = &cl.viewent; if(!currententity->model) return; //johnfitz -- this fixes a crash if(currententity->model->type != mod_alias) return; //johnfitz // hack the depth range to prevent view model from poking into walls glDepthRange(0, 0.3); R_DrawAliasModel(currententity); glDepthRange(0, 1); } /* ================ R_EmitWirePoint -- johnfitz -- draws a wireframe cross shape for point entities ================ */ void R_EmitWirePoint(vec3_t origin) { int32_t size = 8; glBegin(GL_LINES); glVertex3f(origin[0] - size, origin[1], origin[2]); glVertex3f(origin[0] + size, origin[1], origin[2]); glVertex3f(origin[0], origin[1] - size, origin[2]); glVertex3f(origin[0], origin[1] + size, origin[2]); glVertex3f(origin[0], origin[1], origin[2] - size); glVertex3f(origin[0], origin[1], origin[2] + size); glEnd(); } /* ================ R_EmitWireBox -- johnfitz -- draws one axis aligned bounding box ================ */ void R_EmitWireBox(vec3_t mins, vec3_t maxs) { glBegin(GL_QUAD_STRIP); glVertex3f(mins[0], mins[1], mins[2]); glVertex3f(mins[0], mins[1], maxs[2]); glVertex3f(maxs[0], mins[1], mins[2]); glVertex3f(maxs[0], mins[1], maxs[2]); glVertex3f(maxs[0], maxs[1], mins[2]); glVertex3f(maxs[0], maxs[1], maxs[2]); glVertex3f(mins[0], maxs[1], mins[2]); glVertex3f(mins[0], maxs[1], maxs[2]); glVertex3f(mins[0], mins[1], mins[2]); glVertex3f(mins[0], mins[1], maxs[2]); glEnd(); } /* ================ R_ShowBoundingBoxes -- johnfitz draw bounding boxes -- the server-side boxes, not the renderer cullboxes ================ */ void R_ShowBoundingBoxes(void) { extern edict_t *sv_player; vec3_t mins, maxs; edict_t *ed; int32_t i; if(!r_showbboxes.value || cl.maxclients > 1 || !r_drawentities.value || !sv.active) return; glDisable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); GL_PolygonOffset(OFFSET_SHOWTRIS); glDisable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glColor3f(1, 1, 1); for(i = 0, ed = NextEdict(sv.edicts) ; i < sv.num_edicts ; i++, ed = NextEdict(ed)) { if(ed == sv_player) continue; //don't draw player's own bbox // if (r_showbboxes.value != 2) // if (!SV_VisibleToClient (sv_player, ed, sv.worldmodel)) // continue; //don't draw if not in pvs if(ED_Vector(ed, ED_mins)[0] == ED_Vector(ed, ED_maxs)[0] && ED_Vector(ed, ED_mins)[1] == ED_Vector(ed, ED_maxs)[1] && ED_Vector(ed, ED_mins)[2] == ED_Vector(ed, ED_maxs)[2]) { //point entity R_EmitWirePoint(ED_Vector(ed, ED_origin)); } else { //box entity VectorAdd(ED_Vector(ed, ED_mins), ED_Vector(ed, ED_origin), mins); VectorAdd(ED_Vector(ed, ED_maxs), ED_Vector(ed, ED_origin), maxs); R_EmitWireBox(mins, maxs); } } glColor3f(1, 1, 1); glEnable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); GL_PolygonOffset(OFFSET_NONE); glEnable(GL_DEPTH_TEST); Sbar_Changed(); //so we don't get dots collecting on the statusbar } /* ================ R_ShowTris -- johnfitz ================ */ void R_ShowTris(void) { extern cvar_t r_particles; int32_t i; if(r_showtris.value < 1 || r_showtris.value > 2 || cl.maxclients > 1) return; if(r_showtris.value == 1) glDisable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); GL_PolygonOffset(OFFSET_SHOWTRIS); glDisable(GL_TEXTURE_2D); glColor3f(1, 1, 1); // glEnable (GL_BLEND); // glBlendFunc (GL_ONE, GL_ONE); if(r_drawworld.value) { R_DrawWorld_ShowTris(); } if(r_drawentities.value) { for(i = 0 ; i < cl_numvisedicts ; i++) { currententity = cl_visedicts[i]; if(currententity == &cl_entities[cl.viewentity]) // chasecam currententity->angles[0] *= 0.3; switch(currententity->model->type) { case mod_brush: R_DrawBrushModel_ShowTris(currententity); break; case mod_alias: R_DrawAliasModel_ShowTris(currententity); break; case mod_sprite: R_DrawSpriteModel(currententity); break; default: break; } } // viewmodel currententity = &cl.viewent; if(r_drawviewmodel.value && !chase_active.value && cl.stats[STAT_HEALTH] > 0 && !(cl.items & IT_INVISIBILITY) && currententity->model && currententity->model->type == mod_alias) { glDepthRange(0, 0.3); R_DrawAliasModel_ShowTris(currententity); glDepthRange(0, 1); } } if(r_particles.value) { R_DrawParticles_ShowTris(); } // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glDisable (GL_BLEND); glColor3f(1, 1, 1); glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); GL_PolygonOffset(OFFSET_NONE); if(r_showtris.value == 1) glEnable(GL_DEPTH_TEST); Sbar_Changed(); //so we don't get dots collecting on the statusbar } /* ================ R_DrawShadows ================ */ void R_DrawShadows(void) { int32_t i; if(!r_shadows.value || !r_drawentities.value || r_drawflat_cheatsafe || r_lightmap_cheatsafe) return; // Use stencil buffer to prevent self-intersecting shadows, from Baker (MarkV) if(gl_stencilbits) { glClear(GL_STENCIL_BUFFER_BIT); glStencilFunc(GL_EQUAL, 0, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glEnable(GL_STENCIL_TEST); } for(i = 0 ; i < cl_numvisedicts ; i++) { currententity = cl_visedicts[i]; if(currententity->model->type != mod_alias) continue; if(currententity == &cl.viewent) return; GL_DrawAliasShadow(currententity); } if(gl_stencilbits) { glDisable(GL_STENCIL_TEST); } } /* ================ R_RenderScene ================ */ void R_RenderScene(void) { R_SetupScene(); //johnfitz -- this does everything that should be done once per call to RenderScene Fog_EnableGFog(); //johnfitz Sky_DrawSky(); //johnfitz R_DrawWorld(); S_ExtraUpdate(); // don't let sound get messed up if going slow R_DrawShadows(); //johnfitz -- render entity shadows R_DrawEntitiesOnList(false); //johnfitz -- false means this is the pass for nonalpha entities R_DrawWorld_Water(); //johnfitz -- drawn here since they might have transparency R_DrawEntitiesOnList(true); //johnfitz -- true means this is the pass for alpha entities R_RenderDlights(); //triangle fan dlights -- johnfitz -- moved after water R_DrawParticles(); Fog_DisableGFog(); //johnfitz R_DrawViewModel(); //johnfitz -- moved here from R_RenderView R_ShowTris(); //johnfitz R_ShowBoundingBoxes(); //johnfitz } static GLuint r_scaleview_texture; static int32_t r_scaleview_texture_width, r_scaleview_texture_height; /* ============= R_ScaleView_DeleteTexture ============= */ void R_ScaleView_DeleteTexture(void) { glDeleteTextures(1, &r_scaleview_texture); r_scaleview_texture = 0; } /* ================ R_ScaleView The r_scale cvar allows rendering the 3D view at 1/2, 1/3, or 1/4 resolution. This function scales the reduced resolution 3D view back up to fill r_refdef.vrect. This is for emulating a low-resolution pixellated look, or possibly as a perforance boost on slow graphics cards. ================ */ void R_ScaleView(void) { float smax, tmax; int32_t scale; int32_t srcx, srcy, srcw, srch; // copied from R_SetupGL() scale = CLAMP(1, (int32_t)r_scale.value, 4); srcx = glx + r_refdef.vrect.x; srcy = gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height; srcw = r_refdef.vrect.width / scale; srch = r_refdef.vrect.height / scale; if(scale == 1) return; // make sure texture unit 0 is selected GL_DisableMultitexture(); // create (if needed) and bind the render-to-texture texture if(!r_scaleview_texture) { glGenTextures(1, &r_scaleview_texture); r_scaleview_texture_width = 0; r_scaleview_texture_height = 0; } glBindTexture(GL_TEXTURE_2D, r_scaleview_texture); // resize render-to-texture texture if needed if(r_scaleview_texture_width < srcw || r_scaleview_texture_height < srch) { r_scaleview_texture_width = srcw; r_scaleview_texture_height = srch; if(!gl_texture_NPOT) { r_scaleview_texture_width = TexMgr_Pad(r_scaleview_texture_width); r_scaleview_texture_height = TexMgr_Pad(r_scaleview_texture_height); } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r_scaleview_texture_width, r_scaleview_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } // copy the framebuffer to the texture glBindTexture(GL_TEXTURE_2D, r_scaleview_texture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, srcx, srcy, srcw, srch); // draw the texture back to the framebuffer glDisable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glViewport(srcx, srcy, r_refdef.vrect.width, r_refdef.vrect.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // correction factor if we lack NPOT textures, normally these are 1.0f smax = srcw / (float)r_scaleview_texture_width; tmax = srch / (float)r_scaleview_texture_height; glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(-1, -1); glTexCoord2f(smax, 0); glVertex2f(1, -1); glTexCoord2f(smax, tmax); glVertex2f(1, 1); glTexCoord2f(0, tmax); glVertex2f(-1, 1); glEnd(); // clear cached binding GL_ClearBindings(); } /* ================ R_RenderView ================ */ void R_RenderView(void) { double time1, time2; if(r_norefresh.value) return; if(!cl.worldmodel) Sys_Error("R_RenderView: NULL worldmodel"); time1 = 0; /* avoid compiler warning */ if(r_speeds.value) { glFinish(); time1 = Sys_DoubleTime(); //johnfitz -- rendering statistics rs_brushpolys = rs_aliaspolys = rs_skypolys = rs_particles = rs_fogpolys = rs_megatexels = rs_dynamiclightmaps = rs_aliaspasses = rs_skypasses = rs_brushpasses = 0; } else if(gl_finish.value) glFinish(); R_SetupView(); //johnfitz -- this does everything that should be done once per frame //johnfitz -- stereo rendering -- full of hacky goodness if(r_stereo.value) { float eyesep = CLAMP(-8.0f, r_stereo.value, 8.0f); float fdepth = CLAMP(32.0f, r_stereodepth.value, 1024.0f); AngleVectors(r_refdef.viewangles, vpn, vright, vup); //render left eye (red) glColorMask(1, 0, 0, 1); VectorMA(r_refdef.vieworg, -0.5f * eyesep, vright, r_refdef.vieworg); frustum_skew = 0.5 * eyesep * NEARCLIP / fdepth; srand((int32_t)(cl.time * 1000)); //sync random stuff between eyes R_RenderScene(); //render right eye (cyan) glClear(GL_DEPTH_BUFFER_BIT); glColorMask(0, 1, 1, 1); VectorMA(r_refdef.vieworg, 1.0f * eyesep, vright, r_refdef.vieworg); frustum_skew = -frustum_skew; srand((int32_t)(cl.time * 1000)); //sync random stuff between eyes R_RenderScene(); //restore glColorMask(1, 1, 1, 1); VectorMA(r_refdef.vieworg, -0.5f * eyesep, vright, r_refdef.vieworg); frustum_skew = 0.0f; } else { R_RenderScene(); } //johnfitz R_ScaleView(); //johnfitz -- modified r_speeds output time2 = Sys_DoubleTime(); if(r_pos.value) Con_Printf("x %" PRIi32 " y %" PRIi32 " z %" PRIi32 " (pitch %" PRIi32 " yaw %" PRIi32 " roll %" PRIi32 ")\n", (int32_t)cl_entities[cl.viewentity].origin[0], (int32_t)cl_entities[cl.viewentity].origin[1], (int32_t)cl_entities[cl.viewentity].origin[2], (int32_t)cl.viewangles[PITCH], (int32_t)cl.viewangles[YAW], (int32_t)cl.viewangles[ROLL]); else if(r_speeds.value == 2) Con_Printf("%3" PRIi32 " ms %4" PRIi32 "/%4" PRIi32 " wpoly %4" PRIi32 "/%4" PRIi32 " epoly %3" PRIi32 " lmap %4" PRIi32 "%4" PRIi32 "sky %1.1f mtex\n", (int32_t)((time2 - time1) * 1000), rs_brushpolys, rs_brushpasses, rs_aliaspolys, rs_aliaspasses, rs_dynamiclightmaps, rs_skypolys, rs_skypasses, TexMgr_FrameUsage()); else if(r_speeds.value) Con_Printf("%3" PRIi32 " ms %4" PRIi32 " wpoly %4" PRIi32 " epoly %3" PRIi32 " lmap\n", (int32_t)((time2 - time1) * 1000), rs_brushpolys, rs_aliaspolys, rs_dynamiclightmaps); //johnfitz }