//**************************************************************
//*            OpenGLide - Glide to OpenGL Wrapper
//*             http://openglide.sourceforge.net
//*
//*                    OpenGL Extensions
//*
//*         OpenGLide is OpenSource under LGPL license
//*              Originaly made by Fabio Barros
//*      Modified by Paul for Glidos (http://www.glidos.net)
//*  Mac version and additional features by Jens-Olaf Hemprich
//**************************************************************

#include "Glide.h"
#include "GlideSettings.h"
#include "GLColorAlphaCombineEnvTables.h"
#include "GLextensions.h"
#include "GLRender.h"
#include "GLutil.h"

#include <agl.h>


enum enExtensionType
{
    OGL_EXT_UNUSED = 0,
    OGL_EXT_REQUIRED,
    OGL_EXT_DESIRED
};

struct stExtensionSupport
{
    char *          name;
    enExtensionType type;
    bool *          userVar;
    bool *          internalVar;
};

// Running in Classic ?
#ifdef OPENGLIDE_HOST_MAC
bool RunningInClassic = false;
#endif

// It is important that dummyExtVariable retains the value true, so
// we pass dummyExtVariable2 in places where the value may be altered.
bool dummyExtVariable = true;
bool dummyExtVariable2 = true;

// Env Combine fog data
static GLuint fogtexturename = 0;
static FxU8* fogalpharamp = NULL;
static GLint dummytexture = 0x00000000;

stExtensionSupport glNecessaryExt[] =
{
		// new
    { "GL_EXT_abgr",                      OGL_EXT_REQUIRED,   &dummyExtVariable,                    &dummyExtVariable2 },
    { "GL_EXT_bgra",                      OGL_EXT_REQUIRED,   &dummyExtVariable,                    &dummyExtVariable2 },
    { "GL_EXT_secondary_color",           OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_secondary_color },
    { "GL_ARB_multitexture",              OGL_EXT_DESIRED,    &UserConfig.ARB_multitexture,         &InternalConfig.ARB_multitexture },
    { "GL_ARB_texture_env_add",           OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_texture_env_add },
    { "GL_ARB_texture_env_combine",       OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_texture_env_combine },
    { "GL_ATI_texture_env_combine3",      OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.ATI_texture_env_combine3 },
    { "GL_EXT_texture_lod_bias",          OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_texture_lod_bias },
    { "GL_SGIS_generate_mipmap",          OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_SGIS_generate_mipmap },
    { "GL_SGIS_texture_edge_clamp",       OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_SGIS_texture_edge_clamp },
    { "GL_EXT_paletted_texture",          OGL_EXT_DESIRED,    &UserConfig.EXT_paletted_texture,     &InternalConfig.EXT_paletted_texture },
    { "GL_APPLE_packed_pixels",           OGL_EXT_REQUIRED,   &dummyExtVariable,                    &dummyExtVariable2 },
    { "GL_APPLE_client_storage",          OGL_EXT_DESIRED,    &UserConfig.APPLE_client_storage,     &InternalConfig.APPLE_client_storage },
    { "GL_EXT_compiled_vertex_array",     OGL_EXT_DESIRED,    &UserConfig.EXT_compiled_vertex_array,&InternalConfig.EXT_compiled_vertex_array },
    { "GL_ARB_texture_rectangle",         OGL_EXT_DESIRED,    &UserConfig.ARB_texture_rectangle,    &InternalConfig.ARB_texture_rectangle },
#ifdef OPENGLIDE_SYSTEM_HAS_FOGCOORD
    { "GL_EXT_fog_coord",                 OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_fog_coord },
#endif
#ifdef OPENGLIDE_SYSTEM_HAS_BLENDFUNC_SEPERATE
    { "GL_EXT_blend_func_separate",       OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_blend_func_separate },
#endif
    { "GL_EXT_texture_filter_anisotropic",OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.EXT_texture_filter_anisotropic },
    { "GL_ARB_multisample",               OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.ARB_multisample },
    { "GL_NV_multisample_filter_hint",    OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.NV_multisample_filter_hint },
    { "GL_EXT_clip_volume_hint",          OGL_EXT_DESIRED,    &UserConfig.EXT_clip_volume_hint,     &InternalConfig.EXT_clip_volume_hint },
    { "GL_APPLE_transform_hint",          OGL_EXT_DESIRED,    &dummyExtVariable,                    &InternalConfig.APPLE_transform_hint },
    { "",                                 OGL_EXT_UNUSED,     &dummyExtVariable,                    &dummyExtVariable2 }
}; 

