1055 lines
27 KiB
C++
1055 lines
27 KiB
C++
/*
|
|
gl_backend.cpp - renderer backend routines
|
|
Copyright (C) 2013 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 "const.h"
|
|
#include "com_model.h"
|
|
#include <stringlib.h>
|
|
#include "r_studioint.h"
|
|
#include <pm_movevars.h>
|
|
#include <pm_defs.h>
|
|
#include "ref_params.h"
|
|
#include "pmtrace.h"
|
|
#include "event_api.h"
|
|
#include "gl_local.h"
|
|
#include "gl_studio.h"
|
|
#include "gl_sprite.h"
|
|
#include "gl_world.h"
|
|
#include "gl_grass.h"
|
|
#include "gl_shader.h"
|
|
#include "screenfade.h"
|
|
#include "shake.h"
|
|
|
|
#define SKY_FOG_FACTOR 16.0f // experimentally determined value (chislo s potolka)
|
|
|
|
/*
|
|
==============
|
|
R_Speeds_Printf
|
|
|
|
helper to print into r_speeds message
|
|
==============
|
|
*/
|
|
void R_Speeds_Printf( const char *msg, ... )
|
|
{
|
|
va_list argptr;
|
|
char text[2048];
|
|
|
|
va_start( argptr, msg );
|
|
Q_vsprintf( text, msg, argptr );
|
|
va_end( argptr );
|
|
|
|
Q_strncat( r_speeds_msg, text, sizeof( r_speeds_msg ));
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_PrintStats
|
|
|
|
display renderer stats
|
|
==============
|
|
*/
|
|
void GL_PrintStats( int params )
|
|
{
|
|
GLint cur_avail_mem_kb = 0;
|
|
GLint total_mem_kb = 0;
|
|
|
|
if( r_speeds->value <= 0.0f || !FBitSet( params, RP_DRAW_WORLD ))
|
|
return;
|
|
|
|
msurface_t *surf = r_stats.debug_surface;
|
|
|
|
switch( (int)r_speeds->value )
|
|
{
|
|
case 1:
|
|
R_Speeds_Printf( "%3i solid faces\n%3i solid meshes\n", RI->frame.solid_faces.Count(), RI->frame.solid_meshes.Count() );
|
|
R_Speeds_Printf( "%3i trans entries\n%3i grass faces\n", RI->frame.trans_list.Count(), RI->frame.grass_list.Count() );
|
|
R_Speeds_Printf( "%3i mirror faces\n%3i tess verts\n", RI->frame.num_subview_faces, RI->frame.primverts.Count() );
|
|
break;
|
|
case 2:
|
|
R_Speeds_Printf( "DIP count %3i\nShader bind %3i\n", r_stats.num_flushes, r_stats.num_shader_binds );
|
|
R_Speeds_Printf( "Frame total tris %3i\n", r_stats.c_total_tris );
|
|
R_Speeds_Printf( "Total GLSL shaders %3i", num_glsl_programs - 1 );
|
|
break;
|
|
case 3:
|
|
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i mirrors\n%3i shadow passes\n%3i screencopy\n%3i occluded",
|
|
r_stats.c_subview_passes, r_stats.c_shadow_passes, r_stats.c_screen_copy, r_stats.c_occlusion_culled );
|
|
break;
|
|
case 4:
|
|
if( glConfig.hardware_type == GLHW_NVIDIA )
|
|
{
|
|
pglGetIntegerv( GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX, &total_mem_kb );
|
|
pglGetIntegerv( GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX, &cur_avail_mem_kb );
|
|
}
|
|
R_Speeds_Printf( "TEX used memory %s\n", Q_memprint( RENDER_GET_PARM( PARM_TEX_MEMORY, 0 )));
|
|
R_Speeds_Printf( "VBO used memory %s\n", Q_memprint( tr.total_vbo_memory ));
|
|
R_Speeds_Printf( "GPU used memory %s\n", Q_memprint(( total_mem_kb - cur_avail_mem_kb ) * 1024 ));
|
|
break;
|
|
case 5:
|
|
// draw hierarchy map of recursion calls
|
|
R_Speeds_Printf( "total %3i subview passes\n", r_stats.c_subview_passes );
|
|
R_Speeds_Printf( "%s", r_depth_msg );
|
|
break;
|
|
case 6:
|
|
if( r_stats.debug_surface != NULL )
|
|
{
|
|
glsl_program_t *cur = surf->info->forwardScene[0].GetShader();
|
|
R_Speeds_Printf( "Surf: ^1%s^7\n", surf->texinfo->texture->name );
|
|
R_Speeds_Printf( "Shader: ^3#%i %s^7\n", surf->info->forwardScene[0].GetHandle(), cur->name );
|
|
R_Speeds_Printf( "List Options:\n" );
|
|
R_Speeds_Printf( "%s\n", GL_PretifyListOptions( cur->options, true ));
|
|
}
|
|
break;
|
|
case 7:
|
|
R_Speeds_Printf( "%3i world lights (from %d lights)\n", r_stats.c_worldlights, world->numworldlights );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_ComputeScreenRays
|
|
==============
|
|
*/
|
|
void GL_ComputeScreenRays( void )
|
|
{
|
|
vec3_t axis[3];
|
|
float tx, ty;
|
|
int i;
|
|
|
|
// compute the world-space rays to the far plane corners
|
|
tx = tan( DEG2RAD( RI->view.fov_x * 0.5f ));
|
|
ty = tan( DEG2RAD( RI->view.fov_y * 0.5f ));
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
axis[0][i] = RI->view.matrix[0][i];
|
|
axis[1][i] = RI->view.matrix[1][i] * tx;
|
|
axis[2][i] = RI->view.matrix[2][i] * ty;
|
|
|
|
// counter-clockwise order
|
|
tr.screen_normals[0][i] = axis[0][i] + axis[1][i] + axis[2][i]; // top left
|
|
tr.screen_normals[1][i] = axis[0][i] + axis[1][i] - axis[2][i]; // bottom left
|
|
tr.screen_normals[2][i] = axis[0][i] - axis[1][i] - axis[2][i]; // bottom right
|
|
tr.screen_normals[3][i] = axis[0][i] - axis[1][i] + axis[2][i]; // top right
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_ComputeSunParams
|
|
==============
|
|
*/
|
|
void GL_ComputeSunParams( const Vector &skyVector )
|
|
{
|
|
float sunPos = -skyVector.z;
|
|
float RefractionFactor = ( 1.0f - sqrt( Q_max( sunPos, 0.0f )));
|
|
Vector SunColor = Vector( 1.0f, 1.0f, 1.0f ) - Vector( 0.0f, 0.5f, 1.0f ) * RefractionFactor;
|
|
|
|
// calculate ambient and diffuse light color
|
|
Vector DiffuseColor = Vector( 1.0f, 1.0f, 1.0f ) - Vector( 0.0f, 0.25f, 0.5f ) * RefractionFactor;
|
|
float AmbientIntensity = 0.0025f + 0.1875f * Q_min( 1.0f, Q_max( 0.0f, ( 0.375f + sunPos ) / 0.25f ));
|
|
float DiffuseIntensity = 0.75f * Q_min( 1.0f, Q_max( 0.0f, ( 0.03125f + sunPos ) / 0.0625f ));
|
|
|
|
float ambientFactor = RemapVal( tr.ambientFactor, AMBIENT_EPSILON, r_lighting_modulate->value, 0.0f, 1.0f );
|
|
tr.sun_ambient = AmbientIntensity * ambientFactor * 2.0f;
|
|
tr.sun_ambient = bound( 0.0f, tr.sun_ambient, tr.ambientFactor );
|
|
tr.sun_diffuse = DiffuseColor * DiffuseIntensity;
|
|
tr.sun_refract = RefractionFactor;
|
|
tr.sky_normal = skyVector;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_BackendStartFrame
|
|
==============
|
|
*/
|
|
bool GL_BackendStartFrame( ref_viewpass_t *rvp, int params )
|
|
{
|
|
bool allow_dynamic_sun = false;
|
|
static float cached_lighting = 0.0f;
|
|
static float shadowmap_size = 0.0f;
|
|
static int waterlevel_old;
|
|
|
|
r_speeds_msg[0] = r_depth_msg[0] = '\0';
|
|
tr.fCustomRendering = false;
|
|
|
|
tr.fGamePaused = RENDER_GET_PARM( PARAM_GAMEPAUSED, 0 );
|
|
memset( &r_stats, 0, sizeof( r_stats ));
|
|
tr.sunlight = NULL;
|
|
|
|
if( r_buildstats.total_buildtime > 0.0 && RENDER_GET_PARM( PARM_CLIENT_ACTIVE, 0 ))
|
|
{
|
|
// display time to building some stuff: lighting, VBO, shaders etc
|
|
if( r_buildstats.compile_shader > 0.0 )
|
|
ALERT( at_aiconsole, "shader compiled in %g secs\n", r_buildstats.compile_shader );
|
|
if( r_buildstats.create_buffer_object > 0.0 )
|
|
ALERT( at_aiconsole, "creating VBO in %g secs\n", r_buildstats.create_buffer_object - r_buildstats.compile_shader );
|
|
if( r_buildstats.create_light_cache > 0.0 )
|
|
ALERT( at_aiconsole, "creating VL cache in %g secs\n", r_buildstats.create_light_cache );
|
|
if( r_buildstats.create_buffer_object > 0.0 || r_buildstats.create_light_cache > 0.0 )
|
|
ALERT( at_aiconsole, "total building time %g\n", r_buildstats.total_buildtime );
|
|
memset( &r_buildstats, 0, sizeof( r_buildstats ));
|
|
}
|
|
|
|
if( !g_fRenderInitialized )
|
|
return 0;
|
|
|
|
if( !FBitSet( params, RP_DRAW_WORLD ))
|
|
GL_Setup3D();
|
|
|
|
if( CVAR_TO_BOOL( r_finish ) && FBitSet( params, RP_DRAW_WORLD ))
|
|
pglFinish();
|
|
|
|
// setup light factors
|
|
tr.ambientFactor = bound( AMBIENT_EPSILON, r_lighting_ambient->value, r_lighting_modulate->value );
|
|
float cachedFactor = bound( tr.ambientFactor, r_lighting_modulate->value, 1.0f );
|
|
|
|
if( cachedFactor != tr.diffuseFactor )
|
|
{
|
|
tr.diffuseFactor = cachedFactor;
|
|
R_StudioClearLightCache();
|
|
}
|
|
|
|
if( cached_lighting != r_lighting_extended->value )
|
|
{
|
|
cached_lighting = r_lighting_extended->value;
|
|
R_StudioClearLightCache();
|
|
}
|
|
|
|
if( shadowmap_size != r_shadowmap_size->value )
|
|
{
|
|
shadowmap_size = r_shadowmap_size->value;
|
|
R_InitShadowTextures();
|
|
}
|
|
|
|
// FIXME: allow player overview for custom renderer
|
|
if( !FBitSet( params, RP_DRAW_WORLD ))
|
|
return 0;
|
|
|
|
// use engine renderer
|
|
if( cv_renderer->value == 0 )
|
|
{
|
|
glState.depthmin = -1.0f;
|
|
glState.depthmax = -1.0f;
|
|
glState.depthmask = -1;
|
|
return 0;
|
|
}
|
|
|
|
// keep world in actual state
|
|
GET_ENTITY( 0 )->curstate.messagenum = r_currentMessageNum;
|
|
|
|
if( gHUD.m_flLevelTime != -1.0f && CVAR_TO_BOOL( r_sun_allowed ))
|
|
{
|
|
Vector skyVector;
|
|
matrix4x4 a;
|
|
|
|
float time = gHUD.m_flLevelTime;
|
|
float timeAng = ((( time + 12.0f ) / 24.0f ) * M_PI * 2.0f );
|
|
float longitude = RAD2DEG( 0.5f * M_PI ) - 90.0f;
|
|
|
|
a.CreateRotate( RAD2DEG( timeAng ) - 180.0f, 1.0, 0.5f, 0.0f );
|
|
a.ConcatRotate( longitude, -0.5f, 1.0f, 0.0f );
|
|
skyVector = a.VectorRotate( Vector( 0.0f, 0.0f, 1.0f ));
|
|
|
|
gEngfuncs.Con_NPrintf( 7, "time %i:%02.f\n", (int)time, ( time - (int)time ) * 59.0f );
|
|
skyVector = skyVector.Normalize();
|
|
GL_ComputeSunParams( skyVector );
|
|
allow_dynamic_sun = true;
|
|
}
|
|
else
|
|
{
|
|
Vector skyVector;
|
|
|
|
skyVector.x = tr.movevars->skyvec_x;
|
|
skyVector.y = tr.movevars->skyvec_y;
|
|
skyVector.z = tr.movevars->skyvec_z;
|
|
skyVector = skyVector.Normalize();
|
|
|
|
GL_ComputeSunParams( skyVector );
|
|
|
|
// hidden test mode
|
|
if( r_sun_allowed->value == 2.0 )
|
|
allow_dynamic_sun = true;
|
|
}
|
|
|
|
tr.sky_ambient.x = tr.movevars->skycolor_r;
|
|
tr.sky_ambient.y = tr.movevars->skycolor_g;
|
|
tr.sky_ambient.z = tr.movevars->skycolor_b;
|
|
|
|
// NOTE: sunlight must be added first in list
|
|
if( FBitSet( world->features, WORLD_HAS_SKYBOX ) && allow_dynamic_sun )
|
|
{
|
|
tr.sunlight = CL_AllocDlight( SUNLIGHT_KEY ); // alloc sun source
|
|
R_SetupLightParams( tr.sunlight, g_vecZero, g_vecZero, 32768.0f, 90.0f, LIGHT_DIRECTIONAL );
|
|
R_SetupLightTexture( tr.sunlight, tr.whiteTexture );
|
|
tr.sunlight->die = GET_CLIENT_TIME();
|
|
tr.sunlight->color = tr.sun_diffuse;
|
|
tr.sun_light_enabled = true;
|
|
}
|
|
|
|
tr.gravity = tr.movevars->gravity;
|
|
tr.farclip = tr.movevars->zmax;
|
|
|
|
if( tr.farclip == 0.0f || worldmodel == NULL )
|
|
tr.farclip = 4096.0f;
|
|
|
|
tr.cached_vieworigin = rvp->vieworigin;
|
|
tr.cached_viewangles = rvp->viewangles;
|
|
tr.waterlevel = g_pViewParams->waterlevel;
|
|
|
|
if(( tr.waterlevel >= 3 ) != ( waterlevel_old >= 3 ))
|
|
{
|
|
waterlevel_old = tr.waterlevel;
|
|
tr.glsl_valid_sequence++; // now all uber-shaders are invalidate and possible need for recompile
|
|
tr.params_changed = true;
|
|
}
|
|
|
|
if( tr.waterlevel >= 3 )
|
|
{
|
|
int waterent = WATER_ENTITY( g_pViewParams->vieworg );
|
|
|
|
// FIXME: how to allow fog on a world water?
|
|
if( waterent > 0 && waterent < g_pViewParams->max_entities )
|
|
tr.waterentity = GET_ENTITY( waterent );
|
|
else tr.waterentity = NULL;
|
|
}
|
|
else tr.waterentity = NULL;
|
|
|
|
R_GrassSetupFrame();
|
|
|
|
// check for fog
|
|
if( tr.waterentity )
|
|
{
|
|
entity_state_t *state = &tr.waterentity->curstate;
|
|
|
|
if( state->rendercolor.r || state->rendercolor.g || state->rendercolor.b )
|
|
{
|
|
// enable global exponential color fog
|
|
tr.fogColor[0] = (state->rendercolor.r) / 255.0f;
|
|
tr.fogColor[1] = (state->rendercolor.g) / 255.0f;
|
|
tr.fogColor[2] = (state->rendercolor.b) / 255.0f;
|
|
tr.fogDensity = (state->renderamt) * 0.000025f;
|
|
tr.fogSkyDensity = tr.fogDensity * SKY_FOG_FACTOR;
|
|
tr.fogEnabled = true;
|
|
}
|
|
}
|
|
else if( tr.movevars->fog_settings != 0 )
|
|
{
|
|
// enable global exponential color fog
|
|
tr.fogColor[0] = (float)TEXTURE_TO_TEXGAMMA((tr.movevars->fog_settings & 0xFF000000) >> 24) / 255.0f;
|
|
tr.fogColor[1] = (float)TEXTURE_TO_TEXGAMMA((tr.movevars->fog_settings & 0xFF0000) >> 16) / 255.0f;
|
|
tr.fogColor[2] = (float)TEXTURE_TO_TEXGAMMA((tr.movevars->fog_settings & 0xFF00) >> 8) / 255.0f;
|
|
tr.fogDensity = (tr.movevars->fog_settings & 0xFF) * 0.000025f;
|
|
tr.fogSkyDensity = tr.fogDensity * SKY_FOG_FACTOR;
|
|
tr.fogEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
tr.fogColor[0] = 0.0f;
|
|
tr.fogColor[1] = 0.0f;
|
|
tr.fogColor[2] = 0.0f;
|
|
tr.fogDensity = 0.0f;
|
|
tr.fogSkyDensity = 0.0f;
|
|
tr.fogEnabled = false;
|
|
}
|
|
|
|
// apply the underwater warp
|
|
if( tr.waterlevel >= 3 )
|
|
{
|
|
float f = sin( tr.time * 0.4f * ( M_PI * 1.7f ));
|
|
rvp->fov_x += f;
|
|
rvp->fov_y -= f;
|
|
}
|
|
|
|
// apply the blur effect
|
|
if( gHUD.m_flBlurAmount > 0.0f )
|
|
{
|
|
float f = sin( tr.time * 0.4 * ( M_PI * 1.7f )) * gHUD.m_flBlurAmount * 20.0f;
|
|
rvp->fov_x += f;
|
|
rvp->fov_y += f;
|
|
|
|
screenfade_t fade;
|
|
|
|
f = ( sin( tr.time * 0.5f * ( M_PI * 1.7f )) * 127 ) + 128;
|
|
fade.fadeFlags = FFADE_STAYOUT;
|
|
fade.fader = fade.fadeg = fade.fadeb = 0;
|
|
fade.fadeReset = tr.time + 0.1f;
|
|
fade.fadeEnd = tr.time + 0.1f;
|
|
fade.fadealpha = bound( 0, (byte)f, gHUD.m_flBlurAmount * 255 );
|
|
|
|
gEngfuncs.pfnSetScreenFade( &fade );
|
|
}
|
|
|
|
Mod_ResortFaces();
|
|
GL_LoadAndRebuildCubemaps( params );
|
|
tr.fCustomRendering = true;
|
|
r_stats.debug_surface = NULL;
|
|
|
|
if( r_speeds->value == 6.0f )
|
|
{
|
|
Vector vecSrc = RI->view.origin;
|
|
Vector vecEnd = vecSrc + GetVForward() * RI->view.farClip;
|
|
pmtrace_t trace;
|
|
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( (float *)&vecSrc, (float *)&vecEnd, PM_NORMAL, -1, &trace );
|
|
r_stats.debug_surface = gEngfuncs.pEventAPI->EV_TraceSurface( trace.ent, (float *)&vecSrc, (float *)&vecEnd );
|
|
}
|
|
|
|
// setup light animation tables
|
|
R_AnimateLight();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_BackendEndFrame
|
|
==============
|
|
*/
|
|
void GL_BackendEndFrame( ref_viewpass_t *rvp, int params )
|
|
{
|
|
mstudiolight_t light;
|
|
|
|
tr.frametime = tr.saved_frametime;
|
|
|
|
// go into 2D mode (in case we draw PlayerSetup between two 2d calls)
|
|
if( !FBitSet( params, RP_DRAW_WORLD ))
|
|
GL_Setup2D();
|
|
|
|
if( tr.show_uniforms_peak )
|
|
{
|
|
ALERT( at_aiconsole, "peak used uniforms: %i\n", glConfig.peak_used_uniforms );
|
|
tr.show_uniforms_peak = false;
|
|
}
|
|
|
|
DrawLightProbes(); // 3D
|
|
|
|
DrawCubeMaps(); // 3D
|
|
|
|
DrawViewLeaf(); // 3D
|
|
|
|
DrawWireFrame(); // 3D
|
|
|
|
DrawTangentSpaces(); // 3D
|
|
|
|
DrawWirePoly( r_stats.debug_surface ); // 3D
|
|
|
|
DBG_DrawLightFrustum(); // 3D
|
|
|
|
R_PushRefState();
|
|
RI->params = params;
|
|
RI->view.fov_x = rvp->fov_x;
|
|
RI->view.fov_y = rvp->fov_y;
|
|
|
|
if( !CVAR_TO_BOOL( cv_deferred ))
|
|
R_DrawViewModel(); // 3D
|
|
|
|
RenderSunShafts(); // 2D
|
|
RenderDOF(); // 2D
|
|
|
|
RenderNerveGasBlur(); // 2D
|
|
RenderUnderwaterBlur(); // 2D
|
|
|
|
if( !CVAR_TO_BOOL( cv_deferred ))
|
|
R_DrawHeadShield(); // 3D
|
|
R_RenderDebugStudioList( true ); // 3D
|
|
|
|
RenderMonochrome(); // 2D
|
|
R_ShowLightMaps(); // 2D
|
|
|
|
if( g_iGunMode == 3 )
|
|
{
|
|
// used for lighting scope
|
|
R_LightVec( rvp->vieworigin, &light, true );
|
|
tr.ambientLight = light.diffuse;
|
|
}
|
|
|
|
GL_CleanupDrawState();
|
|
R_PopRefState();
|
|
|
|
// fill the r_speeds message
|
|
GL_PrintStats( params );
|
|
|
|
R_UnloadFarGrass();
|
|
|
|
tr.params_changed = false;
|
|
tr.realframecount++;
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ScreenToWorld
|
|
|
|
transform point from 3D position to ortho
|
|
===============
|
|
*/
|
|
bool R_WorldToScreen( const Vector &point, Vector &screen )
|
|
{
|
|
matrix4x4 worldToScreen;
|
|
bool behind;
|
|
float w;
|
|
|
|
worldToScreen = RI->view.worldProjectionMatrix;
|
|
|
|
screen[0] = worldToScreen[0][0] * point[0] + worldToScreen[1][0] * point[1] + worldToScreen[2][0] * point[2] + worldToScreen[3][0];
|
|
screen[1] = worldToScreen[0][1] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[2][1] * point[2] + worldToScreen[3][1];
|
|
w = worldToScreen[0][3] * point[0] + worldToScreen[1][3] * point[1] + worldToScreen[2][3] * point[2] + worldToScreen[3][3];
|
|
screen[2] = 0.0f; // just so we have something valid here
|
|
|
|
if( w < 0.001f )
|
|
{
|
|
behind = true;
|
|
screen[0] *= 100000;
|
|
screen[1] *= 100000;
|
|
}
|
|
else
|
|
{
|
|
float invw = 1.0f / w;
|
|
behind = false;
|
|
screen[0] *= invw;
|
|
screen[1] *= invw;
|
|
}
|
|
|
|
return behind;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ScreenToWorld
|
|
|
|
transform point from ortho to 3D position
|
|
===============
|
|
*/
|
|
void R_ScreenToWorld( const Vector &screen, Vector &point )
|
|
{
|
|
matrix4x4 screenToWorld;
|
|
Vector temp;
|
|
float w;
|
|
|
|
screenToWorld = RI->view.worldProjectionMatrix.InvertFull();
|
|
|
|
temp[0] = 2.0f * (screen[0] - RI->view.port[0]) / RI->view.port[2] - 1;
|
|
temp[1] = -2.0f * (screen[1] - RI->view.port[1]) / RI->view.port[3] + 1;
|
|
temp[2] = 0.0f; // just so we have something valid here
|
|
|
|
point[0] = temp[0] * screenToWorld[0][0] + temp[1] * screenToWorld[0][1] + temp[2] * screenToWorld[0][2] + screenToWorld[0][3];
|
|
point[1] = temp[0] * screenToWorld[1][0] + temp[1] * screenToWorld[1][1] + temp[2] * screenToWorld[1][2] + screenToWorld[1][3];
|
|
point[2] = temp[0] * screenToWorld[2][0] + temp[1] * screenToWorld[2][1] + temp[2] * screenToWorld[2][2] + screenToWorld[2][3];
|
|
w = temp[0] * screenToWorld[3][0] + temp[1] * screenToWorld[3][1] + temp[2] * screenToWorld[3][2] + screenToWorld[3][3];
|
|
if( w ) point *= ( 1.0f / w );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_GetSpriteTexture
|
|
|
|
=============
|
|
*/
|
|
int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame )
|
|
{
|
|
if( !m_pSpriteModel || m_pSpriteModel->type != mod_sprite || !m_pSpriteModel->cache.data )
|
|
return 0;
|
|
|
|
return R_GetSpriteFrame( m_pSpriteModel, frame )->gl_texturenum;
|
|
}
|
|
|
|
void R_RenderQuadPrimitive( CSolidEntry *entry )
|
|
{
|
|
if( entry->m_bDrawType != DRAWTYPE_QUAD )
|
|
return;
|
|
|
|
GL_CleanupDrawState();
|
|
|
|
// select properly rendermode
|
|
switch( entry->m_iRenderMode )
|
|
{
|
|
case kRenderTransAlpha:
|
|
GL_AlphaTest( GL_TRUE );
|
|
pglAlphaFunc( GL_GREATER, 0.0f );
|
|
case kRenderTransColor:
|
|
case kRenderTransTexture:
|
|
GL_Blend( GL_TRUE );
|
|
pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
GL_DepthMask( GL_FALSE );
|
|
break;
|
|
case kRenderGlow:
|
|
pglDisable( GL_DEPTH_TEST );
|
|
case kRenderTransAdd:
|
|
GL_Blend( GL_TRUE );
|
|
pglBlendFunc( GL_SRC_ALPHA, GL_ONE );
|
|
GL_DepthMask( GL_FALSE );
|
|
break;
|
|
case kRenderNormal:
|
|
default:
|
|
GL_DepthMask( GL_TRUE );
|
|
GL_Blend( GL_FALSE );
|
|
break;
|
|
}
|
|
|
|
int r, g, b, a;
|
|
|
|
// draw the particle
|
|
GL_BindTexture( GL_TEXTURE0, entry->m_hTexture );
|
|
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
UnpackRGBA( r, g, b, a, entry->m_iColor );
|
|
pglColor4ub( r, g, b, a );
|
|
|
|
pglBegin( GL_QUADS );
|
|
pglTexCoord2f( 0.0f, 1.0f );
|
|
pglVertex3fv( RI->frame.primverts[entry->m_iStartVertex+0] );
|
|
|
|
pglTexCoord2f( 0.0f, 0.0f );
|
|
pglVertex3fv( RI->frame.primverts[entry->m_iStartVertex+1] );
|
|
|
|
pglTexCoord2f( 1.0f, 0.0f );
|
|
pglVertex3fv( RI->frame.primverts[entry->m_iStartVertex+2] );
|
|
|
|
pglTexCoord2f( 1.0f, 1.0f );
|
|
pglVertex3fv( RI->frame.primverts[entry->m_iStartVertex+3] );
|
|
pglEnd();
|
|
|
|
if( entry->m_iRenderMode == kRenderGlow )
|
|
pglEnable( GL_DEPTH_TEST );
|
|
|
|
if( entry->m_iRenderMode == kRenderTransAlpha )
|
|
GL_AlphaTest( GL_FALSE );
|
|
}
|
|
|
|
int R_AllocFrameBuffer( int viewport[4] )
|
|
{
|
|
int i = tr.num_framebuffers;
|
|
|
|
if( i >= MAX_FRAMEBUFFERS )
|
|
{
|
|
ALERT( at_error, "R_AllocateFrameBuffer: FBO limit exceeded!\n" );
|
|
return -1; // disable
|
|
}
|
|
|
|
gl_fbo_t *fbo = &tr.frame_buffers[i];
|
|
tr.num_framebuffers++;
|
|
|
|
if( fbo->init )
|
|
{
|
|
ALERT( at_warning, "R_AllocFrameBuffer: buffer %i already initialized\n", i );
|
|
return i;
|
|
}
|
|
|
|
// create a depth-buffer
|
|
pglGenRenderbuffers( 1, &fbo->renderbuffer );
|
|
pglBindRenderbuffer( GL_RENDERBUFFER_EXT, fbo->renderbuffer );
|
|
pglRenderbufferStorage( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, viewport[2], viewport[3] );
|
|
pglBindRenderbuffer( GL_RENDERBUFFER_EXT, 0 );
|
|
|
|
// create frame-buffer
|
|
pglGenFramebuffers( 1, &fbo->framebuffer );
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, fbo->framebuffer );
|
|
pglFramebufferRenderbuffer( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->renderbuffer );
|
|
pglDrawBuffer( GL_COLOR_ATTACHMENT0_EXT );
|
|
pglReadBuffer( GL_COLOR_ATTACHMENT0_EXT );
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 );
|
|
fbo->init = true;
|
|
|
|
return i;
|
|
}
|
|
|
|
void R_FreeFrameBuffer( int buffer )
|
|
{
|
|
if( buffer < 0 || buffer >= MAX_FRAMEBUFFERS )
|
|
{
|
|
ALERT( at_error, "R_FreeFrameBuffer: invalid buffer enum %i\n", buffer );
|
|
return;
|
|
}
|
|
|
|
gl_fbo_t *fbo = &tr.frame_buffers[buffer];
|
|
|
|
pglDeleteRenderbuffers( 1, &fbo->renderbuffer );
|
|
pglDeleteFramebuffers( 1, &fbo->framebuffer );
|
|
memset( fbo, 0, sizeof( *fbo ));
|
|
}
|
|
|
|
void GL_BindFrameBuffer( int buffer, int texture )
|
|
{
|
|
gl_fbo_t *fbo = NULL;
|
|
|
|
if( buffer >= 0 && buffer < MAX_FRAMEBUFFERS )
|
|
fbo = &tr.frame_buffers[buffer];
|
|
|
|
if( !fbo )
|
|
{
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 );
|
|
glState.frameBuffer = buffer;
|
|
return;
|
|
}
|
|
|
|
// at this point FBO index is always positive
|
|
if((unsigned int)glState.frameBuffer != fbo->framebuffer )
|
|
{
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, fbo->framebuffer );
|
|
glState.frameBuffer = fbo->framebuffer;
|
|
}
|
|
|
|
if( fbo->texture != texture )
|
|
{
|
|
// change texture attachment
|
|
GLuint texnum = RENDER_GET_PARM( PARM_TEX_TEXNUM, texture );
|
|
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texnum, 0 );
|
|
fbo->texture = texture;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_BindFBO
|
|
==============
|
|
*/
|
|
void GL_BindFBO( GLuint buffer )
|
|
{
|
|
if( !GL_Support( R_FRAMEBUFFER_OBJECT ))
|
|
return;
|
|
|
|
if( glState.frameBuffer == buffer )
|
|
return;
|
|
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, buffer );
|
|
glState.frameBuffer = buffer;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
GL_BindDrawbuffer
|
|
==================
|
|
*/
|
|
void GL_BindDrawbuffer( gl_drawbuffer_t *drawbuffer )
|
|
{
|
|
if( !GL_Support( R_FRAMEBUFFER_OBJECT ))
|
|
return;
|
|
|
|
if( !drawbuffer )
|
|
{
|
|
if( !glState.frameBuffer )
|
|
return;
|
|
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 );
|
|
glState.frameBuffer = 0;
|
|
return;
|
|
}
|
|
|
|
if( glState.frameBuffer == drawbuffer->id )
|
|
return;
|
|
|
|
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, drawbuffer->id );
|
|
glState.frameBuffer = drawbuffer->id;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_DepthRange
|
|
==============
|
|
*/
|
|
void GL_DepthRange( GLfloat depthmin, GLfloat depthmax )
|
|
{
|
|
if( glState.depthmin == depthmin && glState.depthmax == depthmax )
|
|
return;
|
|
|
|
pglDepthRange( depthmin, depthmax );
|
|
glState.depthmin = depthmin;
|
|
glState.depthmax = depthmax;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_DepthMask
|
|
==============
|
|
*/
|
|
void GL_DepthMask( GLint enable )
|
|
{
|
|
// it's won't work anyway
|
|
// if( glState.depthmask == enable )
|
|
// return;
|
|
|
|
glState.depthmask = enable;
|
|
pglDepthMask( enable );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_AlphaTest
|
|
==============
|
|
*/
|
|
void GL_AlphaTest( GLint enable )
|
|
{
|
|
if( pglIsEnabled( GL_ALPHA_TEST ) == enable )
|
|
return;
|
|
|
|
if( enable ) pglEnable( GL_ALPHA_TEST );
|
|
else pglDisable( GL_ALPHA_TEST );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_DepthTest
|
|
==============
|
|
*/
|
|
void GL_DepthTest( GLint enable )
|
|
{
|
|
if( pglIsEnabled( GL_DEPTH_TEST ) == enable )
|
|
return;
|
|
|
|
if( enable ) pglEnable( GL_DEPTH_TEST );
|
|
else pglDisable( GL_DEPTH_TEST );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_Blend
|
|
==============
|
|
*/
|
|
void GL_Blend( GLint enable )
|
|
{
|
|
if( pglIsEnabled( GL_BLEND ) == enable )
|
|
return;
|
|
|
|
if( enable ) pglEnable( GL_BLEND );
|
|
else pglDisable( GL_BLEND );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_CleanupAllTextureUnits
|
|
==============
|
|
*/
|
|
void GL_CleanupAllTextureUnits( void )
|
|
{
|
|
// force to cleanup all the units
|
|
GL_SelectTexture( glConfig.max_texture_units - 1 );
|
|
GL_CleanUpTextureUnits( 0 );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_CleanupDrawState
|
|
==============
|
|
*/
|
|
void GL_CleanupDrawState( void )
|
|
{
|
|
GL_CleanupAllTextureUnits();
|
|
pglBindVertexArray( GL_FALSE );
|
|
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
|
|
GL_BindShader( NULL );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_CheckVertexArrayBinding
|
|
==============
|
|
*/
|
|
void GL_CheckVertexArrayBinding( void )
|
|
{
|
|
int current_vao = 0;
|
|
|
|
pglGetIntegerv( GL_VERTEX_ARRAY_BINDING, (int *)¤t_vao );
|
|
if( !current_vao ) return;
|
|
|
|
ALERT( at_error, "detected active VAO %d while uploading vertex buffer!\n", current_vao );
|
|
pglBindVertexArray( GL_FALSE );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
GL_DisableAllTexGens
|
|
==============
|
|
*/
|
|
void GL_DisableAllTexGens( void )
|
|
{
|
|
GL_TexGen( GL_S, 0 );
|
|
GL_TexGen( GL_T, 0 );
|
|
GL_TexGen( GL_R, 0 );
|
|
GL_TexGen( GL_Q, 0 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_ClipPlane
|
|
=================
|
|
*/
|
|
void GL_ClipPlane( bool enable )
|
|
{
|
|
// if cliplane was not set - do nothing
|
|
if( !FBitSet( RI->params, RP_CLIPPLANE ))
|
|
return;
|
|
|
|
if( glConfig.hardware_type == GLHW_RADEON )
|
|
return; // ATI doesn't need clip planes
|
|
|
|
if( enable )
|
|
{
|
|
GLdouble clip[4];
|
|
mplane_t *p = &RI->clipPlane;
|
|
|
|
clip[0] = p->normal[0];
|
|
clip[1] = p->normal[1];
|
|
clip[2] = p->normal[2];
|
|
clip[3] = -p->dist;
|
|
|
|
pglClipPlane( GL_CLIP_PLANE0, clip );
|
|
pglEnable( GL_CLIP_PLANE0 );
|
|
}
|
|
else
|
|
{
|
|
pglDisable( GL_CLIP_PLANE0 );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
GL_Cull
|
|
=================
|
|
*/
|
|
void GL_Cull( GLenum cull )
|
|
{
|
|
if( !cull )
|
|
{
|
|
pglDisable( GL_CULL_FACE );
|
|
glState.faceCull = 0;
|
|
return;
|
|
}
|
|
|
|
pglEnable( GL_CULL_FACE );
|
|
pglCullFace( cull );
|
|
glState.faceCull = cull;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_FrontFace
|
|
=================
|
|
*/
|
|
void GL_FrontFace( GLenum front )
|
|
{
|
|
pglFrontFace( front ? GL_CW : GL_CCW );
|
|
glState.frontFace = front;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_LoadTexMatrix
|
|
=================
|
|
*/
|
|
void GL_LoadTexMatrix( const matrix4x4 &source )
|
|
{
|
|
GLfloat dest[16];
|
|
|
|
source.CopyToArray( dest );
|
|
GL_LoadTextureMatrix( dest );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_LoadMatrix
|
|
=================
|
|
*/
|
|
void GL_LoadMatrix( const matrix4x4 &source )
|
|
{
|
|
GLfloat dest[16];
|
|
|
|
source.CopyToArray( dest );
|
|
pglLoadMatrixf( dest );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_Setup2D
|
|
=================
|
|
*/
|
|
void GL_Setup2D( void )
|
|
{
|
|
// set up full screen workspace
|
|
pglMatrixMode( GL_PROJECTION );
|
|
pglLoadIdentity();
|
|
|
|
pglOrtho( 0, glState.width, glState.height, 0, -99999, 99999 );
|
|
|
|
pglMatrixMode( GL_MODELVIEW );
|
|
pglLoadIdentity();
|
|
|
|
pglDisable( GL_DEPTH_TEST );
|
|
GL_AlphaTest( GL_FALSE );
|
|
GL_DepthMask( GL_FALSE );
|
|
GL_Blend( GL_FALSE );
|
|
|
|
GL_Cull( GL_FRONT );
|
|
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
pglViewport( 0, 0, glState.width, glState.height );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_Setup3D
|
|
=================
|
|
*/
|
|
void GL_Setup3D( void )
|
|
{
|
|
pglViewport( RI->glstate.viewport[0], RI->glstate.viewport[1], RI->glstate.viewport[2], RI->glstate.viewport[3] );
|
|
|
|
pglMatrixMode( GL_PROJECTION );
|
|
GL_LoadMatrix( RI->glstate.projectionMatrix );
|
|
|
|
pglMatrixMode( GL_MODELVIEW );
|
|
GL_LoadMatrix( RI->glstate.modelviewMatrix );
|
|
|
|
pglEnable( GL_DEPTH_TEST );
|
|
GL_DepthMask( GL_TRUE );
|
|
GL_Cull( GL_FRONT );
|
|
}
|
|
|
|
void CompressNormalizedVector( char outVec[3], const Vector &inVec )
|
|
{
|
|
const byte *bestFitTexture = GET_TEXTURE_DATA( tr.normalsFitting );
|
|
Vector vNorm = inVec.Normalize(); // should be normalized
|
|
|
|
if( bestFitTexture != NULL )
|
|
{
|
|
Vector uNorm = vNorm.Abs(); // get unsigned normal
|
|
float maxNAbs = VectorMax( uNorm ); // get the main axis for cubemap lookup
|
|
|
|
// get texture dimensions (probably always equal 512)
|
|
uint width = RENDER_GET_PARM( PARM_TEX_SRC_WIDTH, tr.normalsFitting );
|
|
uint height = RENDER_GET_PARM( PARM_TEX_SRC_HEIGHT, tr.normalsFitting );
|
|
|
|
// get texture coordinates in a collapsed cubemap
|
|
float u = (uNorm.z < maxNAbs ? (uNorm.y < maxNAbs ? uNorm.y : uNorm.x) : uNorm.x);
|
|
float v = (uNorm.z < maxNAbs ? (uNorm.y < maxNAbs ? uNorm.z : uNorm.z) : uNorm.y);
|
|
vNorm /= maxNAbs; // fit normal into the edge of unit cube
|
|
|
|
if( u < v ) swap( v, u );
|
|
if( u != 0.0f ) v /= u;
|
|
|
|
uint x = uint( u * (float)width );
|
|
uint y = uint( v * (float)height );
|
|
|
|
// look-up fitting length and scale the normal to get the best fit
|
|
float fFittingScale = (float)bestFitTexture[(y * width + x)] * (1.0f / 255.0f);
|
|
// scale the normal to get the best fit
|
|
vNorm *= fFittingScale;
|
|
}
|
|
|
|
outVec[0] = FloatToChar( vNorm.x );
|
|
outVec[1] = FloatToChar( vNorm.y );
|
|
outVec[2] = FloatToChar( vNorm.z );
|
|
} |