+void gl_main_shutdown(void)
+{
+ memset(r_qwskincache, 0, sizeof(r_qwskincache));
+ memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
+
+ // clear out the r_skinframe state
+ Mem_ExpandableArray_FreeArray(&r_skinframe.array);
+ memset(&r_skinframe, 0, sizeof(r_skinframe));
+
+ if (r_svbsp.nodes)
+ Mem_Free(r_svbsp.nodes);
+ memset(&r_svbsp, 0, sizeof (r_svbsp));
+ R_FreeTexturePool(&r_main_texturepool);
+ r_texture_blanknormalmap = NULL;
+ r_texture_white = NULL;
+ r_texture_grey128 = NULL;
+ r_texture_black = NULL;
+ r_texture_whitecube = NULL;
+ r_texture_normalizationcube = NULL;
+ memset(&r_bloomstate, 0, sizeof(r_bloomstate));
+ memset(&r_waterstate, 0, sizeof(r_waterstate));
+ R_GLSL_Restart_f();
+}
+
+extern void CL_ParseEntityLump(char *entitystring);
+void gl_main_newmap(void)
+{
+ // FIXME: move this code to client
+ int l;
+ char *entities, entname[MAX_QPATH];
+ if (cl.worldmodel)
+ {
+ strlcpy(entname, cl.worldmodel->name, sizeof(entname));
+ l = (int)strlen(entname) - 4;
+ if (l >= 0 && !strcmp(entname + l, ".bsp"))
+ {
+ memcpy(entname + l, ".ent", 5);
+ if ((entities = (char *)FS_LoadFile(entname, tempmempool, true, NULL)))
+ {
+ CL_ParseEntityLump(entities);
+ Mem_Free(entities);
+ return;
+ }
+ }
+ if (cl.worldmodel->brush.entities)
+ CL_ParseEntityLump(cl.worldmodel->brush.entities);
+ }
+}
+
+void GL_Main_Init(void)
+{
+ r_main_mempool = Mem_AllocPool("Renderer", 0, NULL);
+
+ Cmd_AddCommand("r_glsl_restart", R_GLSL_Restart_f, "unloads GLSL shaders, they will then be reloaded as needed");
+ Cmd_AddCommand("r_glsl_dumpshader", R_GLSL_DumpShader_f, "dumps the engine internal default.glsl shader into glsl/default.glsl");
+ // FIXME: the client should set up r_refdef.fog stuff including the fogmasktable
+ if (gamemode == GAME_NEHAHRA)
+ {
+ Cvar_RegisterVariable (&gl_fogenable);
+ Cvar_RegisterVariable (&gl_fogdensity);
+ Cvar_RegisterVariable (&gl_fogred);
+ Cvar_RegisterVariable (&gl_foggreen);
+ Cvar_RegisterVariable (&gl_fogblue);
+ Cvar_RegisterVariable (&gl_fogstart);
+ Cvar_RegisterVariable (&gl_fogend);
+ }
+ Cvar_RegisterVariable(&r_depthfirst);
+ Cvar_RegisterVariable(&r_nearclip);
+ Cvar_RegisterVariable(&r_showbboxes);
+ Cvar_RegisterVariable(&r_showsurfaces);
+ Cvar_RegisterVariable(&r_showtris);
+ Cvar_RegisterVariable(&r_shownormals);
+ Cvar_RegisterVariable(&r_showlighting);
+ Cvar_RegisterVariable(&r_showshadowvolumes);
+ Cvar_RegisterVariable(&r_showcollisionbrushes);
+ Cvar_RegisterVariable(&r_showcollisionbrushes_polygonfactor);
+ Cvar_RegisterVariable(&r_showcollisionbrushes_polygonoffset);
+ Cvar_RegisterVariable(&r_showdisabledepthtest);
+ Cvar_RegisterVariable(&r_drawportals);
+ Cvar_RegisterVariable(&r_drawentities);
+ Cvar_RegisterVariable(&r_cullentities_trace);
+ Cvar_RegisterVariable(&r_cullentities_trace_samples);
+ Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
+ Cvar_RegisterVariable(&r_cullentities_trace_delay);
+ Cvar_RegisterVariable(&r_drawviewmodel);
+ Cvar_RegisterVariable(&r_speeds);
+ Cvar_RegisterVariable(&r_fullbrights);
+ Cvar_RegisterVariable(&r_wateralpha);
+ Cvar_RegisterVariable(&r_dynamic);
+ Cvar_RegisterVariable(&r_fullbright);
+ Cvar_RegisterVariable(&r_shadows);
+ Cvar_RegisterVariable(&r_shadows_throwdistance);
+ Cvar_RegisterVariable(&r_q1bsp_skymasking);
+ Cvar_RegisterVariable(&r_polygonoffset_submodel_factor);
+ Cvar_RegisterVariable(&r_polygonoffset_submodel_offset);
+ Cvar_RegisterVariable(&r_textureunits);
+ Cvar_RegisterVariable(&r_glsl);
+ Cvar_RegisterVariable(&r_glsl_offsetmapping);
+ Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping);
+ Cvar_RegisterVariable(&r_glsl_offsetmapping_scale);
+ Cvar_RegisterVariable(&r_glsl_deluxemapping);
+ Cvar_RegisterVariable(&r_water);
+ Cvar_RegisterVariable(&r_water_resolutionmultiplier);
+ Cvar_RegisterVariable(&r_water_clippingplanebias);
+ Cvar_RegisterVariable(&r_water_refractdistort);
+ Cvar_RegisterVariable(&r_water_reflectdistort);
+ Cvar_RegisterVariable(&r_lerpsprites);
+ Cvar_RegisterVariable(&r_lerpmodels);
+ Cvar_RegisterVariable(&r_waterscroll);
+ Cvar_RegisterVariable(&r_bloom);
+ Cvar_RegisterVariable(&r_bloom_colorscale);
+ Cvar_RegisterVariable(&r_bloom_brighten);
+ Cvar_RegisterVariable(&r_bloom_blur);
+ Cvar_RegisterVariable(&r_bloom_resolution);
+ Cvar_RegisterVariable(&r_bloom_colorexponent);
+ Cvar_RegisterVariable(&r_bloom_colorsubtract);
+ Cvar_RegisterVariable(&r_hdr);
+ Cvar_RegisterVariable(&r_hdr_scenebrightness);
+ Cvar_RegisterVariable(&r_glsl_contrastboost);
+ Cvar_RegisterVariable(&r_hdr_glowintensity);
+ Cvar_RegisterVariable(&r_hdr_range);
+ Cvar_RegisterVariable(&r_smoothnormals_areaweighting);
+ Cvar_RegisterVariable(&developer_texturelogging);
+ Cvar_RegisterVariable(&gl_lightmaps);
+ Cvar_RegisterVariable(&r_test);
+ Cvar_RegisterVariable(&r_batchmode);
+ if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
+ Cvar_SetValue("r_fullbrights", 0);
+ R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap);
+
+ Cvar_RegisterVariable(&r_track_sprites);
+ Cvar_RegisterVariable(&r_track_sprites_flags);
+ Cvar_RegisterVariable(&r_track_sprites_scalew);
+ Cvar_RegisterVariable(&r_track_sprites_scaleh);
+}
+
+extern void R_Textures_Init(void);
+extern void GL_Draw_Init(void);
+extern void GL_Main_Init(void);
+extern void R_Shadow_Init(void);
+extern void R_Sky_Init(void);
+extern void GL_Surf_Init(void);
+extern void R_Light_Init(void);
+extern void R_Particles_Init(void);
+extern void R_Explosion_Init(void);
+extern void gl_backend_init(void);
+extern void Sbar_Init(void);
+extern void R_LightningBeams_Init(void);
+extern void Mod_RenderInit(void);
+
+void Render_Init(void)
+{
+ gl_backend_init();
+ R_Textures_Init();
+ GL_Main_Init();
+ GL_Draw_Init();
+ R_Shadow_Init();
+ R_Sky_Init();
+ GL_Surf_Init();
+ Sbar_Init();
+ R_Light_Init();
+ R_Particles_Init();
+ R_Explosion_Init();
+ R_LightningBeams_Init();
+ Mod_RenderInit();
+}
+
+/*
+===============
+GL_Init
+===============
+*/
+extern char *ENGINE_EXTENSIONS;
+void GL_Init (void)
+{
+ VID_CheckExtensions();
+
+ // LordHavoc: report supported extensions
+ Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
+
+ // clear to black (loading plaque will be seen over this)
+ CHECKGLERROR
+ qglClearColor(0,0,0,1);CHECKGLERROR
+ qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
+}
+
+int R_CullBox(const vec3_t mins, const vec3_t maxs)
+{
+ int i;
+ mplane_t *p;
+ for (i = 0;i < r_view.numfrustumplanes;i++)
+ {
+ // skip nearclip plane, it often culls portals when you are very close, and is almost never useful
+ if (i == 4)
+ continue;
+ p = r_view.frustum + i;
+ switch(p->signbits)
+ {
+ default:
+ case 0:
+ if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 1:
+ if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 2:
+ if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 3:
+ if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 4:
+ if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ case 5:
+ if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ case 6:
+ if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ case 7:
+ if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ }
+ }
+ return false;
+}
+
+int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes)
+{
+ int i;
+ const mplane_t *p;
+ for (i = 0;i < numplanes;i++)
+ {
+ p = planes + i;
+ switch(p->signbits)
+ {
+ default:
+ case 0:
+ if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 1:
+ if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 2:
+ if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 3:
+ if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
+ return true;
+ break;
+ case 4:
+ if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ case 5:
+ if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ case 6:
+ if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ case 7:
+ if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
+ return true;
+ break;
+ }
+ }
+ return false;
+}
+
+//==================================================================================
+
+static void R_UpdateEntityLighting(entity_render_t *ent)
+{
+ vec3_t tempdiffusenormal;
+
+ // fetch the lighting from the worldmodel data
+ VectorSet(ent->modellight_ambient, r_ambient.value * (2.0f / 128.0f), r_ambient.value * (2.0f / 128.0f), r_ambient.value * (2.0f / 128.0f));
+ VectorClear(ent->modellight_diffuse);
+ VectorClear(tempdiffusenormal);
+ if ((ent->flags & RENDER_LIGHT) && r_refdef.worldmodel && r_refdef.worldmodel->brush.LightPoint)
+ {
+ vec3_t org;
+ Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+ r_refdef.worldmodel->brush.LightPoint(r_refdef.worldmodel, org, ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal);
+ }
+ else // highly rare
+ VectorSet(ent->modellight_ambient, 1, 1, 1);
+
+ // move the light direction into modelspace coordinates for lighting code
+ Matrix4x4_Transform3x3(&ent->inversematrix, tempdiffusenormal, ent->modellight_lightdir);
+ if(VectorLength2(ent->modellight_lightdir) > 0)
+ {
+ VectorNormalize(ent->modellight_lightdir);
+ }
+ else
+ {
+ VectorSet(ent->modellight_lightdir, 0, 0, 1); // have to set SOME valid vector here
+ }
+
+ // scale ambient and directional light contributions according to rendering variables
+ ent->modellight_ambient[0] *= ent->colormod[0] * r_refdef.lightmapintensity;
+ ent->modellight_ambient[1] *= ent->colormod[1] * r_refdef.lightmapintensity;
+ ent->modellight_ambient[2] *= ent->colormod[2] * r_refdef.lightmapintensity;
+ ent->modellight_diffuse[0] *= ent->colormod[0] * r_refdef.lightmapintensity;
+ ent->modellight_diffuse[1] *= ent->colormod[1] * r_refdef.lightmapintensity;
+ ent->modellight_diffuse[2] *= ent->colormod[2] * r_refdef.lightmapintensity;
+}
+
+static void R_View_UpdateEntityVisible (void)
+{
+ int i, renderimask;
+ entity_render_t *ent;
+
+ if (!r_drawentities.integer)
+ return;
+
+ renderimask = r_refdef.envmap ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL) : ((chase_active.integer || r_waterstate.renderingscene) ? RENDER_VIEWMODEL : RENDER_EXTERIORMODEL);
+ if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs)
+ {
+ // worldmodel can check visibility
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ ent = r_refdef.entities[i];
+ r_viewcache.entityvisible[i] = !(ent->flags & renderimask) && ((ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)) || !R_CullBox(ent->mins, ent->maxs)) && ((ent->effects & EF_NODEPTHTEST) || (ent->flags & RENDER_VIEWMODEL) || r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.worldmodel, r_viewcache.world_leafvisible, ent->mins, ent->maxs));
+
+ }
+ if(r_cullentities_trace.integer)
+ {
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ ent = r_refdef.entities[i];
+ if(r_viewcache.entityvisible[i] && !(ent->effects & EF_NODEPTHTEST) && !(ent->flags & RENDER_VIEWMODEL) && !(ent->model && (ent->model->name[0] == '*')))
+ {
+ if(Mod_CanSeeBox_Trace(r_cullentities_trace_samples.integer, r_cullentities_trace_enlarge.value, r_refdef.worldmodel, r_view.origin, ent->mins, ent->maxs))
+ ent->last_trace_visibility = realtime;
+ if(ent->last_trace_visibility < realtime - r_cullentities_trace_delay.value)
+ r_viewcache.entityvisible[i] = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ // no worldmodel or it can't check visibility
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ ent = r_refdef.entities[i];
+ r_viewcache.entityvisible[i] = !(ent->flags & renderimask) && ((ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)) || !R_CullBox(ent->mins, ent->maxs));
+ }
+ }
+
+ // update entity lighting (even on hidden entities for r_shadows)
+ for (i = 0;i < r_refdef.numentities;i++)
+ R_UpdateEntityLighting(r_refdef.entities[i]);
+}
+
+// only used if skyrendermasked, and normally returns false
+int R_DrawBrushModelsSky (void)
+{
+ int i, sky;
+ entity_render_t *ent;
+
+ if (!r_drawentities.integer)
+ return false;
+
+ sky = false;
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ if (!r_viewcache.entityvisible[i])
+ continue;
+ ent = r_refdef.entities[i];
+ if (!ent->model || !ent->model->DrawSky)
+ continue;
+ ent->model->DrawSky(ent);
+ sky = true;
+ }
+ return sky;
+}
+
+static void R_DrawNoModel(entity_render_t *ent);
+static void R_DrawModels(void)
+{
+ int i;
+ entity_render_t *ent;
+
+ if (!r_drawentities.integer)
+ return;
+
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ if (!r_viewcache.entityvisible[i])
+ continue;
+ ent = r_refdef.entities[i];
+ r_refdef.stats.entities++;
+ if (ent->model && ent->model->Draw != NULL)
+ ent->model->Draw(ent);
+ else
+ R_DrawNoModel(ent);
+ }
+}
+
+static void R_DrawModelsDepth(void)
+{
+ int i;
+ entity_render_t *ent;
+
+ if (!r_drawentities.integer)
+ return;
+
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ if (!r_viewcache.entityvisible[i])
+ continue;
+ ent = r_refdef.entities[i];
+ if (ent->model && ent->model->DrawDepth != NULL)
+ ent->model->DrawDepth(ent);
+ }
+}
+
+static void R_DrawModelsDebug(void)
+{
+ int i;
+ entity_render_t *ent;
+
+ if (!r_drawentities.integer)
+ return;
+
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ if (!r_viewcache.entityvisible[i])
+ continue;
+ ent = r_refdef.entities[i];
+ if (ent->model && ent->model->DrawDebug != NULL)
+ ent->model->DrawDebug(ent);
+ }
+}
+
+static void R_DrawModelsAddWaterPlanes(void)
+{
+ int i;
+ entity_render_t *ent;
+
+ if (!r_drawentities.integer)
+ return;
+
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ if (!r_viewcache.entityvisible[i])
+ continue;
+ ent = r_refdef.entities[i];
+ if (ent->model && ent->model->DrawAddWaterPlanes != NULL)
+ ent->model->DrawAddWaterPlanes(ent);
+ }
+}
+
+static void R_View_SetFrustum(void)
+{
+ int i;
+ double slopex, slopey;
+
+ // break apart the view matrix into vectors for various purposes
+ Matrix4x4_ToVectors(&r_view.matrix, r_view.forward, r_view.left, r_view.up, r_view.origin);
+ VectorNegate(r_view.left, r_view.right);
+
+#if 0
+ r_view.frustum[0].normal[0] = 0 - 1.0 / r_view.frustum_x;
+ r_view.frustum[0].normal[1] = 0 - 0;
+ r_view.frustum[0].normal[2] = -1 - 0;
+ r_view.frustum[1].normal[0] = 0 + 1.0 / r_view.frustum_x;
+ r_view.frustum[1].normal[1] = 0 + 0;
+ r_view.frustum[1].normal[2] = -1 + 0;
+ r_view.frustum[2].normal[0] = 0 - 0;
+ r_view.frustum[2].normal[1] = 0 - 1.0 / r_view.frustum_y;
+ r_view.frustum[2].normal[2] = -1 - 0;
+ r_view.frustum[3].normal[0] = 0 + 0;
+ r_view.frustum[3].normal[1] = 0 + 1.0 / r_view.frustum_y;
+ r_view.frustum[3].normal[2] = -1 + 0;
+#endif
+
+#if 0
+ zNear = r_refdef.nearclip;
+ nudge = 1.0 - 1.0 / (1<<23);
+ r_view.frustum[4].normal[0] = 0 - 0;
+ r_view.frustum[4].normal[1] = 0 - 0;
+ r_view.frustum[4].normal[2] = -1 - -nudge;
+ r_view.frustum[4].dist = 0 - -2 * zNear * nudge;
+ r_view.frustum[5].normal[0] = 0 + 0;
+ r_view.frustum[5].normal[1] = 0 + 0;
+ r_view.frustum[5].normal[2] = -1 + -nudge;
+ r_view.frustum[5].dist = 0 + -2 * zNear * nudge;
+#endif
+
+
+
+#if 0
+ r_view.frustum[0].normal[0] = m[3] - m[0];
+ r_view.frustum[0].normal[1] = m[7] - m[4];
+ r_view.frustum[0].normal[2] = m[11] - m[8];
+ r_view.frustum[0].dist = m[15] - m[12];
+
+ r_view.frustum[1].normal[0] = m[3] + m[0];
+ r_view.frustum[1].normal[1] = m[7] + m[4];
+ r_view.frustum[1].normal[2] = m[11] + m[8];
+ r_view.frustum[1].dist = m[15] + m[12];
+
+ r_view.frustum[2].normal[0] = m[3] - m[1];
+ r_view.frustum[2].normal[1] = m[7] - m[5];
+ r_view.frustum[2].normal[2] = m[11] - m[9];
+ r_view.frustum[2].dist = m[15] - m[13];
+
+ r_view.frustum[3].normal[0] = m[3] + m[1];
+ r_view.frustum[3].normal[1] = m[7] + m[5];
+ r_view.frustum[3].normal[2] = m[11] + m[9];
+ r_view.frustum[3].dist = m[15] + m[13];
+
+ r_view.frustum[4].normal[0] = m[3] - m[2];
+ r_view.frustum[4].normal[1] = m[7] - m[6];
+ r_view.frustum[4].normal[2] = m[11] - m[10];
+ r_view.frustum[4].dist = m[15] - m[14];
+
+ r_view.frustum[5].normal[0] = m[3] + m[2];
+ r_view.frustum[5].normal[1] = m[7] + m[6];
+ r_view.frustum[5].normal[2] = m[11] + m[10];
+ r_view.frustum[5].dist = m[15] + m[14];
+#endif
+
+ if (r_view.useperspective)
+ {
+ slopex = 1.0 / r_view.frustum_x;
+ slopey = 1.0 / r_view.frustum_y;
+ VectorMA(r_view.forward, -slopex, r_view.left, r_view.frustum[0].normal);
+ VectorMA(r_view.forward, slopex, r_view.left, r_view.frustum[1].normal);
+ VectorMA(r_view.forward, -slopey, r_view.up , r_view.frustum[2].normal);
+ VectorMA(r_view.forward, slopey, r_view.up , r_view.frustum[3].normal);
+ VectorCopy(r_view.forward, r_view.frustum[4].normal);
+
+ // Leaving those out was a mistake, those were in the old code, and they
+ // fix a reproducable bug in this one: frustum culling got fucked up when viewmatrix was an identity matrix
+ // I couldn't reproduce it after adding those normalizations. --blub
+ VectorNormalize(r_view.frustum[0].normal);
+ VectorNormalize(r_view.frustum[1].normal);
+ VectorNormalize(r_view.frustum[2].normal);
+ VectorNormalize(r_view.frustum[3].normal);
+
+ // calculate frustum corners, which are used to calculate deformed frustum planes for shadow caster culling
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, -1024 * slopex, r_view.left, -1024 * slopey, r_view.up, r_view.frustumcorner[0]);
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, 1024 * slopex, r_view.left, -1024 * slopey, r_view.up, r_view.frustumcorner[1]);
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, -1024 * slopex, r_view.left, 1024 * slopey, r_view.up, r_view.frustumcorner[2]);
+ VectorMAMAMAM(1, r_view.origin, 1024, r_view.forward, 1024 * slopex, r_view.left, 1024 * slopey, r_view.up, r_view.frustumcorner[3]);
+
+ r_view.frustum[0].dist = DotProduct (r_view.origin, r_view.frustum[0].normal);
+ r_view.frustum[1].dist = DotProduct (r_view.origin, r_view.frustum[1].normal);
+ r_view.frustum[2].dist = DotProduct (r_view.origin, r_view.frustum[2].normal);
+ r_view.frustum[3].dist = DotProduct (r_view.origin, r_view.frustum[3].normal);
+ r_view.frustum[4].dist = DotProduct (r_view.origin, r_view.frustum[4].normal) + r_refdef.nearclip;
+ }
+ else
+ {
+ VectorScale(r_view.left, -r_view.ortho_x, r_view.frustum[0].normal);
+ VectorScale(r_view.left, r_view.ortho_x, r_view.frustum[1].normal);
+ VectorScale(r_view.up, -r_view.ortho_y, r_view.frustum[2].normal);
+ VectorScale(r_view.up, r_view.ortho_y, r_view.frustum[3].normal);
+ VectorCopy(r_view.forward, r_view.frustum[4].normal);
+ r_view.frustum[0].dist = DotProduct (r_view.origin, r_view.frustum[0].normal) + r_view.ortho_x;
+ r_view.frustum[1].dist = DotProduct (r_view.origin, r_view.frustum[1].normal) + r_view.ortho_x;
+ r_view.frustum[2].dist = DotProduct (r_view.origin, r_view.frustum[2].normal) + r_view.ortho_y;
+ r_view.frustum[3].dist = DotProduct (r_view.origin, r_view.frustum[3].normal) + r_view.ortho_y;
+ r_view.frustum[4].dist = DotProduct (r_view.origin, r_view.frustum[4].normal) + r_refdef.nearclip;
+ }
+ r_view.numfrustumplanes = 5;
+
+ if (r_view.useclipplane)
+ {
+ r_view.numfrustumplanes = 6;
+ r_view.frustum[5] = r_view.clipplane;
+ }
+
+ for (i = 0;i < r_view.numfrustumplanes;i++)
+ PlaneClassify(r_view.frustum + i);
+
+ // LordHavoc: note to all quake engine coders, Quake had a special case
+ // for 90 degrees which assumed a square view (wrong), so I removed it,
+ // Quake2 has it disabled as well.
+
+ // rotate R_VIEWFORWARD right by FOV_X/2 degrees
+ //RotatePointAroundVector( r_view.frustum[0].normal, r_view.up, r_view.forward, -(90 - r_refdef.fov_x / 2));
+ //r_view.frustum[0].dist = DotProduct (r_view.origin, frustum[0].normal);
+ //PlaneClassify(&frustum[0]);
+
+ // rotate R_VIEWFORWARD left by FOV_X/2 degrees
+ //RotatePointAroundVector( r_view.frustum[1].normal, r_view.up, r_view.forward, (90 - r_refdef.fov_x / 2));
+ //r_view.frustum[1].dist = DotProduct (r_view.origin, frustum[1].normal);
+ //PlaneClassify(&frustum[1]);
+
+ // rotate R_VIEWFORWARD up by FOV_X/2 degrees
+ //RotatePointAroundVector( r_view.frustum[2].normal, r_view.left, r_view.forward, -(90 - r_refdef.fov_y / 2));
+ //r_view.frustum[2].dist = DotProduct (r_view.origin, frustum[2].normal);
+ //PlaneClassify(&frustum[2]);
+
+ // rotate R_VIEWFORWARD down by FOV_X/2 degrees
+ //RotatePointAroundVector( r_view.frustum[3].normal, r_view.left, r_view.forward, (90 - r_refdef.fov_y / 2));
+ //r_view.frustum[3].dist = DotProduct (r_view.origin, frustum[3].normal);
+ //PlaneClassify(&frustum[3]);
+
+ // nearclip plane
+ //VectorCopy(r_view.forward, r_view.frustum[4].normal);
+ //r_view.frustum[4].dist = DotProduct (r_view.origin, frustum[4].normal) + r_nearclip.value;
+ //PlaneClassify(&frustum[4]);
+}
+
+void R_View_Update(void)
+{
+ R_View_SetFrustum();
+ R_View_WorldVisibility(r_view.useclipplane);
+ R_View_UpdateEntityVisible();
+}
+
+void R_SetupView(void)
+{
+ if (!r_view.useperspective)
+ GL_SetupView_Mode_Ortho(-r_view.ortho_x, -r_view.ortho_y, r_view.ortho_x, r_view.ortho_y, -r_refdef.farclip, r_refdef.farclip);
+ else if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows)
+ GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip);
+ else
+ GL_SetupView_Mode_Perspective(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip, r_refdef.farclip);
+
+ GL_SetupView_Orientation_FromEntity(&r_view.matrix);
+
+ if (r_view.useclipplane)
+ {
+ // LordHavoc: couldn't figure out how to make this approach the
+ vec_t dist = r_view.clipplane.dist - r_water_clippingplanebias.value;
+ vec_t viewdist = DotProduct(r_view.origin, r_view.clipplane.normal);
+ if (viewdist < r_view.clipplane.dist + r_water_clippingplanebias.value)
+ dist = r_view.clipplane.dist;
+ GL_SetupView_ApplyCustomNearClipPlane(r_view.clipplane.normal[0], r_view.clipplane.normal[1], r_view.clipplane.normal[2], dist);
+ }
+}
+
+void R_ResetViewRendering2D(void)
+{
+ if (gl_support_fragment_shader)
+ {
+ qglUseProgramObjectARB(0);CHECKGLERROR
+ }
+
+ DrawQ_Finish();
+
+ // GL is weird because it's bottom to top, r_view.y is top to bottom
+ qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
+ GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
+ GL_Color(1, 1, 1, 1);
+ GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ GL_AlphaTest(false);
+ GL_ScissorTest(false);
+ GL_DepthMask(false);
+ GL_DepthRange(0, 1);
+ GL_DepthTest(false);
+ R_Mesh_Matrix(&identitymatrix);
+ R_Mesh_ResetTextureState();
+ GL_PolygonOffset(0, 0);
+ qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
+ qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+ qglDisable(GL_STENCIL_TEST);CHECKGLERROR
+ qglStencilMask(~0);CHECKGLERROR
+ qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
+ qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
+ GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+}
+
+void R_ResetViewRendering3D(void)
+{
+ if (gl_support_fragment_shader)
+ {
+ qglUseProgramObjectARB(0);CHECKGLERROR
+ }
+
+ DrawQ_Finish();
+
+ // GL is weird because it's bottom to top, r_view.y is top to bottom
+ qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ R_SetupView();
+ GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
+ GL_Color(1, 1, 1, 1);
+ GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ GL_AlphaTest(false);
+ GL_ScissorTest(true);
+ GL_DepthMask(true);
+ GL_DepthRange(0, 1);
+ GL_DepthTest(true);
+ R_Mesh_Matrix(&identitymatrix);
+ R_Mesh_ResetTextureState();
+ GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
+ qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
+ qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+ qglDisable(GL_STENCIL_TEST);CHECKGLERROR
+ qglStencilMask(~0);CHECKGLERROR
+ qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
+ qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
+ GL_CullFace(r_view.cullface_back);
+}
+
+/*
+ R_Bloom_SetupShader(
+"// bloom shader\n"
+"// written by Forest 'LordHavoc' Hale\n"
+"\n"
+"// common definitions between vertex shader and fragment shader:\n"
+"\n"
+"#ifdef __GLSL_CG_DATA_TYPES\n"
+"#define myhalf half\n"
+"#define myhvec2 hvec2\n"
+"#define myhvec3 hvec3\n"
+"#define myhvec4 hvec4\n"
+"#else\n"
+"#define myhalf float\n"
+"#define myhvec2 vec2\n"
+"#define myhvec3 vec3\n"
+"#define myhvec4 vec4\n"
+"#endif\n"
+"\n"
+"varying vec2 ScreenTexCoord;\n"
+"varying vec2 BloomTexCoord;\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"// vertex shader specific:\n"
+"#ifdef VERTEX_SHADER\n"
+"\n"
+"void main(void)\n"
+"{\n"
+" ScreenTexCoord = vec2(gl_MultiTexCoord0);\n"
+" BloomTexCoord = vec2(gl_MultiTexCoord1);\n"
+" // transform vertex to camera space, using ftransform to match non-VS\n"
+" // rendering\n"
+" gl_Position = ftransform();\n"
+"}\n"
+"\n"
+"#endif // VERTEX_SHADER\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"// fragment shader specific:\n"
+"#ifdef FRAGMENT_SHADER\n"
+"\n"
+"void main(void)\n"
+"{\n"
+" int x, y;
+" myhvec3 color = myhvec3(texture2D(Texture_Screen, ScreenTexCoord));\n"
+" for (x = -BLUR_X;x <= BLUR_X;x++)
+" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n"
+" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n"
+" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n"
+" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n"
+
+" gl_FragColor = vec4(color);\n"
+"}\n"
+"\n"
+"#endif // FRAGMENT_SHADER\n"
+*/
+
+void R_RenderScene(qboolean addwaterplanes);
+
+static void R_Water_StartFrame(void)
+{
+ int i;
+ int waterwidth, waterheight, texturewidth, textureheight;
+ r_waterstate_waterplane_t *p;
+
+ // set waterwidth and waterheight to the water resolution that will be
+ // used (often less than the screen resolution for faster rendering)
+ waterwidth = (int)bound(1, r_view.width * r_water_resolutionmultiplier.value, r_view.width);
+ waterheight = (int)bound(1, r_view.height * r_water_resolutionmultiplier.value, r_view.height);
+
+ // calculate desired texture sizes
+ // can't use water if the card does not support the texture size
+ if (!r_water.integer || !r_glsl.integer || !gl_support_fragment_shader || waterwidth > gl_max_texture_size || waterheight > gl_max_texture_size)
+ texturewidth = textureheight = waterwidth = waterheight = 0;
+ else if (gl_support_arb_texture_non_power_of_two)
+ {
+ texturewidth = waterwidth;
+ textureheight = waterheight;
+ }
+ else
+ {
+ for (texturewidth = 1;texturewidth < waterwidth ;texturewidth *= 2);
+ for (textureheight = 1;textureheight < waterheight;textureheight *= 2);
+ }
+
+ // allocate textures as needed
+ if (r_waterstate.waterwidth != waterwidth || r_waterstate.waterheight != waterheight || r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
+ {
+ r_waterstate.maxwaterplanes = MAX_WATERPLANES;
+ for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
+ {
+ if (p->texture_refraction)
+ R_FreeTexture(p->texture_refraction);
+ p->texture_refraction = NULL;
+ if (p->texture_reflection)
+ R_FreeTexture(p->texture_reflection);
+ p->texture_reflection = NULL;
+ }
+ memset(&r_waterstate, 0, sizeof(r_waterstate));
+ r_waterstate.waterwidth = waterwidth;
+ r_waterstate.waterheight = waterheight;
+ r_waterstate.texturewidth = texturewidth;
+ r_waterstate.textureheight = textureheight;
+ }
+
+ if (r_waterstate.waterwidth)
+ {
+ r_waterstate.enabled = true;
+
+ // set up variables that will be used in shader setup
+ r_waterstate.screenscale[0] = 0.5f * (float)waterwidth / (float)texturewidth;
+ r_waterstate.screenscale[1] = 0.5f * (float)waterheight / (float)textureheight;
+ r_waterstate.screencenter[0] = 0.5f * (float)waterwidth / (float)texturewidth;
+ r_waterstate.screencenter[1] = 0.5f * (float)waterheight / (float)textureheight;
+ }
+
+ r_waterstate.maxwaterplanes = MAX_WATERPLANES;
+ r_waterstate.numwaterplanes = 0;
+}
+
+static void R_Water_AddWaterPlane(msurface_t *surface)
+{
+ int triangleindex, planeindex;
+ const int *e;
+ vec_t f;
+ vec3_t vert[3];
+ vec3_t normal;
+ vec3_t center;
+ r_waterstate_waterplane_t *p;
+ // just use the first triangle with a valid normal for any decisions
+ VectorClear(normal);
+ VectorClear(center);
+ for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
+ {
+ Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[0]*3, vert[0]);
+ Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[1]*3, vert[1]);
+ Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[2]*3, vert[2]);
+ TriangleNormal(vert[0], vert[1], vert[2], normal);
+ if (VectorLength2(normal) >= 0.001)
+ break;
+ }
+ // now find the center of this surface
+ for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles*3;triangleindex++, e++)
+ {
+ Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[0]*3, vert[0]);
+ VectorAdd(center, vert[0], center);
+ }
+ f = 1.0 / surface->num_triangles*3;
+ VectorScale(center, f, center);
+
+ // find a matching plane if there is one
+ for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+ if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
+ break;
+ if (planeindex >= r_waterstate.maxwaterplanes)
+ return; // nothing we can do, out of planes
+
+ // if this triangle does not fit any known plane rendered this frame, add one
+ if (planeindex >= r_waterstate.numwaterplanes)
+ {
+ // store the new plane
+ r_waterstate.numwaterplanes++;
+ VectorCopy(normal, p->plane.normal);
+ VectorNormalize(p->plane.normal);
+ p->plane.dist = DotProduct(vert[0], p->plane.normal);
+ PlaneClassify(&p->plane);
+ // flip the plane if it does not face the viewer
+ if (PlaneDiff(r_view.origin, &p->plane) < 0)
+ {
+ VectorNegate(p->plane.normal, p->plane.normal);
+ p->plane.dist *= -1;
+ PlaneClassify(&p->plane);
+ }
+ // clear materialflags and pvs
+ p->materialflags = 0;
+ p->pvsvalid = false;
+ }
+ // merge this surface's materialflags into the waterplane
+ p->materialflags |= surface->texture->currentframe->currentmaterialflags;
+ // merge this surface's PVS into the waterplane
+ if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) && r_refdef.worldmodel && r_refdef.worldmodel->brush.FatPVS)
+ {
+ r_refdef.worldmodel->brush.FatPVS(r_refdef.worldmodel, r_view.origin, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
+ p->pvsvalid = true;
+ }
+}
+
+static void R_Water_ProcessPlanes(void)
+{
+ r_view_t originalview;
+ int planeindex;
+ r_waterstate_waterplane_t *p;
+
+ originalview = r_view;
+
+ // make sure enough textures are allocated
+ for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+ {
+ if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
+ {
+ if (!p->texture_refraction)
+ p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ if (!p->texture_refraction)
+ goto error;
+ }
+
+ if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
+ {
+ if (!p->texture_reflection)
+ p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ if (!p->texture_reflection)
+ goto error;
+ }
+ }
+
+ // render views
+ for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+ {
+ r_view.showdebug = false;
+ r_view.width = r_waterstate.waterwidth;
+ r_view.height = r_waterstate.waterheight;
+ r_view.useclipplane = true;
+ r_waterstate.renderingscene = true;
+
+ // render the normal view scene and copy into texture
+ // (except that a clipping plane should be used to hide everything on one side of the water, and the viewer's weapon model should be omitted)
+ if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
+ {
+ r_view.clipplane = p->plane;
+ VectorNegate(r_view.clipplane.normal, r_view.clipplane.normal);
+ r_view.clipplane.dist = -r_view.clipplane.dist;
+ PlaneClassify(&r_view.clipplane);
+
+ R_RenderScene(false);
+
+ // copy view into the screen texture
+ R_Mesh_TexBind(0, R_GetTexture(p->texture_refraction));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ }
+
+ if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
+ {
+ // render reflected scene and copy into texture
+ Matrix4x4_Reflect(&r_view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2);
+ r_view.clipplane = p->plane;
+ // reverse the cullface settings for this render
+ r_view.cullface_front = GL_FRONT;
+ r_view.cullface_back = GL_BACK;
+ if (r_refdef.worldmodel && r_refdef.worldmodel->brush.num_pvsclusterbytes)
+ {
+ r_view.usecustompvs = true;
+ if (p->pvsvalid)
+ memcpy(r_viewcache.world_pvsbits, p->pvsbits, r_refdef.worldmodel->brush.num_pvsclusterbytes);
+ else
+ memset(r_viewcache.world_pvsbits, 0xFF, r_refdef.worldmodel->brush.num_pvsclusterbytes);
+ }
+
+ R_ResetViewRendering3D();
+ R_ClearScreen();
+ if (r_timereport_active)
+ R_TimeReport("viewclear");
+
+ R_RenderScene(false);
+
+ R_Mesh_TexBind(0, R_GetTexture(p->texture_reflection));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+
+ R_ResetViewRendering3D();
+ R_ClearScreen();
+ if (r_timereport_active)
+ R_TimeReport("viewclear");
+ }
+
+ r_view = originalview;
+ r_view.clear = true;
+ r_waterstate.renderingscene = false;
+ }
+ return;
+error:
+ r_view = originalview;
+ r_waterstate.renderingscene = false;
+ Cvar_SetValueQuick(&r_water, 0);
+ Con_Printf("R_Water_ProcessPlanes: Error: texture creation failed! Turned off r_water.\n");
+ return;
+}
+
+void R_Bloom_StartFrame(void)
+{
+ int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight;
+
+ // set bloomwidth and bloomheight to the bloom resolution that will be
+ // used (often less than the screen resolution for faster rendering)
+ r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, r_view.width);
+ r_bloomstate.bloomheight = r_bloomstate.bloomwidth * r_view.height / r_view.width;
+ r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_view.height);
+
+ // calculate desired texture sizes
+ if (gl_support_arb_texture_non_power_of_two)
+ {
+ screentexturewidth = r_view.width;
+ screentextureheight = r_view.height;
+ bloomtexturewidth = r_bloomstate.bloomwidth;
+ bloomtextureheight = r_bloomstate.bloomheight;
+ }
+ else
+ {
+ for (screentexturewidth = 1;screentexturewidth < vid.width ;screentexturewidth *= 2);
+ for (screentextureheight = 1;screentextureheight < vid.height ;screentextureheight *= 2);
+ for (bloomtexturewidth = 1;bloomtexturewidth < r_bloomstate.bloomwidth ;bloomtexturewidth *= 2);
+ for (bloomtextureheight = 1;bloomtextureheight < r_bloomstate.bloomheight;bloomtextureheight *= 2);
+ }
+
+ if (r_hdr.integer)
+ {
+ screentexturewidth = screentextureheight = 0;
+ }
+ else if (r_bloom.integer)
+ {
+ }
+ else
+ {
+ screentexturewidth = screentextureheight = 0;
+ bloomtexturewidth = bloomtextureheight = 0;
+ }
+
+ if ((!bloomtexturewidth && !bloomtextureheight) || r_bloom_resolution.integer < 4 || r_bloom_blur.value < 1 || r_bloom_blur.value >= 512 || screentexturewidth > gl_max_texture_size || screentextureheight > gl_max_texture_size || bloomtexturewidth > gl_max_texture_size || bloomtextureheight > gl_max_texture_size)
+ {
+ // can't use bloom if the parameters are too weird
+ // can't use bloom if the card does not support the texture size
+ if (r_bloomstate.texture_screen)
+ R_FreeTexture(r_bloomstate.texture_screen);
+ if (r_bloomstate.texture_bloom)
+ R_FreeTexture(r_bloomstate.texture_bloom);
+ memset(&r_bloomstate, 0, sizeof(r_bloomstate));
+ return;
+ }
+
+ r_bloomstate.enabled = true;
+ r_bloomstate.hdr = r_hdr.integer != 0;
+
+ // allocate textures as needed
+ if (r_bloomstate.screentexturewidth != screentexturewidth || r_bloomstate.screentextureheight != screentextureheight)
+ {
+ if (r_bloomstate.texture_screen)
+ R_FreeTexture(r_bloomstate.texture_screen);
+ r_bloomstate.texture_screen = NULL;
+ r_bloomstate.screentexturewidth = screentexturewidth;
+ r_bloomstate.screentextureheight = screentextureheight;
+ if (r_bloomstate.screentexturewidth && r_bloomstate.screentextureheight)
+ r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ }
+ if (r_bloomstate.bloomtexturewidth != bloomtexturewidth || r_bloomstate.bloomtextureheight != bloomtextureheight)
+ {
+ if (r_bloomstate.texture_bloom)
+ R_FreeTexture(r_bloomstate.texture_bloom);
+ r_bloomstate.texture_bloom = NULL;
+ r_bloomstate.bloomtexturewidth = bloomtexturewidth;
+ r_bloomstate.bloomtextureheight = bloomtextureheight;
+ if (r_bloomstate.bloomtexturewidth && r_bloomstate.bloomtextureheight)
+ r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ }
+
+ // set up a texcoord array for the full resolution screen image
+ // (we have to keep this around to copy back during final render)
+ r_bloomstate.screentexcoord2f[0] = 0;
+ r_bloomstate.screentexcoord2f[1] = (float)r_view.height / (float)r_bloomstate.screentextureheight;
+ r_bloomstate.screentexcoord2f[2] = (float)r_view.width / (float)r_bloomstate.screentexturewidth;
+ r_bloomstate.screentexcoord2f[3] = (float)r_view.height / (float)r_bloomstate.screentextureheight;
+ r_bloomstate.screentexcoord2f[4] = (float)r_view.width / (float)r_bloomstate.screentexturewidth;
+ r_bloomstate.screentexcoord2f[5] = 0;
+ r_bloomstate.screentexcoord2f[6] = 0;
+ r_bloomstate.screentexcoord2f[7] = 0;
+
+ // set up a texcoord array for the reduced resolution bloom image
+ // (which will be additive blended over the screen image)
+ r_bloomstate.bloomtexcoord2f[0] = 0;
+ r_bloomstate.bloomtexcoord2f[1] = (float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
+ r_bloomstate.bloomtexcoord2f[2] = (float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth;
+ r_bloomstate.bloomtexcoord2f[3] = (float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
+ r_bloomstate.bloomtexcoord2f[4] = (float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth;
+ r_bloomstate.bloomtexcoord2f[5] = 0;
+ r_bloomstate.bloomtexcoord2f[6] = 0;
+ r_bloomstate.bloomtexcoord2f[7] = 0;
+}
+
+void R_Bloom_CopyScreenTexture(float colorscale)
+{
+ r_refdef.stats.bloom++;
+
+ R_ResetViewRendering2D();
+ R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
+
+ // copy view into the screen texture
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_view.width * r_view.height;
+
+ // now scale it down to the bloom texture size
+ CHECKGLERROR
+ qglViewport(r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ GL_Color(colorscale, colorscale, colorscale, 1);
+ // TODO: optimize with multitexture or GLSL
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+
+ // we now have a bloom image in the framebuffer
+ // copy it into the bloom image texture for later processing
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+}
+
+void R_Bloom_CopyHDRTexture(void)
+{
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_view.width * r_view.height;
+}
+
+void R_Bloom_MakeTexture(void)
+{
+ int x, range, dir;
+ float xoffset, yoffset, r, brighten;
+
+ r_refdef.stats.bloom++;
+
+ R_ResetViewRendering2D();
+ R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+ R_Mesh_ColorPointer(NULL, 0, 0);
+
+ // we have a bloom image in the framebuffer
+ CHECKGLERROR
+ qglViewport(r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+
+ for (x = 1;x < min(r_bloom_colorexponent.value, 32);)
+ {
+ x *= 2;
+ r = bound(0, r_bloom_colorexponent.value / x, 1);
+ GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+ GL_Color(r, r, r, 1);
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+
+ // copy the vertically blurred bloom view to a texture
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+ }
+
+ range = r_bloom_blur.integer * r_bloomstate.bloomwidth / 320;
+ brighten = r_bloom_brighten.value;
+ if (r_hdr.integer)
+ brighten *= r_hdr_range.value;
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.offsettexcoord2f, 0, 0);
+
+ for (dir = 0;dir < 2;dir++)
+ {
+ // blend on at multiple vertical offsets to achieve a vertical blur
+ // TODO: do offset blends using GLSL
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ for (x = -range;x <= range;x++)
+ {
+ if (!dir){xoffset = 0;yoffset = x;}
+ else {xoffset = x;yoffset = 0;}
+ xoffset /= (float)r_bloomstate.bloomtexturewidth;
+ yoffset /= (float)r_bloomstate.bloomtextureheight;
+ // compute a texcoord array with the specified x and y offset
+ r_bloomstate.offsettexcoord2f[0] = xoffset+0;
+ r_bloomstate.offsettexcoord2f[1] = yoffset+(float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
+ r_bloomstate.offsettexcoord2f[2] = xoffset+(float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth;
+ r_bloomstate.offsettexcoord2f[3] = yoffset+(float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
+ r_bloomstate.offsettexcoord2f[4] = xoffset+(float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth;
+ r_bloomstate.offsettexcoord2f[5] = yoffset+0;
+ r_bloomstate.offsettexcoord2f[6] = xoffset+0;
+ r_bloomstate.offsettexcoord2f[7] = yoffset+0;
+ // this r value looks like a 'dot' particle, fading sharply to
+ // black at the edges
+ // (probably not realistic but looks good enough)
+ //r = ((range*range+1)/((float)(x*x+1)))/(range*2+1);
+ //r = (dir ? 1.0f : brighten)/(range*2+1);
+ r = (dir ? 1.0f : brighten)/(range*2+1)*(1 - x*x/(float)(range*range));
+ GL_Color(r, r, r, 1);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ }
+
+ // copy the vertically blurred bloom view to a texture
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+ }
+
+ // apply subtract last
+ // (just like it would be in a GLSL shader)
+ if (r_bloom_colorsubtract.value > 0 && gl_support_ext_blend_subtract)
+ {
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
+ GL_Color(1, 1, 1, 1);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
+ R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
+ GL_Color(r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 1);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+ qglBlendEquationEXT(GL_FUNC_ADD_EXT);
+
+ // copy the darkened bloom view to a texture
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+ }
+}
+
+void R_HDR_RenderBloomTexture(void)
+{
+ int oldwidth, oldheight;
+
+ oldwidth = r_view.width;
+ oldheight = r_view.height;
+ r_view.width = r_bloomstate.bloomwidth;
+ r_view.height = r_bloomstate.bloomheight;
+
+ // TODO: support GL_EXT_framebuffer_object rather than reusing the framebuffer? it might improve SLI performance.
+ // TODO: add exposure compensation features
+ // TODO: add fp16 framebuffer support
+
+ r_view.showdebug = false;
+ r_view.colorscale = r_bloom_colorscale.value * r_hdr_scenebrightness.value;
+ if (r_hdr.integer)
+ r_view.colorscale /= r_hdr_range.value;
+ r_waterstate.numwaterplanes = 0;
+ R_RenderScene(r_waterstate.enabled);
+ r_view.showdebug = true;
+
+ R_ResetViewRendering2D();
+
+ R_Bloom_CopyHDRTexture();
+ R_Bloom_MakeTexture();
+
+ R_ResetViewRendering3D();
+
+ R_ClearScreen();
+ if (r_timereport_active)
+ R_TimeReport("viewclear");
+
+ // restore the view settings
+ r_view.width = oldwidth;
+ r_view.height = oldheight;
+}
+
+static void R_BlendView(void)
+{
+ if (r_bloomstate.enabled && r_bloomstate.hdr)
+ {
+ // render high dynamic range bloom effect
+ // the bloom texture was made earlier this render, so we just need to
+ // blend it onto the screen...
+ R_ResetViewRendering2D();
+ R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ GL_Color(1, 1, 1, 1);
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height;
+ }
+ else if (r_bloomstate.enabled)
+ {
+ // render simple bloom effect
+ // copy the screen and shrink it and darken it for the bloom process
+ R_Bloom_CopyScreenTexture(r_bloom_colorscale.value);
+ // make the bloom texture
+ R_Bloom_MakeTexture();
+ // put the original screen image back in place and blend the bloom
+ // texture on it
+ R_ResetViewRendering2D();
+ R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ GL_Color(1, 1, 1, 1);
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ // do both in one pass if possible
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
+ if (r_textureunits.integer >= 2 && gl_combine.integer)
+ {
+ R_Mesh_TexCombine(1, GL_ADD, GL_ADD, 1, 1);
+ R_Mesh_TexBind(1, R_GetTexture(r_bloomstate.texture_screen));
+ R_Mesh_TexCoordPointer(1, 2, r_bloomstate.screentexcoord2f, 0, 0);
+ }
+ else
+ {
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height;
+ // now blend on the bloom texture
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
+ R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
+ }
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height;
+ }
+ if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
+ {
+ // apply a color tint to the whole view
+ R_ResetViewRendering2D();
+ R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ }
+}
+
+void R_RenderScene(qboolean addwaterplanes);
+
+matrix4x4_t r_waterscrollmatrix;
+
+void R_UpdateVariables(void)
+{
+ R_Textures_Frame();
+
+ r_refdef.farclip = 4096;
+ if (r_refdef.worldmodel)
+ r_refdef.farclip += VectorDistance(r_refdef.worldmodel->normalmins, r_refdef.worldmodel->normalmaxs);
+ r_refdef.nearclip = bound (0.001f, r_nearclip.value, r_refdef.farclip - 1.0f);
+
+ if (r_shadow_frontsidecasting.integer < 0 || r_shadow_frontsidecasting.integer > 1)
+ Cvar_SetValueQuick(&r_shadow_frontsidecasting, 1);
+ r_refdef.polygonfactor = 0;
+ r_refdef.polygonoffset = 0;
+ r_refdef.shadowpolygonfactor = r_refdef.polygonfactor + r_shadow_polygonfactor.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
+ r_refdef.shadowpolygonoffset = r_refdef.polygonoffset + r_shadow_polygonoffset.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
+
+ r_refdef.rtworld = r_shadow_realtime_world.integer;
+ r_refdef.rtworldshadows = r_shadow_realtime_world_shadows.integer && gl_stencil;
+ r_refdef.rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer && r_dynamic.integer;
+ r_refdef.rtdlightshadows = r_refdef.rtdlight && r_shadow_realtime_dlight_shadows.integer && gl_stencil;
+ r_refdef.lightmapintensity = r_refdef.rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
+ if (r_showsurfaces.integer)
+ {
+ r_refdef.rtworld = false;
+ r_refdef.rtworldshadows = false;
+ r_refdef.rtdlight = false;
+ r_refdef.rtdlightshadows = false;
+ r_refdef.lightmapintensity = 0;
+ }
+
+ if (gamemode == GAME_NEHAHRA)
+ {
+ if (gl_fogenable.integer)
+ {
+ r_refdef.oldgl_fogenable = true;
+ r_refdef.fog_density = gl_fogdensity.value;
+ r_refdef.fog_red = gl_fogred.value;
+ r_refdef.fog_green = gl_foggreen.value;
+ r_refdef.fog_blue = gl_fogblue.value;
+ }
+ else if (r_refdef.oldgl_fogenable)
+ {
+ r_refdef.oldgl_fogenable = false;
+ r_refdef.fog_density = 0;
+ r_refdef.fog_red = 0;
+ r_refdef.fog_green = 0;
+ r_refdef.fog_blue = 0;
+ }
+ }
+ if (r_refdef.fog_density)
+ {
+ r_refdef.fogcolor[0] = bound(0.0f, r_refdef.fog_red , 1.0f);
+ r_refdef.fogcolor[1] = bound(0.0f, r_refdef.fog_green, 1.0f);
+ r_refdef.fogcolor[2] = bound(0.0f, r_refdef.fog_blue , 1.0f);
+ }
+ if (r_refdef.fog_density)
+ {
+ r_refdef.fogenabled = true;
+ // this is the point where the fog reaches 0.9986 alpha, which we
+ // consider a good enough cutoff point for the texture
+ // (0.9986 * 256 == 255.6)
+ r_refdef.fogrange = 400 / r_refdef.fog_density;
+ r_refdef.fograngerecip = 1.0f / r_refdef.fogrange;
+ r_refdef.fogmasktabledistmultiplier = FOGMASKTABLEWIDTH * r_refdef.fograngerecip;
+ // fog color was already set
+ }
+ else
+ r_refdef.fogenabled = false;
+}
+
+/*
+================
+R_RenderView
+================
+*/
+void R_RenderView(void)
+{
+ if (!r_refdef.entities/* || !r_refdef.worldmodel*/)
+ return; //Host_Error ("R_RenderView: NULL worldmodel");
+
+ R_Shadow_UpdateWorldLightSelection();
+
+ R_Bloom_StartFrame();
+ R_Water_StartFrame();
+
+ CHECKGLERROR
+ if (r_timereport_active)
+ R_TimeReport("viewsetup");
+
+ R_ResetViewRendering3D();
+
+ if (r_view.clear)
+ {
+ R_ClearScreen();
+ if (r_timereport_active)
+ R_TimeReport("viewclear");
+ }
+ r_view.clear = true;
+
+ r_view.showdebug = true;
+
+ // this produces a bloom texture to be used in R_BlendView() later
+ if (r_hdr.integer)
+ R_HDR_RenderBloomTexture();
+
+ r_view.colorscale = r_hdr_scenebrightness.value;
+ r_waterstate.numwaterplanes = 0;
+ R_RenderScene(r_waterstate.enabled);
+
+ R_BlendView();
+ if (r_timereport_active)
+ R_TimeReport("blendview");
+
+ GL_Scissor(0, 0, vid.width, vid.height);
+ GL_ScissorTest(false);
+ CHECKGLERROR
+}
+
+extern void R_DrawLightningBeams (void);
+extern void VM_CL_AddPolygonsToMeshQueue (void);
+extern void R_DrawPortals (void);
+extern cvar_t cl_locs_show;
+static void R_DrawLocs(void);
+static void R_DrawEntityBBoxes(void);
+void R_RenderScene(qboolean addwaterplanes)
+{
+ if (addwaterplanes)
+ {
+ R_ResetViewRendering3D();
+
+ R_View_Update();
+ if (r_timereport_active)
+ R_TimeReport("watervis");
+
+ if (cl.csqc_vidvars.drawworld && r_refdef.worldmodel && r_refdef.worldmodel->DrawAddWaterPlanes)
+ {
+ r_refdef.worldmodel->DrawAddWaterPlanes(r_refdef.worldentity);
+ if (r_timereport_active)
+ R_TimeReport("waterworld");
+ }
+
+ // don't let sound skip if going slow
+ if (r_refdef.extraupdate)
+ S_ExtraUpdate ();
+
+ R_DrawModelsAddWaterPlanes();
+ if (r_timereport_active)
+ R_TimeReport("watermodels");
+
+ R_Water_ProcessPlanes();
+ if (r_timereport_active)
+ R_TimeReport("waterscenes");
+ }
+
+ R_ResetViewRendering3D();
+
+ // don't let sound skip if going slow