mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-11-29 23:22:55 +00:00
3b4f4cdfa9
Some revision messages: Cache servers for each master server in q3_ui, otherwise servers from last updated master for shown for all Internet# sources. Play correct team sounds when in spectator mode and following a player. Check last listener number instead of clc.clientNum in S_AL_HearingThroughEntity so sound work correctly when spectate following a client. (Related to bug 5741.) When in third person, don't play player's sounds as full volume in Base sound system. OpenAL already does this. (Related to bug 5741.) really fix the confusion with game entity and refentity numbers to further reduce confusion, rename constants like MAX_ENTITIES to MAX_REFENTITIES Added Rend2, an alternate renderer. (Bug #4358) Fix restoring fs_game when default.cfg is missing. Fix restoring old fs_game upon leaving a server. Patch by Ensiform. Change more operator commands to require sv_running to be usable. Patch by Ensiform. Fix some "> MAX_*" to be ">= MAX_*". Fix follow command to find clients whose name begins with a number. Fix up "gc" command, make it more like "tell". Based on patch by Ensiform. Add usage messages for gc, tell, vtell, and votell commands. Check player names in gc, tell, vtell, and votell commands. #5799 - Change messagemode text box to display colors like in console input box. Improve "play" command, based on a patch from Ensiform. Check for invalid filename in OpenAL's RegisterSound function. Changed Base sound system to warn not error when sound filename is empty or too long. Remove references to non-existent functions CM_MarkFragments and CM_LerpTag.
851 lines
21 KiB
C
851 lines
21 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
#include "tr_local.h"
|
|
|
|
|
|
|
|
/*
|
|
================
|
|
R_CullSurface
|
|
|
|
Tries to cull surfaces before they are lighted or
|
|
added to the sorting list.
|
|
================
|
|
*/
|
|
static qboolean R_CullSurface( msurface_t *surf ) {
|
|
if ( r_nocull->integer || surf->cullinfo.type == CULLINFO_NONE) {
|
|
return qfalse;
|
|
}
|
|
|
|
if (surf->cullinfo.type & CULLINFO_PLANE)
|
|
{
|
|
// Only true for SF_FACE, so treat like its own function
|
|
float d;
|
|
cullType_t ct;
|
|
|
|
if ( !r_facePlaneCull->integer ) {
|
|
return qfalse;
|
|
}
|
|
|
|
ct = surf->shader->cullType;
|
|
|
|
if (ct == CT_TWO_SIDED)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
// don't cull for depth shadow
|
|
/*
|
|
if ( tr.viewParms.flags & VPF_DEPTHSHADOW )
|
|
{
|
|
return qfalse;
|
|
}
|
|
*/
|
|
|
|
// shadowmaps draw back surfaces
|
|
if ( tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW) )
|
|
{
|
|
if (ct == CT_FRONT_SIDED)
|
|
{
|
|
ct = CT_BACK_SIDED;
|
|
}
|
|
else
|
|
{
|
|
ct = CT_FRONT_SIDED;
|
|
}
|
|
}
|
|
|
|
// do proper cull for orthographic projection
|
|
if (tr.viewParms.flags & VPF_ORTHOGRAPHIC) {
|
|
d = DotProduct(tr.viewParms.or.axis[0], surf->cullinfo.plane.normal);
|
|
if ( ct == CT_FRONT_SIDED ) {
|
|
if (d > 0)
|
|
return qtrue;
|
|
} else {
|
|
if (d < 0)
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
d = DotProduct (tr.or.viewOrigin, surf->cullinfo.plane.normal);
|
|
|
|
// don't cull exactly on the plane, because there are levels of rounding
|
|
// through the BSP, ICD, and hardware that may cause pixel gaps if an
|
|
// epsilon isn't allowed here
|
|
if ( ct == CT_FRONT_SIDED ) {
|
|
if ( d < surf->cullinfo.plane.dist - 8 ) {
|
|
return qtrue;
|
|
}
|
|
} else {
|
|
if ( d > surf->cullinfo.plane.dist + 8 ) {
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
if (surf->cullinfo.type & CULLINFO_SPHERE)
|
|
{
|
|
int sphereCull;
|
|
|
|
if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) {
|
|
sphereCull = R_CullLocalPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius );
|
|
} else {
|
|
sphereCull = R_CullPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius );
|
|
}
|
|
|
|
if ( sphereCull == CULL_OUT )
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
if ( sphereCull == CULL_IN )
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
if (surf->cullinfo.type & CULLINFO_BOX)
|
|
{
|
|
int boxCull;
|
|
|
|
if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) {
|
|
boxCull = R_CullLocalBox( surf->cullinfo.bounds );
|
|
} else {
|
|
boxCull = R_CullBox( surf->cullinfo.bounds );
|
|
}
|
|
|
|
if ( boxCull == CULL_OUT )
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
if ( boxCull == CULL_IN )
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
R_DlightSurface
|
|
|
|
The given surface is going to be drawn, and it touches a leaf
|
|
that is touched by one or more dlights, so try to throw out
|
|
more dlights if possible.
|
|
====================
|
|
*/
|
|
static int R_DlightSurface( msurface_t *surf, int dlightBits ) {
|
|
float d;
|
|
int i;
|
|
dlight_t *dl;
|
|
|
|
if ( surf->cullinfo.type & CULLINFO_PLANE )
|
|
{
|
|
int i;
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
dl = &tr.refdef.dlights[i];
|
|
d = DotProduct( dl->origin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist;
|
|
if ( d < -dl->radius || d > dl->radius ) {
|
|
// dlight doesn't reach the plane
|
|
dlightBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( surf->cullinfo.type & CULLINFO_BOX )
|
|
{
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
dl = &tr.refdef.dlights[i];
|
|
if ( dl->origin[0] - dl->radius > surf->cullinfo.bounds[1][0]
|
|
|| dl->origin[0] + dl->radius < surf->cullinfo.bounds[0][0]
|
|
|| dl->origin[1] - dl->radius > surf->cullinfo.bounds[1][1]
|
|
|| dl->origin[1] + dl->radius < surf->cullinfo.bounds[0][1]
|
|
|| dl->origin[2] - dl->radius > surf->cullinfo.bounds[1][2]
|
|
|| dl->origin[2] + dl->radius < surf->cullinfo.bounds[0][2] ) {
|
|
// dlight doesn't reach the bounds
|
|
dlightBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( surf->cullinfo.type & CULLINFO_SPHERE )
|
|
{
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
dl = &tr.refdef.dlights[i];
|
|
if (!SpheresIntersect(dl->origin, dl->radius, surf->cullinfo.localOrigin, surf->cullinfo.radius))
|
|
{
|
|
// dlight doesn't reach the bounds
|
|
dlightBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( *surf->data == SF_FACE ) {
|
|
((srfSurfaceFace_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits;
|
|
} else if ( *surf->data == SF_GRID ) {
|
|
((srfGridMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits;
|
|
} else if ( *surf->data == SF_TRIANGLES ) {
|
|
((srfTriangles_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits;
|
|
} else if ( *surf->data == SF_VBO_MESH ) {
|
|
((srfVBOMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits;
|
|
} else {
|
|
dlightBits = 0;
|
|
}
|
|
|
|
if ( dlightBits ) {
|
|
tr.pc.c_dlightSurfaces++;
|
|
}
|
|
|
|
return dlightBits;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_PshadowSurface
|
|
|
|
Just like R_DlightSurface, cull any we can
|
|
====================
|
|
*/
|
|
static int R_PshadowSurface( msurface_t *surf, int pshadowBits ) {
|
|
float d;
|
|
int i;
|
|
pshadow_t *ps;
|
|
|
|
if ( surf->cullinfo.type & CULLINFO_PLANE )
|
|
{
|
|
int i;
|
|
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
|
|
if ( ! ( pshadowBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
ps = &tr.refdef.pshadows[i];
|
|
d = DotProduct( ps->lightOrigin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist;
|
|
if ( d < -ps->lightRadius || d > ps->lightRadius ) {
|
|
// pshadow doesn't reach the plane
|
|
pshadowBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( surf->cullinfo.type & CULLINFO_BOX )
|
|
{
|
|
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
|
|
if ( ! ( pshadowBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
ps = &tr.refdef.pshadows[i];
|
|
if ( ps->lightOrigin[0] - ps->lightRadius > surf->cullinfo.bounds[1][0]
|
|
|| ps->lightOrigin[0] + ps->lightRadius < surf->cullinfo.bounds[0][0]
|
|
|| ps->lightOrigin[1] - ps->lightRadius > surf->cullinfo.bounds[1][1]
|
|
|| ps->lightOrigin[1] + ps->lightRadius < surf->cullinfo.bounds[0][1]
|
|
|| ps->lightOrigin[2] - ps->lightRadius > surf->cullinfo.bounds[1][2]
|
|
|| ps->lightOrigin[2] + ps->lightRadius < surf->cullinfo.bounds[0][2]
|
|
|| BoxOnPlaneSide(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1], &ps->cullPlane) == 2 ) {
|
|
// pshadow doesn't reach the bounds
|
|
pshadowBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( surf->cullinfo.type & CULLINFO_SPHERE )
|
|
{
|
|
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
|
|
if ( ! ( pshadowBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
ps = &tr.refdef.pshadows[i];
|
|
if (!SpheresIntersect(ps->viewOrigin, ps->viewRadius, surf->cullinfo.localOrigin, surf->cullinfo.radius)
|
|
|| DotProduct( surf->cullinfo.localOrigin, ps->cullPlane.normal ) - ps->cullPlane.dist < -surf->cullinfo.radius)
|
|
{
|
|
// pshadow doesn't reach the bounds
|
|
pshadowBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( *surf->data == SF_FACE ) {
|
|
((srfSurfaceFace_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits;
|
|
} else if ( *surf->data == SF_GRID ) {
|
|
((srfGridMesh_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits;
|
|
} else if ( *surf->data == SF_TRIANGLES ) {
|
|
((srfTriangles_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits;
|
|
} else if ( *surf->data == SF_VBO_MESH ) {
|
|
((srfVBOMesh_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits;
|
|
} else {
|
|
pshadowBits = 0;
|
|
}
|
|
|
|
if ( pshadowBits ) {
|
|
//tr.pc.c_dlightSurfaces++;
|
|
}
|
|
|
|
return pshadowBits;
|
|
}
|
|
|
|
|
|
/*
|
|
======================
|
|
R_AddWorldSurface
|
|
======================
|
|
*/
|
|
static void R_AddWorldSurface( msurface_t *surf, int dlightBits, int pshadowBits ) {
|
|
// FIXME: bmodel fog?
|
|
|
|
// try to cull before dlighting or adding
|
|
if ( R_CullSurface( surf ) ) {
|
|
return;
|
|
}
|
|
|
|
// check for dlighting
|
|
if ( dlightBits ) {
|
|
dlightBits = R_DlightSurface( surf, dlightBits );
|
|
dlightBits = ( dlightBits != 0 );
|
|
}
|
|
|
|
// check for pshadows
|
|
/*if ( pshadowBits ) */{
|
|
pshadowBits = R_PshadowSurface( surf, pshadowBits);
|
|
pshadowBits = ( pshadowBits != 0 );
|
|
}
|
|
|
|
R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits, pshadowBits );
|
|
}
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
BRUSH MODELS
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
R_AddBrushModelSurfaces
|
|
=================
|
|
*/
|
|
void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) {
|
|
bmodel_t *bmodel;
|
|
int clip;
|
|
model_t *pModel;
|
|
int i;
|
|
|
|
pModel = R_GetModelByHandle( ent->e.hModel );
|
|
|
|
bmodel = pModel->bmodel;
|
|
|
|
clip = R_CullLocalBox( bmodel->bounds );
|
|
if ( clip == CULL_OUT ) {
|
|
return;
|
|
}
|
|
|
|
R_SetupEntityLighting( &tr.refdef, ent );
|
|
R_DlightBmodel( bmodel );
|
|
|
|
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
|
|
int surf = bmodel->firstSurface + i;
|
|
|
|
if (tr.world->surfacesViewCount[surf] != tr.viewCount)
|
|
{
|
|
tr.world->surfacesViewCount[surf] = tr.viewCount;
|
|
R_AddWorldSurface( tr.world->surfaces + surf, tr.currentEntity->needDlights, 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
WORLD MODEL
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
================
|
|
R_RecursiveWorldNode
|
|
================
|
|
*/
|
|
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits, int pshadowBits ) {
|
|
|
|
do {
|
|
int newDlights[2];
|
|
unsigned int newPShadows[2];
|
|
|
|
// if the node wasn't marked as potentially visible, exit
|
|
// pvs is skipped for depth shadows
|
|
if (!(tr.viewParms.flags & VPF_DEPTHSHADOW) && node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex]) {
|
|
return;
|
|
}
|
|
|
|
// if the bounding volume is outside the frustum, nothing
|
|
// inside can be visible OPTIMIZE: don't do this all the way to leafs?
|
|
|
|
if ( !r_nocull->integer ) {
|
|
int r;
|
|
|
|
if ( planeBits & 1 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~1; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 2 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~2; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 4 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~4; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 8 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~8; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 16 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[4]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~16; // all descendants will also be in front
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( node->contents != -1 ) {
|
|
break;
|
|
}
|
|
|
|
// node is just a decision point, so go down both sides
|
|
// since we don't care about sort orders, just go positive to negative
|
|
|
|
// determine which dlights are needed
|
|
newDlights[0] = 0;
|
|
newDlights[1] = 0;
|
|
if ( dlightBits ) {
|
|
int i;
|
|
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
dlight_t *dl;
|
|
float dist;
|
|
|
|
if ( dlightBits & ( 1 << i ) ) {
|
|
dl = &tr.refdef.dlights[i];
|
|
dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist;
|
|
|
|
if ( dist > -dl->radius ) {
|
|
newDlights[0] |= ( 1 << i );
|
|
}
|
|
if ( dist < dl->radius ) {
|
|
newDlights[1] |= ( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
newPShadows[0] = 0;
|
|
newPShadows[1] = 0;
|
|
if ( pshadowBits ) {
|
|
int i;
|
|
|
|
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
|
|
pshadow_t *shadow;
|
|
float dist;
|
|
|
|
if ( pshadowBits & ( 1 << i ) ) {
|
|
shadow = &tr.refdef.pshadows[i];
|
|
dist = DotProduct( shadow->lightOrigin, node->plane->normal ) - node->plane->dist;
|
|
|
|
if ( dist > -shadow->lightRadius ) {
|
|
newPShadows[0] |= ( 1 << i );
|
|
}
|
|
if ( dist < shadow->lightRadius ) {
|
|
newPShadows[1] |= ( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// recurse down the children, front side first
|
|
R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0], newPShadows[0] );
|
|
|
|
// tail recurse
|
|
node = node->children[1];
|
|
dlightBits = newDlights[1];
|
|
pshadowBits = newPShadows[1];
|
|
} while ( 1 );
|
|
|
|
{
|
|
// leaf node, so add mark surfaces
|
|
int c;
|
|
int surf, *view;
|
|
|
|
tr.pc.c_leafs++;
|
|
|
|
// add to z buffer bounds
|
|
if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) {
|
|
tr.viewParms.visBounds[0][0] = node->mins[0];
|
|
}
|
|
if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) {
|
|
tr.viewParms.visBounds[0][1] = node->mins[1];
|
|
}
|
|
if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) {
|
|
tr.viewParms.visBounds[0][2] = node->mins[2];
|
|
}
|
|
|
|
if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) {
|
|
tr.viewParms.visBounds[1][0] = node->maxs[0];
|
|
}
|
|
if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) {
|
|
tr.viewParms.visBounds[1][1] = node->maxs[1];
|
|
}
|
|
if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) {
|
|
tr.viewParms.visBounds[1][2] = node->maxs[2];
|
|
}
|
|
|
|
// add merged and unmerged surfaces
|
|
if (tr.world->viewSurfaces)
|
|
view = tr.world->viewSurfaces + node->firstmarksurface;
|
|
else
|
|
view = tr.world->marksurfaces + node->firstmarksurface;
|
|
|
|
c = node->nummarksurfaces;
|
|
while (c--) {
|
|
// just mark it as visible, so we don't jump out of the cache derefencing the surface
|
|
surf = *view;
|
|
if (surf < 0)
|
|
{
|
|
if (tr.world->mergedSurfacesViewCount[-surf - 1] != tr.viewCount)
|
|
{
|
|
tr.world->mergedSurfacesViewCount[-surf - 1] = tr.viewCount;
|
|
tr.world->mergedSurfacesDlightBits[-surf - 1] = dlightBits;
|
|
tr.world->mergedSurfacesPshadowBits[-surf - 1] = pshadowBits;
|
|
}
|
|
else
|
|
{
|
|
tr.world->mergedSurfacesDlightBits[-surf - 1] |= dlightBits;
|
|
tr.world->mergedSurfacesPshadowBits[-surf - 1] |= pshadowBits;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tr.world->surfacesViewCount[surf] != tr.viewCount)
|
|
{
|
|
tr.world->surfacesViewCount[surf] = tr.viewCount;
|
|
tr.world->surfacesDlightBits[surf] = dlightBits;
|
|
tr.world->surfacesPshadowBits[surf] = pshadowBits;
|
|
}
|
|
else
|
|
{
|
|
tr.world->surfacesDlightBits[surf] |= dlightBits;
|
|
tr.world->surfacesPshadowBits[surf] |= pshadowBits;
|
|
}
|
|
}
|
|
view++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_PointInLeaf
|
|
===============
|
|
*/
|
|
static mnode_t *R_PointInLeaf( const vec3_t p ) {
|
|
mnode_t *node;
|
|
float d;
|
|
cplane_t *plane;
|
|
|
|
if ( !tr.world ) {
|
|
ri.Error (ERR_DROP, "R_PointInLeaf: bad model");
|
|
}
|
|
|
|
node = tr.world->nodes;
|
|
while( 1 ) {
|
|
if (node->contents != -1) {
|
|
break;
|
|
}
|
|
plane = node->plane;
|
|
d = DotProduct (p,plane->normal) - plane->dist;
|
|
if (d > 0) {
|
|
node = node->children[0];
|
|
} else {
|
|
node = node->children[1];
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_ClusterPVS
|
|
==============
|
|
*/
|
|
static const byte *R_ClusterPVS (int cluster) {
|
|
if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) {
|
|
return tr.world->novis;
|
|
}
|
|
|
|
return tr.world->vis + cluster * tr.world->clusterBytes;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_inPVS
|
|
=================
|
|
*/
|
|
qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) {
|
|
mnode_t *leaf;
|
|
byte *vis;
|
|
|
|
leaf = R_PointInLeaf( p1 );
|
|
vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ??
|
|
leaf = R_PointInLeaf( p2 );
|
|
|
|
if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) {
|
|
return qfalse;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_MarkLeaves
|
|
|
|
Mark the leaves and nodes that are in the PVS for the current
|
|
cluster
|
|
===============
|
|
*/
|
|
static void R_MarkLeaves (void) {
|
|
const byte *vis;
|
|
mnode_t *leaf, *parent;
|
|
int i;
|
|
int cluster;
|
|
|
|
// lockpvs lets designers walk around to determine the
|
|
// extent of the current pvs
|
|
if ( r_lockpvs->integer ) {
|
|
return;
|
|
}
|
|
|
|
// current viewcluster
|
|
leaf = R_PointInLeaf( tr.viewParms.pvsOrigin );
|
|
cluster = leaf->cluster;
|
|
|
|
// if the cluster is the same and the area visibility matrix
|
|
// hasn't changed, we don't need to mark everything again
|
|
|
|
for(i = 0; i < MAX_VISCOUNTS; i++)
|
|
{
|
|
if(tr.visClusters[i] == cluster)
|
|
{
|
|
//tr.visIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if r_showcluster was just turned on, remark everything
|
|
if(i != MAX_VISCOUNTS && !tr.refdef.areamaskModified && !r_showcluster->modified)// && !r_dynamicBspOcclusionCulling->modified)
|
|
{
|
|
if(tr.visClusters[i] != tr.visClusters[tr.visIndex] && r_showcluster->integer)
|
|
{
|
|
ri.Printf(PRINT_ALL, "found cluster:%i area:%i index:%i\n", cluster, leaf->area, i);
|
|
}
|
|
tr.visIndex = i;
|
|
return;
|
|
}
|
|
|
|
// if the areamask was modified, invalidate all visclusters
|
|
// this caused doors to open into undrawn areas
|
|
if (tr.refdef.areamaskModified)
|
|
{
|
|
memset(tr.visClusters, -2, sizeof(tr.visClusters));
|
|
}
|
|
|
|
tr.visIndex = (tr.visIndex + 1) % MAX_VISCOUNTS;
|
|
tr.visCounts[tr.visIndex]++;
|
|
tr.visClusters[tr.visIndex] = cluster;
|
|
|
|
if ( r_showcluster->modified || r_showcluster->integer ) {
|
|
r_showcluster->modified = qfalse;
|
|
if ( r_showcluster->integer ) {
|
|
ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area );
|
|
}
|
|
}
|
|
|
|
// set all nodes to visible if there is no vis
|
|
// this caused some levels to simply not render
|
|
if (r_novis->integer || !tr.world->vis || tr.visClusters[tr.visIndex] == -1) {
|
|
for (i=0 ; i<tr.world->numnodes ; i++) {
|
|
if (tr.world->nodes[i].contents != CONTENTS_SOLID) {
|
|
tr.world->nodes[i].visCounts[tr.visIndex] = tr.visCounts[tr.visIndex];
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
vis = R_ClusterPVS(tr.visClusters[tr.visIndex]);
|
|
|
|
for (i=0,leaf=tr.world->nodes ; i<tr.world->numnodes ; i++, leaf++) {
|
|
cluster = leaf->cluster;
|
|
if ( cluster < 0 || cluster >= tr.world->numClusters ) {
|
|
continue;
|
|
}
|
|
|
|
// check general pvs
|
|
if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) {
|
|
continue;
|
|
}
|
|
|
|
// check for door connection
|
|
if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) {
|
|
continue; // not visible
|
|
}
|
|
|
|
parent = leaf;
|
|
do {
|
|
if(parent->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex])
|
|
break;
|
|
parent->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex];
|
|
parent = parent->parent;
|
|
} while (parent);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
R_AddWorldSurfaces
|
|
=============
|
|
*/
|
|
void R_AddWorldSurfaces (void) {
|
|
if ( !r_drawworld->integer ) {
|
|
return;
|
|
}
|
|
|
|
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
|
return;
|
|
}
|
|
|
|
tr.currentEntityNum = REFENTITYNUM_WORLD;
|
|
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
|
|
|
|
// determine which leaves are in the PVS / areamask
|
|
if (!(tr.viewParms.flags & VPF_DEPTHSHADOW))
|
|
R_MarkLeaves ();
|
|
|
|
// clear out the visible min/max
|
|
ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] );
|
|
|
|
// perform frustum culling and flag all the potentially visible surfaces
|
|
if ( tr.refdef.num_dlights > 32 ) {
|
|
tr.refdef.num_dlights = 32 ;
|
|
}
|
|
|
|
if ( tr.refdef.num_pshadows > 32 ) {
|
|
tr.refdef.num_pshadows = 32 ;
|
|
}
|
|
|
|
if ( tr.viewParms.flags & VPF_DEPTHSHADOW )
|
|
{
|
|
R_RecursiveWorldNode( tr.world->nodes, 31, 0, 0);
|
|
}
|
|
else if ( !(tr.viewParms.flags & VPF_SHADOWMAP) )
|
|
{
|
|
R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1, ( 1 << tr.refdef.num_pshadows ) - 1 );
|
|
}
|
|
else
|
|
{
|
|
R_RecursiveWorldNode( tr.world->nodes, 31, ( 1 << tr.refdef.num_dlights ) - 1, 0 );
|
|
}
|
|
|
|
// now add all the potentially visible surfaces
|
|
// also mask invisible dlights for next frame
|
|
{
|
|
int i;
|
|
|
|
tr.refdef.dlightMask = 0;
|
|
|
|
for (i = 0; i < tr.world->numWorldSurfaces; i++)
|
|
{
|
|
if (tr.world->surfacesViewCount[i] != tr.viewCount)
|
|
continue;
|
|
|
|
R_AddWorldSurface( tr.world->surfaces + i, tr.world->surfacesDlightBits[i], tr.world->surfacesPshadowBits[i] );
|
|
tr.refdef.dlightMask |= tr.world->surfacesDlightBits[i];
|
|
}
|
|
for (i = 0; i < tr.world->numMergedSurfaces; i++)
|
|
{
|
|
if (tr.world->mergedSurfacesViewCount[i] != tr.viewCount)
|
|
continue;
|
|
|
|
R_AddWorldSurface( tr.world->mergedSurfaces + i, tr.world->mergedSurfacesDlightBits[i], tr.world->mergedSurfacesPshadowBits[i] );
|
|
tr.refdef.dlightMask |= tr.world->mergedSurfacesDlightBits[i];
|
|
}
|
|
|
|
tr.refdef.dlightMask = ~tr.refdef.dlightMask;
|
|
}
|
|
}
|