2023-05-08 19:53:53 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
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
|
2024-12-01 15:26:01 +01:00
|
|
|
along with Foobar; if not, write to the Free Software
|
2023-05-08 19:53:53 +02:00
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
#include "tr_local.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;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (!tr.refdef.render_terrain) {
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( tr.currentEntityNum != ENTITYNUM_WORLD ) {
|
2023-05-08 19:53:53 +02:00
|
|
|
sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius );
|
|
|
|
} else {
|
|
|
|
sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius );
|
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
boxCull = CULL_OUT;
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
// 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->meshBounds );
|
|
|
|
|
|
|
|
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 ) {
|
|
|
|
srfSurfaceFace_t *sface;
|
|
|
|
float d;
|
|
|
|
|
|
|
|
if ( r_nocull->integer ) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( *surface == SF_GRID ) {
|
|
|
|
return R_CullGrid( (srfGridMesh_t *)surface );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( *surface == SF_TRIANGLES ) {
|
|
|
|
return R_CullTriSurf( (srfTriangles_t *)surface );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( *surface != SF_FACE ) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( shader->cullType == CT_TWO_SIDED ) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
// face culling
|
|
|
|
if ( !r_facePlaneCull->integer ) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
sface = ( srfSurfaceFace_t * ) surface;
|
2024-12-01 15:26:01 +01:00
|
|
|
d = DotProduct (tr.ori.viewOrigin, sface->plane.normal);
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
// 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 < sface->plane.dist - 8 ) {
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( d > sface->plane.dist + 8 ) {
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_FastDlightFace
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
static int R_FastDlightFace( srfSurfaceFace_t *face, int dlightBits ) {
|
2023-05-08 19:53:53 +02:00
|
|
|
float d;
|
|
|
|
int i;
|
|
|
|
dlight_t *dl;
|
|
|
|
|
|
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dl = &tr.refdef.dlights[i];
|
2024-12-01 15:26:01 +01:00
|
|
|
d = DotProduct( dl->transformed, face->plane.normal ) - face->plane.dist;
|
2023-05-08 19:53:53 +02:00
|
|
|
if ( d < -dl->radius || d > dl->radius ) {
|
|
|
|
// dlight doesn't reach the plane
|
|
|
|
dlightBits &= ~( 1 << i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !dlightBits ) {
|
|
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
face->dlightBits[ tr.smpFrame ] = dlightBits;
|
2023-05-08 19:53:53 +02:00
|
|
|
return dlightBits;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_FastDlightGrid
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
static int R_FastDlightGrid( srfGridMesh_t *grid, int dlightBits ) {
|
2023-05-08 19:53:53 +02:00
|
|
|
int i;
|
|
|
|
dlight_t *dl;
|
|
|
|
|
|
|
|
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 > grid->meshBounds[1][0]
|
|
|
|
|| dl->origin[0] + dl->radius < grid->meshBounds[0][0]
|
|
|
|
|| dl->origin[1] - dl->radius > grid->meshBounds[1][1]
|
|
|
|
|| dl->origin[1] + dl->radius < grid->meshBounds[0][1]
|
|
|
|
|| dl->origin[2] - dl->radius > grid->meshBounds[1][2]
|
|
|
|
|| dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) {
|
|
|
|
// dlight doesn't reach the bounds
|
|
|
|
dlightBits &= ~( 1 << i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !dlightBits ) {
|
|
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
grid->dlightBits[ tr.smpFrame ] = dlightBits;
|
2023-05-08 19:53:53 +02:00
|
|
|
return dlightBits;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_FastDlightTerrain
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
static int R_FastDlightTerrain(cTerraPatchUnpacked_t* srf, int dlightBits) {
|
|
|
|
dlight_t* dl;
|
|
|
|
float* origin;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < tr.refdef.num_dlights; i++) {
|
|
|
|
dl = &tr.refdef.dlights[i];
|
|
|
|
origin = dl->transformed;
|
|
|
|
|
|
|
|
if (srf->x0 - origin[0] > dl->radius
|
|
|
|
|| srf->x0 - origin[0] < -512.0 - dl->radius
|
|
|
|
|| srf->y0 - origin[1] > dl->radius
|
|
|
|
|| srf->y0 - origin[1] < -512.0 - dl->radius
|
|
|
|
|| srf->z0 - origin[2] > dl->radius
|
|
|
|
|| srf->z0 - origin[2] < -srf->zmax - dl->radius
|
|
|
|
) {
|
|
|
|
// dlight doesn't reach the bounds
|
|
|
|
dlightBits &= ~(1 << i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dlightBits) {
|
|
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
|
|
}
|
|
|
|
|
|
|
|
srf->drawinfo.dlightBits[tr.smpFrame] = dlightBits;
|
|
|
|
return dlightBits;
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_DlightTrisurf
|
|
|
|
======================
|
|
|
|
*/
|
2023-05-08 19:53:53 +02:00
|
|
|
static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) {
|
|
|
|
// FIXME: more dlight culling to trisurfs...
|
2024-12-01 15:26:01 +01:00
|
|
|
surf->dlightBits[ tr.smpFrame ] = dlightBits;
|
2023-05-08 19:53:53 +02:00
|
|
|
return dlightBits;
|
|
|
|
#if 0
|
|
|
|
int i;
|
|
|
|
dlight_t *dl;
|
|
|
|
|
|
|
|
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 > grid->meshBounds[1][0]
|
|
|
|
|| dl->origin[0] + dl->radius < grid->meshBounds[0][0]
|
|
|
|
|| dl->origin[1] - dl->radius > grid->meshBounds[1][1]
|
|
|
|
|| dl->origin[1] + dl->radius < grid->meshBounds[0][1]
|
|
|
|
|| dl->origin[2] - dl->radius > grid->meshBounds[1][2]
|
|
|
|
|| dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) {
|
|
|
|
// dlight doesn't reach the bounds
|
|
|
|
dlightBits &= ~( 1 << i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !dlightBits ) {
|
|
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
grid->dlightBits[ tr.smpFrame ] = dlightBits;
|
2023-05-08 19:53:53 +02:00
|
|
|
return dlightBits;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
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 ) {
|
2024-12-01 15:26:01 +01:00
|
|
|
int dlightMap;
|
|
|
|
|
|
|
|
dlightMap = 0;
|
|
|
|
|
2023-05-08 19:53:53 +02:00
|
|
|
if ( *surf->data == SF_FACE ) {
|
2024-12-01 15:26:01 +01:00
|
|
|
if (!r_fastdlights->integer) {
|
|
|
|
if (surf->shader->lightmapIndex >= 0) {
|
|
|
|
dlightMap = R_RealDlightFace((srfSurfaceFace_t*)surf->data, dlightBits);
|
|
|
|
} else {
|
|
|
|
((srfSurfaceFace_t*)surf->data)->dlightMap[tr.smpFrame] = 0;
|
|
|
|
dlightMap = 0;
|
|
|
|
}
|
|
|
|
return dlightMap;
|
|
|
|
} else {
|
|
|
|
dlightBits = R_FastDlightFace((srfSurfaceFace_t*)surf->data, dlightBits);
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
} else if ( *surf->data == SF_GRID ) {
|
2024-12-01 15:26:01 +01:00
|
|
|
if (!r_fastdlights->integer) {
|
|
|
|
if (surf->shader->lightmapIndex >= 0) {
|
|
|
|
dlightMap = R_RealDlightPatch((srfGridMesh_t*)surf->data, dlightBits);
|
|
|
|
} else {
|
|
|
|
((srfGridMesh_t*)surf->data)->dlightMap[tr.smpFrame] = 0.f;
|
|
|
|
dlightMap = 0;
|
|
|
|
}
|
|
|
|
return dlightMap;
|
|
|
|
} else {
|
|
|
|
dlightBits = R_FastDlightGrid((srfGridMesh_t*)surf->data, dlightBits);
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
} else {
|
2024-12-01 15:26:01 +01:00
|
|
|
return dlightMap;
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (dlightBits) {
|
2023-05-08 19:53:53 +02:00
|
|
|
tr.pc.c_dlightSurfaces++;
|
2024-12-01 15:26:01 +01:00
|
|
|
return 1;
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
return dlightMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_DlightTerrain
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
int R_DlightTerrain(cTerraPatchUnpacked_t* surf, int dlightBits)
|
|
|
|
{
|
|
|
|
int dlightMap;
|
|
|
|
|
|
|
|
dlightMap = 0;
|
|
|
|
|
|
|
|
if (r_fastdlights->integer)
|
|
|
|
{
|
|
|
|
if (R_FastDlightTerrain(surf, dlightBits)) {
|
|
|
|
++tr.pc.c_dlightSurfaces;
|
|
|
|
dlightMap = 1;
|
|
|
|
}
|
|
|
|
} else if (surf->shader->lightmapIndex < 0) {
|
|
|
|
surf->drawinfo.dlightMap[tr.smpFrame] = 0;
|
|
|
|
} else {
|
|
|
|
dlightMap = R_RealDlightTerrain(surf, dlightBits);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dlightMap;
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_CheckDlightSurface
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
static int R_CheckDlightSurface(msurface_t* surf, int dlightBits)
|
|
|
|
{
|
|
|
|
if (dlightBits && surf->frameCount != tr.frameCount)
|
|
|
|
{
|
|
|
|
surf->frameCount = tr.frameCount;
|
|
|
|
return R_DlightSurface(surf, dlightBits);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dlightBits)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Added in 2.0
|
|
|
|
// Return the existing dlight map
|
|
|
|
if (*surf->data == SF_FACE)
|
|
|
|
{
|
|
|
|
return ((srfSurfaceFace_t*)surf->data)->dlightMap[tr.smpFrame];
|
|
|
|
}
|
|
|
|
else if (*surf->data == SF_GRID)
|
|
|
|
{
|
|
|
|
return ((srfGridMesh_t*)surf->data)->dlightMap[tr.smpFrame];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*surf->data == SF_FACE)
|
|
|
|
{
|
|
|
|
((srfSurfaceFace_t*)surf->data)->dlightMap[tr.smpFrame] = 0;
|
|
|
|
((srfSurfaceFace_t*)surf->data)->dlightBits[tr.smpFrame] = 0;
|
|
|
|
}
|
|
|
|
else if (*surf->data == SF_GRID)
|
|
|
|
{
|
|
|
|
((srfGridMesh_t*)surf->data)->dlightMap[tr.smpFrame] = 0;
|
|
|
|
((srfGridMesh_t*)surf->data)->dlightBits[tr.smpFrame] = 0;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_CheckDlightTerrain
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
int R_CheckDlightTerrain(cTerraPatchUnpacked_t* surf, int dlightBits)
|
|
|
|
{
|
|
|
|
if (surf->frameCount == tr.frameCount) {
|
|
|
|
return surf->drawinfo.dlightMap[tr.smpFrame];
|
|
|
|
}
|
|
|
|
|
|
|
|
surf->frameCount = tr.frameCount;
|
|
|
|
if (dlightBits) {
|
|
|
|
return R_DlightTerrain(surf, dlightBits);
|
|
|
|
}
|
|
|
|
|
|
|
|
surf->drawinfo.dlightMap[tr.smpFrame] = 0;
|
|
|
|
surf->drawinfo.dlightBits[tr.smpFrame] = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
======================
|
|
|
|
R_AddWorldSurface
|
|
|
|
======================
|
|
|
|
*/
|
|
|
|
static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) {
|
2024-12-01 15:26:01 +01:00
|
|
|
//if ( surf->viewCount == tr.viewCount ) {
|
|
|
|
// return; // already in this view
|
|
|
|
//}
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
surf->viewCount = tr.viewCount;
|
|
|
|
// FIXME: bmodel fog?
|
|
|
|
|
|
|
|
// try to cull before dlighting or adding
|
|
|
|
if ( R_CullSurface( surf->data, surf->shader ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for dlighting
|
2024-12-01 15:26:01 +01:00
|
|
|
dlightBits = R_CheckDlightSurface(surf, dlightBits);
|
|
|
|
if (surf->shader && surf->shader->isPortalSky) {
|
|
|
|
// Sky portal
|
|
|
|
R_Sky_AddSurf(surf);
|
|
|
|
return;
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
R_AddDrawSurf( surf->data, surf->shader, dlightBits );
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============================================================
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
bmodel = pModel->d.bmodel;
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
clip = R_CullLocalBox( bmodel->bounds );
|
|
|
|
if ( clip == CULL_OUT ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
R_DlightBmodel( bmodel );
|
|
|
|
|
|
|
|
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
|
|
|
|
R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============================================================
|
|
|
|
|
|
|
|
WORLD MODEL
|
|
|
|
|
|
|
|
=============================================================
|
|
|
|
*/
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
void R_GetInlineModelBounds(int iIndex, vec3_t vMins, vec3_t vMaxs)
|
|
|
|
{
|
|
|
|
bmodel_t* bmodel;
|
|
|
|
|
|
|
|
bmodel = &tr.world->bmodels[iIndex];
|
|
|
|
VectorCopy(bmodel->bounds[0], vMins);
|
|
|
|
VectorCopy(bmodel->bounds[1], vMaxs);
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
================
|
|
|
|
R_RecursiveWorldNode
|
|
|
|
================
|
|
|
|
*/
|
2024-12-01 15:26:01 +01:00
|
|
|
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) {
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
do {
|
|
|
|
// if the node wasn't marked as potentially visible, exit
|
|
|
|
if (node->visframe != tr.visCount) {
|
|
|
|
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;
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (planeBits & 1) {
|
2023-05-08 19:53:53 +02:00
|
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]);
|
|
|
|
if (r == 2) {
|
|
|
|
return; // culled
|
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
if (r == 1) {
|
2023-05-08 19:53:53 +02:00
|
|
|
planeBits &= ~1; // all descendants will also be in front
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (planeBits & 2) {
|
2023-05-08 19:53:53 +02:00
|
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]);
|
|
|
|
if (r == 2) {
|
|
|
|
return; // culled
|
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
if (r == 1) {
|
2023-05-08 19:53:53 +02:00
|
|
|
planeBits &= ~2; // all descendants will also be in front
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (planeBits & 4) {
|
2023-05-08 19:53:53 +02:00
|
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]);
|
|
|
|
if (r == 2) {
|
|
|
|
return; // culled
|
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
if (r == 1) {
|
2023-05-08 19:53:53 +02:00
|
|
|
planeBits &= ~4; // all descendants will also be in front
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (planeBits & 8) {
|
2023-05-08 19:53:53 +02:00
|
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]);
|
|
|
|
if (r == 2) {
|
|
|
|
return; // culled
|
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
if (r == 1) {
|
2023-05-08 19:53:53 +02:00
|
|
|
planeBits &= ~8; // all descendants will also be in front
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( node->contents != -1 ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// recurse down the children, front side first
|
2024-12-01 15:26:01 +01:00
|
|
|
R_RecursiveWorldNode (node->children[0], planeBits, dlightBits);
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
// tail recurse
|
|
|
|
node = node->children[1];
|
|
|
|
} while ( 1 );
|
|
|
|
|
|
|
|
{
|
|
|
|
// leaf node, so add mark surfaces
|
|
|
|
int c;
|
|
|
|
msurface_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];
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
tr.portalsky.cntNode = node;
|
|
|
|
|
|
|
|
if (r_drawbrushes->integer) {
|
|
|
|
// add the individual surfaces
|
|
|
|
mark = node->firstmarksurface;
|
|
|
|
c = node->nummarksurfaces;
|
|
|
|
while (c--) {
|
|
|
|
// the surface may have already been added if it
|
|
|
|
// spans multiple leafs
|
|
|
|
surf = *mark;
|
|
|
|
if (surf->viewCount != tr.viewCount) {
|
|
|
|
R_AddWorldSurface(surf, dlightBits);
|
|
|
|
}
|
|
|
|
mark++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r_drawterrain->integer && tr.refdef.render_terrain && !tr.viewParms.isPortalSky)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < node->numTerraPatches; i++) {
|
|
|
|
R_MarkTerrainPatch(tr.world->visTerraPatches[node->firstTerraPatch + i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r_drawstaticdecals->integer) {
|
|
|
|
if (node->pFirstMarkFragment) {
|
|
|
|
R_AddPermanentMarkFragmentSurfaces(node->pFirstMarkFragment, node->iNumMarkFragment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r_drawstaticmodels->integer) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < node->numStaticModels; i++) {
|
|
|
|
tr.world->visStaticModels[node->firstStaticModel + i]->visCount = tr.visCount;
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
int R_SphereInLeafs(const vec3_t p, float r, mnode_t** nodes, int nMaxNodes) {
|
|
|
|
mnode_t* nodestack[1024];
|
|
|
|
int iNodeStackPos;
|
|
|
|
mnode_t* pCurNode;
|
|
|
|
int nFoundNodes;
|
|
|
|
|
|
|
|
iNodeStackPos = 0;
|
|
|
|
pCurNode = tr.world->nodes;
|
|
|
|
nFoundNodes = 0;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
cplane_t* plane;
|
|
|
|
float d;
|
|
|
|
|
|
|
|
while (pCurNode->contents == -1)
|
|
|
|
{
|
|
|
|
plane = pCurNode->plane;
|
|
|
|
if (plane->type >= PLANE_NON_AXIAL) {
|
|
|
|
d = DotProduct(p, plane->normal) - plane->dist;
|
|
|
|
} else {
|
|
|
|
d = p[plane->type] - plane->dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d < r) {
|
|
|
|
if (d > -r) {
|
|
|
|
nodestack[iNodeStackPos++] = pCurNode->children[0];
|
|
|
|
}
|
|
|
|
pCurNode = pCurNode->children[1];
|
|
|
|
} else {
|
|
|
|
pCurNode = pCurNode->children[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nodes[nFoundNodes++] = pCurNode;
|
|
|
|
if (!iNodeStackPos || nFoundNodes == nMaxNodes) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
iNodeStackPos--;
|
|
|
|
pCurNode = nodestack[iNodeStackPos];
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
return nFoundNodes;
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
/*
|
|
|
|
===============
|
|
|
|
R_PointInLeaf
|
|
|
|
===============
|
|
|
|
*/
|
2024-12-01 15:26:01 +01:00
|
|
|
mnode_t *R_PointInLeaf( const vec3_t p ) {
|
2023-05-08 19:53:53 +02:00
|
|
|
mnode_t *node;
|
|
|
|
float d;
|
|
|
|
cplane_t *plane;
|
|
|
|
|
|
|
|
if ( !tr.world ) {
|
|
|
|
ri.Error (ERR_DROP, "R_PointInLeaf: bad model");
|
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
|
2023-05-08 19:53:53 +02:00
|
|
|
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) {
|
2024-12-01 15:26:01 +01:00
|
|
|
if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) {
|
2023-05-08 19:53:53 +02:00
|
|
|
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 );
|
2024-12-01 15:26:01 +01:00
|
|
|
vis = CM_ClusterPVS( leaf->cluster );
|
2023-05-08 19:53:53 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
// if r_showcluster was just turned on, remark everything
|
|
|
|
if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified
|
|
|
|
&& !r_showcluster->modified ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.visCount++;
|
|
|
|
tr.viewCluster = cluster;
|
|
|
|
|
|
|
|
if ( r_novis->integer || tr.viewCluster == -1 ) {
|
|
|
|
for (i=0 ; i<tr.world->numnodes ; i++) {
|
|
|
|
if (tr.world->nodes[i].contents != CONTENTS_SOLID) {
|
|
|
|
tr.world->nodes[i].visframe = tr.visCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vis = R_ClusterPVS (tr.viewCluster);
|
|
|
|
|
|
|
|
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->visframe == tr.visCount)
|
|
|
|
break;
|
|
|
|
parent->visframe = tr.visCount;
|
|
|
|
parent = parent->parent;
|
|
|
|
} while (parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_AddWorldSurfaces
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
void R_AddWorldSurfaces (void) {
|
2024-12-01 15:26:01 +01:00
|
|
|
if (!r_drawworld->integer) {
|
2023-05-08 19:53:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
if (tr.refdef.rdflags & RDF_NOWORLDMODEL) {
|
2023-05-08 19:53:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:26:01 +01:00
|
|
|
tr.currentEntityNum = ENTITYNUM_WORLD;
|
|
|
|
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT;
|
|
|
|
|
|
|
|
if (r_drawterrain->integer && tr.refdef.render_terrain && !tr.viewParms.isPortalSky) {
|
|
|
|
R_TerrainPrepareFrame();
|
|
|
|
}
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
// determine which leaves are in the PVS / areamask
|
2024-12-01 15:26:01 +01:00
|
|
|
R_MarkLeaves();
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
// clear out the visible min/max
|
2024-12-01 15:26:01 +01:00
|
|
|
ClearBounds(tr.viewParms.visBounds[0], tr.viewParms.visBounds[1]);
|
2023-05-08 19:53:53 +02:00
|
|
|
|
|
|
|
// perform frustum culling and add all the potentially visible surfaces
|
2024-12-01 15:26:01 +01:00
|
|
|
if (tr.refdef.num_dlights > 32) {
|
|
|
|
tr.refdef.num_dlights = 32;
|
|
|
|
}
|
|
|
|
R_TransformDlights(tr.refdef.num_dlights, tr.refdef.dlights, &tr.viewParms.world);
|
|
|
|
R_RecursiveWorldNode(tr.world->nodes, tr.viewParms.fog.extrafrustums ? 31 : 15, (1 << tr.refdef.num_dlights) - 1);
|
|
|
|
|
|
|
|
if (r_drawterrain->integer && tr.refdef.render_terrain && !tr.viewParms.isPortalSky) {
|
|
|
|
R_AddTerrainSurfaces();
|
|
|
|
}
|
|
|
|
if (r_drawstaticmodels->integer) {
|
|
|
|
R_AddStaticModelSurfaces();
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|
2024-12-01 15:26:01 +01:00
|
|
|
|
|
|
|
if (g_bInfostaticmodels) {
|
|
|
|
g_bInfostaticmodels = 0;
|
|
|
|
R_PrintInfoStaticModels();
|
|
|
|
}
|
|
|
|
|
|
|
|
R_UpdateLevelMarksSystem();
|
2023-05-08 19:53:53 +02:00
|
|
|
}
|