// check to see if Extension is Supported
// code by Mark J. Kilgard of NVidia modified by Fabio Barros
bool OGLIsExtensionSupported(const char* extensions, const char* extension)
{
	char* where = (char *) strchr( extension, ' ' );
	if ( where || ( *extension == '\0' ) )
	{
		return false;
	}
	const char* start = extensions;
	if ( *start == '\0' )
	{
		GlideError( "No OpenGL extension supported, using all emulated.\n" );
		return false;
	}
	
	while ( true )
	{
		where = (char *)strstr( start, extension );
		if ( !where )
		{
			break;
		}
		const char* terminator = where + strlen( extension );
		if ( ( where == start ) || ( *( where - 1 ) == ' ' ) )
		{
		    if ( ( *terminator == ' ' ) || ( *terminator == '\0' ) )
			{
				return true;
			}
		}
		start = terminator;
	}
	
	// fix for Rage 128 OpenGL Engine 1.1.ATI-5.99
	// (See apple techote TN2014)
	if (strcmp(extension, "GL_APPLE_packed_pixels") == 0)
	{
		return OGLIsExtensionSupported(extensions, "GL_APPLE_packed_pixel");
	}
	// fix for Rage 128 OpenGL Engine 1.1.ATI-5.99
	// support for GL_EXT_texture_env_combine
	else if (strcmp(extension, "GL_ARB_texture_env_combine") == 0)
	{
		return OGLIsExtensionSupported(extensions, "GL_EXT_texture_env_combine");	
	}
	// also support the older GL_EXT_texture_env_add
	else if (strcmp(extension, "GL_ARB_texture_env_add") == 0)
	{
		return OGLIsExtensionSupported(extensions, "GL_EXT_texture_env_add");	
	}
	else
	{
		return false;
	}
}

void ValidateUserConfig( void )
{
	glReportErrors("ValidateUserConfig");
	
	// Copy config
	InternalConfig = UserConfig;

	GlideMsg( OGL_LOG_SEPARATE );
	GlideMsg( "** OpenGL Information **\n" );
	const unsigned char* renderer = glGetString( GL_RENDERER );
	if (renderer == NULL || strlen(reinterpret_cast<const char*>(renderer)) == 0)
	{
		GlideError("The OpenGL driver failed to report its version/vendor/renderer.\nThis may be caused by beta drivers located in thge game directory.\nThese drivers should be deleted.\n");
	}
	GlideMsg( OGL_LOG_SEPARATE );
	GlideMsg( "Vendor:      %s\n", glGetString( GL_VENDOR ) );
	GlideMsg( "Renderer:    %s\n", glGetString( GL_RENDERER ) );
	GlideMsg( "Version:     %s\n", glGetString( GL_VERSION ) );
	// Extension string is too long for the temp buffer, so we don't use GlideMsg()
	UserConfig.write_log("Available Extensions: ");
	const char* extensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
	UserConfig.write_log(extensions);
	UserConfig.write_log("\n");

	GlideMsg( OGL_LOG_SEPARATE );
	GlideMsg( "OpenGL Extensions:\n" );
	GlideMsg( OGL_LOG_SEPARATE );
	int index = 0;
	while ( strlen( glNecessaryExt[ index ].name ) > 0 )
	{
		*glNecessaryExt[ index ].internalVar = false;
		switch ( glNecessaryExt[ index ].type )
		{
		case OGL_EXT_REQUIRED:
			if (!OGLIsExtensionSupported(extensions, glNecessaryExt[index].name ))
			{
				char buffer[StringBufferSize];
				sprintf(buffer, "Severe Problem: OpenGL %s extension is required for %s!", 
				        glNecessaryExt[ index ].name, OpenGLideProductName);
				GlideError(buffer);
			}
			break;
		case OGL_EXT_DESIRED:
			if (!OGLIsExtensionSupported(extensions, glNecessaryExt[index].name))
			{
				char buffer[StringBufferSize];
				sprintf(buffer, "Note: OpenGL %s extension is not supported, emulating behavior.\n", 
				        glNecessaryExt[ index ].name );
				GlideMsg(buffer);
			}
			else
			{
				if ( *glNecessaryExt[ index ].userVar )
				{
					*glNecessaryExt[ index ].internalVar = true;
					GlideMsg( "Extension %s is present and ENABLED\n", glNecessaryExt[ index ].name );
				}
				else
				{
					char buffer[StringBufferSize];
					sprintf(buffer, "Note: OpenGL %s extension is supported but disabled by user\n", 
					        glNecessaryExt[ index ].name );
					GlideMsg(buffer);
				}
			}
			break;
		case OGL_EXT_UNUSED:
			break;
		}
		++index;
	}
	GlideMsg( OGL_LOG_SEPARATE );
	GLExtensions( );
}

