1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 
30 #include "tr_local.h"
31 
32 int r_firstSceneDrawSurf;
33 
34 int r_numdlights;
35 int r_firstSceneDlight;
36 
37 int r_numcoronas;
38 int r_firstSceneCorona;
39 
40 int r_numentities;
41 int r_firstSceneEntity;
42 
43 int r_numpolys;
44 int r_firstScenePoly;
45 
46 int r_numpolyverts;
47 
48 int skyboxportal;
49 
50 /*
51 ====================
52 R_InitNextFrame
53 
54 ====================
55 */
R_InitNextFrame(void)56 void R_InitNextFrame( void ) {
57 	backEndData->commands.used = 0;
58 
59 	r_firstSceneDrawSurf = 0;
60 
61 	r_numdlights = 0;
62 	r_firstSceneDlight = 0;
63 
64 	r_numcoronas = 0;
65 	r_firstSceneCorona = 0;
66 
67 	r_numentities = 0;
68 	r_firstSceneEntity = 0;
69 
70 	r_numpolys = 0;
71 	r_firstScenePoly = 0;
72 
73 	r_numpolyverts = 0;
74 }
75 
76 
77 /*
78 ====================
79 RE_ClearScene
80 
81 ====================
82 */
RE_ClearScene(void)83 void RE_ClearScene( void ) {
84 	r_firstSceneDlight = r_numdlights;
85 	r_firstSceneCorona = r_numcoronas;
86 	r_firstSceneEntity = r_numentities;
87 	r_firstScenePoly = r_numpolys;
88 }
89 
90 /*
91 ===========================================================================
92 
93 DISCRETE POLYS
94 
95 ===========================================================================
96 */
97 
98 /*
99 =====================
100 R_AddPolygonSurfaces
101 
102 Adds all the scene's polys into this view's drawsurf list
103 =====================
104 */
R_AddPolygonSurfaces(void)105 void R_AddPolygonSurfaces( void ) {
106 	int i;
107 	shader_t    *sh;
108 	srfPoly_t   *poly;
109 	int		fogMask;
110 
111 	tr.currentEntityNum = REFENTITYNUM_WORLD;
112 	tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
113 	fogMask = -((tr.refdef.rdflags & RDF_NOFOG) == 0);
114 
115 	for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) {
116 		sh = R_GetShaderByHandle( poly->hShader );
117 		R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex & fogMask, qfalse, qfalse, 0 /*cubeMap*/ );
118 	}
119 }
120 
121 /*
122 =====================
123 RE_AddPolyToScene
124 
125 =====================
126 */
RE_AddPolyToScene(qhandle_t hShader,int numVerts,const polyVert_t * verts)127 void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts ) {
128 	srfPoly_t   *poly;
129 	int i;
130 	int fogIndex;
131 	fog_t       *fog;
132 	vec3_t bounds[2];
133 
134 	if ( !tr.registered ) {
135 		return;
136 	}
137 
138 	if ( !hShader ) {
139 		// This isn't a useful warning, and an hShader of zero isn't a null shader, it's
140 		// the default shader.
141 		//ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n");
142 		//return;
143 	}
144 
145 	if ( ( ( r_numpolyverts + numVerts ) >= max_polyverts ) || ( r_numpolys >= max_polys ) ) {
146 		return;
147 	}
148 
149 	poly = &backEndData->polys[r_numpolys];
150 	poly->surfaceType = SF_POLY;
151 	poly->hShader = hShader;
152 	poly->numVerts = numVerts;
153 	poly->verts = &backEndData->polyVerts[r_numpolyverts];
154 
155 	memcpy( poly->verts, verts, numVerts * sizeof( *verts ) );
156 	// Ridah
157 	if ( glConfig.hardwareType == GLHW_RAGEPRO ) {
158 		poly->verts->modulate[0] = 255;
159 		poly->verts->modulate[1] = 255;
160 		poly->verts->modulate[2] = 255;
161 		poly->verts->modulate[3] = 255;
162 	}
163 	// done.
164 	r_numpolys++;
165 	r_numpolyverts += numVerts;
166 
167 	// see if it is in a fog volume
168 	if ( tr.world->numfogs == 1 ) {
169 		fogIndex = 0;
170 	} else {
171 		// find which fog volume the poly is in
172 		VectorCopy( poly->verts[0].xyz, bounds[0] );
173 		VectorCopy( poly->verts[0].xyz, bounds[1] );
174 		for ( i = 1 ; i < poly->numVerts ; i++ ) {
175 			AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
176 		}
177 		for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) {
178 			fog = &tr.world->fogs[fogIndex];
179 			if ( bounds[1][0] >= fog->bounds[0][0]
180 				 && bounds[1][1] >= fog->bounds[0][1]
181 				 && bounds[1][2] >= fog->bounds[0][2]
182 				 && bounds[0][0] <= fog->bounds[1][0]
183 				 && bounds[0][1] <= fog->bounds[1][1]
184 				 && bounds[0][2] <= fog->bounds[1][2] ) {
185 				break;
186 			}
187 		}
188 		if ( fogIndex == tr.world->numfogs ) {
189 			fogIndex = 0;
190 		}
191 	}
192 	poly->fogIndex = fogIndex;
193 }
194 
195 // Ridah
196 /*
197 =====================
198 RE_AddPolysToScene
199 
200 =====================
201 */
RE_AddPolysToScene(qhandle_t hShader,int numVerts,const polyVert_t * verts,int numPolys)202 void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) {
203 	srfPoly_t   *poly;
204 	int i;
205 	int fogIndex;
206 	fog_t       *fog;
207 	vec3_t bounds[2];
208 	int j;
209 
210 	if ( !tr.registered ) {
211 		return;
212 	}
213 
214 	if ( !hShader ) {
215 		ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolysToScene: NULL poly shader\n" );
216 		return;
217 	}
218 
219 	for ( j = 0; j < numPolys; j++ ) {
220 		if ( r_numpolyverts + numVerts >= max_polyverts || r_numpolys >= max_polys ) {
221 //			ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolysToScene: MAX_POLYS or MAX_POLYVERTS reached\n");
222 			return;
223 		}
224 
225 		poly = &backEndData->polys[r_numpolys];
226 		poly->surfaceType = SF_POLY;
227 		poly->hShader = hShader;
228 		poly->numVerts = numVerts;
229 		poly->verts = &backEndData->polyVerts[r_numpolyverts];
230 
231 		memcpy( poly->verts, &verts[numVerts * j], numVerts * sizeof( *verts ) );
232 		// Ridah
233 		if ( glConfig.hardwareType == GLHW_RAGEPRO ) {
234 			poly->verts->modulate[0] = 255;
235 			poly->verts->modulate[1] = 255;
236 			poly->verts->modulate[2] = 255;
237 			poly->verts->modulate[3] = 255;
238 		}
239 		// done.
240 		r_numpolys++;
241 		r_numpolyverts += numVerts;
242 
243 		// if no world is loaded
244 		if ( tr.world == NULL ) {
245 			fogIndex = 0;
246 		}
247 		// see if it is in a fog volume
248 		else if ( tr.world->numfogs == 1 ) {
249 			fogIndex = 0;
250 		} else {
251 			// find which fog volume the poly is in
252 			VectorCopy( poly->verts[0].xyz, bounds[0] );
253 			VectorCopy( poly->verts[0].xyz, bounds[1] );
254 			for ( i = 1 ; i < poly->numVerts ; i++ ) {
255 				AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
256 			}
257 			for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) {
258 				fog = &tr.world->fogs[fogIndex];
259 				if ( bounds[1][0] >= fog->bounds[0][0]
260 					 && bounds[1][1] >= fog->bounds[0][1]
261 					 && bounds[1][2] >= fog->bounds[0][2]
262 					 && bounds[0][0] <= fog->bounds[1][0]
263 					 && bounds[0][1] <= fog->bounds[1][1]
264 					 && bounds[0][2] <= fog->bounds[1][2] ) {
265 					break;
266 				}
267 			}
268 			if ( fogIndex == tr.world->numfogs ) {
269 				fogIndex = 0;
270 			}
271 		}
272 		poly->fogIndex = fogIndex;
273 	}
274 }
275 // done.
276 
277 
278 //=================================================================================
279 
280 
281 /*
282 =====================
283 RE_AddRefEntityToScene
284 
285 =====================
286 */
RE_AddRefEntityToScene(const refEntity_t * ent)287 void RE_AddRefEntityToScene( const refEntity_t *ent ) {
288 	vec3_t cross;
289 
290 	if ( !tr.registered ) {
291 		return;
292 	}
293 	if ( r_numentities >= MAX_REFENTITIES ) {
294 		ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n");
295 		return;
296 	}
297 	if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) {
298 		static qboolean firstTime = qtrue;
299 		if (firstTime) {
300 			firstTime = qfalse;
301 			ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n");
302 		}
303 		return;
304 	}
305 	if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) {
306 		ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType );
307 	}
308 
309 	backEndData->entities[r_numentities].e = *ent;
310 	backEndData->entities[r_numentities].lightingCalculated = qfalse;
311 
312 	CrossProduct(ent->axis[0], ent->axis[1], cross);
313 	backEndData->entities[r_numentities].mirrored = (DotProduct(ent->axis[2], cross) < 0.f);
314 
315 	r_numentities++;
316 }
317 
318 // Ridah, added support for overdraw field
319 /*
320 =====================
321 RE_AddLightToScene
322 
323 =====================
324 */
RE_AddLightToScene(const vec3_t org,float intensity,float r,float g,float b,int overdraw)325 void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b, int overdraw ) {
326 	dlight_t    *dl;
327 
328 	if ( !tr.registered ) {
329 		return;
330 	}
331 	if ( r_numdlights >= MAX_DLIGHTS ) {
332 		return;
333 	}
334 	if ( intensity <= 0 ) {
335 		return;
336 	}
337 	// these cards don't have the correct blend mode
338 	if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
339 		return;
340 	}
341 	// RF, allow us to force some dlights under all circumstances
342 	if ( !( overdraw & REF_FORCE_DLIGHT ) ) {
343 		if ( r_dynamiclight->integer == 0 ) {
344 			return;
345 		}
346 		if ( r_dynamiclight->integer == 2 && !( backEndData->dlights[r_numdlights].forced ) ) {
347 			return;
348 		}
349 	}
350 
351 	overdraw &= ~REF_FORCE_DLIGHT;
352 	overdraw &= ~REF_JUNIOR_DLIGHT; //----(SA)	added
353 
354 	dl = &backEndData->dlights[r_numdlights++];
355 	VectorCopy( org, dl->origin );
356 	dl->radius = intensity;
357 	dl->color[0] = r;
358 	dl->color[1] = g;
359 	dl->color[2] = b;
360 	dl->dlshader = NULL;
361 	dl->overdraw = 0;
362 
363 	if ( overdraw == 10 ) { // sorry, hijacking 10 for a quick hack (SA)
364 		dl->dlshader = R_GetShaderByHandle( RE_RegisterShader( "negdlightshader" ) );
365 	} else if ( overdraw == 11 ) { // 11 is flames
366 		dl->dlshader = R_GetShaderByHandle( RE_RegisterShader( "flamedlightshader" ) );
367 	} else {
368 		dl->overdraw = overdraw;
369 	}
370 }
371 // done.
372 
373 
374 /*
375 ==============
376 RE_AddCoronaToScene
377 ==============
378 */
RE_AddCoronaToScene(const vec3_t org,float r,float g,float b,float scale,int id,qboolean visible)379 void RE_AddCoronaToScene( const vec3_t org, float r, float g, float b, float scale, int id, qboolean visible ) {
380 	corona_t    *cor;
381 
382 	if ( !tr.registered ) {
383 		return;
384 	}
385 	if ( r_numcoronas >= MAX_CORONAS ) {
386 		return;
387 	}
388 
389 	cor = &backEndData->coronas[r_numcoronas++];
390 	VectorCopy( org, cor->origin );
391 	cor->color[0] = r;
392 	cor->color[1] = g;
393 	cor->color[2] = b;
394 	cor->scale = scale;
395 	cor->id = id;
396 	cor->visible = visible;
397 }
398 
RE_BeginScene(const refdef_t * fd)399 void RE_BeginScene(const refdef_t *fd)
400 {
401 	memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) );
402 
403 	tr.refdef.x = fd->x;
404 	tr.refdef.y = fd->y;
405 	tr.refdef.width = fd->width;
406 	tr.refdef.height = fd->height;
407 	tr.refdef.fov_x = fd->fov_x;
408 	tr.refdef.fov_y = fd->fov_y;
409 
410 	VectorCopy( fd->vieworg, tr.refdef.vieworg );
411 	VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] );
412 	VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] );
413 	VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] );
414 
415 	tr.refdef.time = fd->time;
416 	tr.refdef.rdflags = fd->rdflags;
417 
418 	if ( fd->rdflags & RDF_SKYBOXPORTAL ) {
419 		skyboxportal = 1;
420 	}
421 
422 	// copy the areamask data over and note if it has changed, which
423 	// will force a reset of the visible leafs even if the view hasn't moved
424 	tr.refdef.areamaskModified = qfalse;
425 	if ( !( tr.refdef.rdflags & RDF_NOWORLDMODEL ) ) {
426 		int areaDiff;
427 		int i;
428 
429 		// compare the area bits
430 		areaDiff = 0;
431 		for ( i = 0 ; i < MAX_MAP_AREA_BYTES / 4 ; i++ ) {
432 			areaDiff |= ( (int *)tr.refdef.areamask )[i] ^ ( (int *)fd->areamask )[i];
433 			( (int *)tr.refdef.areamask )[i] = ( (int *)fd->areamask )[i];
434 		}
435 
436 		if ( areaDiff ) {
437 			// a door just opened or something
438 			tr.refdef.areamaskModified = qtrue;
439 		}
440 	}
441 
442 	tr.refdef.sunDir[3] = 0.0f;
443 	tr.refdef.sunCol[3] = 1.0f;
444 	tr.refdef.sunAmbCol[3] = 1.0f;
445 
446 	VectorCopy(tr.sunDirection, tr.refdef.sunDir);
447 	if ( (tr.refdef.rdflags & RDF_NOWORLDMODEL) || !(r_depthPrepass->value) ){
448 		VectorSet(tr.refdef.sunCol, 0, 0, 0);
449 		VectorSet(tr.refdef.sunAmbCol, 0, 0, 0);
450 	}
451 	else
452 	{
453 		float scale = (1 << r_mapOverBrightBits->integer) / 255.0f;
454 
455 		if (r_forceSun->integer)
456 			VectorScale(tr.sunLight, scale * r_forceSunLightScale->value, tr.refdef.sunCol);
457 		else
458 			VectorScale(tr.sunLight, scale, tr.refdef.sunCol);
459 
460 		if (r_sunlightMode->integer == 1)
461 		{
462 			tr.refdef.sunAmbCol[0] =
463 			tr.refdef.sunAmbCol[1] =
464 			tr.refdef.sunAmbCol[2] = r_forceSun->integer ? r_forceSunAmbientScale->value : tr.sunShadowScale;
465 		}
466 		else
467 		{
468 			if (r_forceSun->integer)
469 				VectorScale(tr.sunLight, scale * r_forceSunAmbientScale->value, tr.refdef.sunAmbCol);
470 			else
471 				VectorScale(tr.sunLight, scale * tr.sunShadowScale, tr.refdef.sunAmbCol);
472 		}
473 	}
474 
475 	if (r_forceAutoExposure->integer)
476 	{
477 		tr.refdef.autoExposureMinMax[0] = r_forceAutoExposureMin->value;
478 		tr.refdef.autoExposureMinMax[1] = r_forceAutoExposureMax->value;
479 	}
480 	else
481 	{
482 		tr.refdef.autoExposureMinMax[0] = tr.autoExposureMinMax[0];
483 		tr.refdef.autoExposureMinMax[1] = tr.autoExposureMinMax[1];
484 	}
485 
486 	if (r_forceToneMap->integer)
487 	{
488 		tr.refdef.toneMinAvgMaxLinear[0] = pow(2, r_forceToneMapMin->value);
489 		tr.refdef.toneMinAvgMaxLinear[1] = pow(2, r_forceToneMapAvg->value);
490 		tr.refdef.toneMinAvgMaxLinear[2] = pow(2, r_forceToneMapMax->value);
491 	}
492 	else
493 	{
494 		tr.refdef.toneMinAvgMaxLinear[0] = pow(2, tr.toneMinAvgMaxLevel[0]);
495 		tr.refdef.toneMinAvgMaxLinear[1] = pow(2, tr.toneMinAvgMaxLevel[1]);
496 		tr.refdef.toneMinAvgMaxLinear[2] = pow(2, tr.toneMinAvgMaxLevel[2]);
497 	}
498 
499 	// Makro - copy exta info if present
500 	if (fd->rdflags & RDF_EXTRA) {
501 		const refdefex_t* extra = (const refdefex_t*) (fd+1);
502 
503 		tr.refdef.blurFactor = extra->blurFactor;
504 
505 		if (fd->rdflags & RDF_SUNLIGHT)
506 		{
507 			VectorCopy(extra->sunDir,    tr.refdef.sunDir);
508 			VectorCopy(extra->sunCol,    tr.refdef.sunCol);
509 			VectorCopy(extra->sunAmbCol, tr.refdef.sunAmbCol);
510 		}
511 	}
512 	else
513 	{
514 		tr.refdef.blurFactor = 0.0f;
515 	}
516 
517 	// derived info
518 
519 	tr.refdef.floatTime = tr.refdef.time * 0.001;
520 
521 	tr.refdef.numDrawSurfs = r_firstSceneDrawSurf;
522 	tr.refdef.drawSurfs = backEndData->drawSurfs;
523 
524 	tr.refdef.num_entities = r_numentities - r_firstSceneEntity;
525 	tr.refdef.entities = &backEndData->entities[r_firstSceneEntity];
526 
527 	tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight;
528 	tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight];
529 
530 	tr.refdef.num_coronas = r_numcoronas - r_firstSceneCorona;
531 	tr.refdef.coronas = &backEndData->coronas[r_firstSceneCorona];
532 
533 	tr.refdef.numPolys = r_numpolys - r_firstScenePoly;
534 	tr.refdef.polys = &backEndData->polys[r_firstScenePoly];
535 
536 	tr.refdef.num_pshadows = 0;
537 	tr.refdef.pshadows = &backEndData->pshadows[0];
538 
539 	// turn off dynamic lighting globally by clearing all the
540 	// dlights if it needs to be disabled or if vertex lighting is enabled
541 	if ( /*r_dynamiclight->integer == 0 ||	// RF, disabled so we can force things like lightning dlights
542 		 r_vertexLight->integer == 1 ||*/
543 		glConfig.hardwareType == GLHW_PERMEDIA2 ) {
544 		tr.refdef.num_dlights = 0;
545 	}
546 
547 	// a single frame may have multiple scenes draw inside it --
548 	// a 3D game view, 3D status bar renderings, 3D menus, etc.
549 	// They need to be distinguished by the light flare code, because
550 	// the visibility state for a given surface may be different in
551 	// each scene / view.
552 	tr.frameSceneNum++;
553 	tr.sceneCount++;
554 }
555 
RE_EndScene(void)556 void RE_EndScene(void)
557 {
558 	// the next scene rendered in this frame will tack on after this one
559 	r_firstSceneDrawSurf = tr.refdef.numDrawSurfs;
560 	r_firstSceneEntity = r_numentities;
561 	r_firstSceneDlight = r_numdlights;
562 	r_firstScenePoly = r_numpolys;
563 }
564 
565 /*
566 @@@@@@@@@@@@@@@@@@@@@
567 RE_RenderScene
568 
569 Draw a 3D view into a part of the window, then return
570 to 2D drawing.
571 
572 Rendering a scene may require multiple views to be rendered
573 to handle mirrors,
574 @@@@@@@@@@@@@@@@@@@@@
575 */
RE_RenderScene(const refdef_t * fd)576 void RE_RenderScene( const refdef_t *fd ) {
577 	viewParms_t		parms;
578 	int				startTime;
579 
580 	if ( !tr.registered ) {
581 		return;
582 	}
583 	GLimp_LogComment( "====== RE_RenderScene =====\n" );
584 
585 	if ( r_norefresh->integer ) {
586 		return;
587 	}
588 
589 	startTime = ri.Milliseconds();
590 
591 	if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) {
592 		ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel");
593 	}
594 
595 	RE_BeginScene(fd);
596 
597 	// SmileTheory: playing with shadow mapping
598 	if (!( fd->rdflags & RDF_NOWORLDMODEL ) && tr.refdef.num_dlights && r_dlightMode->integer >= 2)
599 	{
600 		R_RenderDlightCubemaps(fd);
601 	}
602 
603 	/* playing with more shadows */
604 	if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && r_shadows->integer == 4)
605 	{
606 		R_RenderPshadowMaps(fd);
607 	}
608 
609 	// playing with even more shadows
610 	if(glRefConfig.framebufferObject && r_sunlightMode->integer && !( fd->rdflags & RDF_NOWORLDMODEL ) && (r_forceSun->integer || tr.sunShadows))
611 	{
612 		if (r_shadowCascadeZFar->integer != 0)
613 		{
614 			R_RenderSunShadowMaps(fd, 0);
615 			R_RenderSunShadowMaps(fd, 1);
616 			R_RenderSunShadowMaps(fd, 2);
617 		}
618 		else
619 		{
620 			Mat4Zero(tr.refdef.sunShadowMvp[0]);
621 			Mat4Zero(tr.refdef.sunShadowMvp[1]);
622 			Mat4Zero(tr.refdef.sunShadowMvp[2]);
623 		}
624 
625 		// only rerender last cascade if sun has changed position
626 		if (r_forceSun->integer == 2 || !VectorCompare(tr.refdef.sunDir, tr.lastCascadeSunDirection))
627 		{
628 			VectorCopy(tr.refdef.sunDir, tr.lastCascadeSunDirection);
629 			R_RenderSunShadowMaps(fd, 3);
630 			Mat4Copy(tr.refdef.sunShadowMvp[3], tr.lastCascadeSunMvp);
631 		}
632 		else
633 		{
634 			Mat4Copy(tr.lastCascadeSunMvp, tr.refdef.sunShadowMvp[3]);
635 		}
636 	}
637 
638 	// playing with cube maps
639 	// this is where dynamic cubemaps would be rendered
640 	if (0) //(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ))
641 	{
642 		int i, j;
643 
644 		for (i = 0; i < tr.numCubemaps; i++)
645 		{
646 			for (j = 0; j < 6; j++)
647 			{
648 				R_RenderCubemapSide(i, j, qtrue);
649 			}
650 		}
651 	}
652 
653 	// setup view parms for the initial view
654 	//
655 	// set up viewport
656 	// The refdef takes 0-at-the-top y coordinates, so
657 	// convert to GL's 0-at-the-bottom space
658 	//
659 	memset( &parms, 0, sizeof( parms ) );
660 	parms.viewportX = tr.refdef.x;
661 	parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height );
662 	parms.viewportWidth = tr.refdef.width;
663 	parms.viewportHeight = tr.refdef.height;
664 	parms.isPortal = qfalse;
665 
666 	parms.fovX = tr.refdef.fov_x;
667 	parms.fovY = tr.refdef.fov_y;
668 
669 	parms.stereoFrame = tr.refdef.stereoFrame;
670 
671 	VectorCopy( fd->vieworg, parms.or.origin );
672 	VectorCopy( fd->viewaxis[0], parms.or.axis[0] );
673 	VectorCopy( fd->viewaxis[1], parms.or.axis[1] );
674 	VectorCopy( fd->viewaxis[2], parms.or.axis[2] );
675 
676 	VectorCopy( fd->vieworg, parms.pvsOrigin );
677 
678 	if(!( fd->rdflags & RDF_NOWORLDMODEL ) && r_depthPrepass->value && ((r_forceSun->integer) || tr.sunShadows))
679 	{
680 		parms.flags = VPF_USESUNLIGHT;
681 	}
682 
683 	R_RenderView( &parms );
684 
685 	if(!( fd->rdflags & RDF_NOWORLDMODEL ))
686 		R_AddPostProcessCmd();
687 
688 	RE_EndScene();
689 
690 	tr.frontEndMsec += ri.Milliseconds() - startTime;
691 }
692