Paranoia2/cl_dll/render/gl_cull.cpp

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 );
}