void GLExtensions(void)
{
	glReportErrors("GLExtensions");
	const float one = 1.0f;
	
 	#ifdef OPENGLIDE_HOST_MAC
		RunningInClassic = strstr(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)), "GL_EXT_fog_coord") != NULL;
	#endif

	GlideMsg("OpenGL configuration:\n");
	GlideMsg(OGL_LOG_SEPARATE);

 	// query the actual buffer size
 	GLint size;
	glGetIntegerv(GL_DEPTH_BITS, &size);
	glReportError();
	GlideMsg("Depthbuffer size = %d\n", size);
 	// Rendering quality seems to be better when this is left enabled for depth buffer size < 24 bits only
	// (Try spinning around at the portal in the first level of Tomb Raider Gold)
	const int disablePrecisionFixAtDepthSize = 24;
	if (size >= disablePrecisionFixAtDepthSize && InternalConfig.PrecisionFix == 1)
	{
		GlideMsg("Actual number of depthbuffer bits is >= %d - precision fix can safely be disabled\n", disablePrecisionFixAtDepthSize, size);
		InternalConfig.PrecisionFix = 0;
	}
	if (UserConfig.GapFix & OpenGLideGapFixFlag_Enabled)
	{
		glGetIntegerv(GL_STENCIL_BITS, &size);
		GlideMsg("Stencilbuffer size = %d\n", size);
	}
 	GLint MaxAnisotropyLevel;
	glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropyLevel);
	GlideMsg("Maximum level of anisotropy = %d\n", MaxAnisotropyLevel);

	// Since this a global setting, texture data must not be uploaded from temp buffers
	if (InternalConfig.APPLE_client_storage)
	{
		glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, true);
		glReportError();
	}
	// Anisotropic filtering
	if (InternalConfig.EXT_texture_filter_anisotropic)
	{
 		GLint MaxAnisotropyLevel;
		glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropyLevel);
		if (UserConfig.AnisotropylLevel > MaxAnisotropyLevel)
		{
			InternalConfig.AnisotropylLevel = MaxAnisotropyLevel;
		}
		else
		{
			InternalConfig.AnisotropylLevel = UserConfig.AnisotropylLevel;
		}
	}
	else
	{
		InternalConfig.AnisotropylLevel = 1;
	}
	
	if (InternalConfig.ARB_multisample == false)
	{
		InternalConfig.FullSceneAntiAliasing = 0;
	}
	else if (InternalConfig.FullSceneAntiAliasing > 0)
	{
		// Thanks to Frank Condello <developerNOSPAM@NOSPAMchaoticbox.com> for posting this trick at
		// http://support.realsoftware.com/listarchives/realbasic-games/2004-09/msg00273.html
		aglSetInteger(pWindowInfo->aglContext, AGL_ATI_FSAA_LEVEL, reinterpret_cast<const long*>(&InternalConfig.FullSceneAntiAliasing));
		GLenum err = aglGetError();
		if (AGL_NO_ERROR != err)
		{
			GlideError("Couldn't set ATI FSAA level: %s\n", reinterpret_cast<const char *>(aglErrorString(err)));
		}
		// Initially enable multisampling
		glEnable(GL_MULTISAMPLE_ARB);
		glReportError();
	}
	if (InternalConfig.FullSceneAntiAliasing > 0
	 &&	InternalConfig.NV_multisample_filter_hint)
	{
		glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
		glReportError();
	}
	
