/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006 Robert Beckebans <trebor_7@users.sourceforge.net>

This file is part of XreaL source code.

XreaL 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.

XreaL 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 XreaL 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;
glConfig2_t     glConfig2;
glstate_t       glState;
float			displayAspect = 0.0f;

static void     GfxInfo_f(void);

cvar_t         *r_flareSize;
cvar_t         *r_flareFade;
cvar_t		   *r_flareCoeff;

cvar_t         *r_railWidth;
cvar_t         *r_railCoreWidth;
cvar_t         *r_railSegmentLength;

cvar_t         *r_verbose;
cvar_t         *r_ignore;

cvar_t         *r_displayRefresh;

cvar_t         *r_detailTextures;

cvar_t         *r_znear;
cvar_t         *r_zfar;

cvar_t         *r_smp;
cvar_t         *r_showSmp;
cvar_t         *r_skipBackEnd;

cvar_t         *r_ignorehwgamma;
cvar_t         *r_measureOverdraw;

cvar_t         *r_inGameVideo;
cvar_t         *r_fastsky;
cvar_t         *r_drawSun;
cvar_t         *r_drawBloom;
cvar_t         *r_bloomBlur;
cvar_t         *r_lighting;
cvar_t         *r_dynamicLighting;

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_novis;
cvar_t         *r_nocull;
cvar_t         *r_showcluster;
cvar_t         *r_nocurves;
cvar_t         *r_nobatching;
cvar_t         *r_noLightScissors;
cvar_t         *r_noLightVisCull;
cvar_t         *r_noInteractionSort;

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_multisample;
cvar_t         *r_ext_texture_non_power_of_two;
cvar_t         *r_ext_texture_cube_map;
cvar_t         *r_ext_depth_texture;
cvar_t         *r_ext_vertex_program;
cvar_t         *r_ext_vertex_buffer_object;
cvar_t         *r_ext_occlusion_query;
cvar_t         *r_ext_shading_language_100;
cvar_t         *r_ext_stencil_wrap;
cvar_t         *r_ext_texture_filter_anisotropic;
cvar_t         *r_ext_stencil_two_side;
cvar_t         *r_ext_depth_bounds_test;
cvar_t         *r_ext_framebuffer_object;
cvar_t         *r_ext_generate_mipmap;

cvar_t         *r_ignoreGLErrors;
cvar_t         *r_logFile;

cvar_t         *r_stencilbits;
cvar_t         *r_depthbits;
cvar_t         *r_colorbits;
cvar_t         *r_stereo;
cvar_t         *r_texturebits;

cvar_t         *r_drawBuffer;
cvar_t         *r_uiFullScreen;
cvar_t         *r_shadows;
cvar_t         *r_shadowOffsetFactor;
cvar_t         *r_shadowOffsetUnits;
cvar_t         *r_drawFlares;
cvar_t         *r_noLightFrustums;
cvar_t         *r_mode;
cvar_t         *r_collapseStages;
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_showTangentSpaces;
cvar_t         *r_finish;
cvar_t         *r_clear;
cvar_t         *r_swapInterval;
cvar_t         *r_textureMode;
cvar_t         *r_offsetFactor;
cvar_t         *r_offsetUnits;
cvar_t         *r_specularExponent;
cvar_t         *r_gamma;
cvar_t         *r_intensity;
cvar_t         *r_lockpvs;
cvar_t         *r_noportals;
cvar_t         *r_portalOnly;

cvar_t         *r_subdivisions;

cvar_t         *r_fullscreen;

cvar_t         *r_customwidth;
cvar_t         *r_customheight;
cvar_t         *r_customaspect;

cvar_t         *r_overBrightBits;
cvar_t         *r_mapOverBrightBits;

cvar_t         *r_simpleMipMaps;

cvar_t         *r_showImages;

cvar_t         *r_ambientScale;
cvar_t         *r_directedScale;
cvar_t         *r_lightScale;
cvar_t         *r_debugLight;
cvar_t         *r_debugSort;

cvar_t         *r_maxpolys;
int             max_polys;
cvar_t         *r_maxpolyverts;
int             max_polyverts;

cvar_t         *r_showLightMaps;
cvar_t         *r_showDeluxeMaps;
cvar_t         *r_showNormalMaps;
cvar_t         *r_showShadowVolumes;
cvar_t         *r_showSkeleton;
cvar_t         *r_showEntityTransforms;
cvar_t         *r_showLightTransforms;
cvar_t         *r_showLightInteractions;
cvar_t         *r_showLightScissors;
cvar_t         *r_showOcclusionQueries;

cvar_t         *r_vboFaces;
cvar_t         *r_vboCurves;
cvar_t         *r_vboTriangles;

static void AssertCvarRange(cvar_t * cv, float minVal, float maxVal, qboolean shouldBeIntegral)
{
	if(shouldBeIntegral)
	{
		if((int)cv->value != cv->integer)
		{
			ri.Printf(PRINT_WARNING, "WARNING: cvar '%s' must be integral (%f)\n", cv->name, cv->value);
			ri.Cvar_Set(cv->name, va("%d", cv->integer));
		}
	}

	if(cv->value < minVal)
	{
		ri.Printf(PRINT_WARNING, "WARNING: cvar '%s' out of range (%f < %f)\n", cv->name, cv->value, minVal);
		ri.Cvar_Set(cv->name, va("%f", minVal));
	}
	else if(cv->value > maxVal)
	{
		ri.Printf(PRINT_WARNING, "WARNING: cvar '%s' out of range (%f > %f)\n", cv->name, cv->value, maxVal);
		ri.Cvar_Set(cv->name, va("%f", maxVal));
	}
}



