// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright (C) 1998-2000 by DooM Legacy Team. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //----------------------------------------------------------------------------- /// \file /// \brief Windows specific part of the OpenGL API for Doom Legacy /// /// TODO: /// - check if windowed mode works /// - support different pixel formats #if defined (_WIN32) //#define WIN32_LEAN_AND_MEAN #define RPC_NO_WINDOWS_H #include #include #undef GETTEXT #include "r_opengl.h" // ************************************************************************** // GLOBALS // ************************************************************************** #ifdef DEBUG_TO_FILE static unsigned long nb_frames = 0; static clock_t my_clock; FILE *logstream; #endif static HDC hDC = NULL; // the window's device context static HGLRC hGLRC = NULL; // the OpenGL rendering context static HWND hWnd = NULL; static BOOL WasFullScreen = FALSE; static void UnSetRes(void); #ifdef USE_WGL_SWAP PFNWGLEXTSWAPCONTROLPROC wglSwapIntervalEXT = NULL; #endif PFNglClear pglClear; PFNglGetIntegerv pglGetIntegerv; PFNglGetString pglGetString; #define MAX_VIDEO_MODES 32 static vmode_t video_modes[MAX_VIDEO_MODES]; INT32 oglflags = 0; // ************************************************************************** // FUNCTIONS // ************************************************************************** // -----------------+ // APIENTRY DllMain : DLL Entry Point, // : open/close debug log // Returns : // -----------------+ BOOL WINAPI DllMain(HINSTANCE hinstDLL, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID lpvReserved) // reserved { // Perform actions based on the reason for calling. UNREFERENCED_PARAMETER(lpvReserved); switch (fdwReason) { case DLL_PROCESS_ATTACH: // Initialize once for each new process. // Return FALSE to fail DLL load. #ifdef DEBUG_TO_FILE logstream = fopen("ogllog.txt", "wt"); if (logstream == NULL) return FALSE; #endif DisableThreadLibraryCalls(hinstDLL); break; case DLL_THREAD_ATTACH: // Do thread-specific initialization. break; case DLL_THREAD_DETACH: // Do thread-specific cleanup. break; case DLL_PROCESS_DETACH: // Perform any necessary cleanup. #ifdef DEBUG_TO_FILE if (logstream) { fclose(logstream); logstream = NULL; } #endif break; } return TRUE; // Successful DLL_PROCESS_ATTACH. } #ifdef STATIC_OPENGL #define pwglGetProcAddress wglGetProcAddress; #define pwglCreateContext wglCreateContext; #define pwglDeleteContext wglDeleteContext; #define pwglMakeCurrent wglMakeCurrent; #else static HMODULE OGL32, GLU32; typedef void *(WINAPI *PFNwglGetProcAddress) (const char *); static PFNwglGetProcAddress pwglGetProcAddress; typedef HGLRC (WINAPI *PFNwglCreateContext) (HDC hdc); static PFNwglCreateContext pwglCreateContext; typedef BOOL (WINAPI *PFNwglDeleteContext) (HGLRC hglrc); static PFNwglDeleteContext pwglDeleteContext; typedef BOOL (WINAPI *PFNwglMakeCurrent) (HDC hdc, HGLRC hglrc); static PFNwglMakeCurrent pwglMakeCurrent; #endif #ifndef STATIC_OPENGL void *GetGLFunc(const char *proc) { void *func = NULL; if (strncmp(proc, "glu", 3) == 0) { if (GLU32) func = GetProcAddress(GLU32, proc); else return NULL; } if (pwglGetProcAddress) func = pwglGetProcAddress(proc); if (!func) func = GetProcAddress(OGL32, proc); return func; } #endif boolean LoadGL(void) { #ifndef STATIC_OPENGL OGL32 = LoadLibrary("OPENGL32.DLL"); if (!OGL32) return 0; GLU32 = LoadLibrary("GLU32.DLL"); pwglGetProcAddress = GetGLFunc("wglGetProcAddress"); pwglCreateContext = GetGLFunc("wglCreateContext"); pwglDeleteContext = GetGLFunc("wglDeleteContext"); pwglMakeCurrent = GetGLFunc("wglMakeCurrent"); #endif return SetupGLfunc(); } // -----------------+ // SetupPixelFormat : Set the device context's pixel format // Note : Because we currently use only the desktop's BPP format, all the // : video modes in Doom Legacy OpenGL are of the same BPP, thus the // : PixelFormat is set only once. // : Setting the pixel format more than once on the same window // : doesn't work. (ultimately for different pixel formats, we // : should close the window, and re-create it) // -----------------+ int SetupPixelFormat(INT32 WantColorBits, INT32 WantStencilBits, INT32 WantDepthBits) { static DWORD iLastPFD = 0; INT32 nPixelFormat; PIXELFORMATDESCRIPTOR pfd = { sizeof (PIXELFORMATDESCRIPTOR), // size 1, // version PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, // color type 32 /*WantColorBits*/, // cColorBits : prefered color depth 0, 0, // cRedBits, cRedShift 0, 0, // cGreenBits, cGreenShift 0, 0, // cBlueBits, cBlueShift 0, 0, // cAlphaBits, cAlphaShift 0, // cAccumBits 0, 0, 0, 0, // cAccum Red/Green/Blue/Alpha Bits 0, // cDepthBits (0,16,24,32) 0, // cStencilBits 0, // cAuxBuffers PFD_MAIN_PLANE, // iLayerType 0, // reserved, must be zero 0, 0, 0, // dwLayerMask, dwVisibleMask, dwDamageMask }; DWORD iPFD = (WantColorBits<<16) | (WantStencilBits<<8) | WantDepthBits; pfd.cDepthBits = (BYTE)WantDepthBits; pfd.cStencilBits = (BYTE)WantStencilBits; if (iLastPFD) { DBG_Printf("WARNING : SetPixelFormat() called twise not supported by all drivers !\n"); } // set the pixel format only if different than the current if (iPFD == iLastPFD) return 2; else iLastPFD = iPFD; DBG_Printf("SetupPixelFormat() - %d ColorBits - %d StencilBits - %d DepthBits\n", WantColorBits, WantStencilBits, WantDepthBits); nPixelFormat = ChoosePixelFormat(hDC, &pfd); if (nPixelFormat == 0) DBG_Printf("ChoosePixelFormat() FAILED\n"); if (SetPixelFormat(hDC, nPixelFormat, &pfd) == 0) { DBG_Printf("SetPixelFormat() FAILED\n"); return 0; } return 1; } // -----------------+ // SetRes : Set a display mode // Notes : pcurrentmode is actually not used // -----------------+ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode) { LPCSTR renderer; BOOL WantFullScreen = !(lvid->u.windowed); //(lvid->u.windowed ? 0 : CDS_FULLSCREEN); UNREFERENCED_PARAMETER(pcurrentmode); DBG_Printf ("SetMode(): %dx%d %d bits (%s)\n", lvid->width, lvid->height, lvid->bpp*8, WantFullScreen ? "fullscreen" : "windowed"); hWnd = lvid->WndParent; // BP : why flush texture ? // if important flush also the first one (white texture) and restore it ! Flush(); // Flush textures. // TODO: if not fullscreen, skip display stuff and just resize viewport stuff ... // Exit previous mode //if (hGLRC) //Hurdler: TODO: check if this is valid // UnSetRes(); if (WasFullScreen) ChangeDisplaySettings(NULL, CDS_FULLSCREEN); //switch in and out of fullscreen // Change display settings. if (WantFullScreen) { DEVMODE dm; ZeroMemory(&dm, sizeof (dm)); dm.dmSize = sizeof (dm); dm.dmPelsWidth = lvid->width; dm.dmPelsHeight = lvid->height; dm.dmBitsPerPel = lvid->bpp*8; dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; if (ChangeDisplaySettings(&dm, CDS_TEST) != DISP_CHANGE_SUCCESSFUL) return -2; if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) !=DISP_CHANGE_SUCCESSFUL) return -3; SetWindowLong(hWnd, GWL_STYLE, WS_POPUP|WS_VISIBLE); // faB : book says to put these, surely for windowed mode //WS_CLIPCHILDREN|WS_CLIPSIBLINGS); SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, lvid->width, lvid->height, SWP_NOACTIVATE|SWP_NOZORDER); } else { RECT bounds; INT32 x, y; INT32 w = lvid->width, h = lvid->height; GetWindowRect(hWnd, &bounds); bounds.right = bounds.left+w; bounds.bottom = bounds.top+h; AdjustWindowRectEx(&bounds, GetWindowLong(hWnd, GWL_STYLE), (GetMenu(hWnd) != NULL), 0); w = bounds.right-bounds.left; h = bounds.bottom-bounds.top; x = (GetSystemMetrics(SM_CXSCREEN)-w)/2; y = (GetSystemMetrics(SM_CYSCREEN)-h)/2; SetWindowPos(hWnd, NULL, x, y, w, h, SWP_NOACTIVATE|SWP_NOZORDER); } if (!hDC) hDC = GetDC(hWnd); if (!hDC) { DBG_Printf("GetDC() FAILED\n"); return 0; } { INT32 res; // Set res. res = SetupPixelFormat(lvid->bpp*8, 0, 16); if (res == 0) return 0; else if (res == 1) { // Exit previous mode if (hGLRC) UnSetRes(); hGLRC = pwglCreateContext(hDC); if (!hGLRC) { DBG_Printf("pwglCreateContext() FAILED\n"); return 0; } if (!pwglMakeCurrent(hDC, hGLRC)) { DBG_Printf("wglMakeCurrent() FAILED\n"); return 0; } } } gl_extensions = pglGetString(GL_EXTENSIONS); // Get info and extensions. //BP: why don't we make it earlier ? //Hurdler: we cannot do that before intialising gl context renderer = (LPCSTR)pglGetString(GL_RENDERER); DBG_Printf("Vendor : %s\n", pglGetString(GL_VENDOR)); DBG_Printf("Renderer : %s\n", renderer); DBG_Printf("Version : %s\n", pglGetString(GL_VERSION)); DBG_Printf("Extensions : %s\n", gl_extensions); // BP: disable advenced feature that don't work on somes hardware // Hurdler: Now works on G400 with bios 1.6 and certified drivers 6.04 if (strstr(renderer, "810")) oglflags |= GLF_NOZBUFREAD; DBG_Printf("oglflags : 0x%X\n", oglflags); #ifdef USE_PALETTED_TEXTURE if (isExtAvailable("GL_EXT_paletted_texture",gl_extensions)) glColorTableEXT = GetGLFunc("glColorTableEXT"); else glColorTableEXT = NULL; #endif #ifdef USE_WGL_SWAP if (isExtAvailable("WGL_EXT_swap_control",gl_extensions)) wglSwapIntervalEXT = GetGLFunc("wglSwapIntervalEXT"); else wglSwapIntervalEXT = NULL; #endif if (isExtAvailable("GL_EXT_texture_filter_anisotropic",gl_extensions)) pglGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maximumAnisotropy); else maximumAnisotropy = 0; SetupGLFunc13(); screen_depth = (GLbyte)(lvid->bpp*8); if (screen_depth > 16) textureformatGL = GL_RGBA; else textureformatGL = GL_RGB5_A1; SetModelView(lvid->width, lvid->height); SetStates(); // we need to clear the depth buffer. Very important!!! pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); lvid->buffer = NULL; // unless we use the software view lvid->direct = NULL; // direct access to video memory, old DOS crap WasFullScreen = WantFullScreen; return 1; // on renvoie une valeur pour dire que cela s'est bien pass� } // -----------------+ // UnSetRes : Restore the original display mode // -----------------+ static void UnSetRes(void) { DBG_Printf("UnSetRes()\n"); pwglMakeCurrent(hDC, NULL); pwglDeleteContext(hGLRC); hGLRC = NULL; if (WasFullScreen) ChangeDisplaySettings(NULL, CDS_FULLSCREEN); } // -----------------+ // GetModeList : Return the list of available display modes. // Returns : pvidmodes - points to list of detected OpenGL video modes // : numvidmodes - number of detected OpenGL video modes // -----------------+ EXPORT void HWRAPI(GetModeList) (vmode_t** pvidmodes, INT32 *numvidmodes) { INT32 i; #if 1 INT32 iMode; /* faB test code Commented out because there might be a problem with combo (Voodoo2 + other card), we need to get the 3D card's display modes only. */ (*pvidmodes) = &video_modes[0]; // Get list of device modes for (i = 0,iMode = 0; iMode < MAX_VIDEO_MODES; i++) { DEVMODE Tmp; ZeroMemory(&Tmp, sizeof (Tmp)); Tmp.dmSize = sizeof (Tmp); if (!EnumDisplaySettings(NULL, i, &Tmp)) break; // add video mode if (Tmp.dmBitsPerPel == 16 && (iMode == 0 || ! (Tmp.dmPelsWidth == video_modes[iMode-1].width && Tmp.dmPelsHeight == video_modes[iMode-1].height) ) ) { video_modes[iMode].pnext = &video_modes[iMode+1]; video_modes[iMode].windowed = 0; // fullscreen is the default video_modes[iMode].misc = 0; video_modes[iMode].name = malloc(12 * sizeof (CHAR)); sprintf(video_modes[iMode].name, "%dx%d", (INT32)Tmp.dmPelsWidth, (INT32)Tmp.dmPelsHeight); DBG_Printf ("Mode: %s\n", video_modes[iMode].name); video_modes[iMode].width = Tmp.dmPelsWidth; video_modes[iMode].height = Tmp.dmPelsHeight; video_modes[iMode].bytesperpixel = Tmp.dmBitsPerPel/8; video_modes[iMode].rowbytes = Tmp.dmPelsWidth * video_modes[iMode].bytesperpixel; video_modes[iMode].pextradata = NULL; video_modes[iMode].setmode = SetRes; iMode++; } } (*numvidmodes) = iMode; #else // classic video modes (fullscreen/windowed) // Added some. Tails INT32 res[][2] = { { 320, 200}, { 320, 240}, { 400, 300}, { 512, 384}, { 640, 400}, { 640, 480}, { 800, 600}, { 960, 600}, {1024, 768}, {1152, 864}, {1280, 800}, {1280, 960}, {1280,1024}, {1600,1000}, {1680,1050}, {1920,1200}, }; HDC bpphdc; INT32 iBitsPerPel; DBG_Printf ("HWRAPI GetModeList()\n"); bpphdc = GetDC(NULL); // on obtient le bpp actuel iBitsPerPel = GetDeviceCaps(bpphdc, BITSPIXEL); ReleaseDC(NULL, bpphdc); (*pvidmodes) = &video_modes[0]; (*numvidmodes) = sizeof (res) / sizeof (res[0]); for (i = 0; i < (*numvidmodes); i++) { video_modes[i].pnext = &video_modes[i+1]; video_modes[i].windowed = 0; // fullscreen is the default video_modes[i].misc = 0; video_modes[i].name = malloc(12 * sizeof (CHAR)); sprintf(video_modes[i].name, "%dx%d", res[i][0], res[i][1]); DBG_Printf ("Mode: %s\n", video_modes[i].name); video_modes[i].width = res[i][0]; video_modes[i].height = res[i][1]; video_modes[i].bytesperpixel = iBitsPerPel/8; video_modes[i].rowbytes = res[i][0] * video_modes[i].bytesperpixel; video_modes[i].pextradata = NULL; video_modes[i].setmode = SetRes; } #endif video_modes[(*numvidmodes)-1].pnext = NULL; } // -----------------+ // Shutdown : Shutdown OpenGL, restore the display mode // -----------------+ EXPORT void HWRAPI(Shutdown) (void) { #ifdef DEBUG_TO_FILE long nb_centiemes; DBG_Printf ("HWRAPI Shutdown()\n"); nb_centiemes = ((clock()-my_clock)*100)/CLOCKS_PER_SEC; DBG_Printf("Nb frames: %li; Nb sec: %2.2f -> %2.1f fps\n", nb_frames, nb_centiemes/100.0f, (100*nb_frames)/(double)nb_centiemes); #endif Flush(); // Exit previous mode if (hGLRC) UnSetRes(); if (hDC) { ReleaseDC(hWnd, hDC); hDC = NULL; } FreeLibrary(GLU32); FreeLibrary(OGL32); DBG_Printf ("HWRAPI Shutdown(DONE)\n"); } // -----------------+ // FinishUpdate : Swap front and back buffers // -----------------+ EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl) { #ifdef USE_WGL_SWAP static INT32 oldwaitvbl = 0; #else UNREFERENCED_PARAMETER(waitvbl); #endif // DBG_Printf ("FinishUpdate()\n"); #ifdef DEBUG_TO_FILE if ((++nb_frames)==2) // on ne commence pas � la premi�re frame my_clock = clock(); #endif #ifdef USE_WGL_SWAP if (oldwaitvbl != waitvbl && wglSwapIntervalEXT) wglSwapIntervalEXT(waitvbl); oldwaitvbl = waitvbl; #endif SwapBuffers(hDC); } // -----------------+ // SetPalette : Set the color lookup table for paletted textures // : in OpenGL, we store values for conversion of paletted graphics when // : they are downloaded to the 3D card. // -----------------+ EXPORT void HWRAPI(SetPalette) (RGBA_t *pal, RGBA_t *gamma) { INT32 i; for (i = 0; i < 256; i++) { myPaletteData[i].s.red = (UINT8)MIN((pal[i].s.red*gamma->s.red)/127, 255); myPaletteData[i].s.green = (UINT8)MIN((pal[i].s.green*gamma->s.green)/127, 255); myPaletteData[i].s.blue = (UINT8)MIN((pal[i].s.blue*gamma->s.blue)/127, 255); myPaletteData[i].s.alpha = pal[i].s.alpha; } #ifdef USE_PALETTED_TEXTURE if (glColorTableEXT) { for (i = 0; i < 256; i++) { palette_tex[3*i+0] = pal[i].s.red; palette_tex[3*i+1] = pal[i].s.green; palette_tex[3*i+2] = pal[i].s.blue; } glColorTableEXT(GL_TEXTURE_2D, GL_RGB8, 256, GL_RGB, GL_UNSIGNED_BYTE, palette_tex); } #endif // on a chang� de palette, il faut recharger toutes les textures Flush(); } #endif