#ifdef OPENGLIDE_SYSTEM_HAS_FOGCOORD
	if (InternalConfig.EXT_fog_coord == false && InternalConfig.FogMode > OpenGLideFogEmulation_EnvCombine)
	{
		InternalConfig.FogMode = OpenGLideFogEmulation_EnvCombine;
	}
#endif

	// FogCoord extension not supported on MacOS9
#ifdef OPENGLIDE_SYSTEM_DOESN_T_HAVE_FOGCOORD
	if (InternalConfig.FogMode > OpenGLideFogEmulation_EnvCombine)
	{
		InternalConfig.FogMode = OpenGLideFogEmulation_EnvCombine;
	}
#endif

	// check for other extensions needed by fog emulation
	if (InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine
	 && InternalConfig.EXT_texture_env_combine == false)
	{
		GlideMsg( "Enhanced fog emulation disabled because GL_ARB_texture_env_combine is missing or disabled\n");
		InternalConfig.FogMode = OpenGLideFogEmulation_Simple;
	}
	else if (InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine
	 && InternalConfig.ARB_multitexture == false)
	{
		GlideMsg( "Enhanced fog emulation disabled because GL_ARB_multitexture is missing or disabled\n");
		InternalConfig.FogMode = OpenGLideFogEmulation_Simple;
	}
	
	// Setup texture units
	if (InternalConfig.GlideTextureUnits > 1)
	{
		GlideMsg( "Only one texure unit is currently supported. Additional units are ignored.\n");
	}
	if (InternalConfig.ARB_multitexture)
	{
		GLint number_of_texture_units = 1;
		glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &number_of_texture_units);
		GlideMsg("MultiTexture Units = %x\n", number_of_texture_units);
		if (InternalConfig.ColorAlphaRenderMode >= OpenGLideColorAlphaRenderMode_Unknown)
		{
			InternalConfig.ColorAlphaRenderMode = OpenGLideColorAlphaRenderMode_Automatic;
		}
		if (InternalConfig.ColorAlphaRenderMode > OpenGLideColorAlphaRenderMode_Simple
		 && InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine
		 && number_of_texture_units <= 2)
		{
	    GlideMsg( "Coloralpha render mode reset to simple in favour of enhanced fog emulation\n");
			InternalConfig.ColorAlphaRenderMode = OpenGLideColorAlphaRenderMode_Simple;
		}
		// Setup coloralpha texture units
		OpenGL.ColorAlphaUnit1 = GL_TEXTURE0_ARB;
		if ((number_of_texture_units > 2 && InternalConfig.EXT_texture_env_combine
			|| number_of_texture_units == 2 && InternalConfig.FogMode != OpenGLideFogEmulation_EnvCombine)
			&& (InternalConfig.ColorAlphaRenderMode == OpenGLideColorAlphaRenderMode_Automatic
			 || InternalConfig.ColorAlphaRenderMode > OpenGLideColorAlphaRenderMode_Simple))
		{
			OpenGL.ColorAlphaUnit2 = GL_TEXTURE1_ARB;
			OpenGL.FogTextureUnit = GL_TEXTURE2_ARB;
			// env combine
			glActiveTextureARB(OpenGL.ColorAlphaUnit1);
			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
			glActiveTextureARB(OpenGL.ColorAlphaUnit2);
			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
			glReportError();
			// Secondary color not needed in the enhanced color alpha rendering model
			InternalConfig.EXT_secondary_color = false;
			if (InternalConfig.ColorAlphaRenderMode == OpenGLideColorAlphaRenderMode_Automatic)
			{
				if (InternalConfig.ATI_texture_env_combine3)
				{
					// With GL_ATI_texture_env_combine3, more combine functions can be modeled
					// equivalently to the Glide combine functions. They can also be rendered more
					// often with one texture unit, which leaves more space for correct rendering
					// of textures that use both chromakeying and alpha blending together.
					InternalConfig.ColorAlphaRenderMode = OpenGLideColorAlphaRenderMode_EnvCombine3_ATI;
					OpenGL.ColorCombineFunctions = ColorCombineFunctionsEnvCombine3ATI;
					OpenGL.AlphaCombineFunctions = AlphaCombineFunctionsEnvCombine3ATI;
				}
				else if (InternalConfig.EXT_texture_env_combine)
				{
					// Standard setup, works with most graphics cards and provides accurate
					// results in most cases
					InternalConfig.ColorAlphaRenderMode = OpenGLideColorAlphaRenderMode_EnvCombine_ARB;
					OpenGL.ColorCombineFunctions = ColorCombineFunctionsEnvCombineARB;
					OpenGL.AlphaCombineFunctions = AlphaCombineFunctionsEnvCombineARB;
				}
			}
			if (InternalConfig.ColorAlphaRenderMode == OpenGLideColorAlphaRenderMode_EnvCombine3_ATI)
			{
				OpenGL.ColorCombineFunctions = ColorCombineFunctionsEnvCombine3ATI;
				OpenGL.AlphaCombineFunctions = AlphaCombineFunctionsEnvCombine3ATI;
			}
			else if (InternalConfig.ColorAlphaRenderMode == OpenGLideColorAlphaRenderMode_EnvCombine_ARB)
			{
				OpenGL.ColorCombineFunctions = ColorCombineFunctionsEnvCombineARB;
				OpenGL.AlphaCombineFunctions = AlphaCombineFunctionsEnvCombineARB;
			}
		}
		else
		{
			OpenGL.ColorAlphaUnit2 = 0;
			OpenGL.FogTextureUnit = number_of_texture_units > 2 ? GL_TEXTURE2_ARB : GL_TEXTURE1_ARB;
			InternalConfig.ColorAlphaRenderMode = OpenGLideColorAlphaRenderMode_Simple;
		}
	}
	else
	{
		OpenGL.ColorAlphaUnit1 = GL_TEXTURE0_ARB;
		OpenGL.ColorAlphaUnit2 = 0;
		OpenGL.FogTextureUnit = 0;
		InternalConfig.ColorAlphaRenderMode = OpenGLideColorAlphaRenderMode_Simple;
	}
	
	// Print out coloralpha render mode
	char* coloralpharendermodename;
	switch (InternalConfig.ColorAlphaRenderMode)
	{
	case OpenGLideColorAlphaRenderMode_Simple:
		coloralpharendermodename = "MultiTextureSimple";
		break;
	case OpenGLideColorAlphaRenderMode_EnvCombine_ARB:
		coloralpharendermodename = "EnvironmentCombineARB";
		break;
	case OpenGLideColorAlphaRenderMode_EnvCombine3_ATI:
		coloralpharendermodename = "EnvironmentCombine3ATI";
		break;
	default:
		assert(false);
		break;
	}
	GlideMsg( "Coloralpha rendermode = %s\n", coloralpharendermodename);

	if (OpenGL.FogTextureUnit == 0 && InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine)
	{
		InternalConfig.FogMode = OpenGLideFogEmulation_Simple;
	}

	if (InternalConfig.EXT_secondary_color)
	{
		glEnable(GL_COLOR_SUM_EXT);
		glReportError();
	}

	if (InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine)
	{
		fogalpharamp = static_cast<unsigned char*> (AllocBuffer(256, 1));
		if (fogalpharamp)
		{
			for(unsigned int i = 0; i < 256; i++)
			{
				fogalpharamp[i] = 255 - i;
			}
		}
		else
		{
			InternalConfig.FogMode = OpenGLideFogEmulation_Simple;
		}
	}

	if (OpenGL.ColorAlphaUnit2 || InternalConfig.FogMode != OpenGLideFogEmulation_EnvCombine)
	{
		glGenTextures(1, &OpenGL.DummyTextureName);
		glPrioritizeTextures(1, &OpenGL.DummyTextureName, &one);
		glActiveTextureARB(OpenGL.ColorAlphaUnit1);
		glBindTexture(GL_TEXTURE_2D, OpenGL.DummyTextureName);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, InternalConfig.EXT_SGIS_texture_edge_clamp ? GL_CLAMP_TO_EDGE : GL_CLAMP); // GL_REPEAT would cause randomly
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, InternalConfig.EXT_SGIS_texture_edge_clamp ? GL_CLAMP_TO_EDGE : GL_CLAMP); // fogged triangles in the foreground
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		if (InternalConfig.AnisotropylLevel >= 2)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
		}
		glTexImage2D(GL_TEXTURE_2D, 0, 4, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &dummytexture);
		glReportError();
	}
	