/*
** 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)
{
	char            renderer_buffer[1024];

	//
	// 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)
	{
		GLint           temp;

		GLimp_Init();

		strcpy(renderer_buffer, glConfig.renderer_string);
		Q_strlwr(renderer_buffer);

		// OpenGL driver constants
		qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &temp);
		glConfig.maxTextureSize = temp;

		// stubbed or broken drivers may have reported 0...
		if(glConfig.maxTextureSize <= 0)
		{
			glConfig.maxTextureSize = 0;
		}
	}

	// init command buffers and SMP
	R_InitCommandBuffers();

	// print info
	GfxInfo_f();

	// set default state
	GL_SetDefaultState();
}

/*
==================
GL_CheckErrors
==================
*/
void GL_CheckErrors_(const char *filename, int line)
{
	int             err;
	char            s[128];
	
	if(glConfig.smpActive)
	{
		// we can't print onto the console while rendering in another thread
		return;
	}

	if(r_ignoreGLErrors->integer)
	{
		return;
	}

	err = qglGetError();
	if(err == GL_NO_ERROR)
	{
		return;
	}

	switch (err)
	{
		case GL_INVALID_ENUM:
			strcpy(s, "GL_INVALID_ENUM");
			break;
		case GL_INVALID_VALUE:
			strcpy(s, "GL_INVALID_VALUE");
			break;
		case GL_INVALID_OPERATION:
			strcpy(s, "GL_INVALID_OPERATION");
			break;
		case GL_STACK_OVERFLOW:
			strcpy(s, "GL_STACK_OVERFLOW");
			break;
		case GL_STACK_UNDERFLOW:
			strcpy(s, "GL_STACK_UNDERFLOW");
			break;
		case GL_OUT_OF_MEMORY:
			strcpy(s, "GL_OUT_OF_MEMORY");
			break;
		case GL_TABLE_TOO_LARGE:
			strcpy(s, "GL_TABLE_TOO_LARGE");
			break;
		case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
			strcpy(s, "GL_INVALID_FRAMEBUFFER_OPERATION_EXT");
			break;
		default:
			Com_sprintf(s, sizeof(s), "%i", err);
			break;
	}

	ri.Error(ERR_FATAL, "caught OpenGL error: %s in file %s line %i", s, filename, line);
}


/*
** R_GetModeInfo
*/
typedef struct vidmode_s
{
	const char     *description;
	int             width, height;
	float           pixelAspect;	// pixel width / height
} vidmode_t;

vidmode_t       r_vidModes[] = {
	{"Mode  0: 320x240", 320, 240, 1},
	{"Mode  1: 400x300", 400, 300, 1},
	{"Mode  2: 512x384", 512, 384, 1},
	{"Mode  3: 640x480", 640, 480, 1},
	{"Mode  4: 800x600", 800, 600, 1},
	{"Mode  5: 960x720", 960, 720, 1},
	{"Mode  6: 1024x768", 1024, 768, 1},
	{"Mode  7: 1152x864", 1152, 864, 1},
	{"Mode  8: 1280x1024", 1280, 1024, 1},
	{"Mode  9: 1600x1200", 1600, 1200, 1},
	{"Mode 10: 2048x1536", 2048, 1536, 1},
	{"Mode 11: 856x480 (wide)", 856, 480, 1}
};
static int      s_numVidModes = (sizeof(r_vidModes) / sizeof(r_vidModes[0]));

qboolean R_GetModeInfo(int *width, int *height, float *windowAspect, int mode)
{
	vidmode_t      *vm;

	if(mode < -1)
	{
		return qfalse;
	}
	if(mode >= s_numVidModes)
	{
		return qfalse;
	}

	if(mode == -1)
	{
		*width = r_customwidth->integer;
		*height = r_customheight->integer;
		*windowAspect = r_customaspect->value;
		return qtrue;
	}

	vm = &r_vidModes[mode];

	*width = vm->width;
	*height = vm->height;
	*windowAspect = (float)vm->width / (vm->height * vm->pixelAspect);

	return qtrue;
}

/*
** R_ModeList_f
*/
static void R_ModeList_f(void)
{
	int             i;

	ri.Printf(PRINT_ALL, "\n");
	for(i = 0; i < s_numVidModes; i++)
	{
		ri.Printf(PRINT_ALL, "%s\n", r_vidModes[i].description);
	}
	ri.Printf(PRINT_ALL, "\n");
}


/* 
============================================================================== 
 
						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_TakeScreenshot
================== 
*/
void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName)
{
	byte           *buffer;
	int             i, c, temp;

	buffer = ri.Hunk_AllocateTempMemory(glConfig.vidWidth * glConfig.vidHeight * 3 + 18);

	Com_Memset(buffer, 0, 18);
	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

	qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer + 18);

	// swap rgb to bgr
	c = 18 + width * height * 3;
	for(i = 18; i < c; i += 3)
	{
		temp = buffer[i];
		buffer[i] = buffer[i + 2];
		buffer[i + 2] = temp;
	}

	// gamma correct
	if((tr.overbrightBits > 0) && glConfig.deviceSupportsGamma)
	{
		R_GammaCorrect(buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3);
	}

	ri.FS_WriteFile(fileName, buffer, c);

	ri.Hunk_FreeTempMemory(buffer);
}

