1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2006 Robert Beckebans <trebor_7@users.sourceforge.net>
5 
6 This file is part of XreaL source code.
7 
8 XreaL source code is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 XreaL source code is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with XreaL source code; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 // tr_world.c
24 #include "tr_local.h"
25 
26 /*
27 =================
28 R_CullTriSurf
29 
30 Returns true if the grid is completely culled away.
31 Also sets the clipped hint bit in tess
32 =================
33 */
R_CullTriSurf(srfTriangles_t * cv)34 static qboolean R_CullTriSurf(srfTriangles_t * cv)
35 {
36 	int             boxCull;
37 
38 	boxCull = R_CullLocalBox(cv->bounds);
39 
40 	if(boxCull == CULL_OUT)
41 	{
42 		return qtrue;
43 	}
44 	return qfalse;
45 }
46 
47 /*
48 =================
49 R_CullGrid
50 
51 Returns true if the grid is completely culled away.
52 Also sets the clipped hint bit in tess
53 =================
54 */
R_CullGrid(srfGridMesh_t * cv)55 static qboolean R_CullGrid(srfGridMesh_t * cv)
56 {
57 	int             boxCull;
58 	int             sphereCull;
59 
60 	if(r_nocurves->integer)
61 	{
62 		return qtrue;
63 	}
64 
65 	if(tr.currentEntity != &tr.worldEntity)
66 	{
67 		sphereCull = R_CullLocalPointAndRadius(cv->localOrigin, cv->meshRadius);
68 	}
69 	else
70 	{
71 		sphereCull = R_CullPointAndRadius(cv->localOrigin, cv->meshRadius);
72 	}
73 	boxCull = CULL_OUT;
74 
75 	// check for trivial reject
76 	if(sphereCull == CULL_OUT)
77 	{
78 		tr.pc.c_sphere_cull_patch_out++;
79 		return qtrue;
80 	}
81 	// check bounding box if necessary
82 	else if(sphereCull == CULL_CLIP)
83 	{
84 		tr.pc.c_sphere_cull_patch_clip++;
85 
86 		boxCull = R_CullLocalBox(cv->meshBounds);
87 
88 		if(boxCull == CULL_OUT)
89 		{
90 			tr.pc.c_box_cull_patch_out++;
91 			return qtrue;
92 		}
93 		else if(boxCull == CULL_IN)
94 		{
95 			tr.pc.c_box_cull_patch_in++;
96 		}
97 		else
98 		{
99 			tr.pc.c_box_cull_patch_clip++;
100 		}
101 	}
102 	else
103 	{
104 		tr.pc.c_sphere_cull_patch_in++;
105 	}
106 
107 	return qfalse;
108 }
109 
110 
111 /*
112 ================
113 R_CullSurface
114 
115 Tries to back face cull surfaces before they are lighted or
116 added to the sorting list.
117 
118 This will also allow mirrors on both sides of a model without recursion.
119 ================
120 */
R_CullSurface(surfaceType_t * surface,shader_t * shader)121 static qboolean R_CullSurface(surfaceType_t * surface, shader_t * shader)
122 {
123 	srfSurfaceFace_t *sface;
124 	float           d;
125 
126 	if(r_nocull->integer)
127 	{
128 		return qfalse;
129 	}
130 
131 	if(*surface == SF_GRID)
132 	{
133 		return R_CullGrid((srfGridMesh_t *) surface);
134 	}
135 
136 	if(*surface == SF_TRIANGLES)
137 	{
138 		return R_CullTriSurf((srfTriangles_t *) surface);
139 	}
140 
141 	if(*surface != SF_FACE)
142 	{
143 		return qfalse;
144 	}
145 
146 	if(shader->cullType == CT_TWO_SIDED)
147 	{
148 		return qfalse;
149 	}
150 
151 	// face culling
152 	sface = (srfSurfaceFace_t *) surface;
153 	d = DotProduct(tr.or.viewOrigin, sface->plane.normal);
154 
155 	// don't cull exactly on the plane, because there are levels of rounding
156 	// through the BSP, ICD, and hardware that may cause pixel gaps if an
157 	// epsilon isn't allowed here
158 	if(shader->cullType == CT_FRONT_SIDED)
159 	{
160 		if(d < sface->plane.dist - 8)
161 		{
162 			return qtrue;
163 		}
164 	}
165 	else
166 	{
167 		if(d > sface->plane.dist + 8)
168 		{
169 			return qtrue;
170 		}
171 	}
172 
173 	return qfalse;
174 }
175 
R_DlightFace(srfSurfaceFace_t * face,trRefDlight_t * light)176 static qboolean R_DlightFace(srfSurfaceFace_t * face, trRefDlight_t  * light)
177 {
178 	// do a quick AABB cull
179 	if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], face->bounds[0], face->bounds[1]))
180 	{
181 		return qfalse;
182 	}
183 
184 	// do a more expensive and precise light frustum cull
185 	if(!r_noLightFrustums->integer)
186 	{
187 		if(R_CullLightWorldBounds(light, face->bounds) == CULL_OUT)
188 		{
189 			return qfalse;
190 		}
191 	}
192 
193 	return qtrue;
194 }
195 
R_DlightGrid(srfGridMesh_t * grid,trRefDlight_t * light)196 static int R_DlightGrid(srfGridMesh_t * grid, trRefDlight_t * light)
197 {
198 	// do a quick AABB cull
199 	if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], grid->meshBounds[0], grid->meshBounds[1]))
200 	{
201 		return qfalse;
202 	}
203 
204 	// do a more expensive and precise light frustum cull
205 	if(!r_noLightFrustums->integer)
206 	{
207 		if(R_CullLightWorldBounds(light, grid->meshBounds) == CULL_OUT)
208 		{
209 			return qfalse;
210 		}
211 	}
212 	return qtrue;
213 }
214 
215 
R_DlightTrisurf(srfTriangles_t * tri,trRefDlight_t * light)216 static int R_DlightTrisurf(srfTriangles_t * tri, trRefDlight_t * light)
217 {
218 	// do a quick AABB cull
219 	if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], tri->bounds[0], tri->bounds[1]))
220 	{
221 		return qfalse;
222 	}
223 
224 	// do a more expensive and precise light frustum cull
225 	if(!r_noLightFrustums->integer)
226 	{
227 		if(R_CullLightWorldBounds(light, tri->bounds) == CULL_OUT)
228 		{
229 			return qfalse;
230 		}
231 	}
232 	return qtrue;
233 }
234 
235 
236 /*
237 ======================
238 R_AddInteractionSurface
239 ======================
240 */
R_AddInteractionSurface(msurface_t * surf,trRefDlight_t * light)241 static void R_AddInteractionSurface(msurface_t * surf, trRefDlight_t * light)
242 {
243 	qboolean        intersects;
244 	interactionType_t iaType = IA_DEFAULT;
245 
246 	// Tr3B - this surface is maybe not in this view but it may still cast a shadow
247 	// into this view
248 	if(surf->viewCount != tr.viewCount)
249 	{
250 		if(r_shadows->integer <= 2 || light->l.noShadows)
251 			return;
252 		else
253 			iaType = IA_SHADOWONLY;
254 	}
255 	else
256 	{
257 		if(r_shadows->integer <= 2)
258 			iaType = IA_LIGHTONLY;
259 	}
260 
261 	if(surf->lightCount == tr.lightCount)
262 	{
263 		return;					// already checked this surface
264 	}
265 	surf->lightCount = tr.lightCount;
266 
267 	//  skip all surfaces that don't matter for lighting only pass
268 	if(surf->shader->isSky || (!surf->shader->interactLight && surf->shader->noShadows))
269 		return;
270 
271 	if(*surf->data == SF_FACE)
272 	{
273 		intersects = R_DlightFace((srfSurfaceFace_t *) surf->data, light);
274 	}
275 	else if(*surf->data == SF_GRID)
276 	{
277 		intersects = R_DlightGrid((srfGridMesh_t *) surf->data, light);
278 	}
279 	else if(*surf->data == SF_TRIANGLES)
280 	{
281 		intersects = R_DlightTrisurf((srfTriangles_t *) surf->data, light);
282 	}
283 	else
284 	{
285 		intersects = qfalse;
286 	}
287 
288 	if(intersects)
289 	{
290 		R_AddDlightInteraction(light, surf->data, surf->shader, 0, NULL, 0, NULL, iaType);
291 
292 		if(light->isStatic)
293 			tr.pc.c_slightSurfaces++;
294 		else
295 			tr.pc.c_dlightSurfaces++;
296 	}
297 	else
298 	{
299 		if(!light->isStatic)
300 			tr.pc.c_dlightSurfacesCulled++;
301 	}
302 }
303 
304 /*
305 ======================
306 R_AddWorldSurface
307 ======================
308 */
R_AddWorldSurface(msurface_t * surf)309 static void R_AddWorldSurface(msurface_t * surf)
310 {
311 	if(surf->viewCount == tr.viewCount)
312 	{
313 		return;					// already in this view
314 	}
315 	surf->viewCount = tr.viewCount;
316 
317 	// FIXME: bmodel fog?
318 
319 	// try to cull before dlighting or adding
320 	if(R_CullSurface(surf->data, surf->shader))
321 	{
322 		return;
323 	}
324 
325 	R_AddDrawSurf(surf->data, surf->shader, surf->lightmapNum, surf->fogIndex);
326 }
327 
328 /*
329 =============================================================
330 
331 	BRUSH MODELS
332 
333 =============================================================
334 */
335 
336 /*
337 ======================
338 R_AddBrushModelSurface
339 ======================
340 */
R_AddBrushModelSurface(msurface_t * surf,int fogIndex)341 static void R_AddBrushModelSurface(msurface_t * surf, int fogIndex)
342 {
343 	if(surf->viewCount == tr.viewCount)
344 	{
345 		return;					// already in this view
346 	}
347 	surf->viewCount = tr.viewCount;
348 
349 	// try to cull before lighting or adding
350 	if(R_CullSurface(surf->data, surf->shader))
351 	{
352 		return;
353 	}
354 
355 	R_AddDrawSurf(surf->data, surf->shader, surf->lightmapNum, fogIndex);
356 }
357 
358 /*
359 =================
360 R_AddBrushModelSurfaces
361 =================
362 */
R_AddBrushModelSurfaces(trRefEntity_t * ent)363 void R_AddBrushModelSurfaces(trRefEntity_t * ent)
364 {
365 	bmodel_t       *bModel;
366 	model_t        *pModel;
367 	int             i;
368 	vec3_t          v;
369 	vec3_t          transformed;
370 	int             fogNum = 0;
371 
372 	pModel = R_GetModelByHandle(ent->e.hModel);
373 	bModel = pModel->bmodel;
374 
375 	// copy local bounds
376 	for(i = 0; i < 3; i++)
377 	{
378 		ent->localBounds[0][i] = bModel->bounds[0][i];
379 		ent->localBounds[1][i] = bModel->bounds[1][i];
380 	}
381 
382 	// setup world bounds for intersection tests
383 	ClearBounds(ent->worldBounds[0], ent->worldBounds[1]);
384 
385 	for(i = 0; i < 8; i++)
386 	{
387 		v[0] = ent->localBounds[i & 1][0];
388 		v[1] = ent->localBounds[(i >> 1) & 1][1];
389 		v[2] = ent->localBounds[(i >> 2) & 1][2];
390 
391 		// transform local bounds vertices into world space
392 		R_LocalPointToWorld(v, transformed);
393 
394 		AddPointToBounds(transformed, ent->worldBounds[0], ent->worldBounds[1]);
395 	}
396 
397 	ent->cull = R_CullLocalBox(bModel->bounds);
398 	if(ent->cull == CULL_OUT)
399 	{
400 		return;
401 	}
402 
403 	fogNum = R_FogWorldBox(ent->worldBounds);
404 
405 	for(i = 0; i < bModel->numSurfaces; i++)
406 	{
407 		R_AddBrushModelSurface(bModel->firstSurface + i, fogNum);
408 	}
409 }
410 
411 
412 /*
413 =============================================================
414 
415 	WORLD MODEL
416 
417 =============================================================
418 */
419 
420 
421 /*
422 ================
423 R_RecursiveWorldNode
424 ================
425 */
R_RecursiveWorldNode(mnode_t * node,int planeBits)426 static void R_RecursiveWorldNode(mnode_t * node, int planeBits)
427 {
428 	do
429 	{
430 		// if the node wasn't marked as potentially visible, exit
431 		if(node->visCount != tr.visCount)
432 		{
433 			return;
434 		}
435 
436 		// if the bounding volume is outside the frustum, nothing
437 		// inside can be visible OPTIMIZE: don't do this all the way to leafs?
438 		if(!r_nocull->integer)
439 		{
440 			int             i;
441 			int             r;
442 
443 			for(i = 0; i < FRUSTUM_PLANES; i++)
444 			{
445 				if(planeBits & (1 << i))
446 				{
447 					r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[i]);
448 					if(r == 2)
449 					{
450 						return;		// culled
451 					}
452 					if(r == 1)
453 					{
454 						planeBits &= ~(1 << i);	// all descendants will also be in front
455 					}
456 				}
457 			}
458 		}
459 
460 		if(node->contents != -1)
461 		{
462 			break;
463 		}
464 
465 		// recurse down the children, front side first
466 		R_RecursiveWorldNode(node->children[0], planeBits);
467 
468 		// tail recurse
469 		node = node->children[1];
470 	} while(1);
471 
472 	{
473 		// leaf node, so add mark surfaces
474 		int             c;
475 		msurface_t     *surf, **mark;
476 
477 		tr.pc.c_leafs++;
478 
479 		// add to z buffer bounds
480 		if(node->mins[0] < tr.viewParms.visBounds[0][0])
481 		{
482 			tr.viewParms.visBounds[0][0] = node->mins[0];
483 		}
484 		if(node->mins[1] < tr.viewParms.visBounds[0][1])
485 		{
486 			tr.viewParms.visBounds[0][1] = node->mins[1];
487 		}
488 		if(node->mins[2] < tr.viewParms.visBounds[0][2])
489 		{
490 			tr.viewParms.visBounds[0][2] = node->mins[2];
491 		}
492 
493 		if(node->maxs[0] > tr.viewParms.visBounds[1][0])
494 		{
495 			tr.viewParms.visBounds[1][0] = node->maxs[0];
496 		}
497 		if(node->maxs[1] > tr.viewParms.visBounds[1][1])
498 		{
499 			tr.viewParms.visBounds[1][1] = node->maxs[1];
500 		}
501 		if(node->maxs[2] > tr.viewParms.visBounds[1][2])
502 		{
503 			tr.viewParms.visBounds[1][2] = node->maxs[2];
504 		}
505 
506 		// add the individual surfaces
507 		mark = node->firstmarksurface;
508 		c = node->nummarksurfaces;
509 		while(c--)
510 		{
511 			// the surface may have already been added if it
512 			// spans multiple leafs
513 			surf = *mark;
514 			R_AddWorldSurface(surf);
515 			mark++;
516 		}
517 	}
518 }
519 
520 /*
521 ================
522 R_RecursiveInteractionNode
523 ================
524 */
R_RecursiveInteractionNode(mnode_t * node,trRefDlight_t * light,int planeBits)525 static void R_RecursiveInteractionNode(mnode_t * node, trRefDlight_t * light, int planeBits)
526 {
527 	int             i;
528 	int             r;
529 
530 	// if the node wasn't marked as potentially visible, exit
531 	if(node->visCount != tr.visCount)
532 	{
533 		return;
534 	}
535 
536 	// light already hit node
537 	if(node->lightCount == tr.lightCount)
538 	{
539 		return;
540 	}
541 	node->lightCount = tr.lightCount;
542 
543 	// if the bounding volume is outside the frustum, nothing
544 	// inside can be visible OPTIMIZE: don't do this all the way to leafs?
545 
546 	// Tr3B - even surfaces that belong to nodes that are outside of the view frustum
547 	// can cast shadows into the view frustum
548 	if(!r_nocull->integer && r_shadows->integer <= 2)
549 	{
550 		for(i = 0; i < FRUSTUM_PLANES; i++)
551 		{
552 			if(planeBits & (1 << i))
553 			{
554 				r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[i]);
555 
556 				if(r == 2)
557 				{
558 					return;		// culled
559 				}
560 
561 				if(r == 1)
562 				{
563 					planeBits &= ~(1 << i);	// all descendants will also be in front
564 				}
565 			}
566 		}
567 	}
568 
569 	if(node->contents != -1)
570 	{
571 		// leaf node, so add mark surfaces
572 		int             c;
573 		msurface_t     *surf, **mark;
574 
575 		// add the individual surfaces
576 		mark = node->firstmarksurface;
577 		c = node->nummarksurfaces;
578 		while(c--)
579 		{
580 			// the surface may have already been added if it
581 			// spans multiple leafs
582 			surf = *mark;
583 			R_AddInteractionSurface(surf, light);
584 			mark++;
585 		}
586 		return;
587 	}
588 
589 	// node is just a decision point, so go down both sides
590 	// since we don't care about sort orders, just go positive to negative
591 	r = BoxOnPlaneSide(light->worldBounds[0], light->worldBounds[1], node->plane);
592 
593 	switch (r)
594 	{
595 		case 1:
596 			R_RecursiveInteractionNode(node->children[0], light, planeBits);
597 			break;
598 
599 		case 2:
600 			R_RecursiveInteractionNode(node->children[1], light, planeBits);
601 			break;
602 
603 		case 3:
604 		default:
605 			// recurse down the children, front side first
606 			R_RecursiveInteractionNode(node->children[0], light, planeBits);
607 			R_RecursiveInteractionNode(node->children[1], light, planeBits);
608 			break;
609 	}
610 }
611 
612 
613 /*
614 ===============
615 R_PointInLeaf
616 ===============
617 */
R_PointInLeaf(const vec3_t p)618 static mnode_t *R_PointInLeaf(const vec3_t p)
619 {
620 	mnode_t        *node;
621 	float           d;
622 	cplane_t       *plane;
623 
624 	if(!tr.world)
625 	{
626 		ri.Error(ERR_DROP, "R_PointInLeaf: bad model");
627 	}
628 
629 	node = tr.world->nodes;
630 	while(1)
631 	{
632 		if(node->contents != -1)
633 		{
634 			break;
635 		}
636 		plane = node->plane;
637 		d = DotProduct(p, plane->normal) - plane->dist;
638 		if(d > 0)
639 		{
640 			node = node->children[0];
641 		}
642 		else
643 		{
644 			node = node->children[1];
645 		}
646 	}
647 
648 	return node;
649 }
650 
651 /*
652 ==============
653 R_ClusterPVS
654 ==============
655 */
R_ClusterPVS(int cluster)656 static const byte *R_ClusterPVS(int cluster)
657 {
658 	if(!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters)
659 	{
660 		return tr.world->novis;
661 	}
662 
663 	return tr.world->vis + cluster * tr.world->clusterBytes;
664 }
665 
666 /*
667 =================
668 R_inPVS
669 =================
670 */
R_inPVS(const vec3_t p1,const vec3_t p2)671 qboolean R_inPVS(const vec3_t p1, const vec3_t p2)
672 {
673 	mnode_t        *leaf;
674 	byte           *vis;
675 
676 	leaf = R_PointInLeaf(p1);
677 	vis = CM_ClusterPVS(leaf->cluster);
678 	leaf = R_PointInLeaf(p2);
679 
680 	if(!(vis[leaf->cluster >> 3] & (1 << (leaf->cluster & 7))))
681 	{
682 		return qfalse;
683 	}
684 	return qtrue;
685 }
686 
687 /*
688 ===============
689 R_MarkLeaves
690 
691 Mark the leaves and nodes that are in the PVS for the current
692 cluster
693 ===============
694 */
R_MarkLeaves(void)695 static void R_MarkLeaves(void)
696 {
697 	const byte     *vis;
698 	mnode_t        *leaf, *parent;
699 	int             i;
700 	int             cluster;
701 
702 	// lockpvs lets designers walk around to determine the
703 	// extent of the current pvs
704 	if(r_lockpvs->integer)
705 	{
706 		return;
707 	}
708 
709 	// current viewcluster
710 	leaf = R_PointInLeaf(tr.viewParms.pvsOrigin);
711 	cluster = leaf->cluster;
712 
713 	// if the cluster is the same and the area visibility matrix
714 	// hasn't changed, we don't need to mark everything again
715 
716 	// if r_showcluster was just turned on, remark everything
717 	if(tr.viewCluster == cluster && !tr.refdef.areamaskModified && !r_showcluster->modified)
718 	{
719 		return;
720 	}
721 
722 	if(r_showcluster->modified || r_showcluster->integer)
723 	{
724 		r_showcluster->modified = qfalse;
725 		if(r_showcluster->integer)
726 		{
727 			ri.Printf(PRINT_ALL, "cluster:%i  area:%i\n", cluster, leaf->area);
728 		}
729 	}
730 
731 	tr.visCount++;
732 	tr.viewCluster = cluster;
733 
734 	if(r_novis->integer || tr.viewCluster == -1)
735 	{
736 		for(i = 0; i < tr.world->numnodes; i++)
737 		{
738 			if(tr.world->nodes[i].contents != CONTENTS_SOLID)
739 			{
740 				tr.world->nodes[i].visCount = tr.visCount;
741 			}
742 		}
743 		return;
744 	}
745 
746 	vis = R_ClusterPVS(tr.viewCluster);
747 
748 	for(i = 0, leaf = tr.world->nodes; i < tr.world->numnodes; i++, leaf++)
749 	{
750 		if(tr.world->vis)
751 		{
752 			cluster = leaf->cluster;
753 
754 			if(cluster >= 0 && cluster < tr.world->numClusters)
755 			{
756 				// check general pvs
757 				if(!(vis[cluster >> 3] & (1 << (cluster & 7))))
758 				{
759 					continue;
760 				}
761 			}
762 		}
763 
764 		// check for door connection
765 		if((tr.refdef.areamask[leaf->area >> 3] & (1 << (leaf->area & 7))))
766 		{
767 			continue;			// not visible
768 		}
769 
770 		parent = leaf;
771 		do
772 		{
773 			if(parent->visCount == tr.visCount)
774 				break;
775 			parent->visCount = tr.visCount;
776 			parent = parent->parent;
777 		} while(parent);
778 	}
779 }
780 
781 
782 /*
783 ** SetFarClip
784 */
R_SetFarClip(void)785 static void R_SetFarClip(void)
786 {
787 	float           farthestCornerDistance = 0;
788 	int             i;
789 
790 	// if not rendering the world (icons, menus, etc)
791 	// set a 2k far clip plane
792 	if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
793 	{
794 		tr.viewParms.skyFar = 2048;
795 		return;
796 	}
797 
798 	// set far clipping planes dynamically
799 	farthestCornerDistance = 0;
800 	for(i = 0; i < 8; i++)
801 	{
802 		vec3_t          v;
803 		vec3_t          vecTo;
804 		float           distance;
805 
806 		if(i & 1)
807 		{
808 			v[0] = tr.viewParms.visBounds[0][0];
809 		}
810 		else
811 		{
812 			v[0] = tr.viewParms.visBounds[1][0];
813 		}
814 
815 		if(i & 2)
816 		{
817 			v[1] = tr.viewParms.visBounds[0][1];
818 		}
819 		else
820 		{
821 			v[1] = tr.viewParms.visBounds[1][1];
822 		}
823 
824 		if(i & 4)
825 		{
826 			v[2] = tr.viewParms.visBounds[0][2];
827 		}
828 		else
829 		{
830 			v[2] = tr.viewParms.visBounds[1][2];
831 		}
832 
833 		VectorSubtract(v, tr.viewParms.or.origin, vecTo);
834 
835 		distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2];
836 
837 		if(distance > farthestCornerDistance)
838 		{
839 			farthestCornerDistance = distance;
840 		}
841 	}
842 	tr.viewParms.skyFar = sqrt(farthestCornerDistance);
843 }
844 
845 /*
846 =============
847 R_AddWorldSurfaces
848 =============
849 */
R_AddWorldSurfaces(void)850 void R_AddWorldSurfaces(void)
851 {
852 	if(!r_drawworld->integer)
853 	{
854 		return;
855 	}
856 
857 	if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
858 	{
859 		return;
860 	}
861 
862 	tr.currentEntity = &tr.worldEntity;
863 
864 	// determine which leaves are in the PVS / areamask
865 	R_MarkLeaves();
866 
867 	// clear out the visible min/max
868 	ClearBounds(tr.viewParms.visBounds[0], tr.viewParms.visBounds[1]);
869 
870 	// perform frustum culling and add all the potentially visible surfaces
871 	R_RecursiveWorldNode(tr.world->nodes, FRUSTUM_CLIPALL);
872 
873 	// dynamically compute far clip plane distance for sky
874 	R_SetFarClip();
875 }
876 
877 /*
878 =============
879 R_AddWorldInteractions
880 =============
881 */
R_AddWorldInteractions(trRefDlight_t * light)882 void R_AddWorldInteractions(trRefDlight_t * light)
883 {
884 	if(!r_drawworld->integer)
885 	{
886 		return;
887 	}
888 
889 	if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
890 	{
891 		return;
892 	}
893 
894 	tr.currentEntity = &tr.worldEntity;
895 
896 	// perform frustum culling and add all the potentially visible surfaces
897 	tr.lightCount++;
898 	R_RecursiveInteractionNode(tr.world->nodes, light, FRUSTUM_CLIPALL);
899 }
900 
901 /*
902 =============
903 R_AddPrecachedWorldInteractions
904 =============
905 */
R_AddPrecachedWorldInteractions(trRefDlight_t * light)906 void R_AddPrecachedWorldInteractions(trRefDlight_t * light)
907 {
908 	interactionCache_t  *iaCache;
909 	msurface_t     *surface;
910 	interactionType_t iaType = IA_DEFAULT;
911 
912 	if(!r_drawworld->integer)
913 	{
914 		return;
915 	}
916 
917 	if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
918 	{
919 		return;
920 	}
921 
922 	if(!light->firstInteractionCache)
923 	{
924 		// this light has no interactions precached
925 		return;
926 	}
927 
928 	tr.currentEntity = &tr.worldEntity;
929 
930 	for(iaCache = light->firstInteractionCache; iaCache; iaCache = iaCache->next)
931 	{
932 		surface = iaCache->surface;
933 
934 		// Tr3B - this surface is maybe not in this view but it may still cast a shadow
935 		// into this view
936 		if(surface->viewCount != tr.viewCount)
937 		{
938 			if(r_shadows->integer <= 2 || light->l.noShadows)
939 				continue;
940 			else
941 				iaType = IA_SHADOWONLY;
942 		}
943 		else
944 		{
945 			if(r_shadows->integer <= 2)
946 				iaType = IA_LIGHTONLY;
947 			else
948 				iaType = IA_DEFAULT;
949 		}
950 
951 		R_AddDlightInteraction(light, surface->data, surface->shader, iaCache->numLightIndexes, iaCache->lightIndexes, iaCache->numShadowIndexes, iaCache->shadowIndexes, iaType);
952 	}
953 }
954 
955 
956 /*
957 ===============
958 R_ShutdownVBOs
959 ===============
960 */
R_ShutdownVBOs()961 void R_ShutdownVBOs()
962 {
963 	int             i;
964 	msurface_t     *surface;
965 
966 	if(!tr.world || (tr.refdef.rdflags & RDF_NOWORLDMODEL))
967 	{
968 		return;
969 	}
970 
971 	if(!glConfig2.vertexBufferObjectAvailable)
972 	{
973 		return;
974 	}
975 
976 	for(i = 0, surface = &tr.world->surfaces[0]; i < tr.world->numsurfaces; i++, surface++)
977 	{
978 		if(*surface->data == SF_FACE)
979 		{
980 			srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface->data;
981 
982 			if(face->indexesVBO)
983 			{
984 				qglDeleteBuffersARB(1, &face->indexesVBO);
985 			}
986 
987 			if(face->vertsVBO)
988 			{
989 				qglDeleteBuffersARB(1, &face->vertsVBO);
990 			}
991 		}
992 		else if(*surface->data == SF_GRID)
993 		{
994 			srfGridMesh_t  *grid = (srfGridMesh_t *) surface->data;
995 
996 			if(grid->indexesVBO)
997 			{
998 				qglDeleteBuffersARB(1, &grid->indexesVBO);
999 			}
1000 
1001 			if(grid->vertsVBO)
1002 			{
1003 				qglDeleteBuffersARB(1, &grid->vertsVBO);
1004 			}
1005 		}
1006 		else if(*surface->data == SF_TRIANGLES)
1007 		{
1008 			srfTriangles_t  *tri = (srfTriangles_t *) surface->data;
1009 
1010 			if(tri->indexesVBO)
1011 			{
1012 				qglDeleteBuffersARB(1, &tri->indexesVBO);
1013 			}
1014 
1015 			if(tri->vertsVBO)
1016 			{
1017 				qglDeleteBuffersARB(1, &tri->vertsVBO);
1018 			}
1019 		}
1020 	}
1021 }
1022