#ifdef OPENGLIDE_SYSTEM_HAS_FOGCOORD
	if (InternalConfig.FogMode == OpenGLideFogEmulation_FogCoord)
	{
		glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT);
		glFogf(GL_FOG_MODE, GL_LINEAR);
		glFogf(GL_FOG_START, 0.0f);
		glFogf(GL_FOG_END, 1.0f);
		glReportError();
	}
	else
#endif
	if (InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine)
	{
		GrFog_t fogtable[GR_FOG_TABLE_SIZE];
		guFogGenerateLinear(fogtable, 0.0f, 1.0f);
		grFogTable(fogtable);
		GlideMsg( "Using env-combine fog emulation\n");
		// Setup fog texture unit
		glActiveTextureARB(OpenGL.FogTextureUnit);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
		// The color part is used for the fog function (see RenderUpdateState.cpp)
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INTERPOLATE_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_COLOR);
		// The alpha is not blended with the fog alpha because this
		// would also fog otherwise invisible parts of the texture
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA);
		glGenTextures(1, &fogtexturename);
		glPrioritizeTextures(1, &fogtexturename, &one);
		glBindTexture(GL_TEXTURE_2D, fogtexturename);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, InternalConfig.EXT_SGIS_texture_edge_clamp ? GL_CLAMP_TO_EDGE : GL_CLAMP); // GL_REPEAT would cause randomly
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, InternalConfig.EXT_SGIS_texture_edge_clamp ? GL_CLAMP_TO_EDGE : GL_CLAMP); // fogged triangles in the foreground
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		if (InternalConfig.AnisotropylLevel >= 2)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
		}
		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 256, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, fogalpharamp);
		glReportError();  
	}
	else if (InternalConfig.FogMode == OpenGLideFogEmulation_Simple)
	{
		// Provide a reasonable default for application that use fog,
		// but don't set the fog table (like Carmageddon)
		// Adjusted to camageddon Main Street Track (compared with Race Track Preview image)
		// and to Coastal Carnage Track so that objects become colored right after they pop up
		// when popup distance option is far (fog is set to black in this level)

		// This doesn't seem to work with MacOS9 OpenGL drivers (beasue of version < 1.3 ?).
		// The whole scene is too dark. However, it works well in the Classic environment
		// (which actually uses the OpenGL driver provided by MacOS X)
			
		// When the fog table is not set as below, the whole scene seems to be a little to dark,
		// whereas when it's set, the foreground looks as brighht as in the software rendrerer.

		GrFog_t fogtable[GR_FOG_TABLE_SIZE];
		// The values below come from comparing screenshots between GeForce 2mx
		// (MacOS X OpenGL 1.3 driver) and the software renderer version of Carmageddon
		guFogGenerateLinear(fogtable, 0.9985f, 1.0f);
		grFogTable(fogtable);
		GlideMsg( "Using simple fog emulation\n");
	}
	else
	{
		InternalConfig.FogMode = OpenGLideFogEmulation_None;
		// These default values must be provied even if fog is
		// disabled or the shadows in Carmageddon Splatpack,
		// Meltdown Alley level will have the fog color
		// (occures on MacOS 9, Nvidia OpenGL 1.22)
		glFogf( GL_FOG_MODE, GL_LINEAR );
		glReportError();
		glFogf( GL_FOG_START, 1.0f );
		glReportError();
		glFogf( GL_FOG_END, 0.0f );
		glReportError();
		glDisable(GL_FOG);
		GlideMsg( "Fog emulation disabled\n");
	}
	
	if (InternalConfig.FogMode != OpenGLideFogEmulation_EnvCombine && OpenGL.DummyTextureName && OpenGL.FogTextureUnit)
	{
		// Setup fog texture unit as inverter (for inverting the color alpha output)
		glActiveTextureARB(OpenGL.FogTextureUnit);
		glBindTexture(GL_TEXTURE_2D, OpenGL.DummyTextureName);
		glEnable(GL_TEXTURE_2D);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA);
		glReportError();
		OpenGL.FogTextureUnitEnabledState = true;
	}
	else
	{
		OpenGL.FogTextureUnitEnabledState = false;
	}
	// End of texture unit setup
	glActiveTextureARB(OpenGL.ColorAlphaUnit1);

	if (InternalConfig.EXT_paletted_texture)
	{
		GlideMsg( "Using Palette Extension\n" );
	}
	