/* 
================== 
RB_TakeScreenshotJPEG
================== 
*/
void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName)
{
	byte           *buffer;

	buffer = ri.Hunk_AllocateTempMemory(glConfig.vidWidth * glConfig.vidHeight * 4);

	qglReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

	// gamma correct
	if((tr.overbrightBits > 0) && glConfig.deviceSupportsGamma)
	{
		R_GammaCorrect(buffer, glConfig.vidWidth * glConfig.vidHeight * 4);
	}

	ri.FS_WriteFile(fileName, buffer, 1);	// create path
//	SaveJPG(fileName, 95, glConfig.vidWidth, glConfig.vidHeight, buffer);

	ri.Hunk_FreeTempMemory(buffer);
}

/*
==================
RB_TakeScreenshotCmd
==================
*/
const void     *RB_TakeScreenshotCmd(const void *data)
{
	const screenshotCommand_t *cmd;

	cmd = (const screenshotCommand_t *)data;

	if(cmd->jpeg)
		RB_TakeScreenshotJPEG(cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName);
	else
		RB_TakeScreenshot(cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName);

	return (const void *)(cmd + 1);
}

/*
==================
R_TakeScreenshot
==================
*/
void R_TakeScreenshot(int x, int y, int width, int height, char *name, qboolean jpeg)
{
	static char     fileName[MAX_OSPATH];	// bad things if two screenshots per frame?
	screenshotCommand_t *cmd;

	cmd = R_GetCommandBuffer(sizeof(*cmd));
	if(!cmd)
	{
		return;
	}
	cmd->commandId = RC_SCREENSHOT;

	cmd->x = x;
	cmd->y = y;
	cmd->width = width;
	cmd->height = height;
	Q_strncpyz(fileName, name, sizeof(fileName));
	cmd->fileName = fileName;
	cmd->jpeg = jpeg;
}


