mirror of
https://github.com/minetest/irrlicht.git
synced 2024-12-25 18:20:30 +01:00
517 lines
15 KiB
C++
517 lines
15 KiB
C++
// Copyright (C) 2013 Christian Stehno
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
|
|
|
#include "CWGLManager.h"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_WGL_MANAGER_
|
|
|
|
#include "os.h"
|
|
|
|
#include <GL/gl.h>
|
|
#include <GL/wglext.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "OpenGL32.lib")
|
|
#endif
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
CWGLManager::CWGLManager()
|
|
: PrimaryContext(SExposedVideoData(0)), PixelFormat(0), libHandle(NULL)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CWGLManager");
|
|
#endif
|
|
memset(FunctionPointers, 0, sizeof(FunctionPointers));
|
|
}
|
|
|
|
CWGLManager::~CWGLManager()
|
|
{
|
|
}
|
|
|
|
bool CWGLManager::initialize(const SIrrlichtCreationParameters& params, const SExposedVideoData& videodata)
|
|
{
|
|
// store params, videoData is set later as it would be overwritten else
|
|
Params=params;
|
|
|
|
// Create a window to test antialiasing support
|
|
const fschar_t* ClassName = __TEXT("CWGLManager");
|
|
HINSTANCE lhInstance = GetModuleHandle(0);
|
|
|
|
// Register Class
|
|
WNDCLASSEX wcex;
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = (WNDPROC)DefWindowProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = lhInstance;
|
|
wcex.hIcon = NULL;
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
|
wcex.lpszMenuName = 0;
|
|
wcex.lpszClassName = ClassName;
|
|
wcex.hIconSm = 0;
|
|
wcex.hIcon = 0;
|
|
RegisterClassEx(&wcex);
|
|
|
|
RECT clientSize;
|
|
clientSize.top = 0;
|
|
clientSize.left = 0;
|
|
clientSize.right = Params.WindowSize.Width;
|
|
clientSize.bottom = Params.WindowSize.Height;
|
|
|
|
DWORD style = WS_POPUP;
|
|
if (!Params.Fullscreen)
|
|
style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
|
|
AdjustWindowRect(&clientSize, style, FALSE);
|
|
|
|
const s32 realWidth = clientSize.right - clientSize.left;
|
|
const s32 realHeight = clientSize.bottom - clientSize.top;
|
|
|
|
const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
|
|
const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
|
|
|
|
HWND temporary_wnd=CreateWindow(ClassName, __TEXT(""), style, windowLeft,
|
|
windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);
|
|
|
|
if (!temporary_wnd)
|
|
{
|
|
os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
HDC HDc = GetDC(temporary_wnd);
|
|
|
|
// Set up pixel format descriptor with desired parameters
|
|
PIXELFORMATDESCRIPTOR tmp_pfd = {
|
|
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
|
|
1, // Version Number
|
|
(DWORD)(PFD_DRAW_TO_WINDOW | // Format Must Support Window
|
|
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
|
|
(Params.Doublebuffer?PFD_DOUBLEBUFFER:0) | // Must Support Double Buffering
|
|
(Params.Stereobuffer?PFD_STEREO:0)), // Must Support Stereo Buffer
|
|
PFD_TYPE_RGBA, // Request An RGBA Format
|
|
Params.Bits, // Select Our 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, // Accumulation Bits Ignored
|
|
Params.ZBufferBits, // Z-Buffer (Depth Buffer)
|
|
BYTE(Params.Stencilbuffer ? 1 : 0), // Stencil Buffer Depth
|
|
0, // No Auxiliary Buffer
|
|
PFD_MAIN_PLANE, // Main Drawing Layer
|
|
0, // Reserved
|
|
0, 0, 0 // Layer Masks Ignored
|
|
};
|
|
pfd=tmp_pfd;
|
|
|
|
for (u32 i=0; i<6; ++i)
|
|
{
|
|
if (i == 1)
|
|
{
|
|
if (Params.Stencilbuffer)
|
|
{
|
|
os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
|
|
Params.Stencilbuffer = false;
|
|
pfd.cStencilBits = 0;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 2)
|
|
{
|
|
pfd.cDepthBits = 24;
|
|
}
|
|
else
|
|
if (i == 3)
|
|
{
|
|
if (Params.Bits!=16)
|
|
pfd.cDepthBits = 16;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 4)
|
|
{
|
|
// try single buffer
|
|
if (Params.Doublebuffer)
|
|
pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 5)
|
|
{
|
|
os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
// choose pixelformat
|
|
PixelFormat = ChoosePixelFormat(HDc, &pfd);
|
|
if (PixelFormat)
|
|
break;
|
|
}
|
|
|
|
SetPixelFormat(HDc, PixelFormat, &pfd);
|
|
os::Printer::log("Create temporary GL rendering context", ELL_DEBUG);
|
|
HGLRC hrc=wglCreateContext(HDc);
|
|
if (!hrc)
|
|
{
|
|
os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
CurrentContext.OpenGLWin32.HDc = HDc;
|
|
CurrentContext.OpenGLWin32.HRc = hrc;
|
|
CurrentContext.OpenGLWin32.HWnd = temporary_wnd;
|
|
|
|
if (!activateContext(CurrentContext, false))
|
|
{
|
|
os::Printer::log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
|
|
wglDeleteContext(hrc);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
core::stringc wglExtensions;
|
|
#ifdef WGL_ARB_extensions_string
|
|
PFNWGLGETEXTENSIONSSTRINGARBPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
|
|
if (irrGetExtensionsString)
|
|
wglExtensions = irrGetExtensionsString(HDc);
|
|
#elif defined(WGL_EXT_extensions_string)
|
|
PFNWGLGETEXTENSIONSSTRINGEXTPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
|
|
if (irrGetExtensionsString)
|
|
wglExtensions = irrGetExtensionsString(HDc);
|
|
#endif
|
|
const bool pixel_format_supported = (wglExtensions.find("WGL_ARB_pixel_format") != -1);
|
|
const bool multi_sample_supported = ((wglExtensions.find("WGL_ARB_multisample") != -1) ||
|
|
(wglExtensions.find("WGL_EXT_multisample") != -1) || (wglExtensions.find("WGL_3DFX_multisample") != -1) );
|
|
#ifdef _DEBUG
|
|
os::Printer::log("WGL_extensions", wglExtensions);
|
|
#endif
|
|
|
|
// Without a GL context we can't call wglGetProcAddress so store this for later
|
|
FunctionPointers[0] = (void*)wglGetProcAddress("wglCreateContextAttribsARB");
|
|
|
|
#ifdef WGL_ARB_pixel_format
|
|
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
|
|
if (pixel_format_supported && wglChoosePixelFormat_ARB)
|
|
{
|
|
// This value determines the number of samples used for antialiasing
|
|
// My experience is that 8 does not show a big
|
|
// improvement over 4, but 4 shows a big improvement
|
|
// over 2.
|
|
|
|
if (Params.AntiAlias > 32)
|
|
Params.AntiAlias = 32;
|
|
|
|
f32 fAttributes[] = {0.0, 0.0};
|
|
s32 iAttributes[] =
|
|
{
|
|
WGL_DRAW_TO_WINDOW_ARB,1,
|
|
WGL_SUPPORT_OPENGL_ARB,1,
|
|
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
|
|
WGL_COLOR_BITS_ARB,(Params.Bits==32) ? 24 : 15,
|
|
WGL_ALPHA_BITS_ARB,(Params.Bits==32) ? 8 : 1,
|
|
WGL_DEPTH_BITS_ARB,Params.ZBufferBits, // 10,11
|
|
WGL_STENCIL_BITS_ARB,Params.Stencilbuffer ? 1 : 0,
|
|
WGL_DOUBLE_BUFFER_ARB,Params.Doublebuffer ? 1 : 0,
|
|
WGL_STEREO_ARB,Params.Stereobuffer ? 1 : 0,
|
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
|
#ifdef WGL_ARB_multisample
|
|
WGL_SAMPLES_ARB,Params.AntiAlias, // 20,21
|
|
WGL_SAMPLE_BUFFERS_ARB, (Params.AntiAlias>0) ? 1 : 0,
|
|
#elif defined(WGL_EXT_multisample)
|
|
WGL_SAMPLES_EXT,AntiAlias, // 20,21
|
|
WGL_SAMPLE_BUFFERS_EXT, (Params.AntiAlias>0) ? 1 : 0,
|
|
#elif defined(WGL_3DFX_multisample)
|
|
WGL_SAMPLES_3DFX,AntiAlias, // 20,21
|
|
WGL_SAMPLE_BUFFERS_3DFX, (Params.AntiAlias>0) ? 1 : 0,
|
|
#endif
|
|
#ifdef WGL_ARB_framebuffer_sRGB
|
|
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, Params.HandleSRGB ? 1:0,
|
|
#elif defined(WGL_EXT_framebuffer_sRGB)
|
|
WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, Params.HandleSRGB ? 1:0,
|
|
#endif
|
|
// WGL_DEPTH_FLOAT_EXT, 1,
|
|
0,0,0,0
|
|
};
|
|
int iAttrSize = sizeof(iAttributes)/sizeof(int);
|
|
const bool framebuffer_srgb_supported = ((wglExtensions.find("WGL_ARB_framebuffer_sRGB") != -1) ||
|
|
(wglExtensions.find("WGL_EXT_framebuffer_sRGB") != -1));
|
|
if (!framebuffer_srgb_supported)
|
|
{
|
|
memmove(&iAttributes[24],&iAttributes[26],sizeof(int)*(iAttrSize-26));
|
|
iAttrSize -= 2;
|
|
}
|
|
if (!multi_sample_supported)
|
|
{
|
|
memmove(&iAttributes[20],&iAttributes[24],sizeof(int)*(iAttrSize-24));
|
|
iAttrSize -= 4;
|
|
}
|
|
|
|
s32 rv=0;
|
|
// Try to get an acceptable pixel format
|
|
do
|
|
{
|
|
int pixelFormat=0;
|
|
UINT numFormats=0;
|
|
const BOOL valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
|
|
|
|
if (valid && numFormats)
|
|
rv = pixelFormat;
|
|
else
|
|
iAttributes[21] -= 1;
|
|
}
|
|
while(rv==0 && iAttributes[21]>1);
|
|
if (rv)
|
|
{
|
|
PixelFormat=rv;
|
|
Params.AntiAlias=iAttributes[21];
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
Params.AntiAlias=0;
|
|
|
|
// this only terminates the temporary HRc
|
|
destroyContext();
|
|
destroySurface();
|
|
terminate();
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
|
|
// now get new window
|
|
CurrentContext.OpenGLWin32.HWnd=videodata.OpenGLWin32.HWnd;
|
|
// get hdc
|
|
if (!(CurrentContext.OpenGLWin32.HDc=GetDC((HWND)videodata.OpenGLWin32.HWnd)))
|
|
{
|
|
os::Printer::log("Cannot create a GL device context.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
if (!PrimaryContext.OpenGLWin32.HWnd)
|
|
{
|
|
PrimaryContext.OpenGLWin32.HWnd=CurrentContext.OpenGLWin32.HWnd;
|
|
PrimaryContext.OpenGLWin32.HDc=CurrentContext.OpenGLWin32.HDc;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CWGLManager::terminate()
|
|
{
|
|
if (CurrentContext.OpenGLWin32.HDc)
|
|
ReleaseDC((HWND)CurrentContext.OpenGLWin32.HWnd, (HDC)CurrentContext.OpenGLWin32.HDc);
|
|
if (PrimaryContext.OpenGLWin32.HDc && PrimaryContext.OpenGLWin32.HDc == CurrentContext.OpenGLWin32.HDc)
|
|
memset(&PrimaryContext, 0, sizeof(PrimaryContext));
|
|
memset(&CurrentContext, 0, sizeof(CurrentContext));
|
|
if (libHandle)
|
|
FreeLibrary(libHandle);
|
|
}
|
|
|
|
bool CWGLManager::generateSurface()
|
|
{
|
|
HDC HDc = (HDC)CurrentContext.OpenGLWin32.HDc;
|
|
// search for pixel format the simple way
|
|
if (PixelFormat==0 || (!SetPixelFormat(HDc, PixelFormat, &pfd)))
|
|
{
|
|
for (u32 i=0; i<5; ++i)
|
|
{
|
|
if (i == 1)
|
|
{
|
|
if (Params.Stencilbuffer)
|
|
{
|
|
os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
|
|
Params.Stencilbuffer = false;
|
|
pfd.cStencilBits = 0;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 2)
|
|
{
|
|
pfd.cDepthBits = 24;
|
|
}
|
|
if (i == 3)
|
|
{
|
|
if (Params.Bits!=16)
|
|
pfd.cDepthBits = 16;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 4)
|
|
{
|
|
os::Printer::log("Cannot create a GL device context", "No suitable format.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// choose pixelformat
|
|
PixelFormat = ChoosePixelFormat(HDc, &pfd);
|
|
if (PixelFormat)
|
|
break;
|
|
}
|
|
|
|
// set pixel format
|
|
if (!SetPixelFormat(HDc, PixelFormat, &pfd))
|
|
{
|
|
os::Printer::log("Cannot set the pixel format.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (pfd.cAlphaBits != 0)
|
|
{
|
|
if (pfd.cRedBits == 8)
|
|
ColorFormat = ECF_A8R8G8B8;
|
|
else
|
|
ColorFormat = ECF_A1R5G5B5;
|
|
}
|
|
else
|
|
{
|
|
if (pfd.cRedBits == 8)
|
|
ColorFormat = ECF_R8G8B8;
|
|
else
|
|
ColorFormat = ECF_R5G6B5;
|
|
}
|
|
os::Printer::log("Pixel Format", core::stringc(PixelFormat).c_str(), ELL_DEBUG);
|
|
return true;
|
|
}
|
|
|
|
void CWGLManager::destroySurface()
|
|
{
|
|
}
|
|
|
|
bool CWGLManager::generateContext()
|
|
{
|
|
HDC HDc=(HDC)CurrentContext.OpenGLWin32.HDc;
|
|
HGLRC hrc;
|
|
// create rendering context
|
|
#ifdef WGL_ARB_create_context
|
|
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs_ARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)FunctionPointers[0];
|
|
if (wglCreateContextAttribs_ARB)
|
|
{
|
|
// with 3.0 all available profiles should be usable, higher versions impose restrictions
|
|
// we need at least 1.1
|
|
const int iAttribs[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 1,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
// WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, // enable to get a debug context (depends on driver if that does anything)
|
|
0
|
|
};
|
|
hrc=wglCreateContextAttribs_ARB(HDc, 0, iAttribs);
|
|
}
|
|
else
|
|
#endif
|
|
hrc=wglCreateContext(HDc);
|
|
os::Printer::log("Irrlicht context");
|
|
|
|
if (!hrc)
|
|
{
|
|
os::Printer::log("Cannot create a GL rendering context.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// set exposed data
|
|
CurrentContext.OpenGLWin32.HRc = hrc;
|
|
if (!PrimaryContext.OpenGLWin32.HRc)
|
|
PrimaryContext.OpenGLWin32.HRc=CurrentContext.OpenGLWin32.HRc;
|
|
|
|
return true;
|
|
}
|
|
|
|
const SExposedVideoData& CWGLManager::getContext() const
|
|
{
|
|
return CurrentContext;
|
|
}
|
|
|
|
bool CWGLManager::activateContext(const SExposedVideoData& videoData, bool restorePrimaryOnZero)
|
|
{
|
|
if (videoData.OpenGLWin32.HWnd && videoData.OpenGLWin32.HDc && videoData.OpenGLWin32.HRc)
|
|
{
|
|
if (!wglMakeCurrent((HDC)videoData.OpenGLWin32.HDc, (HGLRC)videoData.OpenGLWin32.HRc))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
CurrentContext=videoData;
|
|
}
|
|
else if (!restorePrimaryOnZero && !videoData.OpenGLWin32.HDc && !videoData.OpenGLWin32.HRc)
|
|
{
|
|
if (!wglMakeCurrent((HDC)0, (HGLRC)0))
|
|
{
|
|
os::Printer::log("Render Context reset failed.");
|
|
return false;
|
|
}
|
|
CurrentContext = videoData;
|
|
}
|
|
// set back to main context
|
|
else if (!videoData.OpenGLWin32.HWnd && CurrentContext.OpenGLWin32.HDc != PrimaryContext.OpenGLWin32.HDc)
|
|
{
|
|
if (!wglMakeCurrent((HDC)PrimaryContext.OpenGLWin32.HDc, (HGLRC)PrimaryContext.OpenGLWin32.HRc))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
CurrentContext=PrimaryContext;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CWGLManager::destroyContext()
|
|
{
|
|
if (CurrentContext.OpenGLWin32.HRc)
|
|
{
|
|
if (!wglMakeCurrent((HDC)CurrentContext.OpenGLWin32.HDc, 0))
|
|
os::Printer::log("Release of render context failed.", ELL_WARNING);
|
|
|
|
if (!wglDeleteContext((HGLRC)CurrentContext.OpenGLWin32.HRc))
|
|
os::Printer::log("Deletion of render context failed.", ELL_WARNING);
|
|
if (PrimaryContext.OpenGLWin32.HRc==CurrentContext.OpenGLWin32.HRc)
|
|
PrimaryContext.OpenGLWin32.HRc=0;
|
|
CurrentContext.OpenGLWin32.HRc=0;
|
|
}
|
|
}
|
|
|
|
void* CWGLManager::getProcAddress(const std::string &procName)
|
|
{
|
|
void* proc = NULL;
|
|
proc = (void*)wglGetProcAddress(procName.c_str());
|
|
if (!proc) { // Fallback
|
|
if (!libHandle)
|
|
libHandle = LoadLibraryA("opengl32.dll");
|
|
if (libHandle)
|
|
proc = (void*)GetProcAddress(libHandle, procName.c_str());
|
|
}
|
|
return proc;
|
|
}
|
|
|
|
bool CWGLManager::swapBuffers()
|
|
{
|
|
return SwapBuffers((HDC)CurrentContext.OpenGLWin32.HDc) == TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|