mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-29 14:17:57 +03:00
2535 lines
57 KiB
C++
2535 lines
57 KiB
C++
![]() |
/*
|
||
|
===========================================================================
|
||
|
Copyright (C) 1999-2005 Id Software, Inc.
|
||
|
Copyright (C) 2006-2008 Robert Beckebans <trebor_7@users.sourceforge.net>
|
||
|
|
||
|
This file is part of XreaL source code.
|
||
|
|
||
|
XreaL 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.
|
||
|
|
||
|
XreaL 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 XreaL source code; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
===========================================================================
|
||
|
*/
|
||
|
// tr_world.c
|
||
|
|
||
|
#include "tr_local.h"
|
||
|
#include "gl_shader.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
R_CullTriSurf
|
||
|
|
||
|
Returns true if the grid is completely culled away.
|
||
|
Also sets the clipped hint bit in tess
|
||
|
=================
|
||
|
*/
|
||
|
/*
|
||
|
static qboolean R_CullTriSurf(srfTriangles_t * cv)
|
||
|
{
|
||
|
int boxCull;
|
||
|
|
||
|
boxCull = R_CullLocalBox(cv->bounds);
|
||
|
|
||
|
if(boxCull == CULL_OUT)
|
||
|
{
|
||
|
return qtrue;
|
||
|
}
|
||
|
return qfalse;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
R_CullGrid
|
||
|
|
||
|
Returns true if the grid is completely culled away.
|
||
|
Also sets the clipped hint bit in tess
|
||
|
=================
|
||
|
*/
|
||
|
/*
|
||
|
static qboolean R_CullGrid(srfGridMesh_t * cv)
|
||
|
{
|
||
|
int boxCull;
|
||
|
int sphereCull;
|
||
|
|
||
|
if(r_nocurves->integer)
|
||
|
{
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
if(tr.currentEntity != &tr.worldEntity)
|
||
|
{
|
||
|
sphereCull = R_CullLocalPointAndRadius(cv->origin, cv->radius);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sphereCull = R_CullPointAndRadius(cv->origin, cv->radius);
|
||
|
}
|
||
|
boxCull = CULL_OUT;
|
||
|
|
||
|
// check for trivial reject
|
||
|
if(sphereCull == CULL_OUT)
|
||
|
{
|
||
|
tr.pc.c_sphere_cull_patch_out++;
|
||
|
return qtrue;
|
||
|
}
|
||
|
// check bounding box if necessary
|
||
|
else if(sphereCull == CULL_CLIP)
|
||
|
{
|
||
|
tr.pc.c_sphere_cull_patch_clip++;
|
||
|
|
||
|
boxCull = R_CullLocalBox(cv->bounds);
|
||
|
|
||
|
if(boxCull == CULL_OUT)
|
||
|
{
|
||
|
tr.pc.c_box_cull_patch_out++;
|
||
|
return qtrue;
|
||
|
}
|
||
|
else if(boxCull == CULL_IN)
|
||
|
{
|
||
|
tr.pc.c_box_cull_patch_in++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tr.pc.c_box_cull_patch_clip++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tr.pc.c_sphere_cull_patch_in++;
|
||
|
}
|
||
|
|
||
|
return qfalse;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_CullSurface
|
||
|
|
||
|
Tries to back face cull surfaces before they are lighted or
|
||
|
added to the sorting list.
|
||
|
|
||
|
This will also allow mirrors on both sides of a model without recursion.
|
||
|
================
|
||
|
*/
|
||
|
static qboolean R_CullSurface(surfaceType_t * surface, shader_t * shader, int *frontFace)
|
||
|
{
|
||
|
srfGeneric_t *gen;
|
||
|
int cull;
|
||
|
float d;
|
||
|
|
||
|
|
||
|
// force to non-front facing
|
||
|
*frontFace = 0;
|
||
|
|
||
|
// allow culling to be disabled
|
||
|
if(r_nocull->integer)
|
||
|
{
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
// ydnar: made surface culling generic, inline with q3map2 surface classification
|
||
|
switch (*surface)
|
||
|
{
|
||
|
case SF_FACE:
|
||
|
case SF_TRIANGLES:
|
||
|
break;
|
||
|
case SF_GRID:
|
||
|
if(r_nocurves->integer)
|
||
|
{
|
||
|
return qtrue;
|
||
|
}
|
||
|
break;
|
||
|
/*
|
||
|
case SF_FOLIAGE:
|
||
|
if(!r_drawfoliage->value)
|
||
|
{
|
||
|
return qtrue;
|
||
|
}
|
||
|
break;
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
// get generic surface
|
||
|
gen = (srfGeneric_t *) surface;
|
||
|
|
||
|
// plane cull
|
||
|
if(gen->plane.type != PLANE_NON_PLANAR && r_facePlaneCull->integer)
|
||
|
{
|
||
|
d = DotProduct(tr.orientation.viewOrigin, gen->plane.normal) - gen->plane.dist;
|
||
|
if(d > 0.0f)
|
||
|
{
|
||
|
*frontFace = 1;
|
||
|
}
|
||
|
|
||
|
// 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(shader->cullType == CT_FRONT_SIDED)
|
||
|
{
|
||
|
if(d < -8.0f)
|
||
|
{
|
||
|
tr.pc.c_plane_cull_out++;
|
||
|
return qtrue;
|
||
|
}
|
||
|
}
|
||
|
else if(shader->cullType == CT_BACK_SIDED)
|
||
|
{
|
||
|
if(d > 8.0f)
|
||
|
{
|
||
|
tr.pc.c_plane_cull_out++;
|
||
|
return qtrue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tr.pc.c_plane_cull_in++;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// try sphere cull
|
||
|
if(tr.currentEntity != &tr.worldEntity)
|
||
|
{
|
||
|
cull = R_CullLocalPointAndRadius(gen->origin, gen->radius);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cull = R_CullPointAndRadius(gen->origin, gen->radius);
|
||
|
}
|
||
|
if(cull == CULL_OUT)
|
||
|
{
|
||
|
tr.pc.c_sphere_cull_out++;
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
tr.pc.c_sphere_cull_in++;
|
||
|
}
|
||
|
|
||
|
// must be visible
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
static qboolean R_LightSurfaceGeneric(srfGeneric_t * face, trRefLight_t * light, byte * cubeSideBits)
|
||
|
{
|
||
|
// do a quick AABB cull
|
||
|
if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], face->bounds[0], face->bounds[1]))
|
||
|
{
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
// do a more expensive and precise light frustum cull
|
||
|
if(!r_noLightFrustums->integer)
|
||
|
{
|
||
|
if(R_CullLightWorldBounds(light, face->bounds) == CULL_OUT)
|
||
|
{
|
||
|
return qfalse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(r_cullShadowPyramidFaces->integer)
|
||
|
{
|
||
|
*cubeSideBits = R_CalcLightCubeSideBits(light, face->bounds);
|
||
|
}
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
R_AddInteractionSurface
|
||
|
======================
|
||
|
*/
|
||
|
static void R_AddInteractionSurface(bspSurface_t * surf, trRefLight_t * light)
|
||
|
{
|
||
|
qboolean intersects;
|
||
|
interactionType_t iaType = IA_DEFAULT;
|
||
|
byte cubeSideBits = CUBESIDE_CLIPALL;
|
||
|
|
||
|
// Tr3B - this surface is maybe not in this view but it may still cast a shadow
|
||
|
// into this view
|
||
|
if(surf->viewCount != tr.viewCountNoReset)
|
||
|
{
|
||
|
if(r_shadows->integer <= SHADOWING_BLOB || light->l.noShadows)
|
||
|
return;
|
||
|
else
|
||
|
iaType = IA_SHADOWONLY;
|
||
|
}
|
||
|
|
||
|
if(surf->lightCount == tr.lightCount)
|
||
|
{
|
||
|
// already checked this surface
|
||
|
return;
|
||
|
}
|
||
|
surf->lightCount = tr.lightCount;
|
||
|
|
||
|
// skip all surfaces that don't matter for lighting only pass
|
||
|
if(surf->shader->isSky || (!surf->shader->interactLight && surf->shader->noShadows))
|
||
|
return;
|
||
|
|
||
|
switch (*surf->data)
|
||
|
{
|
||
|
case SF_FACE:
|
||
|
case SF_GRID:
|
||
|
case SF_TRIANGLES:
|
||
|
intersects = R_LightSurfaceGeneric((srfGeneric_t *) surf->data, light, &cubeSideBits);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
intersects = qfalse;
|
||
|
};
|
||
|
|
||
|
if(intersects)
|
||
|
{
|
||
|
R_AddLightInteraction(light, surf->data, surf->shader, cubeSideBits, iaType);
|
||
|
|
||
|
if(light->isStatic)
|
||
|
tr.pc.c_slightSurfaces++;
|
||
|
else
|
||
|
tr.pc.c_dlightSurfaces++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(!light->isStatic)
|
||
|
tr.pc.c_dlightSurfacesCulled++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
R_AddWorldSurface
|
||
|
======================
|
||
|
*/
|
||
|
static void R_AddWorldSurface(bspSurface_t * surf, int decalBits)
|
||
|
{
|
||
|
int i, frontFace;
|
||
|
shader_t *shader;
|
||
|
|
||
|
if(surf->viewCount == tr.viewCountNoReset)
|
||
|
return;
|
||
|
surf->viewCount = tr.viewCountNoReset;
|
||
|
|
||
|
shader = surf->shader;
|
||
|
|
||
|
// add decals
|
||
|
if(decalBits)
|
||
|
{
|
||
|
// ydnar: project any decals
|
||
|
for(i = 0; i < tr.refdef.numDecalProjectors; i++)
|
||
|
{
|
||
|
if(decalBits & (1 << i))
|
||
|
{
|
||
|
R_ProjectDecalOntoSurface(&tr.refdef.decalProjectors[i], surf, &tr.world->models[0]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined(USE_BSP_CLUSTERSURFACE_MERGING)
|
||
|
if(r_mergeClusterSurfaces->integer &&
|
||
|
!r_dynamicBspOcclusionCulling->integer &&
|
||
|
((r_mergeClusterFaces->integer && *surf->data == SF_FACE) ||
|
||
|
(r_mergeClusterCurves->integer && *surf->data == SF_GRID) ||
|
||
|
(r_mergeClusterTriangles->integer && *surf->data == SF_TRIANGLES)) &&
|
||
|
!shader->isSky && !shader->isPortal && !ShaderRequiresCPUDeforms(shader))
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
// try to cull before lighting or adding
|
||
|
if(R_CullSurface(surf->data, surf->shader, &frontFace))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
R_AddDrawSurf(surf->data, surf->shader, surf->lightmapNum, surf->fogIndex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============================================================
|
||
|
|
||
|
BRUSH MODELS
|
||
|
|
||
|
=============================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
R_AddBrushModelSurface
|
||
|
======================
|
||
|
*/
|
||
|
static void R_AddBrushModelSurface(bspSurface_t * surf, int fogIndex)
|
||
|
{
|
||
|
int frontFace;
|
||
|
|
||
|
if(surf->viewCount == tr.viewCountNoReset)
|
||
|
{
|
||
|
return; // already in this view
|
||
|
}
|
||
|
surf->viewCount = tr.viewCountNoReset;
|
||
|
|
||
|
// try to cull before lighting or adding
|
||
|
if(R_CullSurface(surf->data, surf->shader, &frontFace))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
R_AddDrawSurf(surf->data, surf->shader, surf->lightmapNum, fogIndex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
R_AddBSPModelSurfaces
|
||
|
=================
|
||
|
*/
|
||
|
void R_AddBSPModelSurfaces(trRefEntity_t * ent)
|
||
|
{
|
||
|
bspModel_t *bspModel;
|
||
|
model_t *pModel;
|
||
|
int i;
|
||
|
vec3_t v;
|
||
|
vec3_t transformed;
|
||
|
vec3_t boundsCenter;
|
||
|
// float boundsRadius;
|
||
|
int fogNum;
|
||
|
|
||
|
pModel = R_GetModelByHandle(ent->e.hModel);
|
||
|
bspModel = pModel->bsp;
|
||
|
|
||
|
// copy local bounds
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
ent->localBounds[0][i] = bspModel->bounds[0][i];
|
||
|
ent->localBounds[1][i] = bspModel->bounds[1][i];
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
boundsRadius = RadiusFromBounds(bspModel->bounds[0], bspModel->bounds[1]);
|
||
|
ent->cull = R_CullPointAndRadius(ent->e.origin, boundsRadius);
|
||
|
#else
|
||
|
ent->cull = R_CullLocalBox(bspModel->bounds);
|
||
|
if(ent->cull == CULL_OUT)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// setup world bounds for intersection tests
|
||
|
ClearBounds(ent->worldBounds[0], ent->worldBounds[1]);
|
||
|
|
||
|
for(i = 0; i < 8; i++)
|
||
|
{
|
||
|
v[0] = ent->localBounds[i & 1][0];
|
||
|
v[1] = ent->localBounds[(i >> 1) & 1][1];
|
||
|
v[2] = ent->localBounds[(i >> 2) & 1][2];
|
||
|
|
||
|
// transform local bounds vertices into world space
|
||
|
R_LocalPointToWorld(v, transformed);
|
||
|
|
||
|
AddPointToBounds(transformed, ent->worldBounds[0], ent->worldBounds[1]);
|
||
|
}
|
||
|
|
||
|
VectorAdd(ent->worldBounds[0], ent->worldBounds[1], boundsCenter);
|
||
|
VectorScale(boundsCenter, 0.5f, boundsCenter);
|
||
|
|
||
|
// Tr3B: BSP inline models should always use vertex lighting
|
||
|
R_SetupEntityLighting(&tr.refdef, ent, boundsCenter);
|
||
|
|
||
|
fogNum = R_FogWorldBox(ent->worldBounds);
|
||
|
|
||
|
if(r_vboModels->integer && bspModel->numVBOSurfaces)
|
||
|
{
|
||
|
int i;
|
||
|
srfVBOMesh_t *vboSurface;
|
||
|
|
||
|
for(i = 0; i < bspModel->numVBOSurfaces; i++)
|
||
|
{
|
||
|
vboSurface = bspModel->vboSurfaces[i];
|
||
|
|
||
|
R_AddDrawSurf((surfaceType_t *) vboSurface, vboSurface->shader, vboSurface->lightmapNum, fogNum);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for(i = 0; i < bspModel->numSurfaces; i++)
|
||
|
{
|
||
|
R_AddBrushModelSurface(bspModel->firstSurface + i, fogNum);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
=============================================================
|
||
|
|
||
|
WORLD MODEL
|
||
|
|
||
|
=============================================================
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
static void R_AddLeafSurfaces(bspNode_t * node, int decalBits)
|
||
|
{
|
||
|
int c;
|
||
|
bspSurface_t *surf, **mark;
|
||
|
|
||
|
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 the individual surfaces
|
||
|
mark = node->markSurfaces;
|
||
|
c = node->numMarkSurfaces;
|
||
|
while(c--)
|
||
|
{
|
||
|
// the surface may have already been added if it
|
||
|
// spans multiple leafs
|
||
|
surf = *mark;
|
||
|
R_AddWorldSurface(surf, decalBits);
|
||
|
mark++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_RecursiveWorldNode
|
||
|
================
|
||
|
*/
|
||
|
static void R_RecursiveWorldNode(bspNode_t * node, int planeBits, int decalBits)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
// if the node wasn't marked as potentially visible, exit
|
||
|
if(node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex])
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(node->contents != -1 && !node->numMarkSurfaces)
|
||
|
{
|
||
|
// don't waste time dealing with this empty leaf
|
||
|
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 i;
|
||
|
int r;
|
||
|
|
||
|
for(i = 0; i < FRUSTUM_PLANES; i++)
|
||
|
{
|
||
|
if(planeBits & (1 << i))
|
||
|
{
|
||
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustums[0][i]);
|
||
|
if(r == 2)
|
||
|
{
|
||
|
return; // culled
|
||
|
}
|
||
|
if(r == 1)
|
||
|
{
|
||
|
planeBits &= ~(1 << i); // all descendants will also be in front
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
InsertLink(&node->visChain, &tr.traversalStack);
|
||
|
|
||
|
// ydnar: cull decals
|
||
|
if(decalBits)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < tr.refdef.numDecalProjectors; i++)
|
||
|
{
|
||
|
if(decalBits & (1 << i))
|
||
|
{
|
||
|
// test decal bounds against node surface bounds
|
||
|
if(tr.refdef.decalProjectors[i].shader == NULL ||
|
||
|
!R_TestDecalBoundingBox(&tr.refdef.decalProjectors[i], node->surfMins, node->surfMaxs))
|
||
|
{
|
||
|
decalBits &= ~(1 << i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// recurse down the children, front side first
|
||
|
R_RecursiveWorldNode(node->children[0], planeBits, decalBits);
|
||
|
|
||
|
// tail recurse
|
||
|
node = node->children[1];
|
||
|
} while(1);
|
||
|
|
||
|
if(node->numMarkSurfaces)
|
||
|
{
|
||
|
// ydnar: moved off to separate function
|
||
|
R_AddLeafSurfaces(node, decalBits);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_RecursiveInteractionNode
|
||
|
================
|
||
|
*/
|
||
|
static void R_RecursiveInteractionNode(bspNode_t * node, trRefLight_t * light, int planeBits)
|
||
|
{
|
||
|
int i;
|
||
|
int r;
|
||
|
|
||
|
// if the node wasn't marked as potentially visible, exit
|
||
|
if(node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex])
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// light already hit node
|
||
|
if(node->lightCount == tr.lightCount)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
node->lightCount = tr.lightCount;
|
||
|
|
||
|
// if the bounding volume is outside the frustum, nothing
|
||
|
// inside can be visible OPTIMIZE: don't do this all the way to leafs?
|
||
|
|
||
|
// Tr3B - even surfaces that belong to nodes that are outside of the view frustum
|
||
|
// can cast shadows into the view frustum
|
||
|
if(!r_nocull->integer && r_shadows->integer <= SHADOWING_BLOB)
|
||
|
{
|
||
|
for(i = 0; i < FRUSTUM_PLANES; i++)
|
||
|
{
|
||
|
if(planeBits & (1 << i))
|
||
|
{
|
||
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustums[0][i]);
|
||
|
|
||
|
if(r == 2)
|
||
|
{
|
||
|
return; // culled
|
||
|
}
|
||
|
|
||
|
if(r == 1)
|
||
|
{
|
||
|
planeBits &= ~(1 << i); // all descendants will also be in front
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
// leaf node, so add mark surfaces
|
||
|
int c;
|
||
|
bspSurface_t *surf, **mark;
|
||
|
|
||
|
// add the individual surfaces
|
||
|
mark = node->markSurfaces;
|
||
|
c = node->numMarkSurfaces;
|
||
|
while(c--)
|
||
|
{
|
||
|
// the surface may have already been added if it
|
||
|
// spans multiple leafs
|
||
|
surf = *mark;
|
||
|
R_AddInteractionSurface(surf, light);
|
||
|
mark++;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// node is just a decision point, so go down both sides
|
||
|
// since we don't care about sort orders, just go positive to negative
|
||
|
r = BoxOnPlaneSide(light->worldBounds[0], light->worldBounds[1], node->plane);
|
||
|
|
||
|
switch (r)
|
||
|
{
|
||
|
case 1:
|
||
|
R_RecursiveInteractionNode(node->children[0], light, planeBits);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
R_RecursiveInteractionNode(node->children[1], light, planeBits);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
default:
|
||
|
// recurse down the children, front side first
|
||
|
R_RecursiveInteractionNode(node->children[0], light, planeBits);
|
||
|
R_RecursiveInteractionNode(node->children[1], light, planeBits);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
R_PointInLeaf
|
||
|
===============
|
||
|
*/
|
||
|
static bspNode_t *R_PointInLeaf(const vec3_t p)
|
||
|
{
|
||
|
bspNode_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)
|
||
|
{
|
||
|
bspNode_t *leaf;
|
||
|
const byte *vis;
|
||
|
|
||
|
leaf = R_PointInLeaf(p1);
|
||
|
vis = R_ClusterPVS(leaf->cluster);
|
||
|
leaf = R_PointInLeaf(p2);
|
||
|
|
||
|
if(!(vis[leaf->cluster >> 3] & (1 << (leaf->cluster & 7))))
|
||
|
{
|
||
|
return qfalse;
|
||
|
}
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
BSPSurfaceCompare
|
||
|
compare function for qsort()
|
||
|
=================
|
||
|
*/
|
||
|
static int BSPSurfaceCompare(const void *a, const void *b)
|
||
|
{
|
||
|
bspSurface_t *aa, *bb;
|
||
|
|
||
|
aa = *(bspSurface_t **) a;
|
||
|
bb = *(bspSurface_t **) b;
|
||
|
|
||
|
// shader first
|
||
|
if(aa->shader < bb->shader)
|
||
|
return -1;
|
||
|
|
||
|
else if(aa->shader > bb->shader)
|
||
|
return 1;
|
||
|
|
||
|
// by lightmap
|
||
|
if(aa->lightmapNum < bb->lightmapNum)
|
||
|
return -1;
|
||
|
|
||
|
else if(aa->lightmapNum > bb->lightmapNum)
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
R_UpdateClusterSurfaces()
|
||
|
===============
|
||
|
*/
|
||
|
#if defined(USE_BSP_CLUSTERSURFACE_MERGING)
|
||
|
static void R_UpdateClusterSurfaces()
|
||
|
{
|
||
|
int i, k, l;
|
||
|
|
||
|
int numVerts;
|
||
|
int numTriangles;
|
||
|
|
||
|
// static glIndex_t indexes[MAX_MAP_DRAW_INDEXES];
|
||
|
// static byte indexes[MAX_MAP_DRAW_INDEXES * sizeof(glIndex_t)];
|
||
|
glIndex_t *indexes;
|
||
|
int indexesSize;
|
||
|
|
||
|
shader_t *shader, *oldShader;
|
||
|
int lightmapNum, oldLightmapNum;
|
||
|
|
||
|
int numSurfaces;
|
||
|
bspSurface_t *surface, *surface2;
|
||
|
bspSurface_t **surfacesSorted;
|
||
|
|
||
|
bspCluster_t *cluster;
|
||
|
|
||
|
srfVBOMesh_t *vboSurf;
|
||
|
IBO_t *ibo;
|
||
|
|
||
|
vec3_t bounds[2];
|
||
|
|
||
|
if(tr.visClusters[tr.visIndex] < 0 || tr.visClusters[tr.visIndex] >= tr.world->numClusters)
|
||
|
{
|
||
|
// Tr3B: this is not a bug, the super cluster is the last one in the array
|
||
|
cluster = &tr.world->clusters[tr.world->numClusters];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cluster = &tr.world->clusters[tr.visClusters[tr.visIndex]];
|
||
|
}
|
||
|
|
||
|
tr.world->numClusterVBOSurfaces[tr.visIndex] = 0;
|
||
|
|
||
|
// count number of static cluster surfaces
|
||
|
numSurfaces = 0;
|
||
|
for(k = 0; k < cluster->numMarkSurfaces; k++)
|
||
|
{
|
||
|
surface = cluster->markSurfaces[k];
|
||
|
shader = surface->shader;
|
||
|
|
||
|
if(shader->isSky)
|
||
|
continue;
|
||
|
|
||
|
if(shader->isPortal)
|
||
|
continue;
|
||
|
|
||
|
if(ShaderRequiresCPUDeforms(shader))
|
||
|
continue;
|
||
|
|
||
|
numSurfaces++;
|
||
|
}
|
||
|
|
||
|
if(!numSurfaces)
|
||
|
return;
|
||
|
|
||
|
// build interaction caches list
|
||
|
surfacesSorted = (bspSurface_t **) ri.Malloc(numSurfaces * sizeof(surfacesSorted[0]));
|
||
|
|
||
|
numSurfaces = 0;
|
||
|
for(k = 0; k < cluster->numMarkSurfaces; k++)
|
||
|
{
|
||
|
surface = cluster->markSurfaces[k];
|
||
|
shader = surface->shader;
|
||
|
|
||
|
if(shader->isSky)
|
||
|
continue;
|
||
|
|
||
|
if(shader->isPortal)
|
||
|
continue;
|
||
|
|
||
|
if(ShaderRequiresCPUDeforms(shader))
|
||
|
continue;
|
||
|
|
||
|
surfacesSorted[numSurfaces] = surface;
|
||
|
numSurfaces++;
|
||
|
}
|
||
|
|
||
|
// sort surfaces by shader
|
||
|
qsort(surfacesSorted, numSurfaces, sizeof(surfacesSorted), BSPSurfaceCompare);
|
||
|
|
||
|
shader = oldShader = NULL;
|
||
|
lightmapNum = oldLightmapNum = -1;
|
||
|
|
||
|
for(k = 0; k < numSurfaces; k++)
|
||
|
{
|
||
|
surface = surfacesSorted[k];
|
||
|
shader = surface->shader;
|
||
|
lightmapNum = surface->lightmapNum;
|
||
|
|
||
|
if(shader != oldShader || (r_precomputedLighting->integer ? lightmapNum != oldLightmapNum : 0))
|
||
|
{
|
||
|
oldShader = shader;
|
||
|
oldLightmapNum = lightmapNum;
|
||
|
|
||
|
// count vertices and indices
|
||
|
numVerts = 0;
|
||
|
numTriangles = 0;
|
||
|
|
||
|
for(l = k; l < numSurfaces; l++)
|
||
|
{
|
||
|
surface2 = surfacesSorted[l];
|
||
|
|
||
|
if(surface2->shader != shader || surface2->lightmapNum != lightmapNum)
|
||
|
continue;
|
||
|
|
||
|
if(*surface2->data == SF_FACE)
|
||
|
{
|
||
|
srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface2->data;
|
||
|
|
||
|
if(!r_mergeClusterFaces->integer)
|
||
|
continue;
|
||
|
|
||
|
if(face->numVerts)
|
||
|
numVerts += face->numVerts;
|
||
|
|
||
|
if(face->numTriangles)
|
||
|
numTriangles += face->numTriangles;
|
||
|
}
|
||
|
else if(*surface2->data == SF_GRID)
|
||
|
{
|
||
|
srfGridMesh_t *grid = (srfGridMesh_t *) surface2->data;
|
||
|
|
||
|
if(!r_mergeClusterCurves->integer)
|
||
|
continue;
|
||
|
|
||
|
if(grid->numVerts)
|
||
|
numVerts += grid->numVerts;
|
||
|
|
||
|
if(grid->numTriangles)
|
||
|
numTriangles += grid->numTriangles;
|
||
|
}
|
||
|
else if(*surface2->data == SF_TRIANGLES)
|
||
|
{
|
||
|
srfTriangles_t *tri = (srfTriangles_t *) surface2->data;
|
||
|
|
||
|
if(!r_mergeClusterTriangles->integer)
|
||
|
continue;
|
||
|
|
||
|
if(tri->numVerts)
|
||
|
numVerts += tri->numVerts;
|
||
|
|
||
|
if(tri->numTriangles)
|
||
|
numTriangles += tri->numTriangles;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!numVerts || !numTriangles)
|
||
|
continue;
|
||
|
|
||
|
ClearBounds(bounds[0], bounds[1]);
|
||
|
|
||
|
// build triangle indices
|
||
|
indexesSize = numTriangles * 3 * sizeof(glIndex_t);
|
||
|
indexes = (glIndex_t *) ri.Malloc(indexesSize);
|
||
|
|
||
|
numTriangles = 0;
|
||
|
for(l = k; l < numSurfaces; l++)
|
||
|
{
|
||
|
surface2 = surfacesSorted[l];
|
||
|
|
||
|
if(surface2->shader != shader || surface2->lightmapNum != lightmapNum)
|
||
|
continue;
|
||
|
|
||
|
// set up triangle indices
|
||
|
if(*surface2->data == SF_FACE)
|
||
|
{
|
||
|
srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface2->data;
|
||
|
|
||
|
if(!r_mergeClusterFaces->integer)
|
||
|
continue;
|
||
|
|
||
|
if(srf->numTriangles)
|
||
|
{
|
||
|
srfTriangle_t *tri;
|
||
|
|
||
|
for(i = 0, tri = tr.world->triangles + srf->firstTriangle; i < srf->numTriangles; i++, tri++)
|
||
|
{
|
||
|
indexes[numTriangles * 3 + i * 3 + 0] = tri->indexes[0];
|
||
|
indexes[numTriangles * 3 + i * 3 + 1] = tri->indexes[1];
|
||
|
indexes[numTriangles * 3 + i * 3 + 2] = tri->indexes[2];
|
||
|
}
|
||
|
|
||
|
numTriangles += srf->numTriangles;
|
||
|
BoundsAdd(bounds[0], bounds[1], srf->bounds[0], srf->bounds[1]);
|
||
|
}
|
||
|
}
|
||
|
else if(*surface2->data == SF_GRID)
|
||
|
{
|
||
|
srfGridMesh_t *srf = (srfGridMesh_t *) surface2->data;
|
||
|
|
||
|
if(!r_mergeClusterCurves->integer)
|
||
|
continue;
|
||
|
|
||
|
if(srf->numTriangles)
|
||
|
{
|
||
|
srfTriangle_t *tri;
|
||
|
|
||
|
for(i = 0, tri = tr.world->triangles + srf->firstTriangle; i < srf->numTriangles; i++, tri++)
|
||
|
{
|
||
|
indexes[numTriangles * 3 + i * 3 + 0] = tri->indexes[0];
|
||
|
indexes[numTriangles * 3 + i * 3 + 1] = tri->indexes[1];
|
||
|
indexes[numTriangles * 3 + i * 3 + 2] = tri->indexes[2];
|
||
|
}
|
||
|
|
||
|
numTriangles += srf->numTriangles;
|
||
|
BoundsAdd(bounds[0], bounds[1], srf->bounds[0], srf->bounds[1]);
|
||
|
}
|
||
|
}
|
||
|
else if(*surface2->data == SF_TRIANGLES)
|
||
|
{
|
||
|
srfTriangles_t *srf = (srfTriangles_t *) surface2->data;
|
||
|
|
||
|
if(!r_mergeClusterTriangles->integer)
|
||
|
continue;
|
||
|
|
||
|
if(srf->numTriangles)
|
||
|
{
|
||
|
srfTriangle_t *tri;
|
||
|
|
||
|
for(i = 0, tri = tr.world->triangles + srf->firstTriangle; i < srf->numTriangles; i++, tri++)
|
||
|
{
|
||
|
indexes[numTriangles * 3 + i * 3 + 0] = tri->indexes[0];
|
||
|
indexes[numTriangles * 3 + i * 3 + 1] = tri->indexes[1];
|
||
|
indexes[numTriangles * 3 + i * 3 + 2] = tri->indexes[2];
|
||
|
}
|
||
|
|
||
|
numTriangles += srf->numTriangles;
|
||
|
BoundsAdd(bounds[0], bounds[1], srf->bounds[0], srf->bounds[1]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(tr.world->numClusterVBOSurfaces[tr.visIndex] < tr.world->clusterVBOSurfaces[tr.visIndex].currentElements)
|
||
|
{
|
||
|
vboSurf =
|
||
|
(srfVBOMesh_t *) Com_GrowListElement(&tr.world->clusterVBOSurfaces[tr.visIndex],
|
||
|
tr.world->numClusterVBOSurfaces[tr.visIndex]);
|
||
|
ibo = vboSurf->ibo;
|
||
|
|
||
|
/*
|
||
|
if(ibo->indexesVBO)
|
||
|
{
|
||
|
glDeleteBuffersARB(1, &ibo->indexesVBO);
|
||
|
ibo->indexesVBO = 0;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//Com_Dealloc(ibo);
|
||
|
//Com_Dealloc(vboSurf);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vboSurf = (srfVBOMesh_t *) ri.Hunk_Alloc(sizeof(*vboSurf), h_low);
|
||
|
vboSurf->surfaceType = SF_VBO_MESH;
|
||
|
|
||
|
vboSurf->vbo = tr.world->vbo;
|
||
|
vboSurf->ibo = ibo = (IBO_t *) ri.Hunk_Alloc(sizeof(*ibo), h_low);
|
||
|
|
||
|
#if defined(USE_D3D10)
|
||
|
// TODO
|
||
|
#else
|
||
|
glGenBuffersARB(1, &ibo->indexesVBO);
|
||
|
#endif
|
||
|
|
||
|
Com_AddToGrowList(&tr.world->clusterVBOSurfaces[tr.visIndex], vboSurf);
|
||
|
}
|
||
|
|
||
|
//ri.Printf(PRINT_ALL, "creating VBO cluster surface for shader '%s'\n", shader->name);
|
||
|
|
||
|
// update surface properties
|
||
|
vboSurf->numIndexes = numTriangles * 3;
|
||
|
vboSurf->numVerts = numVerts;
|
||
|
|
||
|
vboSurf->shader = shader;
|
||
|
vboSurf->lightmapNum = lightmapNum;
|
||
|
|
||
|
VectorCopy(bounds[0], vboSurf->bounds[0]);
|
||
|
VectorCopy(bounds[1], vboSurf->bounds[1]);
|
||
|
|
||
|
// make sure the render thread is stopped
|
||
|
R_SyncRenderThread();
|
||
|
|
||
|
// update IBO
|
||
|
Q_strncpyz(ibo->name,
|
||
|
va("staticWorldMesh_IBO_visIndex%i_surface%i", tr.visIndex, tr.world->numClusterVBOSurfaces[tr.visIndex]),
|
||
|
sizeof(ibo->name));
|
||
|
ibo->indexesSize = indexesSize;
|
||
|
|
||
|
R_BindIBO(ibo);
|
||
|
#if defined(USE_D3D10)
|
||
|
// TODO
|
||
|
#else
|
||
|
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, GL_DYNAMIC_DRAW_ARB);
|
||
|
#endif
|
||
|
R_BindNullIBO();
|
||
|
|
||
|
//GL_CheckErrors();
|
||
|
|
||
|
ri.Free(indexes);
|
||
|
|
||
|
tr.world->numClusterVBOSurfaces[tr.visIndex]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ri.Free(surfacesSorted);
|
||
|
|
||
|
if(r_showcluster->modified || r_showcluster->integer)
|
||
|
{
|
||
|
r_showcluster->modified = qfalse;
|
||
|
if(r_showcluster->integer)
|
||
|
{
|
||
|
ri.Printf(PRINT_ALL, " surfaces:%i\n", tr.world->numClusterVBOSurfaces[tr.visIndex]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // #if defined(USE_BSP_CLUSTERSURFACE_MERGING)
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
R_MarkLeaves
|
||
|
|
||
|
Mark the leaves and nodes that are in the PVS for the current
|
||
|
cluster
|
||
|
===============
|
||
|
*/
|
||
|
static void R_MarkLeaves(void)
|
||
|
{
|
||
|
const byte *vis;
|
||
|
bspNode_t *leaf, *parent;
|
||
|
int i;
|
||
|
int cluster;
|
||
|
|
||
|
// lockpvs lets designers walk around to determine the
|
||
|
// extent of the current pvs
|
||
|
if(r_lockpvs->integer)// || r_dynamicBspOcclusionCulling->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)
|
||
|
{
|
||
|
// if r_showcluster was just turned on, remark everything
|
||
|
if(!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(tr.refdef.areamaskModified)
|
||
|
{
|
||
|
// invalidate old visclusters so they will be updated next time
|
||
|
tr.visClusters[i] = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
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, "update cluster:%i area:%i index:%i\n", cluster, leaf->area, tr.visIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if(r_dynamicBspOcclusionCulling->modified)
|
||
|
{
|
||
|
r_dynamicBspOcclusionCulling->modified = qfalse;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
#if defined(USE_BSP_CLUSTERSURFACE_MERGING)
|
||
|
if(r_mergeClusterSurfaces->integer && !r_dynamicBspOcclusionCulling->integer)
|
||
|
{
|
||
|
R_UpdateClusterSurfaces();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(r_novis->integer || 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++)
|
||
|
{
|
||
|
if(tr.world->vis)
|
||
|
{
|
||
|
cluster = leaf->cluster;
|
||
|
|
||
|
if(cluster >= 0 && cluster < tr.world->numClusters)
|
||
|
{
|
||
|
// 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))))
|
||
|
{
|
||
|
// not visible
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// ydnar: don't want to walk the entire bsp to add skybox surfaces
|
||
|
if(tr.refdef.rdflags & RDF_SKYBOXPORTAL)
|
||
|
{
|
||
|
// this only happens once, as game/cgame know the origin of the skybox
|
||
|
// this also means the skybox portal cannot move, as this list is calculated once and never again
|
||
|
if(tr.world->numSkyNodes < WORLD_MAX_SKY_NODES)
|
||
|
{
|
||
|
tr.world->skyNodes[tr.world->numSkyNodes++] = leaf;
|
||
|
}
|
||
|
R_AddLeafSurfaces(leaf, 0);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void DrawLeaf(bspNode_t * node, int decalBits)
|
||
|
{
|
||
|
// leaf node, so add mark surfaces
|
||
|
int c;
|
||
|
bspSurface_t *surf, **mark;
|
||
|
|
||
|
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 the individual surfaces
|
||
|
mark = node->markSurfaces;
|
||
|
c = node->numMarkSurfaces;
|
||
|
while(c--)
|
||
|
{
|
||
|
// the surface may have already been added if it
|
||
|
// spans multiple leafs
|
||
|
surf = *mark;
|
||
|
R_AddWorldSurface(surf, decalBits);
|
||
|
mark++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// ================================================================================================
|
||
|
//
|
||
|
// BSP OCCLUSION CULLING
|
||
|
//
|
||
|
// ================================================================================================
|
||
|
|
||
|
static qboolean InsideViewFrustum(bspNode_t * node, int planeBits)
|
||
|
{
|
||
|
if(!r_nocull->integer)
|
||
|
{
|
||
|
int i;
|
||
|
int r;
|
||
|
|
||
|
for(i = 0; i < FRUSTUM_PLANES; i++)
|
||
|
{
|
||
|
if(planeBits & (1 << i))
|
||
|
{
|
||
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustums[0][i]);
|
||
|
if(r == 2)
|
||
|
{
|
||
|
return qfalse; // culled
|
||
|
}
|
||
|
if(r == 1)
|
||
|
{
|
||
|
planeBits &= ~(1 << i); // all descendants will also be in front
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
//#define DEBUG_CHC 1
|
||
|
|
||
|
static void DrawNode_r(bspNode_t * node, int planeBits)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
// 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 i;
|
||
|
int r;
|
||
|
|
||
|
for(i = 0; i < FRUSTUM_PLANES; i++)
|
||
|
{
|
||
|
if(planeBits & (1 << i))
|
||
|
{
|
||
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustums[0][i]);
|
||
|
if(r == 2)
|
||
|
{
|
||
|
return; // culled
|
||
|
}
|
||
|
if(r == 1)
|
||
|
{
|
||
|
planeBits &= ~(1 << i); // all descendants will also be in front
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("--- DrawNode_r( node = %i, isLeaf = %i ) ---\n", node - tr.world->nodes, node->contents == -1));
|
||
|
}
|
||
|
|
||
|
if(node->contents != -1)// && !(node->contents & CONTENTS_TRANSLUCENT))
|
||
|
{
|
||
|
gl_genericShader->SetUniform_Color(colorGreen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gl_genericShader->SetUniform_Color(colorMdGrey);
|
||
|
}
|
||
|
|
||
|
// draw bsp leave or node
|
||
|
{
|
||
|
R_BindVBO(node->volumeVBO);
|
||
|
R_BindIBO(node->volumeIBO);
|
||
|
|
||
|
GL_VertexAttribsState(ATTR_POSITION);
|
||
|
|
||
|
tess.numVertexes = node->volumeVerts;
|
||
|
tess.numIndexes = node->volumeIndexes;
|
||
|
|
||
|
Tess_DrawElements();
|
||
|
|
||
|
tess.multiDrawPrimitives = 0;
|
||
|
tess.numIndexes = 0;
|
||
|
tess.numVertexes = 0;
|
||
|
}
|
||
|
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// recurse down the children, front side first
|
||
|
DrawNode_r(node->children[0], planeBits);
|
||
|
|
||
|
// tail recurse
|
||
|
node = node->children[1];
|
||
|
} while(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
static void IssueOcclusionQuery(link_t * queue, bspNode_t * node, qboolean resetMultiQueryLink)
|
||
|
{
|
||
|
#if defined(DEBUG_CHC)
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
if(node->contents != -1)// && !(node->contents & CONTENTS_TRANSLUCENT))
|
||
|
{
|
||
|
GLimp_LogComment(va("--- IssueOcclusionQuery( leaf = %i ) ---\n", node - tr.world->nodes));
|
||
|
gl_genericShader->SetUniform_Color(colorGreen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GLimp_LogComment(va("--- IssueOcclusionQuery( node = %i ) ---\n", node - tr.world->nodes));
|
||
|
gl_genericShader->SetUniform_Color(colorMdGrey);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
EnQueue(queue, node);
|
||
|
|
||
|
// tell GetOcclusionQueryResult that this is not a multi query
|
||
|
if(resetMultiQueryLink)
|
||
|
{
|
||
|
QueueInit(&node->multiQuery);
|
||
|
}
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
#if 0
|
||
|
if(glIsQueryARB(node->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
ri.Error(ERR_FATAL, "IssueOcclusionQuery: node %i has already an occlusion query object in slot %i: %i", node - tr.world->nodes, tr.viewCount, node->occlusionQueryObjects[tr.viewCount]);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// begin the occlusion query
|
||
|
glBeginQueryARB(GL_SAMPLES_PASSED, node->occlusionQueryObjects[tr.viewCount]);
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
R_BindVBO(node->volumeVBO);
|
||
|
R_BindIBO(node->volumeIBO);
|
||
|
|
||
|
GL_VertexAttribsState(ATTR_POSITION);
|
||
|
|
||
|
tess.numVertexes = node->volumeVerts;
|
||
|
tess.numIndexes = node->volumeIndexes;
|
||
|
|
||
|
Tess_DrawElements();
|
||
|
|
||
|
// end the query
|
||
|
glEndQueryARB(GL_SAMPLES_PASSED);
|
||
|
|
||
|
#if 1
|
||
|
if(!glIsQueryARB(node->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
ri.Error(ERR_FATAL, "IssueOcclusionQuery: node %i has no occlusion query object in slot %i: %i", node - tr.world->nodes, tr.viewCount, node->occlusionQueryObjects[tr.viewCount]);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
node->occlusionQueryNumbers[tr.viewCount] = tr.pc.c_occlusionQueries;
|
||
|
tr.pc.c_occlusionQueries++;
|
||
|
|
||
|
tess.multiDrawPrimitives = 0;
|
||
|
tess.numIndexes = 0;
|
||
|
tess.numVertexes = 0;
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
GLimp_LogComment("--- IssueOcclusionQuery end ---\n");
|
||
|
}
|
||
|
|
||
|
static void IssueMultiOcclusionQueries(link_t * multiQueue, link_t * individualQueue)
|
||
|
{
|
||
|
bspNode_t *node;
|
||
|
bspNode_t *multiQueryNode;
|
||
|
link_t *l;
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment("IssueMultiOcclusionQueries([");
|
||
|
|
||
|
for(l = multiQueue->prev; l != multiQueue; l = l->prev)
|
||
|
{
|
||
|
node = (bspNode_t *) l->data;
|
||
|
|
||
|
GLimp_LogComment(va("%i, ", (int)(node - tr.world->nodes)));
|
||
|
}
|
||
|
GLimp_LogComment("])");
|
||
|
}
|
||
|
|
||
|
if(QueueEmpty(multiQueue))
|
||
|
return;
|
||
|
|
||
|
multiQueryNode = (bspNode_t *) QueueFront(multiQueue)->data;
|
||
|
|
||
|
// begin the occlusion query
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
#if 0
|
||
|
if(!glIsQueryARB(multiQueryNode->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
ri.Error(ERR_FATAL, "IssueMultiOcclusionQueries: node %i has already occlusion query object in slot %i: %i", multiQueryNode - tr.world->nodes, tr.viewCount, multiQueryNode->occlusionQueryObjects[tr.viewCount]);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
glBeginQueryARB(GL_SAMPLES_PASSED, multiQueryNode->occlusionQueryObjects[tr.viewCount]);
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
//GLimp_LogComment("rendering nodes:[");
|
||
|
for(l = multiQueue->prev; l != multiQueue; l = l->prev)
|
||
|
{
|
||
|
node = (bspNode_t *) l->data;
|
||
|
|
||
|
if(node->contents != -1)// && !(node->contents & CONTENTS_TRANSLUCENT))
|
||
|
{
|
||
|
gl_genericShader->SetUniform_Color(colorGreen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gl_genericShader->SetUniform_Color(colorMdGrey);
|
||
|
}
|
||
|
|
||
|
//if(r_logFile->integer)
|
||
|
//{
|
||
|
// GLimp_LogComment(va("%i, ", node - tr.world->nodes));
|
||
|
//}
|
||
|
|
||
|
//Tess_EndBegin();
|
||
|
|
||
|
R_BindVBO(node->volumeVBO);
|
||
|
R_BindIBO(node->volumeIBO);
|
||
|
|
||
|
GL_VertexAttribsState(ATTR_POSITION);
|
||
|
|
||
|
tess.multiDrawPrimitives = 0;
|
||
|
tess.numVertexes = node->volumeVerts;
|
||
|
tess.numIndexes = node->volumeIndexes;
|
||
|
|
||
|
Tess_DrawElements();
|
||
|
|
||
|
tess.numIndexes = 0;
|
||
|
tess.numVertexes = 0;
|
||
|
}
|
||
|
//GLimp_LogComment("]\n");
|
||
|
|
||
|
multiQueryNode->occlusionQueryNumbers[tr.viewCount] = tr.pc.c_occlusionQueries;
|
||
|
tr.pc.c_occlusionQueries++;
|
||
|
tr.pc.c_occlusionQueriesMulti++;
|
||
|
|
||
|
// end the query
|
||
|
glEndQueryARB(GL_SAMPLES_PASSED);
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
#if 0
|
||
|
if(!glIsQueryARB(multiQueryNode->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
ri.Error(ERR_FATAL, "IssueMultiOcclusionQueries: node %i has no occlusion query object in slot %i: %i", multiQueryNode - tr.world->nodes, tr.viewCount, multiQueryNode->occlusionQueryObjects[tr.viewCount]);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// move queue to node->multiQuery queue
|
||
|
QueueInit(&multiQueryNode->multiQuery);
|
||
|
DeQueue(multiQueue);
|
||
|
while(!QueueEmpty(multiQueue))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(multiQueue);
|
||
|
EnQueue(&multiQueryNode->multiQuery, node);
|
||
|
}
|
||
|
|
||
|
EnQueue(individualQueue, multiQueryNode);
|
||
|
|
||
|
GLimp_LogComment("--- IssueMultiOcclusionQueries end ---\n");
|
||
|
}
|
||
|
|
||
|
static qboolean ResultAvailable(bspNode_t *node)
|
||
|
{
|
||
|
GLint available;
|
||
|
|
||
|
//glFinish();
|
||
|
|
||
|
available = 0;
|
||
|
//if(glIsQueryARB(node->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
glGetQueryObjectivARB(node->occlusionQueryObjects[tr.viewCount], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
|
||
|
GL_CheckErrors();
|
||
|
}
|
||
|
|
||
|
return (qboolean) available;
|
||
|
}
|
||
|
|
||
|
static void GetOcclusionQueryResult(bspNode_t *node)
|
||
|
{
|
||
|
link_t *l, *sentinel;
|
||
|
int ocSamples;
|
||
|
|
||
|
GLint available;
|
||
|
|
||
|
GLimp_LogComment("--- GetOcclusionQueryResult ---\n");
|
||
|
|
||
|
//glFinish();
|
||
|
|
||
|
#if 0
|
||
|
if(!glIsQueryARB(node->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
ri.Error(ERR_FATAL, "GetOcclusionQueryResult: node %i has no occlusion query object in slot %i: %i", node - tr.world->nodes, tr.viewCount, node->occlusionQueryObjects[tr.viewCount]);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
available = 0;
|
||
|
while(!available)
|
||
|
{
|
||
|
//if(glIsQueryARB(node->occlusionQueryObjects[tr.viewCount]))
|
||
|
{
|
||
|
glGetQueryObjectivARB(node->occlusionQueryObjects[tr.viewCount], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
|
||
|
//GL_CheckErrors();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
glGetQueryObjectivARB(node->occlusionQueryObjects[tr.viewCount], GL_QUERY_RESULT, &ocSamples);
|
||
|
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("GetOcclusionQueryResult(%i): available = %i, samples = %i\n", (int)(node - tr.world->nodes), available, ocSamples));
|
||
|
}
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
node->occlusionQuerySamples[tr.viewCount] = ocSamples;
|
||
|
node->lastQueried[tr.viewCount] = tr.frameCount;
|
||
|
|
||
|
// copy result to all nodes that were linked to this multi query node
|
||
|
sentinel = &node->multiQuery;
|
||
|
for(l = sentinel->prev; l != sentinel; l = l->prev)
|
||
|
{
|
||
|
node = (bspNode_t *) l->data;
|
||
|
|
||
|
node->occlusionQuerySamples[tr.viewCount] = ocSamples;
|
||
|
node->lastQueried[tr.viewCount] = tr.frameCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void PullUpVisibility(bspNode_t * node)
|
||
|
{
|
||
|
bspNode_t *parent;
|
||
|
|
||
|
parent = node;
|
||
|
while(parent && !parent->visible[tr.viewCount])
|
||
|
{
|
||
|
parent->visible[tr.viewCount] = qtrue;
|
||
|
parent->lastVisited[tr.viewCount] = tr.frameCount;
|
||
|
|
||
|
parent = parent->parent;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
static void PushNode(link_t * traversalStack, bspNode_t * node)
|
||
|
{
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
//DrawLeaf(node, tr.refdef.decalBits);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//float d1, d2;
|
||
|
cplane_t *splitPlane;
|
||
|
|
||
|
splitPlane = node->plane;
|
||
|
|
||
|
//d1 = DistanceSquared(tr.viewParms.orientation.origin, node->children[0]->origin);
|
||
|
//d2 = DistanceSquared(tr.viewParms.orientation.origin, node->children[1]->origin);
|
||
|
|
||
|
//if(d1 <= d2)
|
||
|
#if 0
|
||
|
if(DotProduct(splitPlane->normal, tr.viewParms.orientation.axis[0]) <= 0)
|
||
|
{
|
||
|
StackPush(traversalStack, node->children[0]);
|
||
|
StackPush(traversalStack, node->children[1]);
|
||
|
|
||
|
ri.Printf(PRINT_ALL, "--> %i\n", node->children[0] - tr.world->nodes);
|
||
|
ri.Printf(PRINT_ALL, "--> %i\n", node->children[1] - tr.world->nodes);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
#if 1
|
||
|
StackPush(traversalStack, node->children[0]);
|
||
|
StackPush(traversalStack, node->children[1]);
|
||
|
#else
|
||
|
InsertLink(&((bspNode_t*)node->children[0])->visChain, traversalStack);
|
||
|
InsertLink(&((bspNode_t*)node->children[1])->visChain, traversalStack);
|
||
|
|
||
|
traversalStack->numElements += 2;
|
||
|
#endif
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("traversal-stack <-- node %i\n", node->children[0] - tr.world->nodes));
|
||
|
GLimp_LogComment(va("traversal-stack <-- node %i\n", node->children[1] - tr.world->nodes));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
static void TraverseNode(link_t * distanceQueue, bspNode_t * node)
|
||
|
{
|
||
|
#if defined(DEBUG_CHC)
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
GLimp_LogComment(va("--- TraverseNode( leaf = %i ) ---\n", node - tr.world->nodes));
|
||
|
|
||
|
gl_genericShader->SetUniform_Color(colorGreen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GLimp_LogComment(va("--- TraverseNode( node = %i ) ---\n", node - tr.world->nodes));
|
||
|
|
||
|
gl_genericShader->SetUniform_Color(colorMdGrey);
|
||
|
}
|
||
|
|
||
|
// draw bsp leave or node
|
||
|
{
|
||
|
R_BindVBO(node->volumeVBO);
|
||
|
R_BindIBO(node->volumeIBO);
|
||
|
|
||
|
GL_VertexAttribsState(ATTR_POSITION);
|
||
|
|
||
|
tess.numVertexes = node->volumeVerts;
|
||
|
tess.numIndexes = node->volumeIndexes;
|
||
|
|
||
|
Tess_DrawElements();
|
||
|
|
||
|
tess.multiDrawPrimitives = 0;
|
||
|
tess.numIndexes = 0;
|
||
|
tess.numVertexes = 0;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
//DrawLeaf(node, tr.refdef.decalBits);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EnQueue(distanceQueue, node->children[0]);
|
||
|
EnQueue(distanceQueue, node->children[1]);
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("distance-queue <-- node %i\n", (int)(node->children[0] - tr.world->nodes)));
|
||
|
GLimp_LogComment(va("distance-queue <-- node %i\n", (int)(node->children[1] - tr.world->nodes)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void BuildNodeTraversalStackPost_r(bspNode_t * node)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
if(tr.frameCount != node->lastVisited[tr.viewCount])
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if defined(DEBUG_CHC)
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
GLimp_LogComment(va("--- BuildNodeTraversalStackPost_r( leaf = %i, visible = %i ) ---\n", node - tr.world->nodes, node->visible[tr.viewCount]));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GLimp_LogComment(va("--- BuildNodeTraversalStackPost_r( node = %i, visible = %i ) ---\n", node - tr.world->nodes, node->visible[tr.viewCount]));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
InsertLink(&node->visChain, &tr.traversalStack);
|
||
|
|
||
|
if(node->contents != -1)
|
||
|
{
|
||
|
if(node->visible[tr.viewCount])
|
||
|
DrawLeaf(node, tr.refdef.decalBits);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// recurse down the children, front side first
|
||
|
BuildNodeTraversalStackPost_r(node->children[0]);
|
||
|
|
||
|
// tail recurse
|
||
|
node = node->children[1];
|
||
|
} while(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
static bool WasVisible(bspNode_t * node)
|
||
|
{
|
||
|
return (node->visible[tr.viewCount] && ((tr.frameCount - node->lastVisited[tr.viewCount]) <= r_chcMaxVisibleFrames->integer));
|
||
|
}
|
||
|
|
||
|
static bool QueryReasonable(bspNode_t * node)
|
||
|
{
|
||
|
// if r_chcMaxVisibleFrames 10 then range from 5 to 10
|
||
|
//return ((tr.frameCount - node->lastQueried[tr.viewCount]) > r_chcMaxVisibleFrames->integer);
|
||
|
return ((tr.frameCount - node->lastQueried[tr.viewCount]) > Q_min((int)ceil((r_chcMaxVisibleFrames->value * 0.5f) + (r_chcMaxVisibleFrames->value * 0.5f) * random()), r_chcMaxVisibleFrames->integer));
|
||
|
}
|
||
|
|
||
|
static void R_CoherentHierachicalCulling()
|
||
|
{
|
||
|
bspNode_t *node;
|
||
|
bspNode_t *multiQueryNode;
|
||
|
|
||
|
// link_t traversalStack;
|
||
|
link_t distanceQueue;
|
||
|
link_t occlusionQueryQueue;
|
||
|
link_t visibleQueue; // CHC++
|
||
|
link_t invisibleQueue; // CHC++
|
||
|
//link_t renderQueue;
|
||
|
int startTime = 0, endTime = 0;
|
||
|
|
||
|
//ri.Cvar_Set("r_logFile", "1");
|
||
|
|
||
|
GLimp_LogComment("--- R_CoherentHierachicalCulling ---\n");
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("tr.viewCount = %i, tr.viewCountNoReset = %i\n", tr.viewCount, tr.viewCountNoReset));
|
||
|
}
|
||
|
|
||
|
if(r_speeds->integer)
|
||
|
{
|
||
|
glFinish();
|
||
|
startTime = ri.Milliseconds();
|
||
|
}
|
||
|
|
||
|
if(DS_STANDARD_ENABLED())
|
||
|
{
|
||
|
R_BindFBO(tr.geometricRenderFBO);
|
||
|
}
|
||
|
else if(HDR_ENABLED())
|
||
|
{
|
||
|
R_BindFBO(tr.deferredRenderFBO);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
R_BindNullFBO();
|
||
|
}
|
||
|
|
||
|
gl_genericShader->DisableAlphaTesting();
|
||
|
gl_genericShader->DisablePortalClipping();
|
||
|
gl_genericShader->DisableVertexSkinning();
|
||
|
gl_genericShader->DisableVertexAnimation();
|
||
|
gl_genericShader->DisableDeformVertexes();
|
||
|
gl_genericShader->DisableTCGenEnvironment();
|
||
|
|
||
|
gl_genericShader->BindProgram();
|
||
|
GL_Cull(CT_TWO_SIDED);
|
||
|
|
||
|
GL_LoadProjectionMatrix(tr.viewParms.projectionMatrix);
|
||
|
|
||
|
GL_Viewport(tr.viewParms.viewportX, tr.viewParms.viewportY,
|
||
|
tr.viewParms.viewportWidth, tr.viewParms.viewportHeight);
|
||
|
|
||
|
GL_Scissor(tr.viewParms.viewportX, tr.viewParms.viewportY,
|
||
|
tr.viewParms.viewportWidth, tr.viewParms.viewportHeight);
|
||
|
|
||
|
// set uniforms
|
||
|
gl_genericShader->SetUniform_ColorModulate(CGEN_CONST, AGEN_CONST);
|
||
|
gl_genericShader->SetUniform_Color(colorWhite);
|
||
|
|
||
|
// set up the transformation matrix
|
||
|
GL_LoadModelViewMatrix(tr.orientation.modelViewMatrix);
|
||
|
gl_genericShader->SetUniform_ModelViewProjectionMatrix(glState.modelViewProjectionMatrix[glState.stackIndex]);
|
||
|
|
||
|
// bind u_ColorMap
|
||
|
GL_SelectTexture(0);
|
||
|
GL_Bind(tr.whiteImage);
|
||
|
gl_genericShader->SetUniform_ColorTextureMatrix(matrixIdentity);
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
GL_ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||
|
|
||
|
GL_State(GLS_POLYMODE_LINE | GLS_DEPTHTEST_DISABLE);
|
||
|
|
||
|
// draw BSP leaf volumes to color for debugging
|
||
|
DrawNode_r(&tr.world->nodes[0], FRUSTUM_CLIPALL);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
GL_ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||
|
|
||
|
GL_State(GLS_COLORMASK_BITS | GLS_DEPTHMASK_TRUE);
|
||
|
|
||
|
// draw BSP leaf volumes to depth
|
||
|
DrawNode_r(&tr.world->nodes[0], FRUSTUM_CLIPALL);
|
||
|
#endif
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GL_ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||
|
|
||
|
GL_State(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// use the depth buffer of the previous frame for occlusion culling
|
||
|
GL_State(GLS_COLORMASK_BITS);
|
||
|
}
|
||
|
|
||
|
ClearLink(&tr.traversalStack);
|
||
|
QueueInit(&tr.occlusionQueryQueue);
|
||
|
ClearLink(&tr.occlusionQueryList);
|
||
|
|
||
|
//ClearLink(&traversalStack);
|
||
|
QueueInit(&distanceQueue);
|
||
|
QueueInit(&occlusionQueryQueue);
|
||
|
QueueInit(&visibleQueue);
|
||
|
QueueInit(&invisibleQueue);
|
||
|
//QueueInit(&renderQueue);
|
||
|
|
||
|
EnQueue(&distanceQueue, &tr.world->nodes[0]);
|
||
|
//StackPush(&traversalStack, &tr.world->nodes[0]);
|
||
|
|
||
|
/*
|
||
|
ClearLink(&traversalStack);
|
||
|
traversalStack.numElements = 0;
|
||
|
|
||
|
node = &tr.world->nodes[0];
|
||
|
InsertLink(&node->visChain, &traversalStack);
|
||
|
traversalStack.numElements++;
|
||
|
*/
|
||
|
|
||
|
while(!QueueEmpty(&distanceQueue) || !QueueEmpty(&occlusionQueryQueue) || !QueueEmpty(&invisibleQueue) || !QueueEmpty(&visibleQueue))
|
||
|
{
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("--- (distanceQueue = %i, occlusionQueryQueue = %i, invisibleQueue = %i, visibleQueue = %i)\n", QueueSize(&distanceQueue), QueueSize(&occlusionQueryQueue), QueueSize(&invisibleQueue), QueueSize(&visibleQueue)));
|
||
|
}
|
||
|
|
||
|
//--PART 1: process finished occlusion queries
|
||
|
while(!QueueEmpty(&occlusionQueryQueue) && (ResultAvailable((bspNode_t *) QueueFront(&occlusionQueryQueue)->data) || QueueEmpty(&distanceQueue)))
|
||
|
{
|
||
|
if(ResultAvailable((bspNode_t *) QueueFront(&occlusionQueryQueue)->data))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(&occlusionQueryQueue);
|
||
|
|
||
|
// wait if result not available
|
||
|
GetOcclusionQueryResult(node);
|
||
|
|
||
|
if(node->occlusionQuerySamples[tr.viewCount] > r_chcVisibilityThreshold->integer)
|
||
|
{
|
||
|
// if a query of multiple previously invisible objects became visible, we need to
|
||
|
// test all the individual objects ...
|
||
|
if(!QueueEmpty(&node->multiQuery))
|
||
|
{
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("MULTI query node %i visible\n", (int)(node - tr.world->nodes)));
|
||
|
}
|
||
|
|
||
|
multiQueryNode = node;
|
||
|
|
||
|
IssueOcclusionQuery(&occlusionQueryQueue, multiQueryNode, qfalse);
|
||
|
|
||
|
while(!QueueEmpty(&multiQueryNode->multiQuery))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(&multiQueryNode->multiQuery);
|
||
|
|
||
|
// it might be possible that a leaf caused this node to be visible by a PullUpVisibility() call
|
||
|
// so avoid a further query
|
||
|
if(!(node->visible[tr.viewCount] && tr.frameCount == node->lastVisited[tr.viewCount]))
|
||
|
{
|
||
|
IssueOcclusionQuery(&occlusionQueryQueue, node, qtrue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("single query node %i visible\n", (int)(node - tr.world->nodes)));
|
||
|
}
|
||
|
|
||
|
if(r_dynamicBspOcclusionCulling->integer == 1)
|
||
|
{
|
||
|
if(!WasVisible(node))
|
||
|
{
|
||
|
TraverseNode(&distanceQueue, node);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraverseNode(&distanceQueue, node);
|
||
|
}
|
||
|
|
||
|
PullUpVisibility(node);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
node->visible[tr.viewCount] = qfalse;
|
||
|
|
||
|
if(!QueueEmpty(&node->multiQuery))
|
||
|
{
|
||
|
// node was an invisible multi query node so dequeue all its children
|
||
|
multiQueryNode = node;
|
||
|
while(!QueueEmpty(&multiQueryNode->multiQuery))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(&multiQueryNode->multiQuery);
|
||
|
|
||
|
node->visible[tr.viewCount] = qfalse;
|
||
|
|
||
|
tr.pc.c_occlusionQueriesSaved++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#if 1
|
||
|
else if(r_dynamicBspOcclusionCulling->integer == 1)
|
||
|
{
|
||
|
if(!QueueEmpty(&visibleQueue))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(&visibleQueue);
|
||
|
|
||
|
IssueOcclusionQuery(&occlusionQueryQueue, node, qtrue);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
} // end while(!QueueEmpty(&occlusionQueryQueue))
|
||
|
|
||
|
//--PART 2: hierarchical traversal
|
||
|
if(!QueueEmpty(&distanceQueue)) //if(!StackEmpty(&traversalStack))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(&distanceQueue);
|
||
|
//node = (bspNode_t *) StackPop(&traversalStack);
|
||
|
|
||
|
/*
|
||
|
link_t* top = traversalStack.next;
|
||
|
RemoveLink(top);
|
||
|
|
||
|
node = (bspNode_t *) top->data;
|
||
|
*/
|
||
|
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("distance-queue --> node %i\n", (int)(node - tr.world->nodes)));
|
||
|
}
|
||
|
|
||
|
if( node->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex] && // node was marked as potentially visible
|
||
|
(node->contents == -1 || (node->contents != -1 && node->numMarkSurfaces)) &&
|
||
|
InsideViewFrustum(node, FRUSTUM_CLIPALL)
|
||
|
)
|
||
|
{
|
||
|
// identify previously visible nodes
|
||
|
bool wasVisible = WasVisible(node);
|
||
|
|
||
|
if(r_dynamicBspOcclusionCulling->integer > 1)
|
||
|
{
|
||
|
// reset node's visibility classification
|
||
|
node->visible[tr.viewCount] = (qboolean) !QueryReasonable(node);
|
||
|
}
|
||
|
|
||
|
// identify nodes that we cannot skip queries for
|
||
|
bool needsQuery;
|
||
|
|
||
|
bool clipsNearPlane = (BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustums[0][FRUSTUM_NEAR]) == 3);
|
||
|
if(clipsNearPlane)
|
||
|
{
|
||
|
// node clips near plane so avoid the occlusion query test
|
||
|
node->occlusionQuerySamples[tr.viewCount] = r_chcVisibilityThreshold->integer + 1;
|
||
|
node->lastQueried[tr.viewCount] = tr.frameCount;
|
||
|
node->visible[tr.viewCount] = qtrue;
|
||
|
|
||
|
needsQuery = false;
|
||
|
}
|
||
|
#if 1
|
||
|
else if(r_chcIgnoreLeaves->integer && node->contents != -1)
|
||
|
{
|
||
|
// NOTE: this is the fastest dynamic occlusion culling path
|
||
|
|
||
|
// only very few leaves are invisible if we don't traverse through all bsp nodes
|
||
|
// so testing these leaves just causes additional occlusion queries which can be avoided
|
||
|
// by setting all reached leaves to visible
|
||
|
node->occlusionQuerySamples[tr.viewCount] = r_chcVisibilityThreshold->integer + 1;
|
||
|
node->lastQueried[tr.viewCount] = tr.frameCount;
|
||
|
node->visible[tr.viewCount] = qtrue;
|
||
|
|
||
|
needsQuery = false;
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
// CHC default
|
||
|
needsQuery = !wasVisible || (node->contents != -1);
|
||
|
}
|
||
|
|
||
|
// update node's visited flag
|
||
|
node->lastVisited[tr.viewCount] = tr.frameCount;
|
||
|
|
||
|
// optimization
|
||
|
#if 0
|
||
|
if((node->contents != -1) && node->sameAABBAsParent)
|
||
|
{
|
||
|
node->visible[tr.viewCount] = qtrue;
|
||
|
wasVisible = qtrue;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
bool leafThatNeedsQuery = node->contents != -1;
|
||
|
if(leafThatNeedsQuery)
|
||
|
{
|
||
|
if(r_chcIgnoreLeaves->integer)
|
||
|
leafThatNeedsQuery = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
leafThatNeedsQuery = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
if(r_dynamicBspOcclusionCulling->integer == 1)
|
||
|
{
|
||
|
// CHC++
|
||
|
|
||
|
if(!wasVisible && !clipsNearPlane && leafThatNeedsQuery)
|
||
|
{
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("i-queue <-- node %i\n", (int)(node - tr.world->nodes)));
|
||
|
}
|
||
|
|
||
|
EnQueue(&invisibleQueue, node);
|
||
|
|
||
|
if(QueueSize(&invisibleQueue) >= r_chcMaxPrevInvisNodesBatchSize->integer)
|
||
|
IssueMultiOcclusionQueries(&invisibleQueue, &occlusionQueryQueue);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if 1
|
||
|
if((node->contents != -1) && !clipsNearPlane && QueryReasonable(node) && leafThatNeedsQuery)
|
||
|
{
|
||
|
if(r_logFile->integer)
|
||
|
{
|
||
|
GLimp_LogComment(va("v-queue <-- node %i\n", (int)(node - tr.world->nodes)));
|
||
|
}
|
||
|
|
||
|
EnQueue(&visibleQueue, node);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// always traverse a node if it was visible
|
||
|
TraverseNode(&distanceQueue, node);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// CHC default
|
||
|
|
||
|
if(needsQuery)//!wasVisible && !clipsNearPlane)
|
||
|
{
|
||
|
IssueOcclusionQuery(&occlusionQueryQueue, node, qtrue);
|
||
|
}
|
||
|
|
||
|
if(wasVisible)
|
||
|
{
|
||
|
// always traverse a node if it was visible
|
||
|
TraverseNode(&distanceQueue, node);
|
||
|
|
||
|
//if(clipsNearPlane)
|
||
|
//{
|
||
|
// PullUpVisibility(node);
|
||
|
//}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if(r_dynamicBspOcclusionCulling->integer == 1)
|
||
|
{
|
||
|
if(QueueEmpty(&distanceQueue))
|
||
|
//if(StackEmpty(&traversalStack))
|
||
|
{
|
||
|
// remaining previously visible node queries
|
||
|
if(!QueueEmpty(&invisibleQueue))
|
||
|
{
|
||
|
IssueMultiOcclusionQueries(&invisibleQueue, &occlusionQueryQueue);
|
||
|
}
|
||
|
|
||
|
if(!QueueEmpty(&visibleQueue))
|
||
|
{
|
||
|
while(!QueueEmpty(&visibleQueue))
|
||
|
{
|
||
|
node = (bspNode_t *) DeQueue(&visibleQueue);
|
||
|
|
||
|
IssueOcclusionQuery(&occlusionQueryQueue, node, qtrue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//ri.Printf(PRINT_ALL, "--- (%i, %i, %i)\n", !StackEmpty(&traversalStack), !QueueEmpty(&occlusionQueryQueue), !QueueEmpty(&invisibleQueue));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ClearLink(&tr.traversalStack);
|
||
|
BuildNodeTraversalStackPost_r(&tr.world->nodes[0]);
|
||
|
|
||
|
R_BindNullFBO();
|
||
|
|
||
|
// reenable color buffer and depth buffer writes
|
||
|
GL_State(GLS_DEFAULT);
|
||
|
|
||
|
GL_CheckErrors();
|
||
|
|
||
|
//ri.Printf(PRINT_ALL, "--- R_CHC++ end ---\n");
|
||
|
|
||
|
if(r_speeds->integer)
|
||
|
{
|
||
|
glFinish();
|
||
|
endTime = ri.Milliseconds();
|
||
|
tr.pc.c_CHCTime = endTime - startTime;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_AddWorldSurfaces
|
||
|
=============
|
||
|
*/
|
||
|
void R_AddWorldSurfaces(void)
|
||
|
{
|
||
|
if(!r_drawworld->integer)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tr.currentEntity = &tr.worldEntity;
|
||
|
|
||
|
// clear out the visible min/max
|
||
|
ClearBounds(tr.viewParms.visBounds[0], tr.viewParms.visBounds[1]);
|
||
|
|
||
|
// render sky or world?
|
||
|
if(tr.refdef.rdflags & RDF_SKYBOXPORTAL && tr.world->numSkyNodes > 0)
|
||
|
{
|
||
|
int i;
|
||
|
bspNode_t **node;
|
||
|
|
||
|
for(i = 0, node = tr.world->skyNodes; i < tr.world->numSkyNodes; i++, node++)
|
||
|
R_AddLeafSurfaces(*node, 0); // no decals on skybox nodes
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
// determine which leaves are in the PVS / areamask
|
||
|
R_MarkLeaves();
|
||
|
|
||
|
// update the bsp nodes with the dynamic occlusion query results
|
||
|
if(glConfig2.occlusionQueryBits && glConfig.driverType != GLDRV_MESA && r_dynamicBspOcclusionCulling->integer)
|
||
|
{
|
||
|
R_CoherentHierachicalCulling();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ClearLink(&tr.traversalStack);
|
||
|
ClearLink(&tr.occlusionQueryQueue);
|
||
|
ClearLink(&tr.occlusionQueryList);
|
||
|
|
||
|
// update visbounds and add surfaces that weren't cached with VBOs
|
||
|
R_RecursiveWorldNode(tr.world->nodes, FRUSTUM_CLIPALL, tr.refdef.decalBits);
|
||
|
}
|
||
|
|
||
|
// ydnar: add decal surfaces
|
||
|
R_AddDecalSurfaces(tr.world->models);
|
||
|
|
||
|
#if defined(USE_BSP_CLUSTERSURFACE_MERGING)
|
||
|
if(r_mergeClusterSurfaces->integer && !r_dynamicBspOcclusionCulling->integer)
|
||
|
{
|
||
|
int j, i;
|
||
|
srfVBOMesh_t *srf;
|
||
|
shader_t *shader;
|
||
|
cplane_t *frust;
|
||
|
int r;
|
||
|
|
||
|
for(j = 0; j < tr.world->numClusterVBOSurfaces[tr.visIndex]; j++)
|
||
|
{
|
||
|
srf = (srfVBOMesh_t *) Com_GrowListElement(&tr.world->clusterVBOSurfaces[tr.visIndex], j);
|
||
|
shader = srf->shader;
|
||
|
|
||
|
for(i = 0; i < FRUSTUM_PLANES; i++)
|
||
|
{
|
||
|
frust = &tr.viewParms.frustums[0][i];
|
||
|
|
||
|
r = BoxOnPlaneSide(srf->bounds[0], srf->bounds[1], frust);
|
||
|
|
||
|
if(r == 2)
|
||
|
{
|
||
|
// completely outside frustum
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
R_AddDrawSurf((surfaceType_t *)srf, shader, srf->lightmapNum, 0);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_AddWorldInteractions
|
||
|
=============
|
||
|
*/
|
||
|
void R_AddWorldInteractions(trRefLight_t * light)
|
||
|
{
|
||
|
if(!r_drawworld->integer)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tr.currentEntity = &tr.worldEntity;
|
||
|
|
||
|
// perform frustum culling and add all the potentially visible surfaces
|
||
|
tr.lightCount++;
|
||
|
R_RecursiveInteractionNode(tr.world->nodes, light, FRUSTUM_CLIPALL);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_AddPrecachedWorldInteractions
|
||
|
=============
|
||
|
*/
|
||
|
void R_AddPrecachedWorldInteractions(trRefLight_t * light)
|
||
|
{
|
||
|
interactionType_t iaType = IA_DEFAULT;
|
||
|
|
||
|
if(!r_drawworld->integer)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!light->firstInteractionCache)
|
||
|
{
|
||
|
// this light has no interactions precached
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tr.currentEntity = &tr.worldEntity;
|
||
|
|
||
|
if((r_vboShadows->integer || r_vboLighting->integer))// && light->l.rlType != RL_DIRECTIONAL)
|
||
|
{
|
||
|
interactionCache_t *iaCache;
|
||
|
interactionVBO_t *iaVBO;
|
||
|
srfVBOMesh_t *srf;
|
||
|
shader_t *shader;
|
||
|
bspSurface_t *surface;
|
||
|
|
||
|
// this can be shadow mapping or shadowless lighting
|
||
|
for(iaVBO = light->firstInteractionVBO; iaVBO; iaVBO = iaVBO->next)
|
||
|
{
|
||
|
if(!iaVBO->vboLightMesh)
|
||
|
continue;
|
||
|
|
||
|
srf = iaVBO->vboLightMesh;
|
||
|
shader = iaVBO->shader;
|
||
|
|
||
|
switch (light->l.rlType)
|
||
|
{
|
||
|
case RL_OMNI:
|
||
|
R_AddLightInteraction(light, (surfaceType_t *)srf, shader, CUBESIDE_CLIPALL, IA_LIGHTONLY);
|
||
|
break;
|
||
|
|
||
|
case RL_DIRECTIONAL:
|
||
|
case RL_PROJ:
|
||
|
R_AddLightInteraction(light, (surfaceType_t *)srf, shader, CUBESIDE_CLIPALL, IA_LIGHTONLY);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
R_AddLightInteraction(light, (surfaceType_t *)srf, shader, CUBESIDE_CLIPALL, IA_DEFAULT);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// add meshes for shadowmap generation if any
|
||
|
for(iaVBO = light->firstInteractionVBO; iaVBO; iaVBO = iaVBO->next)
|
||
|
{
|
||
|
if(!iaVBO->vboShadowMesh)
|
||
|
continue;
|
||
|
|
||
|
srf = iaVBO->vboShadowMesh;
|
||
|
shader = iaVBO->shader;
|
||
|
|
||
|
R_AddLightInteraction(light, (surfaceType_t *)srf, shader, iaVBO->cubeSideBits, IA_SHADOWONLY);
|
||
|
}
|
||
|
|
||
|
// add interactions that couldn't be merged into VBOs
|
||
|
for(iaCache = light->firstInteractionCache; iaCache; iaCache = iaCache->next)
|
||
|
{
|
||
|
if(iaCache->redundant)
|
||
|
continue;
|
||
|
|
||
|
if(iaCache->mergedIntoVBO)
|
||
|
continue;
|
||
|
|
||
|
surface = iaCache->surface;
|
||
|
|
||
|
// Tr3B - this surface is maybe not in this view but it may still cast a shadow
|
||
|
// into this view
|
||
|
if(surface->viewCount != tr.viewCountNoReset)
|
||
|
{
|
||
|
if(r_shadows->integer < SHADOWING_ESM16 || light->l.noShadows)
|
||
|
continue;
|
||
|
else
|
||
|
iaType = IA_SHADOWONLY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iaType = iaCache->type;
|
||
|
}
|
||
|
|
||
|
R_AddLightInteraction(light, surface->data, surface->shader, iaCache->cubeSideBits, iaType);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
interactionCache_t *iaCache;
|
||
|
bspSurface_t *surface;
|
||
|
|
||
|
for(iaCache = light->firstInteractionCache; iaCache; iaCache = iaCache->next)
|
||
|
{
|
||
|
if(iaCache->redundant)
|
||
|
continue;
|
||
|
|
||
|
surface = iaCache->surface;
|
||
|
|
||
|
// Tr3B - this surface is maybe not in this view but it may still cast a shadow
|
||
|
// into this view
|
||
|
if(surface->viewCount != tr.viewCountNoReset)
|
||
|
{
|
||
|
if(r_shadows->integer < SHADOWING_ESM16 || light->l.noShadows)
|
||
|
continue;
|
||
|
else
|
||
|
iaType = IA_SHADOWONLY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iaType = iaCache->type;
|
||
|
}
|
||
|
|
||
|
R_AddLightInteraction(light, surface->data, surface->shader, iaCache->cubeSideBits, iaType);
|
||
|
}
|
||
|
}
|
||
|
}
|