/
noire-dev
/
SourceTech
Обзор
Документация
Войти
/
noire-dev
/
SourceTech
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
sourcetech
code/renderer/tr_init.c
1 427 строк
39 KB
noire-dev
Update: 2025-12-21 16:14 powersaving and clean
21 дек 2025, 07:14
21 дек 2025, 07:14
05bd062
Код
Авторство
О чём код?
/* =========================================================================== 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" glconfig_t glConfig; qboolean nonPowerOfTwoTextures; qboolean textureFilterAnisotropic; int maxAnisotropy; int gl_version; int gl_clamp_mode; // GL_CLAMP or GL_CLAMP_TO_EGGE glstate_t glState; glstatic_t gls; static void GL_SetDefaultState( void ); cvar_t *r_znear; cvar_t *r_zproj; //postFX cvar_t *r_modernMode; cvar_t *r_postfx; cvar_t *r_postprocess; //color cvar_t *r_fx_greyscale; cvar_t *r_fx_sepia; cvar_t *r_fx_contrast; cvar_t *r_fx_brightness; cvar_t *r_fx_invert; cvar_t *r_fx_tint_r; cvar_t *r_fx_tint_g; cvar_t *r_fx_tint_b; cvar_t *r_fx_posterize; cvar_t *r_fx_glow; cvar_t *r_fx_filmic; cvar_t *r_fx_bloom; //fragment cvar_t *r_fx_chromaticAberration; cvar_t *r_fx_chameleon; cvar_t *r_fx_ambientlight; cvar_t *r_fx_blur; cvar_t *r_ignorehwgamma; cvar_t *r_fastsky; cvar_t *r_mergeLightmaps; cvar_t *r_dlightMode; cvar_t *r_hdr; cvar_t *r_bloom_threshold; cvar_t *r_bloom_threshold_mode; cvar_t *r_bloom_modulate; cvar_t *r_bloom_passes; cvar_t *r_bloom_blend_base; cvar_t *r_bloom_intensity; cvar_t *r_bloom_filter_size; cvar_t *r_bloom_reflection; cvar_t *r_lodbias; cvar_t *r_drawentities; cvar_t *r_drawworld; cvar_t *r_speeds; cvar_t *r_nocull; cvar_t *r_allowExtensions; cvar_t *r_ext_compressed_textures; cvar_t *r_ext_multitexture; cvar_t *r_ext_compiled_vertex_array; cvar_t *r_ext_texture_env_add; cvar_t *r_ext_texture_filter_anisotropic; cvar_t *r_ext_max_anisotropy; cvar_t *r_ignoreGLErrors; cvar_t *r_ext_multisample; cvar_t *r_colorMipLevels; cvar_t *r_picmip; cvar_t *r_nomip; cvar_t *r_showtris; cvar_t *r_showsky; cvar_t *r_textureMode; cvar_t *r_gamma; cvar_t *r_lockpvs; cvar_t *r_noportals; cvar_t *r_subdivisions; cvar_t *r_lodCurveError; cvar_t *r_aviMotionJpegQuality; cvar_t *r_screenshotJpegQuality; int max_polys; int max_polyverts; static char gl_extensions[ 32768 ]; #define GLE( ret, name, ... ) ret ( APIENTRY * q##name )( __VA_ARGS__ ); QGL_Core_PROCS; QGL_Ext_PROCS; QGL_ARB_PROGRAM_PROCS; QGL_VBO_PROCS; QGL_FBO_PROCS; QGL_FBO_OPT_PROCS; #undef GLE typedef struct { void **symbol; const char *name; } sym_t; #define GLE( ret, name, ... ) { (void**)&q##name, XSTRING(name) }, static sym_t core_procs[] = { QGL_Core_PROCS }; static sym_t ext_procs[] = { QGL_Ext_PROCS }; static sym_t arb_procs[] = { QGL_ARB_PROGRAM_PROCS }; static sym_t vbo_procs[] = { QGL_VBO_PROCS }; static sym_t fbo_procs[] = { QGL_FBO_PROCS }; static sym_t fbo_opt_procs[] = { QGL_FBO_OPT_PROCS }; #undef GLE /* ================== R_ResolveSymbols returns NULL on success or last failed symbol name otherwise ================== */ static const char *R_ResolveSymbols( sym_t *syms, int count ) { int i; for ( i = 0; i < count; i++ ) { *syms[ i ].symbol = ri.GL_GetProcAddress( syms[ i ].name ); if ( *syms[ i ].symbol == NULL ) { return syms[ i ].name; } } return NULL; } static void R_ClearSymbols( sym_t *syms, int count ) { int i; for ( i = 0; i < count; i++ ) { *syms[ i ].symbol = NULL; } } static void R_ClearSymTables( void ) { R_ClearSymbols( core_procs, ARRAY_LEN( core_procs ) ); R_ClearSymbols( ext_procs, ARRAY_LEN( ext_procs ) ); R_ClearSymbols( arb_procs, ARRAY_LEN( arb_procs ) ); R_ClearSymbols( vbo_procs, ARRAY_LEN( vbo_procs ) ); R_ClearSymbols( fbo_procs, ARRAY_LEN( fbo_procs ) ); R_ClearSymbols( fbo_opt_procs, ARRAY_LEN( fbo_opt_procs ) ); } /* ** R_HaveExtension */ static qboolean R_HaveExtension( const char *ext ) { const char *ptr = Q_stristr( gl_extensions, ext ); if (ptr == NULL) return qfalse; ptr += strlen(ext); return ((*ptr == ' ') || (*ptr == '\0')); // verify its complete string. } /* ** R_InitExtensions */ static void R_InitExtensions( void ) { GLint max_texture_size = 0; float version; size_t len; const char *err; if ( !qglGetString( GL_EXTENSIONS ) ) { ri.Error( ERR_FATAL, "OpenGL installation is broken. Please fix video drivers and/or restart your system" ); } // get our config strings Q_strncpyz( glConfig.vendor_string, (char *)qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); Q_strncpyz( glConfig.renderer_string, (char *)qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); len = strlen( glConfig.renderer_string ); if ( len && glConfig.renderer_string[ len - 1 ] == '\n' ) glConfig.renderer_string[ len - 1 ] = '\0'; Q_strncpyz( glConfig.version_string, (char *)qglGetString( GL_VERSION ), sizeof( glConfig.version_string ) ); Q_strncpyz( gl_extensions, (char *)qglGetString( GL_EXTENSIONS ), sizeof( gl_extensions ) ); Q_strncpyz( glConfig.extensions_string, gl_extensions, sizeof( glConfig.extensions_string ) ); version = Q_atof( (const char *)qglGetString( GL_VERSION ) ); gl_version = (int)(version * 10.001); glConfig.textureCompression = TC_NONE; glConfig.textureEnvAddAvailable = qfalse; textureFilterAnisotropic = qfalse; maxAnisotropy = 0; nonPowerOfTwoTextures = qfalse; qglLockArraysEXT = NULL; qglUnlockArraysEXT = NULL; glConfig.numTextureUnits = 1; qglMultiTexCoord2fARB = NULL; qglActiveTextureARB = NULL; qglClientActiveTextureARB = NULL; gl_clamp_mode = GL_CLAMP; // by default // OpenGL driver constants qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &max_texture_size ); glConfig.maxTextureSize = max_texture_size; // stubbed or broken drivers may have reported 0... if ( glConfig.maxTextureSize <= 0 ) glConfig.maxTextureSize = 0; else if ( glConfig.maxTextureSize > 2048 ) glConfig.maxTextureSize = 2048; // ResampleTexture() relies on that maximum if ( !r_allowExtensions->integer ) { ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" ); return; } ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); if ( R_HaveExtension( "GL_EXT_texture_edge_clamp" ) || R_HaveExtension( "GL_SGIS_texture_edge_clamp" ) ) { gl_clamp_mode = GL_CLAMP_TO_EDGE; ri.Printf( PRINT_ALL, "...using GL_EXT_texture_edge_clamp\n" ); } else { ri.Printf( PRINT_ALL, "...GL_EXT_texture_edge_clamp not found\n" ); ri.Printf( PRINT_ALL, S_COLOR_YELLOW "...Degraded texture support likely!\n" ); } // GL_EXT_texture_compression_s3tc if ( R_HaveExtension( "GL_ARB_texture_compression" ) && R_HaveExtension( "GL_EXT_texture_compression_s3tc" ) ) { if ( r_ext_compressed_textures->integer ){ glConfig.textureCompression = TC_S3TC_ARB; ri.Printf( PRINT_ALL, "...using GL_EXT_texture_compression_s3tc\n" ); } else { ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_compression_s3tc\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_EXT_texture_compression_s3tc not found\n" ); } // GL_S3_s3tc if ( glConfig.textureCompression == TC_NONE && r_ext_compressed_textures->integer ) { if ( R_HaveExtension( "GL_S3_s3tc" ) ) { if ( r_ext_compressed_textures->integer ) { glConfig.textureCompression = TC_S3TC; ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" ); } else { glConfig.textureCompression = TC_NONE; ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" ); } } // GL_EXT_texture_env_add if ( R_HaveExtension( "EXT_texture_env_add" ) ) { if ( r_ext_texture_env_add->integer ) { glConfig.textureEnvAddAvailable = qtrue; ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); } else { glConfig.textureEnvAddAvailable = qfalse; ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" ); } // GL_ARB_multitexture if ( R_HaveExtension( "GL_ARB_multitexture" ) ) { if ( r_ext_multitexture->integer ) { qglMultiTexCoord2fARB = ri.GL_GetProcAddress( "glMultiTexCoord2fARB" ); qglActiveTextureARB = ri.GL_GetProcAddress( "glActiveTextureARB" ); qglClientActiveTextureARB = ri.GL_GetProcAddress( "glClientActiveTextureARB" ); if ( qglActiveTextureARB && qglClientActiveTextureARB ) { GLint textureUnits = 0; qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &textureUnits ); if ( textureUnits > 1 ) { GLint max_shader_units = 0; GLint max_bind_units = 0; qglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &max_shader_units ); qglGetIntegerv( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_bind_units ); if ( max_bind_units > max_shader_units ) max_bind_units = max_shader_units; if ( max_bind_units > MAX_TEXTURE_UNITS ) max_bind_units = MAX_TEXTURE_UNITS; glConfig.numTextureUnits = MAX( textureUnits, max_bind_units ); ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); } else { qglMultiTexCoord2fARB = NULL; qglActiveTextureARB = NULL; qglClientActiveTextureARB = NULL; ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" ); } } } else { ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); } // GL_EXT_compiled_vertex_array if ( R_HaveExtension( "GL_EXT_compiled_vertex_array" ) ) { if ( r_ext_compiled_vertex_array->integer ) { ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); qglLockArraysEXT = ri.GL_GetProcAddress( "glLockArraysEXT" ); qglUnlockArraysEXT = ri.GL_GetProcAddress( "glUnlockArraysEXT" ); if ( !qglLockArraysEXT || !qglUnlockArraysEXT ) { ri.Error( ERR_FATAL, "bad getprocaddress" ); } } else { ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); } if ( R_HaveExtension( "GL_EXT_texture_filter_anisotropic" ) ) { if ( r_ext_texture_filter_anisotropic->integer ) { qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy ); if ( maxAnisotropy <= 0 ) { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); maxAnisotropy = 0; } else { ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy ); textureFilterAnisotropic = qtrue; maxAnisotropy = MIN( r_ext_max_anisotropy->integer, maxAnisotropy ); } } else { ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); } if ( R_HaveExtension( "GL_ARB_vertex_program" ) && R_HaveExtension( "GL_ARB_fragment_program" ) ) { err = R_ResolveSymbols( arb_procs, ARRAY_LEN( arb_procs ) ); if ( err ) { ri.Printf( PRINT_WARNING, "Error resolving ARB program function '%s'\n", err ); qglGenProgramsARB = NULL; // indicates presence of ARB shaders functionality } else { ri.Printf( PRINT_ALL, "...using ARB vertex/fragment programs\n" ); } } if ( R_HaveExtension( "ARB_vertex_buffer_object" ) && qglActiveTextureARB ) { err = R_ResolveSymbols( vbo_procs, ARRAY_LEN( vbo_procs ) ); if ( err ) { ri.Printf( PRINT_WARNING, "Error resolving VBO function '%s'\n", err ); qglBindBufferARB = NULL; // indicates presence of VBO functionality } else { ri.Printf( PRINT_ALL, "...using ARB vertex buffer objects\n" ); } } if ( R_HaveExtension( "GL_EXT_framebuffer_object" ) && R_HaveExtension( "GL_EXT_framebuffer_blit" ) ) { err = R_ResolveSymbols( fbo_procs, ARRAY_LEN( fbo_procs ) ); if ( err ) { ri.Printf( PRINT_WARNING, "Error resolving FBO function '%s'\n", err ); qglGenFramebuffers = NULL; // indicates presence of FBO functionality } else { // resolve optional fbo functions, without any warnings R_ResolveSymbols( fbo_opt_procs, ARRAY_LEN( fbo_opt_procs ) ); } } } /* ** InitOpenGL ** ** This function is responsible for initializing a valid OpenGL subsystem. This ** is done by calling GLimp_Init (which gives us a working OGL subsystem) then ** setting variables, checking GL constants, and reporting the gfx system config ** to the user. */ static void InitOpenGL( void ) { // // initialize OS specific portions of the renderer // // GLimp_Init directly or indirectly references the following cvars: // - r_fullscreen // - r_mode // - r_(color|depth|stencil)bits // - r_ignorehwgamma // - r_gamma // if ( glConfig.vidWidth == 0 ) { const char *err; if ( !ri.GLimp_Init ) { ri.Error( ERR_FATAL, "OpenGL interface is not initialized" ); } ri.GLimp_Init( &glConfig ); R_ClearSymTables(); err = R_ResolveSymbols( core_procs, ARRAY_LEN( core_procs ) ); if ( err ) ri.Error( ERR_FATAL, "Error resolving core OpenGL function '%s'", err ); R_InitExtensions(); gls.windowWidth = glConfig.vidWidth; gls.windowHeight = glConfig.vidHeight; gls.captureWidth = glConfig.vidWidth; gls.captureHeight = glConfig.vidHeight; if ( r_modernMode->integer && qglGenProgramsARB && qglGenFramebuffers ) { gls.captureWidth = glConfig.vidWidth; gls.captureHeight = glConfig.vidHeight; } QGL_InitARB(); glConfig.deviceSupportsGamma = qfalse; ri.GLimp_InitGamma( &glConfig ); gls.deviceSupportsGamma = glConfig.deviceSupportsGamma; if ( r_ignorehwgamma->integer ) glConfig.deviceSupportsGamma = qfalse; gls.initTime = ri.Milliseconds(); } if ( !qglViewport ) { // might happen after REF_KEEP_WINDOW const char *err = R_ResolveSymbols( core_procs, ARRAY_LEN( core_procs ) ); if ( err ) ri.Error( ERR_FATAL, "Error resolving core OpenGL function '%s'", err ); R_InitExtensions(); QGL_InitARB(); gls.initTime = ri.Milliseconds(); } // set default state GL_SetDefaultState(); tr.inited = qtrue; } /* ================== GL_CheckErrors ================== */ void GL_CheckErrors( void ) { int err; const char *s; char buf[32]; err = qglGetError(); if ( err == GL_NO_ERROR ) { return; } if ( r_ignoreGLErrors->integer ) { return; } switch( err ) { case GL_INVALID_ENUM: s = "GL_INVALID_ENUM"; break; case GL_INVALID_VALUE: s = "GL_INVALID_VALUE"; break; case GL_INVALID_OPERATION: s = "GL_INVALID_OPERATION"; break; case GL_STACK_OVERFLOW: s = "GL_STACK_OVERFLOW"; break; case GL_STACK_UNDERFLOW: s = "GL_STACK_UNDERFLOW"; break; case GL_OUT_OF_MEMORY: s = "GL_OUT_OF_MEMORY"; break; default: Com_sprintf( buf, sizeof(buf), "%i", err); s = buf; break; } ri.Error( ERR_FATAL, "GL_CheckErrors: %s", s ); } /* ============================================================================== SCREEN SHOTS NOTE TTimo some thoughts about the screenshots system: screenshots get written in fs_homepath + fs_gamedir vanilla q3 .. baseq3/screenshots/ *.tga team arena .. missionpack/screenshots/ *.tga two commands: "screenshot" and "screenshotJPEG" we use statics to store a count and start writing the first screenshot/screenshot????.tga (.jpg) available (with FS_FileExists / FS_FOpenFileWrite calls) FIXME: the statics don't get a reinit between fs_game changes ============================================================================== */ /* ================== RB_ReadPixels Reads an image but takes care of alignment issues for reading RGB images. Reads a minimum offset for where the RGB data starts in the image from integer stored at pointer offset. When the function has returned the actual offset was written back to address offset. This address will always have an alignment of packAlign to ensure efficient copying. Stores the length of padding after a line of pixels to address padlen Return value must be freed with ri.Hunk_FreeTempMemory() ================== */ static byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen, int lineAlign ) { byte *buffer, *bufstart; int padwidth, linelen; int bufAlign; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width * 3; if ( packAlign < lineAlign ) padwidth = PAD(linelen, lineAlign); else padwidth = PAD(linelen, packAlign); bufAlign = MAX( packAlign, 16 ); // for SIMD // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + bufAlign - 1); bufstart = PADP((intptr_t) buffer + *offset, bufAlign); qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart ); *offset = bufstart - buffer; *padlen = PAD(linelen, packAlign) - linelen; return buffer; } /* ================== RB_TakeScreenshot ================== */ void RB_TakeScreenshot( int x, int y, int width, int height, const char *fileName ) { const int header_size = 18; byte *allbuf, *buffer; byte *srcptr, *destptr; byte *endline, *endmem; byte temp; int linelen, padlen; size_t offset, memcount; offset = header_size; allbuf = RB_ReadPixels( x, y, width, height, &offset, &padlen, 0 ); buffer = allbuf + offset - header_size; Com_Memset( buffer, 0, header_size ); buffer[2] = 2; // uncompressed type buffer[12] = width & 255; buffer[13] = width >> 8; buffer[14] = height & 255; buffer[15] = height >> 8; buffer[16] = 24; // pixel size // swap rgb to bgr and remove padding from line endings linelen = width * 3; srcptr = destptr = allbuf + offset; endmem = srcptr + (linelen + padlen) * height; while(srcptr < endmem) { endline = srcptr + linelen; while(srcptr < endline) { temp = srcptr[0]; *destptr++ = srcptr[2]; *destptr++ = srcptr[1]; *destptr++ = temp; srcptr += 3; } // Skip the pad srcptr += padlen; } memcount = linelen * height; // gamma correction R_GammaCorrect( allbuf + offset, memcount ); ri.FS_WriteFile( fileName, buffer, memcount + header_size ); ri.Hunk_FreeTempMemory( allbuf ); } /* ================== RB_TakeScreenshotJPEG ================== */ void RB_TakeScreenshotJPEG( int x, int y, int width, int height, const char *fileName ) { byte *buffer; size_t offset = 0, memcount; int padlen; buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen, 0); memcount = (width * 3 + padlen) * height; // gamma correction R_GammaCorrect( buffer + offset, memcount ); ri.CL_SaveJPG( fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen ); ri.Hunk_FreeTempMemory( buffer ); } static void FillBMPHeader( byte *buffer, int width, int height, int memcount, int header_size ) { int filesize; Com_Memset( buffer, 0, header_size ); // bitmap file header buffer[0] = 'B'; buffer[1] = 'M'; filesize = memcount + header_size; buffer[2] = (filesize >> 0) & 255; buffer[3] = (filesize >> 8) & 255; buffer[4] = (filesize >> 16) & 255; buffer[5] = (filesize >> 24) & 255; buffer[10] = header_size; // data offset // bitmap info header buffer[14] = 40; // size of this header buffer[18] = (width >> 0) & 255; buffer[19] = (width >> 8) & 255; buffer[20] = (width >> 16) & 255; buffer[21] = (width >> 24) & 255; buffer[22] = (height >> 0) & 255; buffer[23] = (height >> 8) & 255; buffer[24] = (height >> 16) & 255; buffer[25] = (height >> 24) & 255; buffer[26] = 1; // number of color planes buffer[28] = 24; // bpp buffer[34] = (memcount >> 0) & 255; buffer[35] = (memcount >> 8) & 255; buffer[36] = (memcount >> 16) & 255; buffer[37] = (memcount >> 24) & 255; buffer[38] = 0xC4; // horizontal dpi buffer[39] = 0x0E; // horizontal dpi buffer[42] = 0xC4; // vertical dpi buffer[43] = 0x0E; // vertical dpi } /* ================== RB_TakeScreenshotBMP ================== */ void RB_TakeScreenshotBMP( int x, int y, int width, int height, const char *fileName, int clipboardOnly ) { byte *allbuf; byte *buffer; // destination buffer byte *srcptr, *srcline; byte *destptr, *dstline; byte *endmem; byte temp[4]; size_t memcount, offset; const int header_size = 54; // bitmapfileheader(14) + bitmapinfoheader(40) int scanlen, padlen; int scanpad, len; offset = header_size; allbuf = RB_ReadPixels( x, y, width, height, &offset, &padlen, 4 ); buffer = allbuf + offset; // scanline length scanlen = PAD( width*3, 4 ); scanpad = scanlen - width*3; memcount = scanlen * height; // swap rgb to bgr and add line padding if ( scanpad == 0 && padlen == 0 ) { // fastest case srcptr = destptr = allbuf + offset; endmem = srcptr + scanlen * height; while ( srcptr < endmem ) { temp[0] = srcptr[0]; destptr[0] = srcptr[2]; destptr[2] = temp[0]; destptr += 3; srcptr += 3; } } else { // move destination buffer forward if source padding is greater than for BMP if ( padlen > scanpad ) buffer += (width * 3 + padlen - scanlen ) * height; // point on last line srcptr = allbuf + offset + (height-1) * (width * 3 + padlen); destptr = buffer + (height-1) * scanlen; len = (width * 3 - 3); while ( destptr >= buffer ) { srcline = srcptr + len; dstline = destptr + len; while ( srcline >= srcptr ) { temp[2] = srcline[0]; temp[1] = srcline[1]; temp[0] = srcline[2]; dstline[0] = temp[0]; dstline[1] = temp[1]; dstline[2] = temp[2]; dstline-=3; srcline-=3; } srcptr -= (width * 3 + padlen); destptr -= scanlen; } } // fill this last to avoid data overwrite in case when we're moving destination buffer forward FillBMPHeader( buffer - header_size, width, height, memcount, header_size ); // gamma correction R_GammaCorrect( buffer, memcount ); if ( clipboardOnly ) { // copy starting from bitmapinfoheader ri.Sys_SetClipboardBitmap( buffer - 40, memcount + 40 ); } else { ri.FS_WriteFile( fileName, buffer - header_size, memcount + header_size ); } ri.Hunk_FreeTempMemory( allbuf ); } /* ================== R_ScreenshotFilename ================== */ static void R_ScreenshotFilename( char *fileName, const char *fileExt ) { qtime_t t; int count; count = 0; ri.Com_RealTime( &t ); Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot-%04d%02d%02d-%02d%02d%02d.%s", 1900 + t.tm_year, 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, fileExt ); while ( ri.FS_FileExists( fileName ) && ++count < 1000 ) { Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot-%04d%02d%02d-%02d%02d%02d-%d.%s", 1900 + t.tm_year, 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, count, fileExt ); } } /* ================== R_ScreenShot_f screenshot screenshot [silent] screenshot [filename] Doesn't print the pacifier message if there is a second arg ================== */ static void R_ScreenShot_f( void ) { char checkname[MAX_OSPATH]; qboolean silent; int typeMask; const char *ext; if ( ri.CL_IsMinimized() && !RE_CanMinimize() ) { ri.Printf( PRINT_WARNING, "WARNING: unable to take screenshot when minimized because FBO is not available/enabled.\n" ); return; } if ( Q_stricmp( ri.Cmd_Argv(0), "screenshotJPEG" ) == 0 ) { typeMask = SCREENSHOT_JPG; ext = "jpg"; } else if ( Q_stricmp( ri.Cmd_Argv(0), "screenshotBMP" ) == 0 ) { typeMask = SCREENSHOT_BMP; ext = "bmp"; } else { typeMask = SCREENSHOT_TGA; ext = "tga"; } // check if already scheduled if ( backEnd.screenshotMask & typeMask ) return; if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { silent = qtrue; } else if ( typeMask == SCREENSHOT_BMP && !strcmp( ri.Cmd_Argv(1), "clipboard" ) ) { backEnd.screenshotMask |= SCREENSHOT_BMP_CLIPBOARD; silent = qtrue; } else { silent = qfalse; } if ( ri.Cmd_Argc() == 2 && !silent ) { // explicit filename Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.%s", ri.Cmd_Argv( 1 ), ext ); } else { if ( backEnd.screenshotMask & SCREENSHOT_BMP_CLIPBOARD ) { // no need for filename, copy to system buffer checkname[0] = '\0'; } else { // scan for a free filename R_ScreenshotFilename( checkname, ext ); } } // we will make screenshot right at the end of RE_EndFrame() backEnd.screenshotMask |= typeMask; if ( typeMask == SCREENSHOT_JPG ) { backEnd.screenShotJPGsilent = silent; Q_strncpyz( backEnd.screenshotJPG, checkname, sizeof( backEnd.screenshotJPG ) ); } else if ( typeMask == SCREENSHOT_BMP ) { backEnd.screenShotBMPsilent = silent; Q_strncpyz( backEnd.screenshotBMP, checkname, sizeof( backEnd.screenshotBMP ) ); } else { backEnd.screenShotTGAsilent = silent; Q_strncpyz( backEnd.screenshotTGA, checkname, sizeof( backEnd.screenshotTGA ) ); } } //============================================================================ /* ================== RB_TakeVideoFrameCmd ================== */ const void *RB_TakeVideoFrameCmd( const void *data ) { const videoFrameCommand_t *cmd; byte *cBuf; size_t memcount, linelen; int padwidth, avipadwidth, padlen, avipadlen; int packAlign; cmd = (const videoFrameCommand_t *)data; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = cmd->width * 3; // Alignment stuff for glReadPixels padwidth = PAD(linelen, packAlign); padlen = padwidth - linelen; // AVI line padding avipadwidth = PAD(linelen, AVI_LINE_PADDING); avipadlen = avipadwidth - linelen; cBuf = PADP(cmd->captureBuffer, packAlign); qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, GL_UNSIGNED_BYTE, cBuf); memcount = padwidth * cmd->height; // gamma correction R_GammaCorrect( cBuf, memcount ); if ( cmd->motionJpeg ) { memcount = ri.CL_SaveJPGToBuffer( cmd->encodeBuffer, linelen * cmd->height, r_aviMotionJpegQuality->integer, cmd->width, cmd->height, cBuf, padlen ); ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); } else { byte *lineend, *memend; byte *srcptr, *destptr; srcptr = cBuf; destptr = cmd->encodeBuffer; memend = srcptr + memcount; // swap R and B and remove line paddings while(srcptr < memend) { lineend = srcptr + linelen; while(srcptr < lineend) { *destptr++ = srcptr[2]; *destptr++ = srcptr[1]; *destptr++ = srcptr[0]; srcptr += 3; } Com_Memset(destptr, '\0', avipadlen); destptr += avipadlen; srcptr += padlen; } ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); } return (const void *)(cmd + 1); } //============================================================================ /* ** GL_SetDefaultState */ static void GL_SetDefaultState( void ) { int i; glState.currenttmu = 0; glState.currentArray = 0; for ( i = 0; i < MAX_TEXTURE_UNITS; i++ ) { glState.currenttextures[ i ] = 0; glState.glClientStateBits[ i ] = 0; } qglClearDepth( 1.0f ); qglCullFace( GL_FRONT ); glState.faceCulling = -1; qglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); // initialize downstream texture unit if we're running // in a multitexture environment if ( qglActiveTextureARB ) { qglActiveTextureARB( GL_TEXTURE1_ARB ); GL_TextureMode( r_textureMode->string ); GL_TexEnv( GL_MODULATE ); qglDisable( GL_TEXTURE_2D ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); qglActiveTextureARB( GL_TEXTURE0_ARB ); } qglEnable( GL_TEXTURE_2D ); GL_TextureMode( r_textureMode->string ); GL_TexEnv( GL_MODULATE ); qglShadeModel( GL_SMOOTH ); qglDepthFunc( GL_LEQUAL ); // the vertex array is always enabled, but the color and texture // arrays are enabled and disabled around the compiled vertex array call qglEnableClientState( GL_VERTEX_ARRAY ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); qglDisableClientState( GL_COLOR_ARRAY ); qglDisableClientState( GL_NORMAL_ARRAY ); // // make sure our GL state vector is set correctly // glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); qglDepthMask( GL_TRUE ); qglDisable( GL_DEPTH_TEST ); qglEnable( GL_SCISSOR_TEST ); qglDisable( GL_CULL_FACE ); qglDisable( GL_BLEND ); } /* =============== RE_SyncRender =============== */ static void RE_SyncRender( void ) { if ( qglFinish && backEnd.doneSurfaces ) { qglFinish(); } } /* =============== R_Register =============== */ static void R_Register( void ) { ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); ri.Cmd_AddCommand( "shaderlist", R_ShaderList_f ); ri.Cmd_AddCommand( "skinlist", R_SkinList_f ); ri.Cmd_AddCommand( "modellist", R_Modellist_f ); ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShot_f ); ri.Cmd_AddCommand( "screenshotBMP", R_ScreenShot_f ); r_picmip = ri.Cvar_Get( "r_picmip", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_nomip = ri.Cvar_Get( "r_nomip", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_colorMipLevels = ri.Cvar_Get ("r_colorMipLevels", "0", CVAR_LATCH ); r_mergeLightmaps = ri.Cvar_Get( "r_mergeLightmaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_subdivisions = ri.Cvar_Get( "r_subdivisions", "2", CVAR_ARCHIVE | CVAR_LATCH ); r_lodCurveError = ri.Cvar_Get( "r_lodCurveError", "250", CVAR_ARCHIVE ); r_lodbias = ri.Cvar_Get( "r_lodbias", "-2", CVAR_ARCHIVE ); r_znear = ri.Cvar_Get( "r_znear", "1", CVAR_CHEAT ); r_zproj = ri.Cvar_Get( "r_zproj", "64", CVAR_ARCHIVE ); r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); r_dlightMode = ri.Cvar_Get( "r_dlightMode", "1", CVAR_ARCHIVE ); r_ext_multisample = ri.Cvar_Get( "r_ext_multisample", "0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_ext_multisample, CVG_RENDERER ); r_hdr = ri.Cvar_Get( "r_hdr", "1", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_hdr, CVG_RENDERER ); r_bloom_threshold = ri.Cvar_Get( "r_bloom_threshold", "0.00", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_bloom_threshold, CVG_RENDERER ); r_bloom_threshold_mode = ri.Cvar_Get( "r_bloom_threshold_mode", "0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_bloom_threshold_mode, CVG_RENDERER ); r_bloom_intensity = ri.Cvar_Get( "r_bloom_intensity", "0.10", CVAR_ARCHIVE ); r_bloom_passes = ri.Cvar_Get( "r_bloom_passes", "6", CVAR_ARCHIVE | CVAR_LATCH ); r_bloom_blend_base = ri.Cvar_Get( "r_bloom_blend_base", "1", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_bloom_blend_base, CVG_RENDERER ); r_bloom_modulate = ri.Cvar_Get( "r_bloom_modulate", "0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_bloom_modulate, CVG_RENDERER ); r_bloom_filter_size = ri.Cvar_Get( "r_bloom_filter_size", "5", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_bloom_filter_size, CVG_RENDERER ); r_bloom_reflection = ri.Cvar_Get( "r_bloom_reflection", "0.05", CVAR_ARCHIVE ); r_textureMode = ri.Cvar_Get( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_textureMode, CVG_RENDERER ); r_gamma = ri.Cvar_Get( "r_gamma", "1.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_gamma, CVG_RENDERER ); r_modernMode = ri.Cvar_Get( "r_modernMode", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_postfx = ri.Cvar_Get( "r_postfx", "1", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_postfx, CVG_RENDERER ); r_postprocess = ri.Cvar_Get( "r_postprocess", "0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_postprocess, CVG_RENDERER ); r_fx_greyscale = ri.Cvar_Get( "r_fx_greyscale", "-0.25", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_greyscale, CVG_RENDERER ); r_fx_sepia = ri.Cvar_Get( "r_fx_sepia", "1.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_sepia, CVG_RENDERER ); r_fx_contrast = ri.Cvar_Get( "r_fx_contrast", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_contrast, CVG_RENDERER ); r_fx_brightness = ri.Cvar_Get( "r_fx_brightness", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_brightness, CVG_RENDERER ); r_fx_invert = ri.Cvar_Get( "r_fx_invert", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_invert, CVG_RENDERER ); r_fx_tint_r = ri.Cvar_Get( "r_fx_tint_r", "1.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_tint_r, CVG_RENDERER ); r_fx_tint_g = ri.Cvar_Get( "r_fx_tint_g", "1.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_tint_g, CVG_RENDERER ); r_fx_tint_b = ri.Cvar_Get( "r_fx_tint_b", "1.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_tint_b, CVG_RENDERER ); r_fx_posterize = ri.Cvar_Get( "r_fx_posterize", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_posterize, CVG_RENDERER ); r_fx_glow = ri.Cvar_Get( "r_fx_glow", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_glow, CVG_RENDERER ); r_fx_filmic = ri.Cvar_Get( "r_fx_filmic", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_filmic, CVG_RENDERER ); r_fx_bloom = ri.Cvar_Get( "r_fx_bloom", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_bloom, CVG_RENDERER ); r_fx_chromaticAberration = ri.Cvar_Get( "r_fx_chromaticAberration", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_chromaticAberration, CVG_RENDERER ); r_fx_chameleon = ri.Cvar_Get( "r_fx_chameleon", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_chameleon, CVG_RENDERER ); r_fx_ambientlight = ri.Cvar_Get( "r_fx_ambientlight", "0.0", CVAR_ARCHIVE ); ri.Cvar_SetGroup( r_fx_ambientlight, CVG_RENDERER ); r_fx_blur = ri.Cvar_Get( "r_fx_blur", "0.0", 0 ); ri.Cvar_SetGroup( r_fx_blur, CVG_RENDERER ); r_drawworld = ri.Cvar_Get ("r_drawworld", "1", CVAR_CHEAT ); r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT ); r_nocull = ri.Cvar_Get ("r_nocull", "0", CVAR_CHEAT); r_speeds = ri.Cvar_Get ("r_speeds", "0", CVAR_CHEAT); r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT); r_showsky = ri.Cvar_Get( "r_showsky", "0", 0 ); r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", CVAR_CHEAT); r_noportals = ri.Cvar_Get( "r_noportals", "0", 0 ); r_aviMotionJpegQuality = ri.Cvar_Get( "r_aviMotionJpegQuality", "100", CVAR_ARCHIVE ); r_screenshotJpegQuality = ri.Cvar_Get( "r_screenshotJpegQuality", "100", CVAR_ARCHIVE ); if (glConfig.vidWidth) return; r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_max_anisotropy = ri.Cvar_Get( "r_ext_max_anisotropy", "8", CVAR_ARCHIVE | CVAR_LATCH ); r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH ); } #define EPSILON 1e-6f /* =============== R_Init =============== */ void R_Init( void ) { int err; int i; byte *ptr; ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); // clear all our internal state Com_Memset( &tr, 0, sizeof( tr ) ); Com_Memset( &backEnd, 0, sizeof( backEnd ) ); Com_Memset( &tess, 0, sizeof( tess ) ); Com_Memset( &glState, 0, sizeof( glState ) ); if ( sizeof( glconfig_t ) != 11316 ) ri.Error( ERR_FATAL, "Mod ABI incompatible: sizeof(glconfig_t) == %u != 11316", (unsigned int) sizeof( glconfig_t ) ); if ( (intptr_t)tess.xyz & 15 ) { ri.Printf( PRINT_WARNING, "tess.xyz not 16 byte aligned\n" ); } Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); // // init function tables // for ( i = 0; i < FUNCTABLE_SIZE; i++ ) { if ( i == 0 ) { tr.sinTable[i] = EPSILON; } else if ( i == (FUNCTABLE_SIZE - 1) ) { tr.sinTable[i] = -EPSILON; } else { tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ((float)(FUNCTABLE_SIZE - 1)) ) ); } tr.squareTable[i] = (i < FUNCTABLE_SIZE / 2) ? 1.0f : -1.0f; if ( i == 0 ) { tr.sawToothTable[i] = EPSILON; } else { tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; } tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; if ( i < FUNCTABLE_SIZE / 2 ) { if ( i < FUNCTABLE_SIZE / 4 ) { if ( i == 0 ) { tr.triangleTable[i] = EPSILON; } else { tr.triangleTable[i] = (float)i / (FUNCTABLE_SIZE / 4); } } else { 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_POLYS; max_polyverts = MAX_POLYVERTS; ptr = ri.Hunk_Alloc( sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts ); backEndData = (backEndData_t *) ptr; backEndData->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData )); backEndData->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys); R_InitNextFrame(); InitOpenGL(); R_InitImages(); R_InitShaders(); R_InitSkins(); R_ModelInit(); err = qglGetError(); if ( err != GL_NO_ERROR ) ri.Printf( PRINT_WARNING, "glGetError() = 0x%x\n", err ); ri.Printf( PRINT_ALL, "----- finished R_Init -----\n" ); } /* =============== RE_Shutdown =============== */ static void RE_Shutdown( refShutdownCode_t code ) { ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", code ); ri.Cmd_RemoveCommand( "modellist" ); ri.Cmd_RemoveCommand( "screenshotBMP" ); ri.Cmd_RemoveCommand( "screenshotJPEG" ); ri.Cmd_RemoveCommand( "screenshot" ); ri.Cmd_RemoveCommand( "imagelist" ); ri.Cmd_RemoveCommand( "shaderlist" ); ri.Cmd_RemoveCommand( "skinlist" ); ri.Cmd_RemoveCommand( "shaderstate" ); if ( tr.registered ) { R_DeleteTextures(); } // shut down platform specific OpenGL stuff if ( code != REF_KEEP_CONTEXT ) { QGL_DoneARB(); VBO_Cleanup(); R_ClearSymTables(); Com_Memset( &glState, 0, sizeof( glState ) ); if ( code != REF_KEEP_WINDOW ) { ri.GLimp_Shutdown( code == REF_UNLOAD_DLL ? qtrue : qfalse ); Com_Memset( &glConfig, 0, sizeof( glConfig ) ); } } ri.FreeAll(); tr.registered = qfalse; tr.inited = qfalse; } /* @@@@@@@@@@@@@@@@@@@@@ GetRefAPI @@@@@@@@@@@@@@@@@@@@@ */ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { static refexport_t re; ri = *rimp; Com_Memset( &re, 0, sizeof( re ) ); if ( apiVersion != REF_API_VERSION ) { ri.Printf(PRINT_ALL, "Mismatched REF_API_VERSION: expected %i, got %i\n", REF_API_VERSION, apiVersion ); return NULL; } // 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.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.AddLightToScene = RE_AddLightToScene; re.AddLinearLightToScene = RE_AddLinearLightToScene; re.RenderScene = RE_RenderScene; re.SetColor = RE_SetColor; re.DrawStretchPic = RE_StretchPic; re.RemapShader = RE_RemapShader; re.GetEntityToken = RE_GetEntityToken; re.inPVS = R_inPVS; re.TakeVideoFrame = RE_TakeVideoFrame; re.SetColorMappings = R_SetColorMappings; re.ThrottleBackend = RE_ThrottleBackend; re.FinishBloom = RE_FinishBloom; re.CanMinimize = RE_CanMinimize; re.GetConfig = RE_GetConfig; re.SyncRender = RE_SyncRender; return &re; }