2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
30
Returns true if the grid is completely culled away.
31
Also sets the clipped hint bit in tess
34
static qboolean R_CullTriSurf( srfTriangles_t *cv ) {
37
boxCull = R_CullLocalBox( cv->bounds );
39
if ( boxCull == CULL_OUT ) {
49
Returns true if the grid is completely culled away.
50
Also sets the clipped hint bit in tess
53
static qboolean R_CullGrid( srfGridMesh_t *cv ) {
57
if ( r_nocurves->integer ) {
61
if ( tr.currentEntityNum != ENTITYNUM_WORLD ) {
62
sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius );
64
sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius );
68
// check for trivial reject
69
if ( sphereCull == CULL_OUT )
71
tr.pc.c_sphere_cull_patch_out++;
74
// check bounding box if necessary
75
else if ( sphereCull == CULL_CLIP )
77
tr.pc.c_sphere_cull_patch_clip++;
79
boxCull = R_CullLocalBox( cv->meshBounds );
81
if ( boxCull == CULL_OUT )
83
tr.pc.c_box_cull_patch_out++;
86
else if ( boxCull == CULL_IN )
88
tr.pc.c_box_cull_patch_in++;
92
tr.pc.c_box_cull_patch_clip++;
97
tr.pc.c_sphere_cull_patch_in++;
108
Tries to back face cull surfaces before they are lighted or
109
added to the sorting list.
111
This will also allow mirrors on both sides of a model without recursion.
114
static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) {
115
srfSurfaceFace_t *sface;
118
if ( r_nocull->integer ) {
122
if ( *surface == SF_GRID ) {
123
return R_CullGrid( (srfGridMesh_t *)surface );
126
if ( *surface == SF_TRIANGLES ) {
127
return R_CullTriSurf( (srfTriangles_t *)surface );
130
if ( *surface != SF_FACE ) {
134
if ( shader->cullType == CT_TWO_SIDED ) {
139
if ( !r_facePlaneCull->integer ) {
143
sface = ( srfSurfaceFace_t * ) surface;
144
d = DotProduct (tr.or.viewOrigin, sface->plane.normal);
146
// don't cull exactly on the plane, because there are levels of rounding
147
// through the BSP, ICD, and hardware that may cause pixel gaps if an
148
// epsilon isn't allowed here
149
if ( shader->cullType == CT_FRONT_SIDED ) {
150
if ( d < sface->plane.dist - 8 ) {
154
if ( d > sface->plane.dist + 8 ) {
163
static int R_DlightFace( srfSurfaceFace_t *face, int dlightBits ) {
168
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
169
if ( ! ( dlightBits & ( 1 << i ) ) ) {
172
dl = &tr.refdef.dlights[i];
173
d = DotProduct( dl->origin, face->plane.normal ) - face->plane.dist;
174
if ( d < -dl->radius || d > dl->radius ) {
175
// dlight doesn't reach the plane
176
dlightBits &= ~( 1 << i );
181
tr.pc.c_dlightSurfacesCulled++;
184
face->dlightBits[ tr.smpFrame ] = dlightBits;
188
static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) {
192
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
193
if ( ! ( dlightBits & ( 1 << i ) ) ) {
196
dl = &tr.refdef.dlights[i];
197
if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0]
198
|| dl->origin[0] + dl->radius < grid->meshBounds[0][0]
199
|| dl->origin[1] - dl->radius > grid->meshBounds[1][1]
200
|| dl->origin[1] + dl->radius < grid->meshBounds[0][1]
201
|| dl->origin[2] - dl->radius > grid->meshBounds[1][2]
202
|| dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) {
203
// dlight doesn't reach the bounds
204
dlightBits &= ~( 1 << i );
209
tr.pc.c_dlightSurfacesCulled++;
212
grid->dlightBits[ tr.smpFrame ] = dlightBits;
217
static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) {
218
// FIXME: more dlight culling to trisurfs...
219
surf->dlightBits[ tr.smpFrame ] = dlightBits;
225
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
226
if ( ! ( dlightBits & ( 1 << i ) ) ) {
229
dl = &tr.refdef.dlights[i];
230
if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0]
231
|| dl->origin[0] + dl->radius < grid->meshBounds[0][0]
232
|| dl->origin[1] - dl->radius > grid->meshBounds[1][1]
233
|| dl->origin[1] + dl->radius < grid->meshBounds[0][1]
234
|| dl->origin[2] - dl->radius > grid->meshBounds[1][2]
235
|| dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) {
236
// dlight doesn't reach the bounds
237
dlightBits &= ~( 1 << i );
242
tr.pc.c_dlightSurfacesCulled++;
245
grid->dlightBits[ tr.smpFrame ] = dlightBits;
254
The given surface is going to be drawn, and it touches a leaf
255
that is touched by one or more dlights, so try to throw out
256
more dlights if possible.
259
static int R_DlightSurface( msurface_t *surf, int dlightBits ) {
260
if ( *surf->data == SF_FACE ) {
261
dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits );
262
} else if ( *surf->data == SF_GRID ) {
263
dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits );
264
} else if ( *surf->data == SF_TRIANGLES ) {
265
dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits );
271
tr.pc.c_dlightSurfaces++;
280
======================
282
======================
284
static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) {
285
if ( surf->viewCount == tr.viewCount ) {
286
return; // already in this view
289
surf->viewCount = tr.viewCount;
290
// FIXME: bmodel fog?
292
// try to cull before dlighting or adding
293
if ( R_CullSurface( surf->data, surf->shader ) ) {
297
// check for dlighting
299
dlightBits = R_DlightSurface( surf, dlightBits );
300
dlightBits = ( dlightBits != 0 );
303
R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits );
307
=============================================================
311
=============================================================
316
R_AddBrushModelSurfaces
319
void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) {
325
pModel = R_GetModelByHandle( ent->e.hModel );
327
bmodel = pModel->bmodel;
329
clip = R_CullLocalBox( bmodel->bounds );
330
if ( clip == CULL_OUT ) {
334
R_DlightBmodel( bmodel );
336
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
337
R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights );
343
=============================================================
347
=============================================================
356
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) {
361
// if the node wasn't marked as potentially visible, exit
362
if (node->visframe != tr.visCount) {
366
// if the bounding volume is outside the frustum, nothing
367
// inside can be visible OPTIMIZE: don't do this all the way to leafs?
369
if ( !r_nocull->integer ) {
372
if ( planeBits & 1 ) {
373
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]);
378
planeBits &= ~1; // all descendants will also be in front
382
if ( planeBits & 2 ) {
383
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]);
388
planeBits &= ~2; // all descendants will also be in front
392
if ( planeBits & 4 ) {
393
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]);
398
planeBits &= ~4; // all descendants will also be in front
402
if ( planeBits & 8 ) {
403
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]);
408
planeBits &= ~8; // all descendants will also be in front
414
if ( node->contents != -1 ) {
418
// node is just a decision point, so go down both sides
419
// since we don't care about sort orders, just go positive to negative
421
// determine which dlights are needed
427
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
431
if ( dlightBits & ( 1 << i ) ) {
432
dl = &tr.refdef.dlights[i];
433
dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist;
435
if ( dist > -dl->radius ) {
436
newDlights[0] |= ( 1 << i );
438
if ( dist < dl->radius ) {
439
newDlights[1] |= ( 1 << i );
445
// recurse down the children, front side first
446
R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] );
449
node = node->children[1];
450
dlightBits = newDlights[1];
454
// leaf node, so add mark surfaces
456
msurface_t *surf, **mark;
460
// add to z buffer bounds
461
if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) {
462
tr.viewParms.visBounds[0][0] = node->mins[0];
464
if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) {
465
tr.viewParms.visBounds[0][1] = node->mins[1];
467
if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) {
468
tr.viewParms.visBounds[0][2] = node->mins[2];
471
if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) {
472
tr.viewParms.visBounds[1][0] = node->maxs[0];
474
if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) {
475
tr.viewParms.visBounds[1][1] = node->maxs[1];
477
if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) {
478
tr.viewParms.visBounds[1][2] = node->maxs[2];
481
// add the individual surfaces
482
mark = node->firstmarksurface;
483
c = node->nummarksurfaces;
485
// the surface may have already been added if it
486
// spans multiple leafs
488
R_AddWorldSurface( surf, dlightBits );
501
static mnode_t *R_PointInLeaf( const vec3_t p ) {
507
ri.Error (ERR_DROP, "R_PointInLeaf: bad model");
510
node = tr.world->nodes;
512
if (node->contents != -1) {
516
d = DotProduct (p,plane->normal) - plane->dist;
518
node = node->children[0];
520
node = node->children[1];
532
static const byte *R_ClusterPVS (int cluster) {
533
if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) {
534
return tr.world->novis;
537
return tr.world->vis + cluster * tr.world->clusterBytes;
545
qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) {
549
leaf = R_PointInLeaf( p1 );
550
vis = CM_ClusterPVS( leaf->cluster );
551
leaf = R_PointInLeaf( p2 );
553
if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) {
563
Mark the leaves and nodes that are in the PVS for the current
567
static void R_MarkLeaves (void) {
569
mnode_t *leaf, *parent;
573
// lockpvs lets designers walk around to determine the
574
// extent of the current pvs
575
if ( r_lockpvs->integer ) {
579
// current viewcluster
580
leaf = R_PointInLeaf( tr.viewParms.pvsOrigin );
581
cluster = leaf->cluster;
583
// if the cluster is the same and the area visibility matrix
584
// hasn't changed, we don't need to mark everything again
586
// if r_showcluster was just turned on, remark everything
587
if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified
588
&& !r_showcluster->modified ) {
592
if ( r_showcluster->modified || r_showcluster->integer ) {
593
r_showcluster->modified = qfalse;
594
if ( r_showcluster->integer ) {
595
ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area );
600
tr.viewCluster = cluster;
602
if ( r_novis->integer || tr.viewCluster == -1 ) {
603
for (i=0 ; i<tr.world->numnodes ; i++) {
604
if (tr.world->nodes[i].contents != CONTENTS_SOLID) {
605
tr.world->nodes[i].visframe = tr.visCount;
611
vis = R_ClusterPVS (tr.viewCluster);
613
for (i=0,leaf=tr.world->nodes ; i<tr.world->numnodes ; i++, leaf++) {
614
cluster = leaf->cluster;
615
if ( cluster < 0 || cluster >= tr.world->numClusters ) {
620
if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) {
624
// check for door connection
625
if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) {
626
continue; // not visible
631
if (parent->visframe == tr.visCount)
633
parent->visframe = tr.visCount;
634
parent = parent->parent;
645
void R_AddWorldSurfaces (void) {
646
if ( !r_drawworld->integer ) {
650
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
654
tr.currentEntityNum = ENTITYNUM_WORLD;
655
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT;
657
// determine which leaves are in the PVS / areamask
660
// clear out the visible min/max
661
ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] );
663
// perform frustum culling and add all the potentially visible surfaces
664
if ( tr.refdef.num_dlights > 32 ) {
665
tr.refdef.num_dlights = 32 ;
667
R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 );