488 lines
13 KiB
C
488 lines
13 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// r_opengl.c - openg32.dll handler
|
|
//=======================================================================
|
|
|
|
#include "r_local.h"
|
|
|
|
glwstate_t glw_state;
|
|
|
|
#define num_vidmodes ((int)(sizeof(vidmode) / sizeof(vidmode[0])) - 1)
|
|
#define WINDOW_STYLE (WS_OVERLAPPED|WS_BORDER|WS_CAPTION|WS_VISIBLE)
|
|
|
|
typedef enum
|
|
{
|
|
rserr_ok,
|
|
rserr_invalid_fullscreen,
|
|
rserr_invalid_mode,
|
|
rserr_unknown
|
|
} rserr_t;
|
|
|
|
typedef struct vidmode_s
|
|
{
|
|
const char *desc;
|
|
int width;
|
|
int height;
|
|
float pixelheight;
|
|
} vidmode_t;
|
|
|
|
vidmode_t vidmode[] =
|
|
{
|
|
{"Mode 0: 4x3", 640, 480, 1 },
|
|
{"Mode 1: 4x3", 800, 600, 1 },
|
|
{"Mode 2: 4x3", 1024, 768, 1 },
|
|
{"Mode 3: 4x3", 1152, 864, 1 },
|
|
{"Mode 4: 4x3", 1280, 960, 1 },
|
|
{"Mode 5: 4x3", 1400, 1050, 1 },
|
|
{"Mode 6: 4x3", 1600, 1200, 1 },
|
|
{"Mode 7: 4x3", 1920, 1440, 1 },
|
|
{"Mode 8: 4x3", 2048, 1536, 1 },
|
|
{"Mode 9: 14x9", 840, 540, 1 },
|
|
{"Mode 10: 14x9", 1680, 1080, 1 },
|
|
{"Mode 11: 16x9", 640, 360, 1 },
|
|
{"Mode 12: 16x9", 683, 384, 1 },
|
|
{"Mode 13: 16x9", 960, 540, 1 },
|
|
{"Mode 14: 16x9", 1280, 720, 1 },
|
|
{"Mode 15: 16x9", 1366, 768, 1 },
|
|
{"Mode 16: 16x9", 1920, 1080, 1 },
|
|
{"Mode 17: 16x9", 2560, 1440, 1 },
|
|
{"Mode 18: NTSC", 360, 240, 1.125f },
|
|
{"Mode 19: NTSC", 720, 480, 1.125f },
|
|
{"Mode 20: PAL ", 360, 283, 0.9545f },
|
|
{"Mode 21: PAL ", 720, 566, 0.9545f },
|
|
{NULL, 0, 0, 0 },
|
|
};
|
|
|
|
static dllfunc_t wgl_funcs[] =
|
|
{
|
|
{"wglChoosePixelFormat", (void **) &pwglChoosePixelFormat},
|
|
{"wglDescribePixelFormat", (void **) &pwglDescribePixelFormat},
|
|
{"wglGetPixelFormat", (void **) &pwglGetPixelFormat},
|
|
{"wglSetPixelFormat", (void **) &pwglSetPixelFormat},
|
|
{"wglSwapBuffers", (void **) &pwglSwapBuffers},
|
|
{"wglCreateContext", (void **) &pwglCreateContext},
|
|
{"wglDeleteContext", (void **) &pwglDeleteContext},
|
|
{"wglGetProcAddress", (void **) &pwglGetProcAddress},
|
|
{"wglMakeCurrent", (void **) &pwglMakeCurrent},
|
|
{"wglGetCurrentContext", (void **) &pwglGetCurrentContext},
|
|
{"wglGetCurrentDC", (void **) &pwglGetCurrentDC},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
dll_info_t opengl_dll = { "opengl32.dll", wgl_funcs, NULL, NULL, NULL, true, 0 };
|
|
|
|
bool R_DeleteContext( void )
|
|
{
|
|
if( glw_state.hGLRC )
|
|
{
|
|
pwglDeleteContext( glw_state.hGLRC );
|
|
glw_state.hGLRC = NULL;
|
|
}
|
|
if( glw_state.hDC )
|
|
{
|
|
ReleaseDC( glw_state.hWnd, glw_state.hDC );
|
|
glw_state.hDC = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool R_SetPixelformat( void )
|
|
{
|
|
long flags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_GENERIC_ACCELERATED|PFD_DOUBLEBUFFER;
|
|
int pixelformat;
|
|
size_t gamma_size;
|
|
byte *savedGamma;
|
|
|
|
PIXELFORMATDESCRIPTOR pfd =
|
|
{
|
|
sizeof(PIXELFORMATDESCRIPTOR),// size of this pfd
|
|
1, // version number
|
|
flags, // support window|OpenGL|generic accel|double buffer
|
|
PFD_TYPE_RGBA, // RGBA type
|
|
24, // 24-bit color depth
|
|
8, 0, 8, 0, 8, 0, // color bits set to 32
|
|
8, 0, // alpha bit 8
|
|
0, // no accumulation buffer
|
|
0, 0, 0, 0, // accum bits ignored
|
|
32, // 32-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
|
|
};
|
|
|
|
Sys_LoadLibrary( &opengl_dll ); // load opengl32.dll
|
|
if( !opengl_dll.link ) return false;
|
|
|
|
if(( glw_state.hDC = GetDC( glw_state.hWnd )) == NULL )
|
|
return false;
|
|
|
|
glw_state.minidriver = false;
|
|
if( glw_state.minidriver )
|
|
{
|
|
if(!(pixelformat = pwglChoosePixelFormat( glw_state.hDC, &pfd)))
|
|
return false;
|
|
if(!(pwglSetPixelFormat( glw_state.hDC, pixelformat, &pfd)))
|
|
return false;
|
|
pwglDescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
|
|
}
|
|
else
|
|
{
|
|
if(!( pixelformat = ChoosePixelFormat( glw_state.hDC, &pfd )))
|
|
return false;
|
|
if(!(SetPixelFormat( glw_state.hDC, pixelformat, &pfd )))
|
|
return false;
|
|
DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
|
|
}
|
|
|
|
if(!(glw_state.hGLRC = pwglCreateContext( glw_state.hDC )))
|
|
return R_DeleteContext();
|
|
if(!(pwglMakeCurrent( glw_state.hDC, glw_state.hGLRC )))
|
|
return R_DeleteContext();
|
|
|
|
// print out PFD specifics
|
|
MsgDev(D_NOTE, "GL PFD: color(%d-bits) Z(%d-bit)\n", ( int )pfd.cColorBits, ( int )pfd.cDepthBits );
|
|
|
|
// init gamma ramp
|
|
ZeroMemory( gl_state.stateRamp, sizeof(gl_state.stateRamp));
|
|
gl_config.deviceSupportsGamma = GetDeviceGammaRamp( glw_state.hDC, gl_state.stateRamp );
|
|
savedGamma = FS_LoadFile( "config/gamma.rc", &gamma_size );
|
|
if( !savedGamma || gamma_size != sizeof( gl_state.stateRamp ))
|
|
{
|
|
// saved gamma not found or correupted file
|
|
FS_WriteFile( "config/gamma.rc", gl_state.stateRamp, sizeof(gl_state.stateRamp));
|
|
Msg( "gamma.rc initialized\n" );
|
|
}
|
|
else
|
|
{
|
|
GL_BuildGammaTable();
|
|
|
|
// validate base gamma
|
|
if(!memcmp( savedGamma, gl_state.stateRamp, sizeof(gl_state.stateRamp)))
|
|
{
|
|
// all ok, previous gamma is valid
|
|
MsgDev( D_NOTE, "R_SetPixelformat: validate screen gamma - ok\n" );
|
|
}
|
|
else if(!memcmp( gl_state.gammaRamp, gl_state.stateRamp, sizeof(gl_state.stateRamp)))
|
|
{
|
|
// screen gamma is equal to render gamma (probably previous instance crashed)
|
|
// run additional check to make sure it
|
|
if(memcmp( savedGamma, gl_state.stateRamp, sizeof(gl_state.stateRamp)))
|
|
{
|
|
// yes, current gamma it's totally wrong, restore it from gamma.rc
|
|
MsgDev( D_NOTE, "R_SetPixelformat: restore original gamma after crash\n" );
|
|
Mem_Copy( gl_state.stateRamp, savedGamma, sizeof( gl_state.gammaRamp ));
|
|
}
|
|
else
|
|
{
|
|
// oops, savedGamma == gl_state.stateRamp == gl_state.gammaRamp
|
|
// probably vid_gamma set as default
|
|
MsgDev( D_NOTE, "R_SetPixelformat: validate screen gamma - disabled\n" );
|
|
}
|
|
}
|
|
else if(!memcmp( gl_state.gammaRamp, savedGamma, sizeof(gl_state.stateRamp)))
|
|
{
|
|
// saved gamma is equal render gamma, probably gamma.rc writed after crash
|
|
// run additional check to make sure it
|
|
if(memcmp( savedGamma, gl_state.stateRamp, sizeof(gl_state.stateRamp)))
|
|
{
|
|
// yes, saved gamma it's totally wrong, get origianl gamma from screen
|
|
MsgDev( D_NOTE, "R_SetPixelformat: merge gamma.rc after crash\n" );
|
|
FS_WriteFile( "config/gamma.rc", gl_state.stateRamp, sizeof(gl_state.stateRamp));
|
|
}
|
|
else
|
|
{
|
|
// oops, savedGamma == gl_state.stateRamp == gl_state.gammaRamp
|
|
// probably vid_gamma set as default
|
|
MsgDev( D_NOTE, "R_SetPixelformat: validate screen gamma - disabled\n" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// gamma.rc corrupted by stupid user, restore from screen
|
|
// probably never reached, but just in case
|
|
FS_WriteFile( "config/gamma.rc", gl_state.stateRamp, sizeof(gl_state.stateRamp));
|
|
}
|
|
}
|
|
vid_gamma->modified = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void R_Free_OpenGL( void )
|
|
{
|
|
SetDeviceGammaRamp( glw_state.hDC, gl_state.stateRamp );
|
|
|
|
if( pwglMakeCurrent ) pwglMakeCurrent( NULL, NULL );
|
|
if( glw_state.hGLRC )
|
|
{
|
|
if( pwglDeleteContext ) pwglDeleteContext( glw_state.hGLRC );
|
|
glw_state.hGLRC = NULL;
|
|
}
|
|
if( glw_state.hDC )
|
|
{
|
|
ReleaseDC( glw_state.hWnd, glw_state.hDC );
|
|
glw_state.hDC = NULL;
|
|
}
|
|
if( glw_state.hWnd )
|
|
{
|
|
DestroyWindow ( glw_state.hWnd );
|
|
glw_state.hWnd = NULL;
|
|
}
|
|
UnregisterClass( "Xash Window", glw_state.hInst );
|
|
|
|
if( gl_config.fullscreen )
|
|
{
|
|
ChangeDisplaySettings( 0, 0 );
|
|
gl_config.fullscreen = false;
|
|
}
|
|
Sys_FreeLibrary( &opengl_dll );
|
|
|
|
// now all extensions are disabled
|
|
memset( gl_config.extension, 0, sizeof(gl_config.extension[0]) * R_EXTCOUNT);
|
|
}
|
|
|
|
void R_SaveVideoMode( int vid_mode )
|
|
{
|
|
int i = bound(0, vid_mode, num_vidmodes); // check range
|
|
|
|
Cvar_FullSet("width", va("%i", vidmode[i].width ), CVAR_READ_ONLY );
|
|
Cvar_FullSet("height", va("%i", vidmode[i].height ), CVAR_READ_ONLY );
|
|
Cvar_SetValue("r_mode", i ); // merge if out of bounds
|
|
MsgDev(D_NOTE, "Set: %s [%dx%d]\n", vidmode[i].desc, vidmode[i].width, vidmode[i].height );
|
|
}
|
|
|
|
bool R_CreateWindow( int width, int height, bool fullscreen )
|
|
{
|
|
WNDCLASS wc;
|
|
RECT rect;
|
|
cvar_t *r_xpos, *r_ypos;
|
|
int stylebits = WINDOW_STYLE;
|
|
int x = 0, y = 0, w, h;
|
|
int exstyle = 0;
|
|
static char wndname[128];
|
|
|
|
com.strcpy( wndname, GI->title );
|
|
|
|
// register the frame class
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = (WNDPROC)glw_state.wndproc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = glw_state.hInst;
|
|
wc.hIcon = LoadIcon( glw_state.hInst, MAKEINTRESOURCE( 101 ));
|
|
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
|
|
wc.hbrBackground = (void *)COLOR_3DSHADOW;
|
|
wc.lpszClassName = "Xash Window";
|
|
wc.lpszMenuName = 0;
|
|
|
|
if(!RegisterClass( &wc ))
|
|
{
|
|
MsgDev( D_ERROR, "R_CreateWindow: couldn't register window class %s\n" "Xash Window" );
|
|
return false;
|
|
}
|
|
|
|
if( fullscreen )
|
|
{
|
|
exstyle = WS_EX_TOPMOST;
|
|
stylebits = WS_POPUP | WS_VISIBLE;
|
|
}
|
|
|
|
rect.left = 0;
|
|
rect.top = 0;
|
|
rect.right = width;
|
|
rect.bottom = height;
|
|
|
|
AdjustWindowRect( &rect, stylebits, FALSE );
|
|
w = rect.right - rect.left;
|
|
h = rect.bottom - rect.top;
|
|
|
|
if( !fullscreen )
|
|
{
|
|
r_xpos = Cvar_Get( "r_xpos", "3", CVAR_ARCHIVE, "window position by horizontal" );
|
|
r_ypos = Cvar_Get( "r_ypos", "22", CVAR_ARCHIVE, "window position by vertical" );
|
|
x = r_xpos->integer;
|
|
y = r_ypos->integer;
|
|
}
|
|
|
|
glw_state.hWnd = CreateWindowEx( exstyle, "Xash Window", wndname, stylebits, x, y, w, h, NULL, NULL, glw_state.hInst, NULL );
|
|
|
|
if( !glw_state.hWnd )
|
|
{
|
|
MsgDev( D_ERROR, "R_CreateWindow: couldn't create window %s\n", wndname );
|
|
return false;
|
|
}
|
|
|
|
ShowWindow( glw_state.hWnd, SW_SHOW );
|
|
UpdateWindow( glw_state.hWnd );
|
|
|
|
// init all the gl stuff for the window
|
|
if(!R_SetPixelformat())
|
|
{
|
|
MsgDev( D_ERROR, "OpenGL driver not installed\n" );
|
|
return false;
|
|
}
|
|
|
|
SetForegroundWindow( glw_state.hWnd );
|
|
SetFocus( glw_state.hWnd );
|
|
return true;
|
|
}
|
|
|
|
rserr_t R_ChangeDisplaySettings( int vid_mode, bool fullscreen )
|
|
{
|
|
int width, height;
|
|
|
|
R_SaveVideoMode( vid_mode );
|
|
|
|
width = r_width->integer;
|
|
height = r_height->integer;
|
|
|
|
// destroy the existing window
|
|
if( glw_state.hWnd ) R_Free_OpenGL();
|
|
|
|
// do a CDS if needed
|
|
if( fullscreen )
|
|
{
|
|
DEVMODE dm;
|
|
|
|
ZeroMemory( &dm, sizeof( dm ));
|
|
dm.dmSize = sizeof( dm );
|
|
dm.dmPelsWidth = width;
|
|
dm.dmPelsHeight = height;
|
|
dm.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT;
|
|
|
|
if(ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
gl_config.fullscreen = true;
|
|
if(!R_CreateWindow( width, height, true ))
|
|
return rserr_invalid_mode;
|
|
return rserr_ok;
|
|
}
|
|
else
|
|
{
|
|
dm.dmPelsWidth = width * 2;
|
|
dm.dmPelsHeight = height;
|
|
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
if( r_bitdepth->integer != 0 )
|
|
{
|
|
dm.dmBitsPerPel = r_bitdepth->integer;
|
|
dm.dmFields |= DM_BITSPERPEL;
|
|
}
|
|
|
|
// our first CDS failed, so maybe we're running on some weird dual monitor system
|
|
if( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
|
|
{
|
|
ChangeDisplaySettings( 0, 0 );
|
|
gl_config.fullscreen = false;
|
|
if(!R_CreateWindow( width, height, false ))
|
|
return rserr_invalid_mode;
|
|
return rserr_invalid_fullscreen;
|
|
}
|
|
else
|
|
{
|
|
if(!R_CreateWindow( width, height, true ))
|
|
return rserr_invalid_mode;
|
|
gl_config.fullscreen = true;
|
|
return rserr_ok;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ChangeDisplaySettings( 0, 0 );
|
|
gl_config.fullscreen = false;
|
|
if(!R_CreateWindow( width, height, false ))
|
|
return rserr_invalid_mode;
|
|
}
|
|
return rserr_ok;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_Init_OpenGL
|
|
==================
|
|
*/
|
|
bool R_Init_OpenGL( void )
|
|
{
|
|
rserr_t err;
|
|
bool fullscreen;
|
|
|
|
fullscreen = r_fullscreen->integer;
|
|
r_fullscreen->modified = false;
|
|
r_mode->modified = false;
|
|
|
|
if(( err = R_ChangeDisplaySettings( r_mode->integer, fullscreen )) == rserr_ok )
|
|
{
|
|
gl_config.prev_mode = r_mode->integer;
|
|
}
|
|
else
|
|
{
|
|
if( err == rserr_invalid_fullscreen )
|
|
{
|
|
Cvar_SetValue( "fullscreen", 0 );
|
|
r_fullscreen->modified = false;
|
|
MsgDev( D_ERROR, "R_SetMode: fullscreen unavailable in this mode\n" );
|
|
if(( err = R_ChangeDisplaySettings( r_mode->integer, false )) == rserr_ok )
|
|
return true;
|
|
}
|
|
else if( err == rserr_invalid_mode )
|
|
{
|
|
Cvar_SetValue( "r_mode", gl_config.prev_mode );
|
|
r_mode->modified = false;
|
|
MsgDev( D_ERROR, "R_SetMode: invalid mode\n" );
|
|
}
|
|
// try setting it back to something safe
|
|
if(( err = R_ChangeDisplaySettings( gl_config.prev_mode, false )) != rserr_ok )
|
|
{
|
|
MsgDev( D_ERROR, "R_SetMode: could not revert to safe mode\n" );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// setup limits
|
|
gl_config.max_entities = (int)Cvar_VariableValue( "prvm_maxedicts" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CheckForErrors
|
|
=================
|
|
*/
|
|
void R_CheckForErrors( void )
|
|
{
|
|
int err;
|
|
char *str;
|
|
|
|
if( !r_check_errors->integer )
|
|
return;
|
|
if((err = pglGetError()) == GL_NO_ERROR)
|
|
return;
|
|
|
|
switch( err )
|
|
{
|
|
case GL_STACK_OVERFLOW:
|
|
str = "GL_STACK_OVERFLOW";
|
|
break;
|
|
case GL_STACK_UNDERFLOW:
|
|
str = "GL_STACK_UNDERFLOW";
|
|
break;
|
|
case GL_INVALID_ENUM:
|
|
str = "GL_INVALID_ENUM";
|
|
break;
|
|
case GL_INVALID_VALUE:
|
|
str = "GL_INVALID_VALUE";
|
|
break;
|
|
case GL_INVALID_OPERATION:
|
|
str = "GL_INVALID_OPERATION";
|
|
break;
|
|
case GL_OUT_OF_MEMORY:
|
|
str = "GL_OUT_OF_MEMORY";
|
|
break;
|
|
default:
|
|
str = "UNKNOWN ERROR";
|
|
break;
|
|
}
|
|
Host_Error( "R_CheckForErrors: %s\n", str );
|
|
} |