Paranoia2/cl_dll/render/gl_scene.cpp

548 lines
15 KiB
C++

/*
gl_scene.cpp - scene management
Copyright (C) 2016 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 "camera.h"
#include "entity_types.h"
#include "gl_local.h"
#include <mathlib.h>
#include "gl_aurora.h"
#include "gl_rpart.h"
#include "gl_studio.h"
#include "gl_sprite.h"
#include "event_api.h"
#include "gl_world.h"
#include "gl_grass.h"
#include "screenfade.h"
#include "shake.h"
/*
===============
R_CheckChanges
===============
*/
void R_CheckChanges( void )
{
static bool fog_enabled_old;
bool settings_changed = false;
if( FBitSet( r_showlightmaps->flags, FCVAR_CHANGED ))
{
float lightmap = bound( 0.0f, r_showlightmaps->value, MAX_LIGHTMAPS );
CVAR_SET_FLOAT( "r_showlightmaps", lightmap );
ClearBits( r_showlightmaps->flags, FCVAR_CHANGED );
}
if( FBitSet( r_studio_decals->flags, FCVAR_CHANGED ))
{
float maxStudioDecals = bound( 10.0f, r_studio_decals->value, 256.0f );
CVAR_SET_FLOAT( "r_studio_decals", maxStudioDecals );
ClearBits( r_studio_decals->flags, FCVAR_CHANGED );
}
if( FBitSet( cv_deferred_maxlights->flags, FCVAR_CHANGED ))
{
float maxDeferredLights = bound( 1, cv_deferred_maxlights->value, MAXDYNLIGHTS );
if( maxDeferredLights != cv_deferred_maxlights->value )
CVAR_SET_FLOAT( "gl_deferred_maxlights", maxDeferredLights );
ClearBits( cv_deferred_maxlights->flags, FCVAR_CHANGED );
}
if( FBitSet( cv_deferred_tracebmodels->flags, FCVAR_CHANGED ))
{
ClearBits( cv_deferred_tracebmodels->flags, FCVAR_CHANGED );
tr.params_changed = true;
}
if( FBitSet( r_recursion_depth->flags, FCVAR_CHANGED ))
{
float depth = bound( 0.0f, r_recursion_depth->value, MAX_REF_STACK - 2 );
CVAR_SET_FLOAT( "gl_recursion_depth", depth );
ClearBits( r_recursion_depth->flags, FCVAR_CHANGED );
}
if( FBitSet( r_drawentities->flags, FCVAR_CHANGED ))
{
ClearBits( r_drawentities->flags, FCVAR_CHANGED );
tr.params_changed = true;
}
if( FBitSet( r_lightstyles->flags, FCVAR_CHANGED ))
{
ClearBits( r_lightstyles->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_cubemaps->flags, FCVAR_CHANGED ))
{
ClearBits( cv_cubemaps->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_deferred->flags, FCVAR_CHANGED ))
{
ClearBits( cv_deferred->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_lightmap->flags, FCVAR_CHANGED ))
{
ClearBits( r_lightmap->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_allow_mirrors->flags, FCVAR_CHANGED ))
{
ClearBits( r_allow_mirrors->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_realtime_puddles->flags, FCVAR_CHANGED ))
{
ClearBits( cv_realtime_puddles->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_detailtextures->flags, FCVAR_CHANGED ))
{
ClearBits( r_detailtextures->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_fullbright->flags, FCVAR_CHANGED ))
{
ClearBits( r_fullbright->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_sunshadows->flags, FCVAR_CHANGED ))
{
ClearBits( r_sunshadows->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_sun_allowed->flags, FCVAR_CHANGED ))
{
ClearBits( r_sun_allowed->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_parallax->flags, FCVAR_CHANGED ))
{
ClearBits( cv_parallax->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_specular->flags, FCVAR_CHANGED ))
{
ClearBits( cv_specular->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_grass_shadows->flags, FCVAR_CHANGED ))
{
ClearBits( r_grass_shadows->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_shadows->flags, FCVAR_CHANGED ))
{
ClearBits( r_shadows->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_bump->flags, FCVAR_CHANGED ))
{
ClearBits( cv_bump->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_brdf->flags, FCVAR_CHANGED ))
{
ClearBits( cv_brdf->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( r_test->flags, FCVAR_CHANGED ))
{
ClearBits( r_test->flags, FCVAR_CHANGED );
R_StudioClearLightCache();
settings_changed = true;
}
if( FBitSet( r_grass->flags, FCVAR_CHANGED ))
{
if( worldmodel != NULL )
{
for( int i = 0; i < worldmodel->numsurfaces; i++ )
SetBits( worldmodel->surfaces[i].flags, SURF_GRASS_UPDATE );
}
ClearBits( r_grass->flags, FCVAR_CHANGED );
settings_changed = true;
}
if( FBitSet( cv_gamma->flags, FCVAR_CHANGED ) || FBitSet( cv_brightness->flags, FCVAR_CHANGED ))
{
if( worldmodel != NULL )
{
for( int i = 0; i < worldmodel->numsurfaces; i++ )
SetBits( worldmodel->surfaces[i].flags, SURF_LM_UPDATE|SURF_GRASS_UPDATE );
}
R_StudioClearLightCache();
}
if( tr.fogEnabled != fog_enabled_old )
{
fog_enabled_old = tr.fogEnabled;
settings_changed = true;
}
if( settings_changed )
{
tr.glsl_valid_sequence++; // now all uber-shaders are invalidate and possible need for recompile
tr.params_changed = true;
}
}
/*
===============
R_ClearScene
===============
*/
void R_ClearScene( void )
{
if( !g_fRenderInitialized ) return;
CL_DecayLights();
tr.time = GET_CLIENT_TIME();
tr.oldtime = GET_CLIENT_OLDTIME();
tr.frametime = tr.time - tr.oldtime;
tr.saved_frametime = tr.frametime; // backup
memset( &r_stats, 0, sizeof( r_stats ));
tr.local_client_added = false;
tr.num_draw_entities = 0;
tr.cached_state.Purge(); // invalidate cache
GET_ENTITY( 0 )->hCachedMatrix = GL_CacheState( g_vecZero, g_vecZero );
tr.num_2D_shadows_used = tr.num_CM_shadows_used = 0;
tr.sun_light_enabled = false;
if( r_sunshadows->value > 2.0f )
CVAR_SET_FLOAT( "gl_sun_shadows", 2.0f );
else if( r_sunshadows->value < 0.0f )
CVAR_SET_FLOAT( "gl_sun_shadows", 0.0f );
if( tr.shadows_notsupport )
CVAR_SET_FLOAT( "r_shadows", 0.0f );
R_CheckChanges();
if( tr.params_changed )
R_InitDynLightShaders();
}
/*
===============
R_ComputeFxBlend
===============
*/
int R_ComputeFxBlend( cl_entity_t *e )
{
int blend = 0, renderAmt;
float offset, dist;
Vector tmp;
offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx
renderAmt = e->curstate.renderamt;
switch( e->curstate.renderfx )
{
case kRenderFxPulseSlowWide:
blend = renderAmt + 0x40 * sin( tr.time * 2 + offset );
break;
case kRenderFxPulseFastWide:
blend = renderAmt + 0x40 * sin( tr.time * 8 + offset );
break;
case kRenderFxPulseSlow:
blend = renderAmt + 0x10 * sin( tr.time * 2 + offset );
break;
case kRenderFxPulseFast:
blend = renderAmt + 0x10 * sin( tr.time * 8 + offset );
break;
// JAY: HACK for now -- not time based
case kRenderFxFadeSlow:
if( renderAmt > 0 )
renderAmt -= 1;
else renderAmt = 0;
blend = renderAmt;
break;
case kRenderFxFadeFast:
if( renderAmt > 3 )
renderAmt -= 4;
else renderAmt = 0;
blend = renderAmt;
break;
case kRenderFxSolidSlow:
if( renderAmt < 255 )
renderAmt += 1;
else renderAmt = 255;
blend = renderAmt;
break;
case kRenderFxSolidFast:
if( renderAmt < 252 )
renderAmt += 4;
else renderAmt = 255;
blend = renderAmt;
break;
case kRenderFxStrobeSlow:
blend = 20 * sin( tr.time * 4 + offset );
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxStrobeFast:
blend = 20 * sin( tr.time * 16 + offset );
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxStrobeFaster:
blend = 20 * sin( tr.time * 36 + offset );
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxFlickerSlow:
blend = 20 * (sin( tr.time * 2 ) + sin( tr.time * 17 + offset ));
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxFlickerFast:
blend = 20 * (sin( tr.time * 16 ) + sin( tr.time * 23 + offset ));
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxHologram:
case kRenderFxDistort:
tmp = e->origin - GetVieworg();
dist = DotProduct( tmp, GetVForward( ));
// Turn off distance fade
if( e->curstate.renderfx == kRenderFxDistort )
dist = 1;
if( dist <= 0 )
{
blend = 0;
}
else
{
renderAmt = 180;
if( dist <= 100 ) blend = renderAmt;
else blend = (int) ((1.0f - ( dist - 100 ) * ( 1.0f / 400.0f )) * renderAmt );
blend += RANDOM_LONG( -32, 31 );
}
break;
case kRenderFxGlowShell: // safe current renderamt because it's shell scale!
case kRenderFxDeadPlayer: // safe current renderamt because it's player index!
blend = renderAmt;
break;
case kRenderFxNone:
case kRenderFxClampMinScale:
default:
if( e->curstate.rendermode == kRenderNormal )
blend = 255;
else blend = renderAmt;
break;
}
if( e->model->type != mod_brush || R_WaterEntity( e->model ))
{
// NOTE: never pass sprites with rendercolor '0 0 0' it's a stupid Valve Hammer Editor bug
if( !e->curstate.rendercolor.r && !e->curstate.rendercolor.g && !e->curstate.rendercolor.b )
e->curstate.rendercolor.r = e->curstate.rendercolor.g = e->curstate.rendercolor.b = 255;
}
// apply scale to studiomodels and sprites only
if( e->model && e->model->type != mod_brush && !e->curstate.scale )
e->curstate.scale = 1.0f;
blend = bound( 0, blend, 255 );
return blend;
}
/*
===============
R_AddEntity
===============
*/
qboolean R_AddEntity( struct cl_entity_s *clent, int entityType )
{
if( !CVAR_TO_BOOL( r_drawentities ))
return false; // not allow to drawing
if( !clent || !clent->model )
return false; // if set to invisible, skip
if( clent->curstate.effects & EF_NODRAW )
return false; // done
if( entityType == ET_PLAYER && RP_LOCALCLIENT( clent ))
{
if( tr.local_client_added )
return false; // already present in list
tr.local_client_added = true;
}
if( clent->curstate.renderfx == 71 ) // dynamic light
{
CDynLight *dl = CL_AllocDlight( clent->curstate.number );
float radius = clent->curstate.renderamt * 8.0f;
float fov = clent->curstate.scale;
int tex = 0, flags = 0, type;
Vector origin, angles;
if( clent->curstate.scale ) // spotlight
{
int i = bound( 0, clent->curstate.rendermode, 7 );
tex = tr.spotlightTexture[i];
type = LIGHT_SPOT;
}
else type = LIGHT_OMNI;
if( clent->curstate.effects & EF_NOSHADOW )
flags |= DLF_NOSHADOWS;
if( clent->curstate.effects & EF_NOBUMP )
flags |= DLF_NOBUMP;
if( clent->curstate.effects & EF_LENSFLARE )
flags |= DLF_LENSFLARE;
R_GetLightVectors( clent, origin, angles );
R_SetupLightParams( dl, origin, angles, radius, clent->curstate.scale, type, flags );
R_SetupLightTexture( dl, tex );
dl->color[0] = (float)clent->curstate.rendercolor.r / 128;
dl->color[1] = (float)clent->curstate.rendercolor.g / 128;
dl->color[2] = (float)clent->curstate.rendercolor.b / 128;
dl->die = tr.time + 0.05f;
return true; // no reason to drawing this entity
}
else if( clent->curstate.renderfx == 72 ) // dynamic light with avi file
{
if( !clent->curstate.sequence )
return true; // bad avi file
CDynLight *dl = CL_AllocDlight( clent->curstate.number );
if( dl->spotlightTexture == tr.spotlightTexture[1] )
return true; // bad avi file
float radius = clent->curstate.renderamt * 8.0f;
float fov = clent->curstate.scale;
Vector origin, angles;
int flags = DLF_ASPECT3X4; // fit to film01.avi aspect
// found the corresponding cinstate
const char *cinname = gRenderfuncs.GetFileByIndex( clent->curstate.sequence );
int hCin = R_PrecacheCinematic( cinname );
if( hCin >= 0 && !dl->cinTexturenum )
dl->cinTexturenum = R_AllocateCinematicTexture( TF_SPOTLIGHT );
if( hCin == -1 || dl->cinTexturenum <= 0 || !CIN_IS_ACTIVE( tr.cinematics[hCin].state ))
{
// cinematic textures limit exceeded or movie not found
dl->spotlightTexture = tr.spotlightTexture[1];
return true;
}
gl_movie_t *cin = &tr.cinematics[hCin];
float cin_time;
// advances cinematic time
cin_time = fmod( clent->curstate.fuser2, cin->length );
// read the next frame
int cin_frame = CIN_GET_FRAME_NUMBER( cin->state, cin_time );
if( cin_frame != dl->lastframe )
{
// upload the new frame
byte *raw = CIN_GET_FRAMEDATA( cin->state, cin_frame );
CIN_UPLOAD_FRAME( tr.cinTextures[dl->cinTexturenum-1], cin->xres, cin->yres, cin->xres, cin->yres, raw );
dl->lastframe = cin_frame;
}
if( clent->curstate.effects & EF_NOSHADOW )
flags |= DLF_NOSHADOWS;
if( clent->curstate.effects & EF_NOBUMP )
flags |= DLF_NOBUMP;
R_GetLightVectors( clent, origin, angles );
R_SetupLightParams( dl, origin, angles, radius, clent->curstate.scale, LIGHT_SPOT, flags );
R_SetupLightTexture( dl, tr.cinTextures[dl->cinTexturenum-1] );
dl->color[0] = (float)clent->curstate.rendercolor.r / 128;
dl->color[1] = (float)clent->curstate.rendercolor.g / 128;
dl->color[2] = (float)clent->curstate.rendercolor.b / 128;
dl->die = GET_CLIENT_TIME() + 0.05f;
return true; // no reason to drawing this entity
}
if( clent->curstate.effects & EF_SCREENMOVIE )
{
// update cin sound properly
R_UpdateCinSound( clent );
}
clent->curstate.renderamt = R_ComputeFxBlend( clent );
if( !R_OpaqueEntity( clent ))
{
if( clent->curstate.renderamt <= 0.0f )
return true; // invisible
}
#if 0
if( clent->model->type == mod_brush && CVAR_TO_BOOL( r_test ))
return true;
#endif
if( clent->model->type == mod_brush )
clent->hCachedMatrix = GL_CacheState( clent->origin, clent->angles, ( clent->curstate.renderfx == SKYBOX_ENTITY ));
clent->curstate.entityType = entityType;
// mark static entity as visible
if( entityType == ET_FRAGMENTED )
{
// non-solid statics wants a new lighting too :-)
SetBits( clent->curstate.iuser1, CF_STATIC_ENTITY );
clent->visframe = tr.realframecount;
}
if( tr.num_draw_entities < MAX_VISIBLE_ENTS )
{
tr.draw_entities[tr.num_draw_entities] = clent;
tr.num_draw_entities++;
}
return true;
}