254 lines
6.3 KiB
C++
254 lines
6.3 KiB
C++
/*
|
|
gl_cull.cpp - render culling routines
|
|
Copyright (C) 2011 Uncle Mike
|
|
|
|
This program 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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.
|
|
*/
|
|
|
|
#include "hud.h"
|
|
#include "cl_util.h"
|
|
#include "gl_local.h"
|
|
#include "entity_types.h"
|
|
#include "mathlib.h"
|
|
#include "gl_world.h"
|
|
#include "gl_grass.h"
|
|
|
|
/*
|
|
=============
|
|
R_CullModel
|
|
=============
|
|
*/
|
|
bool R_CullModel( cl_entity_t *e, const Vector &absmin, const Vector &absmax )
|
|
{
|
|
if( e == GET_VIEWMODEL( ) || e == gHUD.m_pHeadShieldEnt )
|
|
{
|
|
if( RI->params & RP_NONVIEWERREF )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// don't reflect this entity in mirrors
|
|
if( FBitSet( e->curstate.effects, EF_NOREFLECT ) && FBitSet( RI->params, RP_MIRRORVIEW ))
|
|
return true;
|
|
|
|
// draw only in mirrors
|
|
if( FBitSet( e->curstate.effects, EF_REFLECTONLY ) && !FBitSet( RI->params, RP_MIRRORVIEW ))
|
|
return true;
|
|
|
|
// never draw playermodel for himself flashlight while shadowpass is active
|
|
if( FBitSet( RI->params, RP_SHADOWVIEW ) && RI->currentlight != NULL )
|
|
{
|
|
if( UTIL_IsLocal( e->index ) && RI->currentlight->key == FLASHLIGHT_KEY )
|
|
return true;
|
|
}
|
|
#if 0
|
|
// don't cull local player because we draw legs instead
|
|
if( RP_LOCALCLIENT( e ) && !FBitSet( RI->params, RP_THIRDPERSON ) && UTIL_IsLocal( RI->view.entity ))
|
|
{
|
|
// player can view himself from the portal camera
|
|
if( !FBitSet( RI->params, RP_MIRRORVIEW|RP_SHADOWVIEW ))
|
|
return true;
|
|
}
|
|
#endif
|
|
return R_CullBox( absmin, absmax );
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_CullBrushModel
|
|
|
|
Cull brush model by bbox
|
|
================
|
|
*/
|
|
bool R_CullBrushModel( cl_entity_t *e )
|
|
{
|
|
gl_state_t *glm = GL_GetCache( e->hCachedMatrix );
|
|
model_t *clmodel = e->model;
|
|
Vector absmin, absmax;
|
|
|
|
if( !e || !e->model )
|
|
return true;
|
|
|
|
// skybox entity
|
|
if( e->curstate.renderfx == SKYBOX_ENTITY )
|
|
{
|
|
if( FBitSet( RI->params, RP_SHADOWVIEW ))
|
|
return true;
|
|
|
|
Vector trans = GetVieworg() - tr.sky_origin;
|
|
|
|
if( tr.sky_speed )
|
|
{
|
|
trans = trans - (GetVieworg() - tr.sky_world_origin) / tr.sky_speed;
|
|
Vector skypos = tr.sky_origin + (GetVieworg() - tr.sky_world_origin) / tr.sky_speed;
|
|
tr.modelorg = skypos - e->origin;
|
|
}
|
|
else tr.modelorg = tr.sky_origin - e->origin;
|
|
|
|
absmin = e->origin + trans + clmodel->mins;
|
|
absmax = e->origin + trans + clmodel->maxs;
|
|
}
|
|
else
|
|
{
|
|
if( e->angles != g_vecZero )
|
|
{
|
|
absmin = e->origin - clmodel->radius;
|
|
absmax = e->origin + clmodel->radius;
|
|
}
|
|
else
|
|
{
|
|
absmin = e->origin + clmodel->mins;
|
|
absmax = e->origin + clmodel->maxs;
|
|
}
|
|
tr.modelorg = glm->GetModelOrigin();
|
|
}
|
|
|
|
return R_CullModel( e, absmin, absmax );
|
|
}
|
|
|
|
static bool R_SunLightShadow( void )
|
|
{
|
|
if( RI->currentlight && RI->currentlight->type == LIGHT_DIRECTIONAL && FBitSet( RI->params, RP_SHADOWVIEW ))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AllowFacePlaneCulling
|
|
|
|
allow to culling backfaces
|
|
=================
|
|
*/
|
|
static bool R_AllowFacePlaneCulling( msurface_t *surf )
|
|
{
|
|
if( !glState.faceCull )
|
|
return false;
|
|
|
|
if( FBitSet( surf->flags, SURF_TWOSIDE ))
|
|
return false;
|
|
|
|
if( !FBitSet( RI->params, RP_SHADOWVIEW ) && RI->currentlight && RI->currentlight->type == LIGHT_DIRECTIONAL )
|
|
return false;
|
|
|
|
// don't cull transparent surfaces because we should be draw decals on them
|
|
if( FBitSet( surf->flags, SURF_HAS_DECALS ) && !R_OpaqueEntity( RI->currententity ))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CullSurface
|
|
|
|
cull invisible surfaces
|
|
=================
|
|
*/
|
|
int R_CullSurface( msurface_t *surf, const Vector &vieworg, CFrustum *frustum, int clipFlags )
|
|
{
|
|
cl_entity_t *e = RI->currententity;
|
|
|
|
if( !surf || !surf->texinfo || !surf->texinfo->texture )
|
|
return CULL_OTHER;
|
|
|
|
if( FBitSet( RI->params, RP_WATERPASS ) && R_WaterEntity( e->model ))
|
|
return CULL_OTHER; // don't render water from waterplane reflection
|
|
|
|
if( CVAR_TO_BOOL( r_nocull ))
|
|
return CULL_VISIBLE;
|
|
|
|
if( R_SunLightShadow( ))
|
|
return CULL_VISIBLE;
|
|
|
|
// because light or shadow passes required both sides for right self-shadowing
|
|
if( R_AllowFacePlaneCulling( surf ))
|
|
{
|
|
float dist;
|
|
|
|
// can use normal.z for world (optimisation)
|
|
if( FBitSet( RI->params, RP_DRAW_OVERVIEW ))
|
|
{
|
|
Vector orthonormal;
|
|
|
|
if( e == GET_ENTITY( 0 ) || R_StaticEntity( e ))
|
|
{
|
|
orthonormal.z = surf->plane->normal.z;
|
|
}
|
|
else
|
|
{
|
|
matrix4x4 m = matrix4x4( g_vecZero, e->angles );
|
|
orthonormal = m.VectorRotate( surf->plane->normal );
|
|
}
|
|
|
|
dist = orthonormal.z;
|
|
}
|
|
else dist = PlaneDiff( vieworg, surf->plane );
|
|
|
|
if( glState.faceCull == GL_FRONT || ( RI->params & RP_MIRRORVIEW ))
|
|
{
|
|
if( surf->flags & SURF_PLANEBACK )
|
|
{
|
|
if( dist >= -BACKFACE_EPSILON )
|
|
return CULL_BACKSIDE; // wrong side
|
|
}
|
|
else
|
|
{
|
|
if( dist <= BACKFACE_EPSILON )
|
|
return CULL_BACKSIDE; // wrong side
|
|
}
|
|
}
|
|
else if( glState.faceCull == GL_BACK )
|
|
{
|
|
if( surf->flags & SURF_PLANEBACK )
|
|
{
|
|
if( dist <= BACKFACE_EPSILON )
|
|
return CULL_BACKSIDE; // wrong side
|
|
}
|
|
else
|
|
{
|
|
if( dist >= -BACKFACE_EPSILON )
|
|
return CULL_BACKSIDE; // wrong side
|
|
}
|
|
}
|
|
}
|
|
|
|
if( frustum && frustum->CullBox( surf->info->mins, surf->info->maxs, clipFlags ))
|
|
return CULL_FRUSTUM;
|
|
return CULL_VISIBLE;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_CullNodeTopView
|
|
|
|
cull node by user rectangle (simple scissor)
|
|
================
|
|
*/
|
|
bool R_CullNodeTopView( mnode_t *node )
|
|
{
|
|
Vector2D delta, size;
|
|
Vector center, half, mins, maxs;
|
|
|
|
// build the node center and half-diagonal
|
|
mins = node->minmaxs, maxs = node->minmaxs + 3;
|
|
center = (mins + maxs) * 0.5f;
|
|
half = maxs - center;
|
|
|
|
// cull against the screen frustum or the appropriate area's frustum.
|
|
delta.x = center.x - world->orthocenter.x;
|
|
delta.y = center.y - world->orthocenter.y;
|
|
size.x = half.x + world->orthohalf.x;
|
|
size.y = half.y + world->orthohalf.y;
|
|
|
|
return ( fabs( delta.x ) > size.x ) || ( fabs( delta.y ) > size.y );
|
|
}
|