#ifdef OPENGLIDE_HOST_MAC
	if (InternalConfig.EXT_compiled_vertex_array)
	{
		// Compiled vertex array crashes on MacOS9 (at least on my system)
		// so if anybody who would like to investigate this...
		if (!RunningInClassic)
		{
			InternalConfig.EXT_compiled_vertex_array = false;
			GlideMsg( "Compiled vertex arrays have been disabled (works in Classic only)\n" );    
		}
	}
#endif

	if (InternalConfig.TextureSmoothing)
	{
		OpenGL.MinFilterMode = InternalConfig.Mipmapping ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
		OpenGL.MagFilterMode = GL_LINEAR;
	}

	if (InternalConfig.EXT_clip_volume_hint)
	{
		glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_FASTEST);
		OpenGL.ClipVerticesEnabledState = false;
	}

	if (InternalConfig.APPLE_transform_hint)
	{
		glHint(GL_TRANSFORM_HINT_APPLE, GL_NICEST);
	}

	if (InternalConfig.EXT_SGIS_generate_mipmap)
	{
		glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
	}

	VERIFY_ACTIVE_TEXTURE_UNIT(OpenGL.ColorAlphaUnit1);
}

void GLExtensionsCleanup()
{
	// cleanup fog texture
	if (InternalConfig.FogMode == OpenGLideFogEmulation_EnvCombine)
	{
		if (fogalpharamp)
		{
			FreeBuffer(fogalpharamp);
			glReportErrors("RenderFree");
			glDeleteTextures(1, &fogtexturename);
			glReportError();
		}
	}
	if (OpenGL.DummyTextureName)
	{
		glDeleteTextures(1, &OpenGL.DummyTextureName);
	}
}