/* 
================== 
R_ScreenshotFilename
================== 
*/
void R_ScreenshotFilename(int lastNumber, char *fileName)
{
	int             a, b, c, d;

	if(lastNumber < 0 || lastNumber > 9999)
	{
		Com_sprintf(fileName, MAX_OSPATH, "screenshots/shot9999.tga");
		return;
	}

	a = lastNumber / 1000;
	lastNumber -= a * 1000;
	b = lastNumber / 100;
	lastNumber -= b * 100;
	c = lastNumber / 10;
	lastNumber -= c * 10;
	d = lastNumber;

	Com_sprintf(fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga", a, b, c, d);
}


/* 
================== 
R_ScreenshotFilename
================== 
*/
void R_ScreenshotFilenameJPEG(int lastNumber, char *fileName)
{
	int             a, b, c, d;

	if(lastNumber < 0 || lastNumber > 9999)
	{
		Com_sprintf(fileName, MAX_OSPATH, "screenshots/shot9999.jpg");
		return;
	}

	a = lastNumber / 1000;
	lastNumber -= a * 1000;
	b = lastNumber / 100;
	lastNumber -= b * 100;
	c = lastNumber / 10;
	lastNumber -= c * 10;
	d = lastNumber;

	Com_sprintf(fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg", a, b, c, d);
}


/*
====================
R_LevelShot

levelshots are specialized 128*128 thumbnails for
the menu system, sampled down from full screen distorted images
====================
*/
void R_LevelShot(void)
{
	char            checkname[MAX_OSPATH];
	byte           *buffer;
	byte           *source;
	byte           *src, *dst;
	int             x, y;
	int             r, g, b;
	float           xScale, yScale;
	int             xx, yy;

	sprintf(checkname, "levelshots/%s.tga", tr.world->baseName);

	source = ri.Hunk_AllocateTempMemory(glConfig.vidWidth * glConfig.vidHeight * 3);

	buffer = ri.Hunk_AllocateTempMemory(128 * 128 * 3 + 18);
	Com_Memset(buffer, 0, 18);
	buffer[2] = 2;				// uncompressed type
	buffer[12] = 128;
	buffer[14] = 128;
	buffer[16] = 24;			// pixel size

	qglReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source);

	// resample from source
	xScale = glConfig.vidWidth / 512.0f;
	yScale = glConfig.vidHeight / 384.0f;
	for(y = 0; y < 128; y++)
	{
		for(x = 0; x < 128; x++)
		{
			r = g = b = 0;
			for(yy = 0; yy < 3; yy++)
			{
				for(xx = 0; xx < 4; xx++)
				{
					src = source + 3 * (glConfig.vidWidth * (int)((y * 3 + yy) * yScale) + (int)((x * 4 + xx) * xScale));
					r += src[0];
					g += src[1];
					b += src[2];
				}
			}
			dst = buffer + 18 + 3 * (y * 128 + x);
			dst[0] = b / 12;
			dst[1] = g / 12;
			dst[2] = r / 12;
		}
	}

	// gamma correct
	if((tr.overbrightBits > 0) && glConfig.deviceSupportsGamma)
	{
		R_GammaCorrect(buffer + 18, 128 * 128 * 3);
	}

	ri.FS_WriteFile(checkname, buffer, 128 * 128 * 3 + 18);

	ri.Hunk_FreeTempMemory(buffer);
	ri.Hunk_FreeTempMemory(source);

	ri.Printf(PRINT_ALL, "Wrote %s\n", checkname);
}


/* 
================== 
R_ScreenShot_f

screenshot
screenshot [silent]
screenshot [levelshot]
screenshot [filename]

Doesn't print the pacifier message if there is a second arg
================== 
*/
void R_ScreenShot_f(void)
{
	char            checkname[MAX_OSPATH];
	static int      lastNumber = -1;
	qboolean        silent;

	if(!strcmp(ri.Cmd_Argv(1), "levelshot"))
	{
		R_LevelShot();
		return;
	}

	if(!strcmp(ri.Cmd_Argv(1), "silent"))
	{
		silent = qtrue;
	}
	else
	{
		silent = qfalse;
	}

	if(ri.Cmd_Argc() == 2 && !silent)
	{
		// explicit filename
		Com_sprintf(checkname, MAX_OSPATH, "screenshots/%s.tga", ri.Cmd_Argv(1));
	}
	else
	{
		// scan for a free filename

		// if we have saved a previous screenshot, don't scan
		// again, because recording demo avis can involve
		// thousands of shots
		if(lastNumber == -1)
		{
			lastNumber = 0;
		}
		// scan for a free number
		for(; lastNumber <= 9999; lastNumber++)
		{
			R_ScreenshotFilename(lastNumber, checkname);

			if(!ri.FS_FileExists(checkname))
			{
				break;			// file doesn't exist
			}
		}

		if(lastNumber >= 9999)
		{
			ri.Printf(PRINT_ALL, "ScreenShot: Couldn't create a file\n");
			return;
		}

		lastNumber++;
	}

	R_TakeScreenshot(0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse);

	if(!silent)
	{
		ri.Printf(PRINT_ALL, "Wrote %s\n", checkname);
	}
}


void R_ScreenShotJPEG_f(void)
{
	char            checkname[MAX_OSPATH];
	static int      lastNumber = -1;
	qboolean        silent;

	if(!strcmp(ri.Cmd_Argv(1), "levelshot"))
	{
		R_LevelShot();
		return;
	}

	if(!strcmp(ri.Cmd_Argv(1), "silent"))
	{
		silent = qtrue;
	}
	else
	{
		silent = qfalse;
	}

	if(ri.Cmd_Argc() == 2 && !silent)
	{
		// explicit filename
		Com_sprintf(checkname, MAX_OSPATH, "screenshots/%s.jpg", ri.Cmd_Argv(1));
	}
	else
	{
		// scan for a free filename

		// if we have saved a previous screenshot, don't scan
		// again, because recording demo avis can involve
		// thousands of shots
		if(lastNumber == -1)
		{
			lastNumber = 0;
		}
		// scan for a free number
		for(; lastNumber <= 9999; lastNumber++)
		{
			R_ScreenshotFilenameJPEG(lastNumber, checkname);

			if(!ri.FS_FileExists(checkname))
			{
				break;			// file doesn't exist
			}
		}

		if(lastNumber == 10000)
		{
			ri.Printf(PRINT_ALL, "ScreenShot: Couldn't create a file\n");
			return;
		}

		lastNumber++;
	}

	R_TakeScreenshot(0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue);

	if(!silent)
	{
		ri.Printf(PRINT_ALL, "Wrote %s\n", checkname);
	}
}

//============================================================================

/*
** GL_SetDefaultState
*/
void GL_SetDefaultState(void)
{
	int             i;

	GLimp_LogComment("--- GL_SetDefaultState ---\n");

	qglClearDepth(1.0f);

	if(glConfig.stencilBits >= 4)
	{
		qglClearStencil(128);
	}

	qglFrontFace(GL_CCW);
	qglCullFace(GL_FRONT);

	qglColor4f(1, 1, 1, 1);

	// initialize downstream texture units if we're running
	// in a multitexture environment
	if(qglActiveTextureARB)
	{
		for(i = glConfig.maxTextureUnits - 1; i >= 0; i--)
		{
			GL_SelectTexture(i);
			GL_TextureMode(r_textureMode->string);
			GL_TexEnv(GL_MODULATE);

			if(i != 0)
				qglDisable(GL_TEXTURE_2D);
			else
				qglEnable(GL_TEXTURE_2D);
		}
	}

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

	// make sure our GL state vector is set correctly
	glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE;
	glState.glClientStateBits = GLCS_DEFAULT;
	glState.currentProgram = 0;

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



/*
================
GfxInfo_f
================
*/
void GfxInfo_f(void)
{
	const char     *enablestrings[] = {
		"disabled",
		"enabled"
	};
	const char     *fsstrings[] = {
		"windowed",
		"fullscreen"
	};

	ri.Printf(PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string);
	ri.Printf(PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string);
	ri.Printf(PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string);
	ri.Printf(PRINT_ALL, "GL_EXTENSIONS: %s\n", glConfig.extensions_string);
	ri.Printf(PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize);
	ri.Printf(PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.maxTextureUnits);

	if(glConfig2.textureAnisotropyAvailable)
	{
		ri.Printf(PRINT_ALL, "GL_TEXTURE_MAX_ANISOTROPY_EXT: %f\n", glConfig2.maxTextureAnisotropy);
	}
	
	if(glConfig2.occlusionQueryAvailable)
	{
		ri.Printf(PRINT_ALL, "%d occlusion query bits\n", glConfig2.occlusionQueryBits);
	}
	
	if(glConfig2.framebufferObjectAvailable)
	{
		ri.Printf(PRINT_ALL, "GL_MAX_RENDERBUFFER_SIZE_EXT: %d\n", glConfig2.maxRenderbufferSize);
		ri.Printf(PRINT_ALL, "GL_MAX_COLOR_ATTACHMENTS_EXT: %d\n", glConfig2.maxColorAttachments);
	}

	ri.Printf(PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits,
			  glConfig.depthBits, glConfig.stencilBits);
	ri.Printf(PRINT_ALL, "MODE: %d, %d x %d %s hz:", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight,
			  fsstrings[r_fullscreen->integer == 1]);

	if(glConfig.displayFrequency)
	{
		ri.Printf(PRINT_ALL, "%d\n", glConfig.displayFrequency);
	}
	else
	{
		ri.Printf(PRINT_ALL, "N/A\n");
	}

	if(glConfig.deviceSupportsGamma)
	{
		ri.Printf(PRINT_ALL, "GAMMA: hardware w/ %d overbright bits\n", tr.overbrightBits);
	}
	else
	{
		ri.Printf(PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits);
	}

	ri.Printf(PRINT_ALL, "texturemode: %s\n", r_textureMode->string);
	ri.Printf(PRINT_ALL, "picmip: %d\n", r_picmip->integer);
	ri.Printf(PRINT_ALL, "texture bits: %d\n", r_texturebits->integer);
	ri.Printf(PRINT_ALL, "multitexture: %s\n", enablestrings[qglActiveTextureARB != 0]);
	ri.Printf(PRINT_ALL, "compiled vertex arrays: %s\n", enablestrings[qglLockArraysEXT != 0]);
	ri.Printf(PRINT_ALL, "texenv add: %s\n", enablestrings[glConfig.textureEnvAddAvailable != 0]);
	ri.Printf(PRINT_ALL, "compressed textures: %s\n", enablestrings[glConfig.textureCompression != TC_NONE]);

	if(glConfig.hardwareType == GLHW_RAGEPRO)
	{
		ri.Printf(PRINT_ALL, "HACK: ragePro approximations\n");
	}

	if(glConfig.hardwareType == GLHW_RIVA128)
	{
		ri.Printf(PRINT_ALL, "HACK: riva128 approximations\n");
	}

	if(glConfig.hardwareType == GLHW_ATI)
	{
		ri.Printf(PRINT_ALL, "HACK: ATI approximations\n");
	}

	if(glConfig.smpActive)
	{
		ri.Printf(PRINT_ALL, "Using dual processor acceleration\n");
	}

	if(r_finish->integer)
	{
		ri.Printf(PRINT_ALL, "Forcing glFinish\n");
	}
}

/*
===============
R_Register
===============
*/
void R_Register(void)
{
	//
	// latched and archived variables
	//
	r_allowExtensions = ri.Cvar_Get("r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_compressed_textures = ri.Cvar_Get("r_ext_compressed_textures", "0", 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_multisample = ri.Cvar_Get("r_ext_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_texture_non_power_of_two = ri.Cvar_Get("r_ext_texture_non_power_of_two", "1", CVAR_CHEAT | CVAR_LATCH);
	r_ext_texture_cube_map = ri.Cvar_Get("r_ext_texture_cube_map", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_depth_texture = ri.Cvar_Get("r_ext_depth_texture", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_vertex_program = ri.Cvar_Get("r_ext_vertex_program", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_vertex_buffer_object = ri.Cvar_Get("r_ext_vertex_buffer_object", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_occlusion_query = ri.Cvar_Get("r_ext_occlusion_query", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_stencil_wrap = ri.Cvar_Get("r_ext_stencil_wrap", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_shading_language_100 = ri.Cvar_Get("r_ext_shading_language_100", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_texture_filter_anisotropic = ri.Cvar_Get("r_ext_texture_filter_anisotropic", "16", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_stencil_two_side = ri.Cvar_Get("r_ext_stencil_two_side", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_depth_bounds_test = ri.Cvar_Get("r_ext_depth_bounds_test", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_framebuffer_object = ri.Cvar_Get("r_ext_framebuffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ext_generate_mipmap = ri.Cvar_Get("r_ext_generate_mipmap", "1", CVAR_CHEAT | CVAR_LATCH);

	r_collapseStages = ri.Cvar_Get("r_collapseStages", "1", CVAR_LATCH | CVAR_CHEAT);
	r_picmip = ri.Cvar_Get("r_picmip", "0", CVAR_ARCHIVE | CVAR_LATCH);
	AssertCvarRange(r_picmip, 0, 16, qtrue);
	r_roundImagesDown = ri.Cvar_Get("r_roundImagesDown", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_colorMipLevels = ri.Cvar_Get("r_colorMipLevels", "0", CVAR_LATCH);
	r_detailTextures = ri.Cvar_Get("r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_texturebits = ri.Cvar_Get("r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_colorbits = ri.Cvar_Get("r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_stereo = ri.Cvar_Get("r_stereo", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_stencilbits = ri.Cvar_Get("r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH);
	r_depthbits = ri.Cvar_Get("r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_overBrightBits = ri.Cvar_Get("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_ignorehwgamma = ri.Cvar_Get("r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_mode = ri.Cvar_Get("r_mode", "3", CVAR_ARCHIVE | CVAR_LATCH);
	r_fullscreen = ri.Cvar_Get("r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_customwidth = ri.Cvar_Get("r_customwidth", "1600", CVAR_ARCHIVE | CVAR_LATCH);
	r_customheight = ri.Cvar_Get("r_customheight", "1024", CVAR_ARCHIVE | CVAR_LATCH);
	r_customaspect = ri.Cvar_Get("r_customaspect", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_simpleMipMaps = ri.Cvar_Get("r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH);
	r_uiFullScreen = ri.Cvar_Get("r_uifullscreen", "0", 0);
	r_subdivisions = ri.Cvar_Get("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH);
	r_smp = ri.Cvar_Get("r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH);

	//
	// temporary latched variables that can only change over a restart
	//
	r_displayRefresh = ri.Cvar_Get("r_displayRefresh", "0", CVAR_LATCH);
	AssertCvarRange(r_displayRefresh, 0, 200, qtrue);
	r_fullbright = ri.Cvar_Get("r_fullbright", "0", CVAR_LATCH | CVAR_CHEAT);
	r_mapOverBrightBits = ri.Cvar_Get("r_mapOverBrightBits", "2", CVAR_LATCH);
	r_intensity = ri.Cvar_Get("r_intensity", "1", CVAR_LATCH);
	r_singleShader = ri.Cvar_Get("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH);

	//
	// archived variables that can change at any time
	//
	r_lodbias = ri.Cvar_Get("r_lodbias", "0", CVAR_ARCHIVE);
	r_drawFlares = ri.Cvar_Get("r_drawFlares", "1", CVAR_ARCHIVE);
	r_znear = ri.Cvar_Get("r_znear", "4", CVAR_CHEAT);
	r_zfar = ri.Cvar_Get("r_zfar", "0", CVAR_CHEAT);
	AssertCvarRange(r_znear, 0.001f, 200, qtrue);
	r_ignoreGLErrors = ri.Cvar_Get("r_ignoreGLErrors", "1", CVAR_ARCHIVE);
	r_fastsky = ri.Cvar_Get("r_fastsky", "0", CVAR_ARCHIVE);
	r_inGameVideo = ri.Cvar_Get("r_inGameVideo", "1", CVAR_ARCHIVE);
	r_drawSun = ri.Cvar_Get("r_drawSun", "0", CVAR_ARCHIVE);
	r_drawBloom = ri.Cvar_Get("r_drawBloom", "0", CVAR_ARCHIVE);
	r_bloomBlur = ri.Cvar_Get("r_bloomBlur", "1.2", CVAR_ARCHIVE);
	r_lighting = ri.Cvar_Get("r_lighting", "2", CVAR_ARCHIVE);
	AssertCvarRange(r_lighting, 0, 3, qtrue);
	r_dynamicLighting = ri.Cvar_Get("r_dynamicLighting", "1", CVAR_CHEAT);
	r_finish = ri.Cvar_Get("r_finish", "0", CVAR_ARCHIVE);
	r_textureMode = ri.Cvar_Get("r_textureMode", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE);
	r_swapInterval = ri.Cvar_Get("r_swapInterval", "0", CVAR_ARCHIVE);
	r_gamma = ri.Cvar_Get("r_gamma", "1", CVAR_ARCHIVE);

	r_railWidth = ri.Cvar_Get("r_railWidth", "128", CVAR_ARCHIVE);
	r_railCoreWidth = ri.Cvar_Get("r_railCoreWidth", "16", CVAR_ARCHIVE);
	r_railSegmentLength = ri.Cvar_Get("r_railSegmentLength", "64", CVAR_ARCHIVE);

	r_ambientScale = ri.Cvar_Get("r_ambientScale", "0.6", CVAR_CHEAT);
	r_directedScale = ri.Cvar_Get("r_directedScale", "1", CVAR_CHEAT);
	r_lightScale = ri.Cvar_Get("r_lightScale", "2", CVAR_CHEAT);

	r_vboFaces = ri.Cvar_Get("r_vboFaces", "0", CVAR_ARCHIVE);
	r_vboCurves = ri.Cvar_Get("r_vboCurves", "0", CVAR_ARCHIVE);
	r_vboTriangles = ri.Cvar_Get("r_vboTriangles", "1", CVAR_ARCHIVE);

	//
	// temporary variables that can change at any time
	//
	r_showImages = ri.Cvar_Get("r_showImages", "0", CVAR_TEMP);

	r_debugLight = ri.Cvar_Get("r_debuglight", "0", CVAR_TEMP);
	r_debugSort = ri.Cvar_Get("r_debugSort", "0", CVAR_CHEAT);

	r_nocurves = ri.Cvar_Get("r_nocurves", "0", CVAR_CHEAT);
	r_nobatching = ri.Cvar_Get("r_nobatching", "0", CVAR_CHEAT);
	r_noLightScissors = ri.Cvar_Get("r_noLightScissors", "0", CVAR_CHEAT);
	r_noLightVisCull = ri.Cvar_Get("r_noLightVisCull", "0", CVAR_CHEAT);
	r_noInteractionSort = ri.Cvar_Get("r_noInteractionSort", "0", CVAR_CHEAT);
	r_drawworld = ri.Cvar_Get("r_drawworld", "1", CVAR_CHEAT);
	r_portalOnly = ri.Cvar_Get("r_portalOnly", "0", CVAR_CHEAT);

	r_flareSize = ri.Cvar_Get("r_flareSize", "40", CVAR_CHEAT);
	r_flareFade = ri.Cvar_Get("r_flareFade", "7", CVAR_CHEAT);
	r_flareCoeff = ri.Cvar_Get ("r_flareCoeff", "150", CVAR_CHEAT);

	r_showSmp = ri.Cvar_Get("r_showSmp", "0", CVAR_CHEAT);
	r_skipBackEnd = ri.Cvar_Get("r_skipBackEnd", "0", CVAR_CHEAT);

	r_measureOverdraw = ri.Cvar_Get("r_measureOverdraw", "0", CVAR_CHEAT);
	r_lodscale = ri.Cvar_Get("r_lodscale", "5", CVAR_CHEAT);
	r_norefresh = ri.Cvar_Get("r_norefresh", "0", CVAR_CHEAT);
	r_drawentities = ri.Cvar_Get("r_drawentities", "1", CVAR_CHEAT);
	r_ignore = ri.Cvar_Get("r_ignore", "1", CVAR_CHEAT);
	r_nocull = ri.Cvar_Get("r_nocull", "0", CVAR_CHEAT);
	r_novis = ri.Cvar_Get("r_novis", "0", CVAR_CHEAT);
	r_showcluster = ri.Cvar_Get("r_showcluster", "0", CVAR_CHEAT);
	r_speeds = ri.Cvar_Get("r_speeds", "0", CVAR_CHEAT);
	r_verbose = ri.Cvar_Get("r_verbose", "0", CVAR_CHEAT);
	r_logFile = ri.Cvar_Get("r_logFile", "0", CVAR_CHEAT);
	r_nobind = ri.Cvar_Get("r_nobind", "0", CVAR_CHEAT);
	r_showtris = ri.Cvar_Get("r_showtris", "0", CVAR_CHEAT);
	r_showsky = ri.Cvar_Get("r_showsky", "0", CVAR_CHEAT);
	r_shownormals = ri.Cvar_Get("r_shownormals", "0", CVAR_CHEAT);
	r_showTangentSpaces = ri.Cvar_Get("r_showTangentSpaces", "0", CVAR_CHEAT);
	r_clear = ri.Cvar_Get("r_clear", "1", CVAR_CHEAT);
	r_offsetFactor = ri.Cvar_Get("r_offsetFactor", "-1", CVAR_CHEAT);
	r_offsetUnits = ri.Cvar_Get("r_offsetUnits", "-2", CVAR_CHEAT);
	r_specularExponent = ri.Cvar_Get("r_specularExponent", "16", CVAR_CHEAT);
	r_drawBuffer = ri.Cvar_Get("r_drawBuffer", "GL_BACK", CVAR_CHEAT);
	r_lockpvs = ri.Cvar_Get("r_lockpvs", "0", CVAR_CHEAT);
	r_noportals = ri.Cvar_Get("r_noportals", "0", CVAR_CHEAT);
	r_shadows = ri.Cvar_Get("cg_shadows", "1", 0);
	AssertCvarRange(r_shadows, 0, 3, qtrue);
	r_shadowOffsetFactor = ri.Cvar_Get("r_shadowOffsetFactor", "0", CVAR_CHEAT);
	r_shadowOffsetUnits = ri.Cvar_Get("r_shadowOffsetUnits", "0", CVAR_CHEAT);
	r_noLightFrustums = ri.Cvar_Get("r_noLightFrustums", "0", CVAR_CHEAT);

	r_maxpolys = ri.Cvar_Get("r_maxpolys", va("%d", MAX_POLYS), 0);
	r_maxpolyverts = ri.Cvar_Get("r_maxpolyverts", va("%d", MAX_POLYVERTS), 0);

	r_showLightMaps = ri.Cvar_Get("r_showLightMaps", "0", CVAR_CHEAT);
	r_showDeluxeMaps = ri.Cvar_Get("r_showDeluxeMaps", "0", CVAR_CHEAT);
	r_showNormalMaps = ri.Cvar_Get("r_showNormalMaps", "0", CVAR_CHEAT);
	r_showShadowVolumes = ri.Cvar_Get("r_showShadowVolumes", "0", CVAR_CHEAT);
	r_showSkeleton = ri.Cvar_Get("r_showSkeleton", "0", CVAR_CHEAT);
	r_showEntityTransforms = ri.Cvar_Get("r_showEntityTransforms", "0", CVAR_CHEAT);
	r_showLightTransforms = ri.Cvar_Get("r_showLightTransforms", "0", CVAR_CHEAT);
	r_showLightInteractions = ri.Cvar_Get("r_showLightInteractions", "0", CVAR_CHEAT);
	r_showLightScissors = ri.Cvar_Get("r_showLightScissors", "0", CVAR_CHEAT);
	r_showOcclusionQueries = ri.Cvar_Get("r_showOcclusionQueries", "0", CVAR_CHEAT);

	// make sure all the commands added here are also removed in R_Shutdown
	ri.Cmd_AddCommand("imagelist", R_ImageList_f);
	ri.Cmd_AddCommand("shaderlist", R_ShaderList_f);
	ri.Cmd_AddCommand("shaderexp", R_ShaderExp_f);
	ri.Cmd_AddCommand("skinlist", R_SkinList_f);
	ri.Cmd_AddCommand("modellist", R_Modellist_f);
	ri.Cmd_AddCommand("modelist", R_ModeList_f);
	ri.Cmd_AddCommand("animationlist", R_AnimationList_f);
	ri.Cmd_AddCommand("fbolist", R_FBOList_f);
	ri.Cmd_AddCommand("screenshot", R_ScreenShot_f);
	ri.Cmd_AddCommand("screenshotJPEG", R_ScreenShotJPEG_f);
	ri.Cmd_AddCommand("gfxinfo", GfxInfo_f);
}

/*
===============
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));

	if(sizeof(glConfig_t) != 11332)
		ri.Error(ERR_FATAL, "Mod ABI incompatible: sizeof(glconfig_t) == %zd != 11332", sizeof(glConfig_t));

//  Swap_Init();

	if ( (intptr_t)tess.xyz & 15 )
		ri.Printf(PRINT_ALL, "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++)
	{
		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 / 2)
		{
			if(i < FUNCTABLE_SIZE / 4)
			{
				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 = r_maxpolys->integer;
	if(max_polys < MAX_POLYS)
		max_polys = MAX_POLYS;

	max_polyverts = r_maxpolyverts->integer;
	if(max_polyverts < MAX_POLYVERTS)
		max_polyverts = MAX_POLYVERTS;

	ptr = ri.Hunk_Alloc(sizeof(*backEndData[0]) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low);
	backEndData[0] = (backEndData_t *) ptr;
	backEndData[0]->polys = (srfPoly_t *) ((char *)ptr + sizeof(*backEndData[0]));
	backEndData[0]->polyVerts = (polyVert_t *) ((char *)ptr + sizeof(*backEndData[0]) + sizeof(srfPoly_t) * max_polys);
	if(r_smp->integer)
	{
		ptr = ri.Hunk_Alloc(sizeof(*backEndData[1]) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low);
		backEndData[1] = (backEndData_t *) ptr;
		backEndData[1]->polys = (srfPoly_t *) ((char *)ptr + sizeof(*backEndData[1]));
		backEndData[1]->polyVerts = (polyVert_t *) ((char *)ptr + sizeof(*backEndData[1]) + sizeof(srfPoly_t) * max_polys);
	}
	else
	{
		backEndData[1] = NULL;
	}
	R_ToggleSmpFrame();

	InitOpenGL();

	RB_InitGPUShaders();

	R_InitImages();
	
	R_InitFBOs();

	R_InitShaders();

	R_InitSkins();

	R_ModelInit();

	R_InitAnimations();

	R_InitFreeType();
	
	if(glConfig2.textureAnisotropyAvailable)
	{
		AssertCvarRange(r_ext_texture_filter_anisotropic, 0, glConfig2.maxTextureAnisotropy, qfalse);
	}
	
	if(glConfig2.occlusionQueryBits)
	{
		qglGenQueriesARB(MAX_OCCLUSION_QUERIES, tr.occlusionQueryObjects);
	}

	err = qglGetError();
	if(err != GL_NO_ERROR)
		ri.Printf(PRINT_ALL, "glGetError() = 0x%x\n", err);

	ri.Printf(PRINT_ALL, "----- finished R_Init -----\n");
}

/*
===============
RE_Shutdown
===============
*/
void RE_Shutdown(qboolean destroyWindow)
{
	ri.Printf(PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow);

	ri.Cmd_RemoveCommand("modellist");
	ri.Cmd_RemoveCommand("screenshotJPEG");
	ri.Cmd_RemoveCommand("screenshot");
	ri.Cmd_RemoveCommand("imagelist");
	ri.Cmd_RemoveCommand("shaderlist");
	ri.Cmd_RemoveCommand("shaderexp");
	ri.Cmd_RemoveCommand("skinlist");
	ri.Cmd_RemoveCommand("gfxinfo");
	ri.Cmd_RemoveCommand("modelist");
	ri.Cmd_RemoveCommand("shaderstate");
	ri.Cmd_RemoveCommand("animationlist");
	ri.Cmd_RemoveCommand("fbolist");

	if(tr.registered)
	{
		R_SyncRenderThread();
		R_ShutdownCommandBuffers();
		R_ShutdownImages();
		R_ShutdownVBOs();
		R_ShutdownFBOs();
		
		if(glConfig2.occlusionQueryBits)
		{
			qglDeleteQueriesARB(MAX_OCCLUSION_QUERIES, tr.occlusionQueryObjects);
		}
		
		RB_ShutdownGPUShaders();
	}

	R_DoneFreeType();

	// shut down platform specific OpenGL stuff
	if ( destroyWindow )
		GLimp_Shutdown();

	tr.registered = qfalse;
}


/*
=============
RE_EndRegistration

Touch all images to make sure they are resident
=============
*/
void RE_EndRegistration(void)
{
	R_SyncRenderThread();
	if(!Sys_LowPhysicalMemory())
	{
		RB_ShowImages();
	}
}


/*
@@@@@@@@@@@@@@@@@@@@@
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.RegisterAnimation = RE_RegisterAnimation;
	re.RegisterSkin = RE_RegisterSkin;
	re.RegisterShader = RE_RegisterShader;
	re.RegisterShaderNoMip = RE_RegisterShaderNoMip;
	re.RegisterShaderLightAttenuation = RE_RegisterShaderLightAttenuation;
	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 = RE_LerpTag;
	re.BuildSkeleton = RE_BuildSkeleton;
	re.BlendSkeleton = RE_BlendSkeleton;
	re.BoneIndex = RE_BoneIndex;
	re.AnimNumFrames = RE_AnimNumFrames;
	re.AnimFrameRate = RE_AnimFrameRate;

	re.ModelBounds = R_ModelBounds;

	re.ClearScene = RE_ClearScene;
	re.AddRefEntityToScene = RE_AddRefEntityToScene;
	re.AddRefLightToScene = RE_AddRefDlightToScene;
	re.AddPolyToScene = RE_AddPolyToScene;
	re.LightForPoint = R_LightForPoint;
	re.AddLightToScene = RE_AddLightToScene;
	re.AddAdditiveLightToScene = RE_AddAdditiveLightToScene;
	re.RenderScene = RE_RenderScene;

	re.SetColor = RE_SetColor;
	re.DrawStretchPic = RE_StretchPic;
	re.DrawStretchRaw = RE_StretchRaw;
	re.UploadCinematic = RE_UploadCinematic;

	re.RegisterFont = RE_RegisterFont;
	re.RemapShader = R_RemapShader;
	re.GetEntityToken = R_GetEntityToken;
	re.inPVS = R_inPVS;

	return &re;
}
