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