mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2025-02-02 13:41:44 +00:00
ca9757082f
this is because it would draw (parts of) geometric edges with different colors and that makes visual inspections annoying also, final MSAA sample counts are always reported by GL3 and GL2 now
742 lines
25 KiB
C++
742 lines
25 KiB
C++
/*
|
|
===========================================================================
|
|
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
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// tr_init.c -- functions that are not called every frame
|
|
|
|
#include "tr_local.h"
|
|
#include "tr_help.h"
|
|
|
|
glconfig_t glConfig;
|
|
glinfo_t glInfo;
|
|
|
|
screenshotCommand_t r_delayedScreenshot;
|
|
qbool r_delayedScreenshotPending = qfalse;
|
|
int r_delayedScreenshotFrame = 0;
|
|
|
|
graphicsAPILayer_t gal;
|
|
|
|
cvar_t *r_backend;
|
|
cvar_t *r_frameSleep;
|
|
|
|
cvar_t *r_verbose;
|
|
|
|
cvar_t *r_displayRefresh;
|
|
|
|
cvar_t *r_detailTextures;
|
|
|
|
cvar_t *r_intensity;
|
|
cvar_t *r_gamma;
|
|
cvar_t *r_greyscale;
|
|
|
|
cvar_t *r_measureOverdraw;
|
|
|
|
cvar_t *r_fastsky;
|
|
cvar_t *r_dynamiclight;
|
|
|
|
cvar_t *r_lodbias;
|
|
cvar_t *r_lodscale;
|
|
|
|
cvar_t *r_norefresh;
|
|
cvar_t *r_drawentities;
|
|
cvar_t *r_drawworld;
|
|
cvar_t *r_speeds;
|
|
cvar_t *r_fullbright;
|
|
cvar_t *r_lightmap;
|
|
cvar_t *r_lightmapGreyscale;
|
|
cvar_t *r_novis;
|
|
cvar_t *r_nocull;
|
|
cvar_t *r_nocurves;
|
|
cvar_t *r_softSprites;
|
|
cvar_t *r_gpuMipGen;
|
|
cvar_t *r_alphaToCoverage;
|
|
cvar_t *r_dither;
|
|
cvar_t *r_rtColorFormat;
|
|
|
|
cvar_t *r_mipGenFilter;
|
|
cvar_t *r_mipGenGamma;
|
|
cvar_t *r_noiseScale;
|
|
cvar_t *r_gl3_geoStream;
|
|
cvar_t *r_d3d11_syncOffsets;
|
|
cvar_t *r_d3d11_maxQueuedFrames;
|
|
cvar_t *r_ext_max_anisotropy;
|
|
cvar_t *r_msaa;
|
|
|
|
cvar_t *r_ignoreGLErrors;
|
|
|
|
cvar_t *r_vertexLight;
|
|
cvar_t *r_uiFullScreen;
|
|
cvar_t *r_mode;
|
|
cvar_t *r_blitMode;
|
|
cvar_t *r_nobind;
|
|
cvar_t *r_singleShader;
|
|
cvar_t *r_roundImagesDown;
|
|
cvar_t *r_colorMipLevels;
|
|
cvar_t *r_picmip;
|
|
cvar_t *r_showtris;
|
|
cvar_t *r_showsky;
|
|
cvar_t *r_shownormals;
|
|
cvar_t *r_finish;
|
|
cvar_t *r_clear;
|
|
cvar_t *r_swapInterval;
|
|
cvar_t *r_textureMode;
|
|
cvar_t *r_lockpvs;
|
|
cvar_t *r_noportals;
|
|
cvar_t *r_portalOnly;
|
|
|
|
cvar_t *r_subdivisions;
|
|
cvar_t *r_lodCurveError;
|
|
|
|
cvar_t *r_width;
|
|
cvar_t *r_height;
|
|
cvar_t *r_customaspect;
|
|
|
|
cvar_t *r_brightness;
|
|
cvar_t *r_mapBrightness;
|
|
|
|
cvar_t *r_debugSurface;
|
|
|
|
cvar_t *r_showImages;
|
|
|
|
cvar_t *r_ambientScale;
|
|
cvar_t *r_directedScale;
|
|
cvar_t *r_debugLight;
|
|
cvar_t *r_debugSort;
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
cvar_t *r_maplightBrightness;
|
|
cvar_t *r_maplightSaturation;
|
|
cvar_t *r_maplightColor;
|
|
cvar_t *r_maplightColorMode;
|
|
vec3_t vMaplightColorFilter;
|
|
|
|
// these limits apply to the sum of all scenes in a frame:
|
|
// the main view, all the 3D icons, and even the console etc
|
|
#define DEFAULT_MAX_POLYS 8192
|
|
#define DEFAULT_MAX_POLYVERTS 32768
|
|
static cvar_t* r_maxpolys;
|
|
static cvar_t* r_maxpolyverts;
|
|
int max_polys;
|
|
int max_polyverts;
|
|
|
|
|
|
void R_ConfigureVideoMode( int desktopWidth, int desktopHeight )
|
|
{
|
|
glInfo.winFullscreen = !!r_fullscreen->integer;
|
|
glInfo.vidFullscreen = r_fullscreen->integer && r_mode->integer == VIDEOMODE_CHANGE;
|
|
|
|
if (r_fullscreen->integer && r_mode->integer == VIDEOMODE_DESKTOPRES) {
|
|
glConfig.vidWidth = desktopWidth;
|
|
glConfig.vidHeight = desktopHeight;
|
|
glConfig.windowAspect = (float)glConfig.vidWidth / (float)glConfig.vidHeight;
|
|
glInfo.winWidth = desktopWidth;
|
|
glInfo.winHeight = desktopHeight;
|
|
return;
|
|
}
|
|
|
|
if (r_fullscreen->integer && r_mode->integer == VIDEOMODE_UPSCALE) {
|
|
glConfig.vidWidth = r_width->integer;
|
|
glConfig.vidHeight = r_height->integer;
|
|
glConfig.windowAspect = (float)glConfig.vidWidth / (float)glConfig.vidHeight;
|
|
glInfo.winWidth = desktopWidth;
|
|
glInfo.winHeight = desktopHeight;
|
|
return;
|
|
}
|
|
|
|
glConfig.vidWidth = r_width->integer;
|
|
glConfig.vidHeight = r_height->integer;
|
|
glConfig.windowAspect = (float)glConfig.vidWidth / (float)glConfig.vidHeight;
|
|
glInfo.winWidth = r_width->integer;
|
|
glInfo.winHeight = r_height->integer;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
|
|
static void RB_TakeScreenshotTGA( int x, int y, int width, int height, const char* fileName )
|
|
{
|
|
int c = (width * height * 3);
|
|
RI_AutoPtr p( sizeof(TargaHeader) + c );
|
|
|
|
TargaHeader* tga = p.Get<TargaHeader>();
|
|
Com_Memset( tga, 0, sizeof(TargaHeader) );
|
|
tga->image_type = 2; // uncompressed BGR
|
|
tga->width = LittleShort( width );
|
|
tga->height = LittleShort( height );
|
|
tga->pixel_size = 24;
|
|
gal.ReadPixels( x, y, width, height, 1, CS_BGR, p + sizeof(TargaHeader) );
|
|
ri.FS_WriteFile( fileName, p, sizeof(TargaHeader) + c );
|
|
}
|
|
|
|
|
|
static void RB_TakeScreenshotJPG( int x, int y, int width, int height, const char* fileName )
|
|
{
|
|
RI_AutoPtr p( width * height * 4 );
|
|
gal.ReadPixels( x, y, width, height, 1, CS_RGBA, p );
|
|
|
|
RI_AutoPtr out( width * height * 4 );
|
|
int n = SaveJPGToBuffer( out, 95, width, height, p );
|
|
ri.FS_WriteFile( fileName, out, n );
|
|
}
|
|
|
|
|
|
const void* RB_TakeScreenshotCmd( const screenshotCommand_t* cmd )
|
|
{
|
|
// NOTE: the current read buffer is the last FBO color attachment texture that was written to
|
|
// therefore, ReadPixels will get the latest data even with double/triple buffering enabled
|
|
|
|
switch (cmd->type) {
|
|
case screenshotCommand_t::SS_JPG:
|
|
RB_TakeScreenshotJPG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
|
|
ri.Printf( PRINT_ALL, "Wrote %s\n", cmd->fileName );
|
|
break;
|
|
case screenshotCommand_t::SS_TGA:
|
|
RB_TakeScreenshotTGA( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
|
|
ri.Printf( PRINT_ALL, "Wrote %s\n", cmd->fileName );
|
|
break;
|
|
}
|
|
|
|
if (cmd->conVis > 0.0f) {
|
|
ri.SetConsoleVisibility( cmd->conVis );
|
|
r_delayedScreenshotPending = qfalse;
|
|
r_delayedScreenshotFrame = 0;
|
|
}
|
|
|
|
return (const void*)(cmd + 1);
|
|
}
|
|
|
|
|
|
// screenshot filename is YYYY_MM_DD-HH_MM_SS-TTT
|
|
// so you can find the damn things and you never run out of them for movies :)
|
|
|
|
static void R_TakeScreenshot( const char* ext, screenshotCommand_t::ss_type type, qbool hideConsole )
|
|
{
|
|
static char s[MAX_OSPATH];
|
|
|
|
const float conVis = hideConsole ? ri.SetConsoleVisibility( 0.0f ) : 0.0f;
|
|
screenshotCommand_t* cmd;
|
|
if ( conVis > 0.0f ) {
|
|
cmd = &r_delayedScreenshot;
|
|
r_delayedScreenshotPending = qtrue;
|
|
r_delayedScreenshotFrame = 0;
|
|
cmd->delayed = qtrue;
|
|
} else {
|
|
if ( R_FindRenderCommand( RC_SCREENSHOT ) )
|
|
return;
|
|
cmd = (screenshotCommand_t*)R_GetCommandBuffer( sizeof(screenshotCommand_t) );
|
|
if ( !cmd )
|
|
return;
|
|
cmd->delayed = qfalse;
|
|
}
|
|
|
|
if (ri.Cmd_Argc() == 2) {
|
|
Com_sprintf( s, sizeof(s), "screenshots/%s.%s", ri.Cmd_Argv(1), ext );
|
|
} else {
|
|
qtime_t t;
|
|
Com_RealTime( &t );
|
|
int ms = min( 999, backEnd.refdef.time & 1023 );
|
|
Com_sprintf( s, sizeof(s), "screenshots/%d_%02d_%02d-%02d_%02d_%02d-%03d.%s",
|
|
1900+t.tm_year, 1+t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ms, ext );
|
|
}
|
|
|
|
cmd->commandId = RC_SCREENSHOT;
|
|
cmd->x = 0;
|
|
cmd->y = 0;
|
|
cmd->width = glConfig.vidWidth;
|
|
cmd->height = glConfig.vidHeight;
|
|
cmd->fileName = s;
|
|
cmd->type = type;
|
|
cmd->conVis = conVis;
|
|
}
|
|
|
|
|
|
static void R_ScreenShotTGA_f()
|
|
{
|
|
R_TakeScreenshot( "tga", screenshotCommand_t::SS_TGA, qfalse );
|
|
}
|
|
|
|
|
|
static void R_ScreenShotJPG_f()
|
|
{
|
|
R_TakeScreenshot( "jpg", screenshotCommand_t::SS_JPG, qfalse );
|
|
}
|
|
|
|
|
|
static void R_ScreenShotNoConTGA_f()
|
|
{
|
|
R_TakeScreenshot( "tga", screenshotCommand_t::SS_TGA, qtrue );
|
|
}
|
|
|
|
|
|
static void R_ScreenShotNoConJPG_f()
|
|
{
|
|
R_TakeScreenshot( "jpg", screenshotCommand_t::SS_JPG, qtrue );
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
const void *RB_TakeVideoFrameCmd( const void *data )
|
|
{
|
|
const videoFrameCommand_t* cmd = (const videoFrameCommand_t*)data;
|
|
|
|
if( cmd->motionJpeg )
|
|
{
|
|
gal.ReadPixels( 0, 0, cmd->width, cmd->height, 1, CS_RGBA, cmd->captureBuffer );
|
|
const int frameSize = SaveJPGToBuffer( cmd->encodeBuffer, 95, cmd->width, cmd->height, cmd->captureBuffer );
|
|
ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize );
|
|
}
|
|
else
|
|
{
|
|
gal.ReadPixels( 0, 0, cmd->width, cmd->height, 4, CS_BGR, cmd->captureBuffer );
|
|
const int frameSize = PAD( cmd->width, 4 ) * cmd->height * 3;
|
|
ri.CL_WriteAVIVideoFrame( cmd->captureBuffer, frameSize );
|
|
}
|
|
|
|
return (const void *)(cmd + 1);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
|
|
void GfxInfo_f( void )
|
|
{
|
|
ri.Printf( PRINT_ALL, "Back-end: %s\n", r_backend->string );
|
|
if ( glConfig.vendor_string[0] != '\0' )
|
|
ri.Printf( PRINT_ALL, "Vendor: %s\n", glConfig.vendor_string );
|
|
if ( glConfig.renderer_string[0] != '\0' )
|
|
ri.Printf( PRINT_ALL, "Renderer: %s\n", glConfig.renderer_string );
|
|
if ( glConfig.version_string[0] != '\0' )
|
|
ri.Printf( PRINT_ALL, "OpenGL version: %s\n", glConfig.version_string );
|
|
ri.Printf( PRINT_ALL, "Soft sprites : %s\n", glInfo.softSpriteSupport ? "ON" : "OFF" );
|
|
ri.Printf( PRINT_ALL, "Alpha to coverage : %s\n", glInfo.alphaToCoverageSupport ? "ON" : "OFF" );
|
|
ri.Printf( PRINT_ALL, "GPU mip-map generation: %s\n", glInfo.mipGenSupport ? "ON" : "OFF" );
|
|
gal.PrintInfo();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
|
|
static const cmdTableItem_t r_cmds[] =
|
|
{
|
|
{ "gfxinfo", GfxInfo_f, NULL, "prints display mode info" },
|
|
{ "imagelist", R_ImageList_f, NULL, "prints loaded images" },
|
|
{ "shaderlist", R_ShaderList_f, NULL, "prints loaded shaders" },
|
|
{ "skinlist", R_SkinList_f, NULL, "prints loaded skins" },
|
|
{ "modellist", R_Modellist_f, NULL, "prints loaded models" },
|
|
{ "screenshot", R_ScreenShotTGA_f, NULL, "takes a TARGA (.tga) screenshot" },
|
|
{ "screenshotJPEG", R_ScreenShotJPG_f, NULL, "takes a JPEG (.jpg) screenshot" },
|
|
{ "screenshotnc", R_ScreenShotNoConTGA_f, NULL, "takes a TARGA screenshot w/o the console" },
|
|
{ "screenshotncJPEG", R_ScreenShotNoConJPG_f, NULL, "takes a JPEG screenshot w/o the console" }
|
|
};
|
|
|
|
|
|
static const cvarTableItem_t r_cvars[] =
|
|
{
|
|
//
|
|
// latched and archived variables
|
|
//
|
|
#if defined( _WIN32 )
|
|
{ &r_backend, "r_backend", "D3D11", CVAR_ARCHIVE | CVAR_LATCH, CVART_STRING, NULL, NULL, help_r_backend },
|
|
#else
|
|
{ &r_backend, "r_backend", "GL3", CVAR_ARCHIVE | CVAR_LATCH, CVART_STRING, NULL, NULL, help_r_backend },
|
|
#endif
|
|
{ &r_frameSleep, "r_frameSleep", "2", CVAR_ARCHIVE, CVART_INTEGER, "0", "2", help_r_frameSleep },
|
|
{ &r_mipGenFilter, "r_mipGenFilter", "L4", CVAR_ARCHIVE | CVAR_LATCH, CVART_STRING, NULL, NULL, help_r_mipGenFilter },
|
|
{ &r_mipGenGamma, "r_mipGenGamma", "1.8", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "1.0", "3.0", help_r_mipGenGamma },
|
|
{ &r_gl3_geoStream, "r_gl3_geoStream", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", XSTRING(GL3MAP_MAX), help_r_gl3_geoStream },
|
|
{ &r_d3d11_syncOffsets, "r_d3d11_syncOffsets", "2", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", XSTRING(D3D11SO_MAX), help_r_d3d11_syncOffsets },
|
|
{ &r_d3d11_maxQueuedFrames, "r_d3d11_maxQueuedFrames", "3", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "1", "16", help_r_d3d11_maxQueuedFrames },
|
|
{ &r_ext_max_anisotropy, "r_ext_max_anisotropy", "16", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", "16", help_r_ext_max_anisotropy },
|
|
{ &r_msaa, "r_msaa", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", "32", "anti-aliasing sample count, " S_COLOR_VAL "0" S_COLOR_HELP "=off" },
|
|
{ &r_picmip, "r_picmip", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", "16", help_r_picmip },
|
|
{ &r_roundImagesDown, "r_roundImagesDown", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_roundImagesDown },
|
|
{ &r_colorMipLevels, "r_colorMipLevels", "0", CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_colorMipLevels },
|
|
{ &r_detailTextures, "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "enables detail textures shader stages" },
|
|
{ &r_mode, "r_mode", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", XSTRING(VIDEOMODE_MAX), help_r_mode },
|
|
{ &r_blitMode, "r_blitMode", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", XSTRING(BLITMODE_MAX), help_r_blitMode },
|
|
{ &r_brightness, "r_brightness", "2", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "0.25", "32", "overall brightness" },
|
|
// should be called r_lightmapBrightness
|
|
{ &r_mapBrightness, "r_mapBrightness", "2", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "0.25", "32", "brightness of lightmap textures" },
|
|
// should be called r_textureBrightness
|
|
{ &r_intensity, "r_intensity", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "1", NULL, "brightness of non-lightmap map textures" },
|
|
{ &r_fullscreen, "r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "full-screen mode" },
|
|
{ &r_width, "r_width", "1280", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "320", "65535", "custom window/render width" },
|
|
{ &r_height, "r_height", "720", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "240", "65535", "custom window/render height" },
|
|
{ &r_customaspect, "r_customaspect", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0.1", "10", "custom pixel aspect ratio" },
|
|
{ &r_vertexLight, "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "disables lightmap texture blending" },
|
|
// note that r_subdivisions > 64 will create rendering artefacts because you'll see the other side of a curved surface when against it
|
|
{ &r_subdivisions, "r_subdivisions", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "1", "64", help_r_subdivisions },
|
|
{ &r_fullbright, "r_fullbright", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_fullbright },
|
|
{ &r_lightmap, "r_lightmap", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_lightmap },
|
|
{ &r_lightmapGreyscale, "r_lightmapGreyscale", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "0", "1", "how monochrome the lightmap looks" },
|
|
{ &r_softSprites, "r_softSprites", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_softSprites },
|
|
{ &r_gpuMipGen, "r_gpuMipGen", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_gpuMipGen },
|
|
{ &r_alphaToCoverage, "r_alphaToCoverage", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_alphaToCoverage },
|
|
{ &r_dither, "r_dither", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_dither },
|
|
{ &r_rtColorFormat, "r_rtColorFormat", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", XSTRING(RTCF_MAX), help_r_rtColorFormat },
|
|
|
|
//
|
|
// latched variables that can only change over a restart
|
|
//
|
|
{ &r_displayRefresh, "r_displayRefresh", "0", CVAR_LATCH, CVART_INTEGER, "0", "480", S_COLOR_VAL "0 " S_COLOR_HELP "lets the driver decide" },
|
|
{ &r_singleShader, "r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH },
|
|
|
|
//
|
|
// archived variables that can change at any time
|
|
//
|
|
{ &r_lodbias, "r_lodbias", "-2", CVAR_ARCHIVE, CVART_INTEGER, "-16", "16", help_r_lodbias },
|
|
{ &r_ignoreGLErrors, "r_ignoreGLErrors", "1", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, "if " S_COLOR_VAL "0" S_COLOR_HELP ", OpenGL errors are fatal" },
|
|
{ &r_fastsky, "r_fastsky", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, help_r_fastsky },
|
|
{ &r_noportals, "r_noportals", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, help_r_noportals },
|
|
{ &r_dynamiclight, "r_dynamiclight", "1", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, "enables dynamic lights" },
|
|
{ &r_finish, "r_finish", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, "enables glFinish calls" },
|
|
{ &r_swapInterval, "r_swapInterval", "0", CVAR_ARCHIVE, CVART_INTEGER, "-8", "8", help_r_swapInterval },
|
|
// r_textureMode's default value can't be an empty string because otherwise, a user-created default can stick (see Cvar_Get)
|
|
{ &r_textureMode, "r_textureMode", "best", CVAR_ARCHIVE | CVAR_LATCH, CVART_STRING, NULL, NULL, help_r_textureMode },
|
|
{ &r_gamma, "r_gamma", "1.2", CVAR_ARCHIVE, CVART_FLOAT, "0.5", "3", help_r_gamma },
|
|
{ &r_greyscale, "r_greyscale", "0", CVAR_ARCHIVE, CVART_FLOAT, "0", "1", "controls how monochrome the final image looks" },
|
|
{ &r_noiseScale, "r_noiseScale", "1.0", CVAR_ARCHIVE, CVART_FLOAT, "0.125", "8.0", help_r_noiseScale },
|
|
{ &r_lodCurveError, "r_lodCurveError", "2000", CVAR_ARCHIVE, CVART_FLOAT, "250", "10000", "curved surfaces LOD scale" },
|
|
|
|
//
|
|
// temporary variables that can change at any time
|
|
//
|
|
{ &r_ambientScale, "r_ambientScale", "0.6", CVAR_CHEAT },
|
|
{ &r_directedScale, "r_directedScale", "1", CVAR_CHEAT },
|
|
{ &r_uiFullScreen, "r_uifullscreen", "0", CVAR_TEMP },
|
|
{ &r_showImages, "r_showImages", "0", CVAR_TEMP },
|
|
{ &r_debugLight, "r_debuglight", "0", CVAR_TEMP },
|
|
{ &r_debugSort, "r_debugSort", "0", CVAR_CHEAT, CVART_FLOAT },
|
|
{ &r_nocurves, "r_nocurves", "0", CVAR_CHEAT },
|
|
{ &r_drawworld, "r_drawworld", "1", CVAR_CHEAT },
|
|
{ &r_portalOnly, "r_portalOnly", "0", CVAR_CHEAT },
|
|
{ &r_lodscale, "r_lodscale", "5", CVAR_CHEAT },
|
|
{ &r_norefresh, "r_norefresh", "0", CVAR_CHEAT },
|
|
{ &r_drawentities, "r_drawentities", "1", CVAR_CHEAT },
|
|
{ &r_nocull, "r_nocull", "0", CVAR_CHEAT },
|
|
{ &r_novis, "r_novis", "0", CVAR_CHEAT },
|
|
{ &r_speeds, "r_speeds", "0", CVAR_CHEAT, CVART_BOOL, NULL, NULL, "draw rendering performance counters" },
|
|
{ &r_verbose, "r_verbose", "0", CVAR_CHEAT },
|
|
{ &r_debugSurface, "r_debugSurface", "0", CVAR_CHEAT },
|
|
{ &r_nobind, "r_nobind", "0", CVAR_CHEAT },
|
|
{ &r_showtris, "r_showtris", "0", CVAR_CHEAT },
|
|
{ &r_showsky, "r_showsky", "0", CVAR_CHEAT },
|
|
{ &r_shownormals, "r_shownormals", "0", CVAR_CHEAT },
|
|
{ &r_clear, "r_clear", "0", CVAR_CHEAT },
|
|
{ &r_lockpvs, "r_lockpvs", "0", CVAR_CHEAT },
|
|
{ &r_maxpolys, "r_maxpolys", XSTRING(DEFAULT_MAX_POLYS), 0 },
|
|
{ &r_maxpolyverts, "r_maxpolyverts", XSTRING(DEFAULT_MAX_POLYVERTS), 0 }
|
|
};
|
|
|
|
|
|
static void R_Register()
|
|
{
|
|
ri.Cvar_RegisterTable( r_cvars, ARRAY_LEN(r_cvars) );
|
|
ri.Cmd_RegisterTable( r_cmds, ARRAY_LEN(r_cmds) );
|
|
}
|
|
|
|
|
|
static void R_InitGAL()
|
|
{
|
|
struct gal_t {
|
|
getGALInterface_t grabInterface;
|
|
const char* cvarValue;
|
|
const char* fullName;
|
|
};
|
|
|
|
// preferred option goes first
|
|
const gal_t galArray[] = {
|
|
#if defined( _WIN32 )
|
|
{ &GAL_GetD3D11, "D3D11", "Direct3D 11" },
|
|
#endif
|
|
{ &GAL_GetGL3, "GL3", "OpenGL 3" },
|
|
{ &GAL_GetGL2, "GL2", "OpenGL 2" }
|
|
};
|
|
|
|
int galIndex = -1;
|
|
for ( int i = 0; i < ARRAY_LEN( galArray ); ++i ) {
|
|
if ( !Q_stricmp( r_backend->string, galArray[i].cvarValue ) ) {
|
|
galIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( galIndex < 0 ) {
|
|
galIndex = 0;
|
|
ri.Printf( PRINT_WARNING, "Invalid r_backend value, selecting the %s back-end instead\n", galArray[galIndex].fullName );
|
|
ri.Cvar_Set( r_backend->name, galArray[galIndex].cvarValue );
|
|
}
|
|
|
|
ri.Printf( PRINT_ALL, "Initializing the %s back-end...\n", galArray[galIndex].fullName );
|
|
|
|
#if defined( _DEBUG )
|
|
// helps catch unset function pointers
|
|
memset( &gal, 0, sizeof( gal ) );
|
|
#endif
|
|
|
|
if ( !galArray[galIndex].grabInterface( &gal ) )
|
|
ri.Error( ERR_FATAL, "Failed to grab the %s back-end's interface\n", galArray[galIndex].fullName );
|
|
|
|
if ( !gal.Init() )
|
|
ri.Error( ERR_FATAL, "Failed to initialize the %s back-end\n", galArray[galIndex].fullName );
|
|
|
|
GfxInfo_f();
|
|
}
|
|
|
|
|
|
static void R_InitMipFilter()
|
|
{
|
|
struct filter_t {
|
|
const char* cvarName;
|
|
const char* fullName;
|
|
float kernel[4];
|
|
};
|
|
|
|
// preferred option goes first
|
|
const filter_t filterArray[] = {
|
|
{ "L4", "Lanczos 4", -0.0491683967f, -0.0816986337f, 0.151636884f, 0.479230106f },
|
|
{ "L3", "Lanczos 3", 0.0f, -0.0646646321f, 0.131493837f, 0.433170795f },
|
|
{ "BL", "Bi-linear", 0.0f, 0.0f, 0.0f, 0.5f },
|
|
{ "MN2", "Mitchell-Netravali 2 (B = 1/3, C = 1/3)", 0.0f, 0.0f, 0.123327762f, 0.376672268f },
|
|
{ "BH4", "3-term Blackman-Harris 4", 0.00106591149f, 0.0288433302f, 0.151539952f, 0.318550885f },
|
|
{ "BH3", "3-term Blackman-Harris 3", 0.0f, 0.00302829780f, 0.101031370f, 0.395940334f },
|
|
{ "BH2", "3-term Blackman-Harris 2", 0.0f, 0.0f, 0.0151469158f, 0.484853119f },
|
|
{ "T2", "Tent 2 (1/3 2/3)", 0.0f, 0.0f, 1.0f / 6.0f, 2.0f / 6.0f },
|
|
// The results are so similar to Lanczos...
|
|
//{ "K4", "Kaiser 4", -0.0487834960f, -0.0811581835f, 0.151146635f, 0.478795081f },
|
|
//{ "K3", "Kaiser 3", 0.0f, -0.0642274171f, 0.131010026f, 0.433217406f }
|
|
};
|
|
|
|
int index = -1;
|
|
for ( int i = 0; i < ARRAY_LEN( filterArray ); ++i ) {
|
|
if ( !Q_stricmp( r_mipGenFilter->string, filterArray[i].cvarName ) ) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( index < 0 ) {
|
|
index = 0;
|
|
ri.Printf( PRINT_WARNING, "Invalid %s value, selecting the %s filter instead\n", r_mipGenFilter->name, filterArray[index].fullName );
|
|
}
|
|
|
|
memcpy( tr.mipFilter, filterArray[index].kernel, sizeof( tr.mipFilter ) );
|
|
}
|
|
|
|
|
|
void R_Init()
|
|
{
|
|
COMPILE_TIME_ASSERT( sizeof(glconfig_t) == 11332 );
|
|
COMPILE_TIME_ASSERT( sizeof(TargaHeader) == 18 );
|
|
|
|
QSUBSYSTEM_INIT_START( "Renderer" );
|
|
|
|
// clear all our internal state
|
|
Com_Memset( &tr, 0, sizeof( tr ) );
|
|
Com_Memset( &backEnd, 0, sizeof( backEnd ) );
|
|
Com_Memset( &tess, 0, sizeof( tess ) );
|
|
|
|
if ((intptr_t)tess.xyz & 15)
|
|
Com_Printf( "WARNING: tess.xyz not 16 byte aligned\n" );
|
|
|
|
// init function tables
|
|
//
|
|
for (int i = 0; i < FUNCTABLE_SIZE; ++i)
|
|
{
|
|
tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) );
|
|
tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f;
|
|
tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE;
|
|
tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i];
|
|
|
|
if ( i < FUNCTABLE_SIZE / 4 )
|
|
tr.triangleTable[i] = (float)i / (FUNCTABLE_SIZE / 4);
|
|
else if ( i < FUNCTABLE_SIZE / 2 )
|
|
tr.triangleTable[i] = 1.0f - tr.triangleTable[i - FUNCTABLE_SIZE / 4];
|
|
else
|
|
tr.triangleTable[i] = -tr.triangleTable[i - FUNCTABLE_SIZE / 2];
|
|
}
|
|
|
|
R_InitFogTable();
|
|
|
|
R_NoiseInit();
|
|
|
|
R_Register();
|
|
|
|
max_polys = max( r_maxpolys->integer, DEFAULT_MAX_POLYS );
|
|
max_polyverts = max( r_maxpolyverts->integer, DEFAULT_MAX_POLYVERTS );
|
|
|
|
byte* ptr = (byte*)ri.Hunk_Alloc( sizeof(backEndData_t) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low );
|
|
backEndData = (backEndData_t*)ptr;
|
|
backEndData->polys = (srfPoly_t*)(ptr + sizeof(backEndData_t));
|
|
backEndData->polyVerts = (polyVert_t*)(ptr + sizeof(backEndData_t) + sizeof(srfPoly_t) * max_polys);
|
|
|
|
R_ClearFrame();
|
|
|
|
R_InitMipFilter();
|
|
|
|
R_InitGAL();
|
|
|
|
R_InitImages();
|
|
|
|
R_InitShaders();
|
|
|
|
R_InitSkins();
|
|
|
|
R_ModelInit();
|
|
|
|
QSUBSYSTEM_INIT_DONE( "Renderer" );
|
|
}
|
|
|
|
|
|
static void RE_Shutdown( qbool destroyWindow )
|
|
{
|
|
ri.Printf( PRINT_DEVELOPER, "RE_Shutdown( %i )", destroyWindow );
|
|
|
|
// This will also force the creation of a new context when switching
|
|
// between GL2 and GL3, which is what we want because
|
|
// the implementations have their own resources we can't keep around.
|
|
if ( !destroyWindow && r_backend->latchedString )
|
|
destroyWindow = qtrue;
|
|
|
|
ri.Printf( PRINT_DEVELOPER, " -> %i\n", destroyWindow );
|
|
|
|
if ( tr.registered ) {
|
|
ri.Cmd_UnregisterModule();
|
|
gal.ShutDown( destroyWindow );
|
|
}
|
|
|
|
// shut down platform-specific video stuff
|
|
if ( destroyWindow ) {
|
|
Sys_V_Shutdown();
|
|
memset( &glConfig, 0, sizeof( glConfig ) );
|
|
}
|
|
|
|
tr.registered = qfalse;
|
|
}
|
|
|
|
|
|
static void RE_BeginRegistration( glconfig_t* glconfigOut )
|
|
{
|
|
R_Init();
|
|
|
|
*glconfigOut = glConfig;
|
|
|
|
tr.viewCluster = -1; // force markleafs to regenerate
|
|
RE_ClearScene();
|
|
|
|
tr.registered = qtrue;
|
|
}
|
|
|
|
|
|
static void RE_EndRegistration()
|
|
{
|
|
}
|
|
|
|
|
|
static int RE_GetCameraMatrixTime()
|
|
{
|
|
return re_cameraMatrixTime;
|
|
}
|
|
|
|
|
|
static void RE_StretchRaw( int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qbool dirty )
|
|
{
|
|
if ( !tr.registered )
|
|
return;
|
|
|
|
gal.UpdateScratch( tr.scratchImage[client], cols, rows, data, dirty );
|
|
tr.scratchShader->stages[0]->bundle.image[0] = tr.scratchImage[client];
|
|
RE_StretchPic( x, y, w, h, 0.5f / cols, 0.5f / rows, (cols - 0.5f) / cols, (rows - 0.5f) / rows, (qhandle_t)tr.scratchShader->index );
|
|
}
|
|
|
|
|
|
static qbool RE_Registered()
|
|
{
|
|
return tr.registered;
|
|
}
|
|
|
|
|
|
static qbool RE_IsFrameSleepNeeded()
|
|
{
|
|
if ( r_frameSleep->integer != 2 )
|
|
return r_frameSleep->integer;
|
|
|
|
return !Sys_V_IsVSynced();
|
|
}
|
|
|
|
|
|
const refexport_t* GetRefAPI( const refimport_t* rimp )
|
|
{
|
|
static refexport_t re;
|
|
|
|
ri = *rimp;
|
|
|
|
Com_Memset( &re, 0, sizeof( re ) );
|
|
|
|
// the RE_ functions are Renderer Entry points
|
|
|
|
re.Shutdown = RE_Shutdown;
|
|
|
|
re.BeginRegistration = RE_BeginRegistration;
|
|
re.RegisterModel = RE_RegisterModel;
|
|
re.RegisterSkin = RE_RegisterSkin;
|
|
re.RegisterShader = RE_RegisterShader;
|
|
re.RegisterShaderNoMip = RE_RegisterShaderNoMip;
|
|
re.LoadWorld = RE_LoadWorldMap;
|
|
re.SetWorldVisData = RE_SetWorldVisData;
|
|
re.EndRegistration = RE_EndRegistration;
|
|
|
|
re.BeginFrame = RE_BeginFrame;
|
|
re.EndFrame = RE_EndFrame;
|
|
|
|
re.MarkFragments = R_MarkFragments;
|
|
re.LerpTag = R_LerpTag;
|
|
re.ModelBounds = R_ModelBounds;
|
|
|
|
re.ClearScene = RE_ClearScene;
|
|
re.AddRefEntityToScene = RE_AddRefEntityToScene;
|
|
re.AddPolyToScene = RE_AddPolyToScene;
|
|
re.LightForPoint = R_LightForPoint;
|
|
re.AddLightToScene = RE_AddLightToScene;
|
|
re.RenderScene = RE_RenderScene;
|
|
|
|
re.SetColor = RE_SetColor;
|
|
re.DrawStretchPic = RE_StretchPic;
|
|
re.DrawTriangle = RE_DrawTriangle;
|
|
re.DrawStretchRaw = RE_StretchRaw;
|
|
|
|
re.GetEntityToken = R_GetEntityToken;
|
|
re.inPVS = R_inPVS;
|
|
|
|
re.TakeVideoFrame = RE_TakeVideoFrame;
|
|
|
|
re.GetCameraMatrixTime = RE_GetCameraMatrixTime;
|
|
|
|
re.Registered = RE_Registered;
|
|
|
|
re.ShouldSleep = RE_IsFrameSleepNeeded;
|
|
|
|
return &re;
|
|
}
|