jedi-academy/codemp/win32/win_glimp.cpp
2013-04-25 23:51:56 +10:00

2095 lines
57 KiB
C++

//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
/*
** WIN_GLIMP.C
**
** This file contains ALL Win32 specific stuff having to do with the
** OpenGL refresh. When a port is being made the following functions
** must be implemented by the port:
**
** GLimp_EndFrame
** GLimp_Init
** GLimp_LogComment
** GLimp_Shutdown
**
** Note that the GLW_xxx functions are Windows specific GL-subsystem
** related functions that are relevant ONLY to win_glimp.c
*/
#include <assert.h>
#include "../renderer/tr_local.h"
#include "resource.h"
#include "glw_win.h"
#include "win_local.h"
#include "../qcommon/stringed_ingame.h"
extern void WG_CheckHardwareGamma( void );
extern void WG_RestoreGamma( void );
typedef enum {
RSERR_OK,
RSERR_INVALID_FULLSCREEN,
RSERR_INVALID_MODE,
RSERR_UNKNOWN
} rserr_t;
#define TRY_PFD_SUCCESS 0
#define TRY_PFD_FAIL_SOFT 1
#define TRY_PFD_FAIL_HARD 2
#define WINDOW_CLASS_NAME "Jedi Knight®: Jedi Academy (MP)"
static void GLW_InitExtensions( void );
static rserr_t GLW_SetMode( int mode,
int colorbits,
qboolean cdsFullscreen );
static qboolean s_classRegistered = qfalse;
//
// function declaration
//
void QGL_EnableLogging( qboolean enable );
qboolean QGL_Init( const char *dllname );
void QGL_Shutdown( void );
//
// variable declarations
//
glwstate_t glw_state;
cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software
// Whether the current hardware supports dynamic glows/flares.
extern bool g_bDynamicGlowSupported;
// Hack variable for deciding which kind of texture rectangle thing to do (for some
// reason it acts different on radeon! It's against the spec!).
bool g_bTextureRectangleHack = false;
/*
** GLW_StartDriverAndSetMode
*/
static qboolean GLW_StartDriverAndSetMode( int mode,
int colorbits,
qboolean cdsFullscreen )
{
rserr_t err;
err = GLW_SetMode( mode, colorbits, cdsFullscreen );
switch ( err )
{
case RSERR_INVALID_FULLSCREEN:
Com_Printf("...WARNING: fullscreen unavailable in this mode\n" );
return qfalse;
case RSERR_INVALID_MODE:
Com_Printf ("...WARNING: could not set the given mode (%d)\n", mode );
return qfalse;
default:
break;
}
return qtrue;
}
/*
** ChoosePFD
**
** Helper function that replaces ChoosePixelFormat.
*/
#define MAX_PFDS 256
static int GLW_ChoosePFD( HDC hDC, PIXELFORMATDESCRIPTOR *pPFD )
{
PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1];
int maxPFD = 0;
int i;
int bestMatch = 0;
Com_Printf ("...GLW_ChoosePFD( %d, %d, %d )\n", ( int ) pPFD->cColorBits, ( int ) pPFD->cDepthBits, ( int ) pPFD->cStencilBits );
// count number of PFDs
maxPFD = DescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] );
if ( maxPFD > MAX_PFDS )
{
Com_Printf (S_COLOR_YELLOW "...numPFDs > MAX_PFDS (%d > %d)\n", maxPFD, MAX_PFDS );
maxPFD = MAX_PFDS;
}
Com_Printf ("...%d PFDs found\n", maxPFD - 1 );
// grab information
for ( i = 1; i <= maxPFD; i++ )
{
DescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] );
}
// look for a best match
for ( i = 1; i <= maxPFD; i++ )
{
//
// make sure this has hardware acceleration
//
if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
{
if ( !r_allowSoftwareGL->integer )
{
if ( r_verbose->integer )
{
Com_Printf ("...PFD %d rejected, software acceleration\n", i );
}
continue;
}
}
// verify pixel type
if ( pfds[i].iPixelType != PFD_TYPE_RGBA )
{
if ( r_verbose->integer )
{
Com_Printf ("...PFD %d rejected, not RGBA\n", i );
}
continue;
}
// verify proper flags
if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags )
{
if ( r_verbose->integer )
{
Com_Printf ("...PFD %d rejected, improper flags (%x instead of %x)\n", i, pfds[i].dwFlags, pPFD->dwFlags );
}
continue;
}
// verify enough bits
if ( pfds[i].cDepthBits < 15 )
{
continue;
}
if ( ( pfds[i].cStencilBits < 4 ) && ( pPFD->cStencilBits > 0 ) )
{
continue;
}
//
// selection criteria (in order of priority):
//
// PFD_STEREO
// colorBits
// depthBits
// stencilBits
//
if ( bestMatch )
{
// check stereo
if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) )
{
bestMatch = i;
continue;
}
if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) )
{
bestMatch = i;
continue;
}
// check color
if ( pfds[bestMatch].cColorBits != pPFD->cColorBits )
{
// prefer perfect match
if ( pfds[i].cColorBits == pPFD->cColorBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits )
{
bestMatch = i;
continue;
}
}
// check depth
if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits )
{
// prefer perfect match
if ( pfds[i].cDepthBits == pPFD->cDepthBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits )
{
bestMatch = i;
continue;
}
}
// check stencil
if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits )
{
// prefer perfect match
if ( pfds[i].cStencilBits == pPFD->cStencilBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) &&
( pPFD->cStencilBits > 0 ) )
{
bestMatch = i;
continue;
}
}
}
else
{
bestMatch = i;
}
}
if ( !bestMatch )
return 0;
if ( ( pfds[bestMatch].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
{
if ( !r_allowSoftwareGL->integer )
{
Com_Printf ("...no hardware acceleration found\n" );
return 0;
}
else
{
Com_Printf ("...using software emulation\n" );
}
}
else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED )
{
Com_Printf ("...MCD acceleration found\n" );
}
else
{
Com_Printf ("...hardware acceleration found\n" );
}
*pPFD = pfds[bestMatch];
return bestMatch;
}
/*
** void GLW_CreatePFD
**
** Helper function zeros out then fills in a PFD
*/
static void GLW_CreatePFD( PIXELFORMATDESCRIPTOR *pPFD, int colorbits, int depthbits, int stencilbits, qboolean stereo )
{
PIXELFORMATDESCRIPTOR src =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
24, // 24-bit z-buffer
8, // 8-bit stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
src.cColorBits = colorbits;
src.cDepthBits = depthbits;
src.cStencilBits = stencilbits;
if ( stereo )
{
Com_Printf ("...attempting to use stereo\n" );
src.dwFlags |= PFD_STEREO;
glConfig.stereoEnabled = qtrue;
}
else
{
glConfig.stereoEnabled = qfalse;
}
*pPFD = src;
}
/*
** GLW_MakeContext
*/
static int GLW_MakeContext( PIXELFORMATDESCRIPTOR *pPFD )
{
int pixelformat;
//
// don't putz around with pixelformat if it's already set (e.g. this is a soft
// reset of the graphics system)
//
if ( !glw_state.pixelFormatSet )
{
//
// choose, set, and describe our desired pixel format. If we're
// using a minidriver then we need to bypass the GDI functions,
// otherwise use the GDI functions.
//
if ( ( pixelformat = GLW_ChoosePFD( glw_state.hDC, pPFD ) ) == 0 )
{
Com_Printf ("...GLW_ChoosePFD failed\n");
return TRY_PFD_FAIL_SOFT;
}
Com_Printf ("...PIXELFORMAT %d selected\n", pixelformat );
DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( *pPFD ), pPFD );
if ( SetPixelFormat( glw_state.hDC, pixelformat, pPFD ) == FALSE )
{
Com_Printf ( "...SetPixelFormat failed\n", glw_state.hDC );
return TRY_PFD_FAIL_SOFT;
}
glw_state.pixelFormatSet = qtrue;
}
//
// startup the OpenGL subsystem by creating a context and making it current
//
if ( !glw_state.hGLRC )
{
Com_Printf ("...creating GL context: " );
if ( ( glw_state.hGLRC = qwglCreateContext( glw_state.hDC ) ) == 0 )
{
Com_Printf ( "failed\n");
return TRY_PFD_FAIL_HARD;
}
Com_Printf ("succeeded\n" );
Com_Printf ("...making context current: " );
if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) )
{
qwglDeleteContext( glw_state.hGLRC );
glw_state.hGLRC = NULL;
Com_Printf ( "failed\n");
return TRY_PFD_FAIL_HARD;
}
Com_Printf ("succeeded\n" );
}
return TRY_PFD_SUCCESS;
}
/*
** GLW_InitDriver
**
** - get a DC if one doesn't exist
** - create an HGLRC if one doesn't exist
*/
static qboolean GLW_InitDriver( int colorbits )
{
int tpfd;
int depthbits, stencilbits;
static PIXELFORMATDESCRIPTOR pfd; // save between frames since 'tr' gets cleared
Com_Printf ("Initializing OpenGL driver\n" );
//
// get a DC for our window if we don't already have one allocated
//
if ( glw_state.hDC == NULL )
{
Com_Printf ("...getting DC: " );
if ( ( glw_state.hDC = GetDC( g_wv.hWnd ) ) == NULL )
{
Com_Printf ("failed\n" );
return qfalse;
}
Com_Printf ("succeeded\n" );
}
if ( colorbits == 0 )
{
colorbits = glw_state.desktopBitsPixel;
}
//
// implicitly assume Z-buffer depth == desktop color depth
//
if ( r_depthbits->integer == 0 ) {
if ( colorbits > 16 ) {
depthbits = 24;
} else {
depthbits = 16;
}
} else {
depthbits = r_depthbits->integer;
}
//
// do not allow stencil if Z-buffer depth likely won't contain it
//
stencilbits = r_stencilbits->integer;
if ( depthbits < 24 )
{
stencilbits = 0;
}
//
// make two attempts to set the PIXELFORMAT
//
//
// first attempt: r_colorbits, depthbits, and r_stencilbits
//
if ( !glw_state.pixelFormatSet )
{
GLW_CreatePFD( &pfd, colorbits, depthbits, stencilbits, (qboolean)r_stereo->integer );
if ( ( tpfd = GLW_MakeContext( &pfd ) ) != TRY_PFD_SUCCESS )
{
if ( tpfd == TRY_PFD_FAIL_HARD )
{
Com_Printf (S_COLOR_YELLOW "...failed hard\n" );
return qfalse;
}
//
// punt if we've already tried the desktop bit depth and no stencil bits
//
if ( ( r_colorbits->integer == glw_state.desktopBitsPixel ) &&
( stencilbits == 0 ) )
{
ReleaseDC( g_wv.hWnd, glw_state.hDC );
glw_state.hDC = NULL;
Com_Printf ("...failed to find an appropriate PIXELFORMAT\n" );
return qfalse;
}
//
// second attempt: desktop's color bits and no stencil
//
if ( colorbits > glw_state.desktopBitsPixel )
{
colorbits = glw_state.desktopBitsPixel;
}
GLW_CreatePFD( &pfd, colorbits, depthbits, 0, (qboolean)r_stereo->integer );
if ( GLW_MakeContext( &pfd ) != TRY_PFD_SUCCESS )
{
if ( glw_state.hDC )
{
ReleaseDC( g_wv.hWnd, glw_state.hDC );
glw_state.hDC = NULL;
}
Com_Printf ("...failed to find an appropriate PIXELFORMAT\n" );
return qfalse;
}
}
/*
** report if stereo is desired but unavailable
*/
if ( !( pfd.dwFlags & PFD_STEREO ) && ( r_stereo->integer != 0 ) )
{
Com_Printf ("...failed to select stereo pixel format\n" );
glConfig.stereoEnabled = qfalse;
}
}
/*
** store PFD specifics
*/
glConfig.colorBits = ( int ) pfd.cColorBits;
glConfig.depthBits = ( int ) pfd.cDepthBits;
glConfig.stencilBits = ( int ) pfd.cStencilBits;
return qtrue;
}
/*
** GLW_CreateWindow
**
** Responsible for creating the Win32 window and initializing the OpenGL driver.
*/
#define WINDOW_STYLE (WS_OVERLAPPED|WS_BORDER|WS_CAPTION|WS_VISIBLE)
static qboolean GLW_CreateWindow( int width, int height, int colorbits, qboolean cdsFullscreen )
{
RECT r;
cvar_t *vid_xpos, *vid_ypos;
int stylebits;
int x, y, w, h;
int exstyle;
//
// register the window class if necessary
//
if ( !s_classRegistered )
{
WNDCLASS wc;
memset( &wc, 0, sizeof( wc ) );
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) glw_state.wndproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_wv.hInstance;
wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor = LoadCursor (NULL,IDC_ARROW);
wc.hbrBackground = 0;//(HBRUSH__ *)COLOR_GRAYTEXT;
wc.lpszMenuName = 0;
wc.lpszClassName = WINDOW_CLASS_NAME;
if ( !RegisterClass( &wc ) )
{
Com_Error( ERR_FATAL, "GLW_CreateWindow: could not register window class" );
}
s_classRegistered = qtrue;
// Com_Printf ("...registered window class\n" );
}
//
// create the HWND if one does not already exist
//
if ( !g_wv.hWnd )
{
//
// compute width and height
//
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
if ( cdsFullscreen )
{
exstyle = WS_EX_TOPMOST;
stylebits = WS_SYSMENU|WS_POPUP|WS_VISIBLE; //sysmenu gives you the icon
}
else
{
exstyle = 0;
stylebits = WS_SYSMENU|WINDOW_STYLE|WS_MINIMIZEBOX;
AdjustWindowRect (&r, stylebits, FALSE);
}
w = r.right - r.left;
h = r.bottom - r.top;
if ( cdsFullscreen )
{
x = 0;
y = 0;
}
else
{
vid_xpos = Cvar_Get ("vid_xpos", "", 0);
vid_ypos = Cvar_Get ("vid_ypos", "", 0);
x = vid_xpos->integer;
y = vid_ypos->integer;
// adjust window coordinates if necessary
// so that the window is completely on screen
if ( x < 0 )
x = 0;
if ( y < 0 )
y = 0;
if ( w < glw_state.desktopWidth &&
h < glw_state.desktopHeight )
{
if ( x + w > glw_state.desktopWidth )
x = ( glw_state.desktopWidth - w );
if ( y + h > glw_state.desktopHeight )
y = ( glw_state.desktopHeight - h );
}
}
g_wv.hWnd = CreateWindowEx (
exstyle,
WINDOW_CLASS_NAME,
WINDOW_CLASS_NAME,
stylebits,
x, y, w, h,
NULL,
NULL,
g_wv.hInstance,
NULL);
if ( !g_wv.hWnd )
{
Com_Error (ERR_FATAL, "GLW_CreateWindow() - Couldn't create window");
}
ShowWindow( g_wv.hWnd, SW_SHOW );
UpdateWindow( g_wv.hWnd );
Com_Printf ("...created window@%d,%d (%dx%d)\n", x, y, w, h );
}
else
{
Com_Printf ("...window already present, CreateWindowEx skipped\n" );
}
if ( !GLW_InitDriver( colorbits ) )
{
ShowWindow( g_wv.hWnd, SW_HIDE );
DestroyWindow( g_wv.hWnd );
g_wv.hWnd = NULL;
return qfalse;
}
SetForegroundWindow( g_wv.hWnd );
SetFocus( g_wv.hWnd );
return qtrue;
}
static void PrintCDSError( int value )
{
switch ( value )
{
case DISP_CHANGE_RESTART:
Com_Printf ("restart required\n" );
break;
case DISP_CHANGE_BADPARAM:
Com_Printf ("bad param\n" );
break;
case DISP_CHANGE_BADFLAGS:
Com_Printf ("bad flags\n" );
break;
case DISP_CHANGE_FAILED:
Com_Printf ("DISP_CHANGE_FAILED\n" );
break;
case DISP_CHANGE_BADMODE:
Com_Printf ("bad mode\n" );
break;
case DISP_CHANGE_NOTUPDATED:
Com_Printf ("not updated\n" );
break;
default:
Com_Printf ("unknown error %d\n", value );
break;
}
}
/*
** GLW_SetMode
*/
static rserr_t GLW_SetMode( int mode,
int colorbits,
qboolean cdsFullscreen )
{
HDC hDC;
const char *win_fs[] = { "W", "FS" };
int cdsRet;
DEVMODE dm;
//
// print out informational messages
//
Com_Printf ("...setting mode %d:", mode );
if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, mode ) )
{
Com_Printf (" invalid mode\n" );
return RSERR_INVALID_MODE;
}
Com_Printf (" %d %d %s\n", glConfig.vidWidth, glConfig.vidHeight, win_fs[cdsFullscreen] );
//
// check our desktop attributes
//
hDC = GetDC( GetDesktopWindow() );
glw_state.desktopBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
glw_state.desktopWidth = GetDeviceCaps( hDC, HORZRES );
glw_state.desktopHeight = GetDeviceCaps( hDC, VERTRES );
ReleaseDC( GetDesktopWindow(), hDC );
//
// verify desktop bit depth
//
if ( glw_state.desktopBitsPixel < 15 || glw_state.desktopBitsPixel == 24 )
{
if (!cdsFullscreen && (colorbits == 0 || colorbits >= 15 ) )
{
// since I can't be bothered trying to mess around with asian codepages and MBCS stuff for a windows
// error box that'll only appear if something's seriously fucked then I'm going to fallback to
// english text when these would otherwise be used...
//
char sErrorHead[1024]; // ott
extern qboolean Language_IsAsian(void);
Q_strncpyz(sErrorHead, Language_IsAsian() ? "Low Desktop Color Depth" : SE_GetString("CON_TEXT_LOW_DESKTOP_COLOUR_DEPTH"), sizeof(sErrorHead) );
const char *psErrorBody = Language_IsAsian() ?
"It is highly unlikely that a correct windowed\n"
"display can be initialized with the current\n"
"desktop display depth. Select 'OK' to try\n"
"anyway. Select 'Cancel' to try a fullscreen\n"
"mode instead."
:
SE_GetString("CON_TEXT_TRY_ANYWAY");
if ( MessageBox( NULL,
psErrorBody,
sErrorHead,
MB_OKCANCEL | MB_ICONEXCLAMATION ) != IDOK )
{
return RSERR_INVALID_MODE;
}
}
}
// do a CDS if needed
if ( cdsFullscreen )
{
memset( &dm, 0, sizeof( dm ) );
dm.dmSize = sizeof( dm );
dm.dmPelsWidth = glConfig.vidWidth;
dm.dmPelsHeight = glConfig.vidHeight;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
if ( r_displayRefresh->integer != 0 )
{
dm.dmDisplayFrequency = r_displayRefresh->integer;
dm.dmFields |= DM_DISPLAYFREQUENCY;
}
// try to change color depth if possible
if ( colorbits != 0 )
{
if ( glw_state.allowdisplaydepthchange )
{
dm.dmBitsPerPel = colorbits;
dm.dmFields |= DM_BITSPERPEL;
Com_Printf ("...using colorsbits of %d\n", colorbits );
}
else
{
Com_Printf ("WARNING:...changing depth not supported on Win95 < pre-OSR 2.x\n" );
}
}
else
{
Com_Printf ("...using desktop display depth of %d\n", glw_state.desktopBitsPixel );
}
//
// if we're already in fullscreen then just create the window
//
if ( glw_state.cdsFullscreen )
{
Com_Printf ("...already fullscreen, avoiding redundant CDS\n" );
if ( !GLW_CreateWindow ( glConfig.vidWidth, glConfig.vidHeight, colorbits, qtrue ) )
{
Com_Printf ("...restoring display settings\n" );
ChangeDisplaySettings( 0, 0 );
return RSERR_INVALID_MODE;
}
}
//
// need to call CDS
//
else
{
Com_Printf ("...calling CDS: " );
// try setting the exact mode requested, because some drivers don't report
// the low res modes in EnumDisplaySettings, but still work
if ( ( cdsRet = ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) ) == DISP_CHANGE_SUCCESSFUL )
{
Com_Printf ("ok\n" );
if ( !GLW_CreateWindow ( glConfig.vidWidth, glConfig.vidHeight, colorbits, qtrue) )
{
Com_Printf ("...restoring display settings\n" );
ChangeDisplaySettings( 0, 0 );
return RSERR_INVALID_MODE;
}
glw_state.cdsFullscreen = qtrue;
}
else
{
//
// the exact mode failed, so scan EnumDisplaySettings for the next largest mode
//
DEVMODE devmode;
int modeNum;
Com_Printf ("failed, " );
PrintCDSError( cdsRet );
Com_Printf ("...trying next higher resolution:" );
// we could do a better matching job here...
for ( modeNum = 0 ; ; modeNum++ ) {
if ( !EnumDisplaySettings( NULL, modeNum, &devmode ) ) {
modeNum = -1;
break;
}
if ( devmode.dmPelsWidth >= glConfig.vidWidth
&& devmode.dmPelsHeight >= glConfig.vidHeight
&& devmode.dmBitsPerPel >= 15 ) {
break;
}
}
if ( modeNum != -1 && ( cdsRet = ChangeDisplaySettings( &devmode, CDS_FULLSCREEN ) ) == DISP_CHANGE_SUCCESSFUL )
{
Com_Printf (" ok\n" );
if ( !GLW_CreateWindow( glConfig.vidWidth, glConfig.vidHeight, colorbits, qtrue) )
{
Com_Printf ("...restoring display settings\n" );
ChangeDisplaySettings( 0, 0 );
return RSERR_INVALID_MODE;
}
glw_state.cdsFullscreen = qtrue;
}
else
{
Com_Printf (" failed, " );
PrintCDSError( cdsRet );
Com_Printf ("...restoring display settings\n" );
ChangeDisplaySettings( 0, 0 );
/* jfm: i took out the following code to allow fallback to mode 3, with this code it goes half windowed and just doesn't work.
glw_state.cdsFullscreen = qfalse;
glConfig.isFullscreen = qfalse;
if ( !GLW_CreateWindow( glConfig.vidWidth, glConfig.vidHeight, colorbits, qfalse) )
{
return RSERR_INVALID_MODE;
}
*/
return RSERR_INVALID_FULLSCREEN;
}
}
}
}
else
{
if ( glw_state.cdsFullscreen )
{
ChangeDisplaySettings( 0, 0 );
}
glw_state.cdsFullscreen = qfalse;
if ( !GLW_CreateWindow( glConfig.vidWidth, glConfig.vidHeight, colorbits, qfalse ) )
{
return RSERR_INVALID_MODE;
}
}
//
// success, now check display frequency, although this won't be valid on Voodoo(2)
//
memset( &dm, 0, sizeof( dm ) );
dm.dmSize = sizeof( dm );
if ( EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &dm ) )
{
glConfig.displayFrequency = dm.dmDisplayFrequency;
}
// NOTE: this is overridden later on standalone 3Dfx drivers
glConfig.isFullscreen = cdsFullscreen;
return RSERR_OK;
}
/*
** GLW_CheckForExtension
Cannot use strstr directly to differentiate between (for eg) reg_combiners and reg_combiners2
*/
bool GL_CheckForExtension(const char *ext)
{
char *temp;
char term;
temp = strstr(glConfig.extensions_string, ext);
if(!temp)
{
return(false);
}
// String exists but it may not be terminated
term = temp[strlen(ext)];
if((term == ' ') || !term)
{
return(true);
}
return(false);
}
//--------------------------------------------
static void GLW_InitTextureCompression( void )
{
qboolean newer_tc, old_tc;
// Check for available tc methods.
newer_tc = ( strstr( glConfig.extensions_string, "ARB_texture_compression" )
&& strstr( glConfig.extensions_string, "EXT_texture_compression_s3tc" )) ? qtrue : qfalse;
old_tc = ( strstr( glConfig.extensions_string, "GL_S3_s3tc" )) ? qtrue : qfalse;
if ( old_tc )
{
Com_Printf ("...GL_S3_s3tc available\n" );
}
if ( newer_tc )
{
Com_Printf ("...GL_EXT_texture_compression_s3tc available\n" );
}
if ( !r_ext_compressed_textures->value )
{
// Compressed textures are off
glConfig.textureCompression = TC_NONE;
Com_Printf ("...ignoring texture compression\n" );
}
else if ( !old_tc && !newer_tc )
{
// Requesting texture compression, but no method found
glConfig.textureCompression = TC_NONE;
Com_Printf ("...no supported texture compression method found\n" );
Com_Printf (".....ignoring texture compression\n" );
}
else
{
// some form of supported texture compression is avaiable, so see if the user has a preference
if ( r_ext_preferred_tc_method->integer == TC_NONE )
{
// No preference, so pick the best
if ( newer_tc )
{
Com_Printf ("...no tc preference specified\n" );
Com_Printf (".....using GL_EXT_texture_compression_s3tc\n" );
glConfig.textureCompression = TC_S3TC_DXT;
}
else
{
Com_Printf ("...no tc preference specified\n" );
Com_Printf (".....using GL_S3_s3tc\n" );
glConfig.textureCompression = TC_S3TC;
}
}
else
{
// User has specified a preference, now see if this request can be honored
if ( old_tc && newer_tc )
{
// both are avaiable, so we can use the desired tc method
if ( r_ext_preferred_tc_method->integer == TC_S3TC )
{
Com_Printf ("...using preferred tc method, GL_S3_s3tc\n" );
glConfig.textureCompression = TC_S3TC;
}
else
{
Com_Printf ("...using preferred tc method, GL_EXT_texture_compression_s3tc\n" );
glConfig.textureCompression = TC_S3TC_DXT;
}
}
else
{
// Both methods are not available, so this gets trickier
if ( r_ext_preferred_tc_method->integer == TC_S3TC )
{
// Preferring to user older compression
if ( old_tc )
{
Com_Printf ("...using GL_S3_s3tc\n" );
glConfig.textureCompression = TC_S3TC;
}
else
{
// Drat, preference can't be honored
Com_Printf ("...preferred tc method, GL_S3_s3tc not available\n" );
Com_Printf (".....falling back to GL_EXT_texture_compression_s3tc\n" );
glConfig.textureCompression = TC_S3TC_DXT;
}
}
else
{
// Preferring to user newer compression
if ( newer_tc )
{
Com_Printf ("...using GL_EXT_texture_compression_s3tc\n" );
glConfig.textureCompression = TC_S3TC_DXT;
}
else
{
// Drat, preference can't be honored
Com_Printf ("...preferred tc method, GL_EXT_texture_compression_s3tc not available\n" );
Com_Printf (".....falling back to GL_S3_s3tc\n" );
glConfig.textureCompression = TC_S3TC;
}
}
}
}
}
}
/*
** GLW_InitExtensions
*/
static void GLW_InitExtensions( void )
{
if ( !r_allowExtensions->integer )
{
Com_Printf ("*** IGNORING OPENGL EXTENSIONS ***\n" );
g_bDynamicGlowSupported = false;
Cvar_Set( "r_DynamicGlow","0" );
return;
}
Com_Printf ("Initializing OpenGL extensions\n" );
// Select our tc scheme
GLW_InitTextureCompression();
// GL_EXT_texture_env_add
glConfig.textureEnvAddAvailable = qfalse;
if ( strstr( glConfig.extensions_string, "EXT_texture_env_add" ) )
{
if ( r_ext_texture_env_add->integer )
{
glConfig.textureEnvAddAvailable = qtrue;
Com_Printf ("...using GL_EXT_texture_env_add\n" );
}
else
{
glConfig.textureEnvAddAvailable = qfalse;
Com_Printf ("...ignoring GL_EXT_texture_env_add\n" );
}
}
else
{
Com_Printf ("...GL_EXT_texture_env_add not found\n" );
}
// GL_EXT_texture_filter_anisotropic
glConfig.maxTextureFilterAnisotropy = 0;
if ( strstr( glConfig.extensions_string, "EXT_texture_filter_anisotropic" ) )
{
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF //can't include glext.h here ... sigh
qglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.maxTextureFilterAnisotropy );
Com_Printf ("...GL_EXT_texture_filter_anisotropic available\n" );
if ( r_ext_texture_filter_anisotropic->integer>1 )
{
Com_Printf ("...using GL_EXT_texture_filter_anisotropic\n" );
}
else
{
Com_Printf ("...ignoring GL_EXT_texture_filter_anisotropic\n" );
}
Cvar_Set( "r_ext_texture_filter_anisotropic_avail", va("%f",glConfig.maxTextureFilterAnisotropy) );
if ( r_ext_texture_filter_anisotropic->value > glConfig.maxTextureFilterAnisotropy )
{
Cvar_Set( "r_ext_texture_filter_anisotropic", va("%f",glConfig.maxTextureFilterAnisotropy) );
}
}
else
{
Com_Printf ("...GL_EXT_texture_filter_anisotropic not found\n" );
Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "0" );
}
// GL_EXT_clamp_to_edge
glConfig.clampToEdgeAvailable = qfalse;
if ( strstr( glConfig.extensions_string, "GL_EXT_texture_edge_clamp" ) )
{
glConfig.clampToEdgeAvailable = qtrue;
Com_Printf ("...Using GL_EXT_texture_edge_clamp\n" );
}
// WGL_EXT_swap_control
qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" );
if ( qwglSwapIntervalEXT )
{
Com_Printf ("...using WGL_EXT_swap_control\n" );
r_swapInterval->modified = qtrue; // force a set next frame
}
else
{
Com_Printf ("...WGL_EXT_swap_control not found\n" );
}
// GL_ARB_multitexture
qglMultiTexCoord2fARB = NULL;
qglActiveTextureARB = NULL;
qglClientActiveTextureARB = NULL;
if ( strstr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
{
if ( r_ext_multitexture->integer )
{
qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) qwglGetProcAddress( "glMultiTexCoord2fARB" );
qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) qwglGetProcAddress( "glActiveTextureARB" );
qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) qwglGetProcAddress( "glClientActiveTextureARB" );
if ( qglActiveTextureARB )
{
qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &glConfig.maxActiveTextures );
if ( glConfig.maxActiveTextures > 1 )
{
Com_Printf ("...using GL_ARB_multitexture\n" );
}
else
{
qglMultiTexCoord2fARB = NULL;
qglActiveTextureARB = NULL;
qglClientActiveTextureARB = NULL;
Com_Printf ("...not using GL_ARB_multitexture, < 2 texture units\n" );
}
}
}
else
{
Com_Printf ("...ignoring GL_ARB_multitexture\n" );
}
}
else
{
Com_Printf ("...GL_ARB_multitexture not found\n" );
}
// GL_EXT_compiled_vertex_array
qglLockArraysEXT = NULL;
qglUnlockArraysEXT = NULL;
if ( strstr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
{
if ( r_ext_compiled_vertex_array->integer )
{
Com_Printf ("...using GL_EXT_compiled_vertex_array\n" );
qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) qwglGetProcAddress( "glLockArraysEXT" );
qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) qwglGetProcAddress( "glUnlockArraysEXT" );
if (!qglLockArraysEXT || !qglUnlockArraysEXT) {
Com_Error (ERR_FATAL, "bad getprocaddress");
}
}
else
{
Com_Printf ("...ignoring GL_EXT_compiled_vertex_array\n" );
}
}
else
{
Com_Printf ("...GL_EXT_compiled_vertex_array not found\n" );
}
qglPointParameterfEXT = NULL;
qglPointParameterfvEXT = NULL;
//3d textures -rww
qglTexImage3DEXT = NULL;
qglTexSubImage3DEXT = NULL;
if ( strstr( glConfig.extensions_string, "GL_EXT_point_parameters" ) )
{
if ( r_ext_compiled_vertex_array->integer || 1)
{
Com_Printf ("...using GL_EXT_point_parameters\n" );
qglPointParameterfEXT = ( void ( APIENTRY * )( GLenum, GLfloat) ) qwglGetProcAddress( "glPointParameterfEXT" );
qglPointParameterfvEXT = ( void ( APIENTRY * )( GLenum, GLfloat *) ) qwglGetProcAddress( "glPointParameterfvEXT" );
//3d textures -rww
qglTexImage3DEXT = (void ( APIENTRY * ) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *) ) qwglGetProcAddress( "glTexImage3DEXT" );
qglTexSubImage3DEXT = (void ( APIENTRY * ) (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *) ) qwglGetProcAddress( "glTexSubImage3DEXT" );
if (!qglPointParameterfEXT || !qglPointParameterfvEXT)
{
Com_Error (ERR_FATAL, "bad getprocaddress");
}
}
else
{
Com_Printf ("...ignoring GL_EXT_point_parameters\n" );
}
}
else
{
Com_Printf ("...GL_EXT_point_parameters not found\n" );
}
bool bNVRegisterCombiners = false;
// Register Combiners.
if ( strstr( glConfig.extensions_string, "GL_NV_register_combiners" ) )
{
// NOTE: This extension requires multitexture support (over 2 units).
if ( glConfig.maxActiveTextures >= 2 )
{
bNVRegisterCombiners = true;
// Register Combiners function pointer address load. - AReis
// NOTE: VV guys will _definetly_ not be able to use regcoms. Pixel Shaders are just as good though :-)
// NOTE: Also, this is an nVidia specific extension (of course), so fragment shaders would serve the same purpose
// if we needed some kind of fragment/pixel manipulation support.
qglCombinerParameterfvNV = ( PFNGLCOMBINERPARAMETERFVNV ) qwglGetProcAddress( "glCombinerParameterfvNV" );
qglCombinerParameterivNV = ( PFNGLCOMBINERPARAMETERIVNV ) qwglGetProcAddress( "glCombinerParameterivNV" );
qglCombinerParameterfNV = ( PFNGLCOMBINERPARAMETERFNV ) qwglGetProcAddress( "glCombinerParameterfNV" );
qglCombinerParameteriNV = ( PFNGLCOMBINERPARAMETERINV ) qwglGetProcAddress( "glCombinerParameteriNV" );
qglCombinerInputNV = ( PFNGLCOMBINERINPUTNV ) qwglGetProcAddress( "glCombinerInputNV" );
qglCombinerOutputNV = ( PFNGLCOMBINEROUTPUTNV ) qwglGetProcAddress( "glCombinerOutputNV" );
qglFinalCombinerInputNV = ( PFNGLFINALCOMBINERINPUTNV ) qwglGetProcAddress( "glFinalCombinerInputNV" );
qglGetCombinerInputParameterfvNV = ( PFNGLGETCOMBINERINPUTPARAMETERFVNV ) qwglGetProcAddress( "glGetCombinerInputParameterfvNV" );
qglGetCombinerInputParameterivNV = ( PFNGLGETCOMBINERINPUTPARAMETERIVNV ) qwglGetProcAddress( "glGetCombinerInputParameterivNV" );
qglGetCombinerOutputParameterfvNV = ( PFNGLGETCOMBINEROUTPUTPARAMETERFVNV ) qwglGetProcAddress( "glGetCombinerOutputParameterfvNV" );
qglGetCombinerOutputParameterivNV = ( PFNGLGETCOMBINEROUTPUTPARAMETERIVNV ) qwglGetProcAddress( "glGetCombinerOutputParameterivNV" );
qglGetFinalCombinerInputParameterfvNV = ( PFNGLGETFINALCOMBINERINPUTPARAMETERFVNV ) qwglGetProcAddress( "glGetFinalCombinerInputParameterfvNV" );
qglGetFinalCombinerInputParameterivNV = ( PFNGLGETFINALCOMBINERINPUTPARAMETERIVNV ) qwglGetProcAddress( "glGetFinalCombinerInputParameterivNV" );
// Validate the functions we need.
if ( !qglCombinerParameterfvNV || !qglCombinerParameterivNV || !qglCombinerParameterfNV || !qglCombinerParameteriNV || !qglCombinerInputNV ||
!qglCombinerOutputNV || !qglFinalCombinerInputNV || !qglGetCombinerInputParameterfvNV || !qglGetCombinerInputParameterivNV ||
!qglGetCombinerOutputParameterfvNV || !qglGetCombinerOutputParameterivNV || !qglGetFinalCombinerInputParameterfvNV || !qglGetFinalCombinerInputParameterivNV )
{
bNVRegisterCombiners = false;
qglCombinerParameterfvNV = NULL;
qglCombinerParameteriNV = NULL;
Com_Printf ("...GL_NV_register_combiners failed\n" );
}
}
else
{
bNVRegisterCombiners = false;
Com_Printf ("...ignoring GL_NV_register_combiners\n" );
}
}
else
{
bNVRegisterCombiners = false;
Com_Printf ("...GL_NV_register_combiners not found\n" );
}
// NOTE: Vertex and Fragment Programs are very dependant on each other - this is actually a
// good thing! So, just check to see which we support (one or the other) and load the shared
// function pointers. ARB rocks!
// Vertex Programs.
bool bARBVertexProgram = false;
if ( strstr( glConfig.extensions_string, "GL_ARB_vertex_program" ) )
{
bARBVertexProgram = true;
}
else
{
bARBVertexProgram = false;
Com_Printf ("...GL_ARB_vertex_program not found\n" );
}
// Fragment Programs.
bool bARBFragmentProgram = false;
if ( strstr( glConfig.extensions_string, "GL_ARB_fragment_program" ) )
{
bARBFragmentProgram = true;
}
else
{
bARBFragmentProgram = false;
Com_Printf ("...GL_ARB_fragment_program not found\n" );
}
// If we support one or the other, load the shared function pointers.
if ( bARBVertexProgram || bARBFragmentProgram )
{
qglProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) qwglGetProcAddress("glProgramStringARB");
qglBindProgramARB = (PFNGLBINDPROGRAMARBPROC) qwglGetProcAddress("glBindProgramARB");
qglDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) qwglGetProcAddress("glDeleteProgramsARB");
qglGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) qwglGetProcAddress("glGenProgramsARB");
qglProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) qwglGetProcAddress("glProgramEnvParameter4dARB");
qglProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) qwglGetProcAddress("glProgramEnvParameter4dvARB");
qglProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) qwglGetProcAddress("glProgramEnvParameter4fARB");
qglProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) qwglGetProcAddress("glProgramEnvParameter4fvARB");
qglProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) qwglGetProcAddress("glProgramLocalParameter4dARB");
qglProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) qwglGetProcAddress("glProgramLocalParameter4dvARB");
qglProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) qwglGetProcAddress("glProgramLocalParameter4fARB");
qglProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) qwglGetProcAddress("glProgramLocalParameter4fvARB");
qglGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) qwglGetProcAddress("glGetProgramEnvParameterdvARB");
qglGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) qwglGetProcAddress("glGetProgramEnvParameterfvARB");
qglGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) qwglGetProcAddress("glGetProgramLocalParameterdvARB");
qglGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) qwglGetProcAddress("glGetProgramLocalParameterfvARB");
qglGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) qwglGetProcAddress("glGetProgramivARB");
qglGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) qwglGetProcAddress("glGetProgramStringARB");
qglIsProgramARB = (PFNGLISPROGRAMARBPROC) qwglGetProcAddress("glIsProgramARB");
// Validate the functions we need.
if ( !qglProgramStringARB || !qglBindProgramARB || !qglDeleteProgramsARB || !qglGenProgramsARB ||
!qglProgramEnvParameter4dARB || !qglProgramEnvParameter4dvARB || !qglProgramEnvParameter4fARB ||
!qglProgramEnvParameter4fvARB || !qglProgramLocalParameter4dARB || !qglProgramLocalParameter4dvARB ||
!qglProgramLocalParameter4fARB || !qglProgramLocalParameter4fvARB || !qglGetProgramEnvParameterdvARB ||
!qglGetProgramEnvParameterfvARB || !qglGetProgramLocalParameterdvARB || !qglGetProgramLocalParameterfvARB ||
!qglGetProgramivARB || !qglGetProgramStringARB || !qglIsProgramARB )
{
bARBVertexProgram = false;
bARBFragmentProgram = false;
qglGenProgramsARB = NULL; //clear ptrs that get checked
qglProgramEnvParameter4fARB = NULL;
Com_Printf ("...ignoring GL_ARB_vertex_program\n" );
Com_Printf ("...ignoring GL_ARB_fragment_program\n" );
}
}
// Figure out which texture rectangle extension to use.
bool bTexRectSupported = false;
if ( Q_strnicmp( glConfig.vendor_string, "ATI Technologies",16 )==0
&& Q_strnicmp( glConfig.version_string, "1.3.3",5 )==0
&& glConfig.version_string[5] < '9' ) //1.3.34 and 1.3.37 and 1.3.38 are broken for sure, 1.3.39 is not
{
g_bTextureRectangleHack = true;
}
if ( strstr( glConfig.extensions_string, "GL_NV_texture_rectangle" )
|| strstr( glConfig.extensions_string, "GL_EXT_texture_rectangle" ) )
{
bTexRectSupported = true;
}
// OK, so not so good to put this here, but no one else uses it!!! -AReis
typedef const char * (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
PFNWGLGETEXTENSIONSSTRINGARBPROC qwglGetExtensionsStringARB;
qwglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) qwglGetProcAddress("wglGetExtensionsStringARB");
const char *wglExtensions = NULL;
bool bHasPixelFormat = false;
bool bHasRenderTexture = false;
// Get the WGL extensions string.
if ( qwglGetExtensionsStringARB )
{
wglExtensions = qwglGetExtensionsStringARB( glw_state.hDC );
}
// This externsion is used to get the wgl extension string.
if ( wglExtensions )
{
// Pixel Format.
if ( strstr( wglExtensions, "WGL_ARB_pixel_format" ) )
{
qwglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) qwglGetProcAddress("wglGetPixelFormatAttribivARB");
qwglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC) qwglGetProcAddress("wglGetPixelFormatAttribfvARB");
qwglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) qwglGetProcAddress("wglChoosePixelFormatARB");
// Validate the functions we need.
if ( !qwglGetPixelFormatAttribivARB || !qwglGetPixelFormatAttribfvARB || !qwglChoosePixelFormatARB )
{
Com_Printf ("...ignoring WGL_ARB_pixel_format\n" );
}
else
{
bHasPixelFormat = true;
}
}
else
{
Com_Printf ("...ignoring WGL_ARB_pixel_format\n" );
}
// Offscreen pixel-buffer.
// NOTE: VV guys can use the equivelant SetRenderTarget() with the correct texture surfaces.
bool bWGLARBPbuffer = false;
if ( strstr( wglExtensions, "WGL_ARB_pbuffer" ) && bHasPixelFormat )
{
bWGLARBPbuffer = true;
qwglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC) qwglGetProcAddress("wglCreatePbufferARB");
qwglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC) qwglGetProcAddress("wglGetPbufferDCARB");
qwglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC) qwglGetProcAddress("wglReleasePbufferDCARB");
qwglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC) qwglGetProcAddress("wglDestroyPbufferARB");
qwglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC) qwglGetProcAddress("wglQueryPbufferARB");
// Validate the functions we need.
if ( !qwglCreatePbufferARB || !qwglGetPbufferDCARB || !qwglReleasePbufferDCARB || !qwglDestroyPbufferARB || !qwglQueryPbufferARB )
{
bWGLARBPbuffer = false;
Com_Printf ("...WGL_ARB_pbuffer failed\n" );
}
}
else
{
bWGLARBPbuffer = false;
Com_Printf ("...WGL_ARB_pbuffer not found\n" );
}
// Render-Texture (requires pbuffer ext (and it's dependancies of course).
if ( strstr( wglExtensions, "WGL_ARB_render_texture" ) && bWGLARBPbuffer )
{
qwglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC) qwglGetProcAddress("wglBindTexImageARB");
qwglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC) qwglGetProcAddress("wglReleaseTexImageARB");
qwglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC) qwglGetProcAddress("wglSetPbufferAttribARB");
// Validate the functions we need.
if ( !qwglCreatePbufferARB || !qwglGetPbufferDCARB || !qwglReleasePbufferDCARB || !qwglDestroyPbufferARB || !qwglQueryPbufferARB )
{
Com_Printf ("...ignoring WGL_ARB_render_texture\n" );
}
else
{
bHasRenderTexture = true;
}
}
else
{
Com_Printf ("...ignoring WGL_ARB_render_texture\n" );
}
}
// Find out how many general combiners they have.
#define GL_MAX_GENERAL_COMBINERS_NV 0x854D
GLint iNumGeneralCombiners = 0;
qglGetIntegerv( GL_MAX_GENERAL_COMBINERS_NV, &iNumGeneralCombiners );
// Only allow dynamic glows/flares if they have the hardware
if ( bTexRectSupported && bARBVertexProgram && bHasRenderTexture && qglActiveTextureARB && glConfig.maxActiveTextures >= 4 &&
( ( bNVRegisterCombiners && iNumGeneralCombiners >= 2 ) || bARBFragmentProgram ) )
{
g_bDynamicGlowSupported = true;
// this would overwrite any achived setting gwg
// Cvar_Set( "r_DynamicGlow", "1" );
}
else
{
g_bDynamicGlowSupported = false;
Cvar_Set( "r_DynamicGlow","0" );
}
}
/*
** GLW_CheckOSVersion
*/
static qboolean GLW_CheckOSVersion( void )
{
#define OSR2_BUILD_NUMBER 1111
OSVERSIONINFO vinfo;
vinfo.dwOSVersionInfoSize = sizeof(vinfo);
glw_state.allowdisplaydepthchange = qfalse;
if ( GetVersionEx( &vinfo) )
{
if ( vinfo.dwMajorVersion > 4 )
{
glw_state.allowdisplaydepthchange = qtrue;
}
else if ( vinfo.dwMajorVersion == 4 )
{
if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
{
glw_state.allowdisplaydepthchange = qtrue;
}
else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
{
if ( LOWORD( vinfo.dwBuildNumber ) >= OSR2_BUILD_NUMBER )
{
glw_state.allowdisplaydepthchange = qtrue;
}
}
}
}
else
{
Com_Printf ("GLW_CheckOSVersion() - GetVersionEx failed\n" );
return qfalse;
}
return qtrue;
}
/*
** GLW_LoadOpenGL
**
** GLimp_win.c internal function that attempts to load and use
** a specific OpenGL DLL.
*/
static qboolean GLW_LoadOpenGL( )
{
char buffer[1024];
qboolean cdsFullscreen;
strlwr( strcpy( buffer, OPENGL_DRIVER_NAME ) );
//
// load the driver and bind our function pointers to it
//
if ( QGL_Init( buffer ) )
{
cdsFullscreen = (qboolean)r_fullscreen->integer;
// create the window and set up the context
if ( !GLW_StartDriverAndSetMode( r_mode->integer, r_colorbits->integer, cdsFullscreen ) )
{
// if we're on a 24/32-bit desktop and we're going fullscreen on an ICD,
// try it again but with a 16-bit desktop
if ( r_colorbits->integer != 16 ||
cdsFullscreen != qtrue ||
r_mode->integer != 3 )
{
if ( !GLW_StartDriverAndSetMode( 3, 16, qtrue ) )
{
goto fail;
}
}
}
#ifdef _CRAZY_ATTRIB_DEBUG
//I can get away with this because we don't actually use push/pop attrib anywhere else.
qglPushAttrib(GL_ACCUM_BUFFER_BIT|GL_COLOR_BUFFER_BIT|GL_CURRENT_BIT|GL_DEPTH_BUFFER_BIT|
GL_ENABLE_BIT|GL_EVAL_BIT|GL_FOG_BIT|GL_HINT_BIT|GL_LIGHTING_BIT|GL_LINE_BIT|GL_LIST_BIT|
GL_PIXEL_MODE_BIT|GL_POINT_BIT|GL_POLYGON_BIT|GL_POLYGON_STIPPLE_BIT|GL_SCISSOR_BIT|
GL_STENCIL_BUFFER_BIT|GL_TEXTURE_BIT|GL_TRANSFORM_BIT|GL_VIEWPORT_BIT);
#endif
return qtrue;
}
fail:
QGL_Shutdown();
return qfalse;
}
/*
** GLimp_EndFrame
*/
void GLimp_EndFrame (void)
{
//
// swapinterval stuff
//
if ( r_swapInterval->modified ) {
r_swapInterval->modified = qfalse;
if ( !glConfig.stereoEnabled ) { // why?
if ( qwglSwapIntervalEXT ) {
qwglSwapIntervalEXT( r_swapInterval->integer );
}
}
}
// don't flip if drawing to front buffer
//if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
{
SwapBuffers( glw_state.hDC );
}
// check logging
QGL_EnableLogging( (qboolean)r_logFile->integer );
}
static void GLW_StartOpenGL( void )
{
//
// load and initialize the specific OpenGL driver
//
if ( !GLW_LoadOpenGL() )
{
Com_Error( ERR_FATAL, "GLW_StartOpenGL() - could not load OpenGL subsystem\n" );
}
}
/*
** GLimp_Init
**
** This is the platform specific OpenGL initialization function. It
** is responsible for loading OpenGL, initializing it, setting
** extensions, creating a window of the appropriate size, doing
** fullscreen manipulations, etc. Its overall responsibility is
** to make sure that a functional OpenGL subsystem is operating
** when it returns to the ref.
*/
void GLimp_Init( void )
{
char buf[MAX_STRING_CHARS];
cvar_t *lastValidRenderer = Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE );
cvar_t *cv;
// Com_Printf ("Initializing OpenGL subsystem\n" );
//
// check OS version to see if we can do fullscreen display changes
//
if ( !GLW_CheckOSVersion() )
{
Com_Error( ERR_FATAL, "GLimp_Init() - incorrect operating system\n" );
}
// save off hInstance and wndproc
cv = Cvar_Get( "win_hinstance", "", 0 );
sscanf( cv->string, "%i", (int *)&g_wv.hInstance );
cv = Cvar_Get( "win_wndproc", "", 0 );
sscanf( cv->string, "%i", (int *)&glw_state.wndproc );
r_allowSoftwareGL = Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
// load appropriate DLL and initialize subsystem
GLW_StartOpenGL();
// get our config strings
glConfig.vendor_string = (const char *) qglGetString (GL_VENDOR);
glConfig.renderer_string = (const char *) qglGetString (GL_RENDERER);
glConfig.version_string = (const char *) qglGetString (GL_VERSION);
glConfig.extensions_string = (const char *) qglGetString (GL_EXTENSIONS);
if (!glConfig.vendor_string || !glConfig.renderer_string || !glConfig.version_string || !glConfig.extensions_string)
{
Com_Error( ERR_FATAL, "GLimp_Init() - Invalid GL Driver\n" );
}
// OpenGL driver constants
qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &glConfig.maxTextureSize );
// stubbed or broken drivers may have reported 0...
if ( glConfig.maxTextureSize <= 0 )
{
glConfig.maxTextureSize = 0;
}
//
// chipset specific configuration
//
Q_strncpyz( buf, glConfig.renderer_string, sizeof(buf) );
strlwr( buf );
//
// NOTE: if changing cvars, do it within this block. This allows them
// to be overridden when testing driver fixes, etc. but only sets
// them to their default state when the hardware is first installed/run.
//
extern qboolean Sys_LowPhysicalMemory();
if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) )
{
if (Sys_LowPhysicalMemory())
{
Cvar_Set("s_khz", "11");// this will get called before S_Init
}
//reset to defaults
Cvar_Set( "r_picmip", "1" );
// Savage3D and Savage4 should always have trilinear enabled
if ( strstr( buf, "savage3d" ) || strstr( buf, "s3 savage4" ) || strstr( buf, "geforce" ) || strstr( buf, "quadro" ) )
{
Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
}
else
{
Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" );
}
if ( strstr( buf, "kyro" ) )
{
Cvar_Set( "r_ext_texture_filter_anisotropic", "0"); //KYROs have it avail, but suck at it!
Cvar_Set( "r_ext_preferred_tc_method", "1"); //(Use DXT1 instead of DXT5 - same quality but much better performance on KYRO)
}
if ( strstr( buf, "geforce2" ) )
{
Cvar_Set( "cg_renderToTextureFX", "0"); // slow to zero bug fix
}
if ( strstr( buf, "radeon 9000" ) )
{
Cvar_Set( "cg_renderToTextureFX", "0"); // white texture bug
}
GLW_InitExtensions(); //get the values for test below
//this must be a really sucky card!
if ( (glConfig.textureCompression == TC_NONE) || (glConfig.maxActiveTextures < 2) || (glConfig.maxTextureSize <= 512) )
{
Cvar_Set( "r_picmip", "2");
Cvar_Set( "r_colorbits", "16");
Cvar_Set( "r_texturebits", "16");
Cvar_Set( "r_mode", "3"); //force 640
Cmd_ExecuteString ("exec low.cfg\n"); //get the rest which can be pulled in after init
}
}
Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
GLW_InitExtensions();
WG_CheckHardwareGamma();
}
/*
** GLimp_Shutdown
**
** This routine does all OS specific shutdown procedures for the OpenGL
** subsystem.
*/
void GLimp_Shutdown( void )
{
// const char *strings[] = { "soft", "hard" };
// const char *success[] = { "failed", "success" };
int retVal;
// FIXME: Brian, we need better fallbacks from partially initialized failures
if ( !qwglMakeCurrent ) {
return;
}
Com_Printf ("Shutting down OpenGL subsystem\n" );
// restore gamma. We do this first because 3Dfx's extension needs a valid OGL subsystem
WG_RestoreGamma();
// set current context to NULL
if ( qwglMakeCurrent )
{
retVal = qwglMakeCurrent( NULL, NULL ) != 0;
// Com_Printf ("...wglMakeCurrent( NULL, NULL ): %s\n", success[retVal] );
}
// delete HGLRC
if ( glw_state.hGLRC )
{
retVal = qwglDeleteContext( glw_state.hGLRC ) != 0;
// Com_Printf ("...deleting GL context: %s\n", success[retVal] );
glw_state.hGLRC = NULL;
}
// release DC
if ( glw_state.hDC )
{
retVal = ReleaseDC( g_wv.hWnd, glw_state.hDC ) != 0;
// Com_Printf ("...releasing DC: %s\n", success[retVal] );
glw_state.hDC = NULL;
}
// destroy window
if ( g_wv.hWnd )
{
// Com_Printf ("...destroying window\n" );
ShowWindow( g_wv.hWnd, SW_HIDE );
DestroyWindow( g_wv.hWnd );
g_wv.hWnd = NULL;
glw_state.pixelFormatSet = qfalse;
}
// close the r_logFile
if ( glw_state.log_fp )
{
fclose( glw_state.log_fp );
glw_state.log_fp = 0;
}
// reset display settings
if ( glw_state.cdsFullscreen )
{
// Com_Printf ("...resetting display\n" );
ChangeDisplaySettings( 0, 0 );
glw_state.cdsFullscreen = qfalse;
}
// shutdown QGL subsystem
QGL_Shutdown();
memset( &glConfig, 0, sizeof( glConfig ) );
memset( &glState, 0, sizeof( glState ) );
}
/*
** GLimp_LogComment
*/
void GLimp_LogComment( char *comment )
{
if ( glw_state.log_fp ) {
fprintf( glw_state.log_fp, "%s", comment );
}
}
/*
===========================================================
SMP acceleration
===========================================================
*/
HANDLE renderCommandsEvent;
HANDLE renderCompletedEvent;
HANDLE renderActiveEvent;
void (*glimpRenderThread)( void );
void GLimp_RenderThreadWrapper( void ) {
glimpRenderThread();
// unbind the context before we die
qwglMakeCurrent( glw_state.hDC, NULL );
}
/*
=======================
GLimp_SpawnRenderThread
=======================
*/
HANDLE renderThreadHandle;
int renderThreadId;
qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
renderCommandsEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
renderCompletedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
renderActiveEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
glimpRenderThread = function;
renderThreadHandle = CreateThread(
NULL, // LPSECURITY_ATTRIBUTES lpsa,
0, // DWORD cbStack,
(LPTHREAD_START_ROUTINE)GLimp_RenderThreadWrapper, // LPTHREAD_START_ROUTINE lpStartAddr,
0, // LPVOID lpvThreadParm,
0, // DWORD fdwCreate,
(unsigned long *)&renderThreadId );
if ( !renderThreadHandle ) {
return qfalse;
}
return qtrue;
}
static void *smpData;
static int wglErrors;
void *GLimp_RendererSleep( void ) {
void *data;
if ( !qwglMakeCurrent( glw_state.hDC, NULL ) ) {
wglErrors++;
}
ResetEvent( renderActiveEvent );
// after this, the front end can exit GLimp_FrontEndSleep
SetEvent( renderCompletedEvent );
WaitForSingleObject( renderCommandsEvent, INFINITE );
if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) ) {
wglErrors++;
}
ResetEvent( renderCompletedEvent );
ResetEvent( renderCommandsEvent );
data = smpData;
// after this, the main thread can exit GLimp_WakeRenderer
SetEvent( renderActiveEvent );
return data;
}
void GLimp_FrontEndSleep( void ) {
WaitForSingleObject( renderCompletedEvent, INFINITE );
if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) ) {
wglErrors++;
}
}
void GLimp_WakeRenderer( void *data ) {
smpData = data;
if ( !qwglMakeCurrent( glw_state.hDC, NULL ) ) {
wglErrors++;
}
// after this, the renderer can continue through GLimp_RendererSleep
SetEvent( renderCommandsEvent );
WaitForSingleObject( renderActiveEvent, INFINITE );
}
// Allocate and create a new PBuffer.
bool CPBUFFER::Create( int iWidth, int iHeight, int iColorBits, int iDepthBits, int iStencilBits )
{
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iColorBits = iColorBits;
m_iDepthBits = iDepthBits;
m_iStencilBits = iStencilBits;
extern glwstate_t glw_state;
m_hOldRC = glw_state.hGLRC; //qwglGetCurrentContext();
// Get the current device context.
m_hOldDC = glw_state.hDC; //qwglGetCurrentDC();
if( !m_hOldDC )
{
return false;
}
#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070
// These are standard settings. I suppose if one wanted more control you could pass an attrib list in (but why?).
const int iAttribList[] =
{
WGL_SUPPORT_OPENGL_ARB, true, // P-buffer will be used with OpenGL.
WGL_DRAW_TO_PBUFFER_ARB, true, // Enable render to p-buffer.
WGL_BIND_TO_TEXTURE_RGBA_ARB, true, // P-buffer will be used as a texture.
WGL_RED_BITS_ARB, 8, // At least 8 bits for RED channel.
WGL_GREEN_BITS_ARB, 8, // At least 8 bits for GREEN channel.
WGL_BLUE_BITS_ARB, 8, // At least 8 bits for BLUE channel.
WGL_ALPHA_BITS_ARB, 8, // At least 8 bits for ALPHA channel.
WGL_DEPTH_BITS_ARB, 16, // At least 16 bits for depth buffer.
WGL_DOUBLE_BUFFER_ARB, false, // We don't require double buffering
0 // Zero terminates the list.
};
#define WGL_TEXTURE_RECTANGLE_NV 0x20A2
//const float fAttribList[] = { 0 };
const int iFlags[] =
{
WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB, // Our p-buffer will have a texture format of RGBA.
WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_RECTANGLE_NV, // Texture target will be GL_TEXTURE_RECTANGLE.
0 // Zero terminates the list.
};
// Choose pixel format.
unsigned int numFormats;
GLint pixelFormat;
if( !qwglChoosePixelFormatARB( m_hOldDC, iAttribList, NULL, 1, &pixelFormat, &numFormats ) )
{
return false;
}
// Create the pbuffer.
m_hBuffer = qwglCreatePbufferARB( m_hOldDC, pixelFormat, m_iWidth, m_iHeight, iFlags );
if( !m_hBuffer )
{
return false;
}
// Get the pbuffer's device context.
m_hDC = qwglGetPbufferDCARB( m_hBuffer );
if( !m_hDC )
{
return false;
}
// Create a rendering context for the pbuffer.
m_hRC = qwglCreateContext( m_hDC );
if( !m_hRC )
{
return false;
}
// Share Display Lists and Texture Objects between contexts (NOTE: Could
// also just use parent app RC).
qwglShareLists( m_hOldRC, m_hRC );
// Set and output the actual pBuffer dimensions.
qwglQueryPbufferARB( m_hBuffer, WGL_PBUFFER_WIDTH_ARB, &m_iWidth );
qwglQueryPbufferARB( m_hBuffer, WGL_PBUFFER_HEIGHT_ARB, &m_iHeight );
// Create the PBuffer Texture.
extern int giTextureBindNum;
m_uiPBufferTexture = 1024 + giTextureBindNum++;
qglDisable( GL_TEXTURE_2D );
qglEnable( GL_TEXTURE_RECTANGLE_EXT );
/*int *pTexture = new int [m_iWidth * m_iHeight];
memset( pTexture, 0, m_iWidth * m_iHeight * sizeof( int ) ); */
qglBindTexture( GL_TEXTURE_RECTANGLE_EXT, m_uiPBufferTexture );
//qglTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB, m_iWidth, m_iHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pTexture );
qglTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB, m_iWidth, m_iHeight, 0, GL_RGB, GL_FLOAT, 0 );
qglTexParameteri( GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
qglTexParameteri( GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
qglTexParameteri( GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP );
qglTexParameteri( GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP );
qglDisable( GL_TEXTURE_RECTANGLE_EXT );
qglEnable( GL_TEXTURE_2D );
//delete [] pTexture;
return true;
}
// Destroy and deallocate a PBuffer.
void CPBUFFER::Destroy()
{
// Release the pbuffer texture.
qglDeleteTextures( 1, &m_uiPBufferTexture );
// Release RC.
if( m_hRC )
{
qwglDeleteContext( m_hRC );
m_hRC = NULL;
}
// Release DC.
if( m_hDC )
{
qwglReleasePbufferDCARB( m_hBuffer, m_hDC);
m_hDC = NULL;
}
// Release PBuffer.
qwglDestroyPbufferARB( m_hBuffer );
}
// Make this PBuffer the current render device.
bool CPBUFFER::Begin()
{
if( !qwglMakeCurrent( m_hDC, m_hRC ) )
{
return false;
}
qwglCopyContext( m_hOldRC, m_hRC, GL_ALL_ATTRIB_BITS );
return true;
}
// Restore the previous render device.
bool CPBUFFER::End()
{
if( !qwglMakeCurrent( m_hOldDC, m_hOldRC ) )
{
return false;
}
return true;
}