netgen/ng/Togl2.1/toglWGL.c
2016-02-08 15:53:16 +01:00

596 lines
20 KiB
C

/* $Id: toglWGL.c,v 1.8 2009/12/23 21:50:49 gregcouch Exp $ */
/* vi:set sw=4 expandtab: */
/*
* Togl - a Tk OpenGL widget
*
* Copyright (C) 1996-2002 Brian Paul and Ben Bederson
* Copyright (C) 2005-2009 Greg Couch
* See the LICENSE file for copyright details.
*/
/* TODO: fullscreen support */
#include <windows.h>
#include <GL/gl.h>
#include <wingdi.h>
#include <tk.h>
#include <tkPlatDecls.h>
#include <GL/wglext.h>
#ifndef PFD_SUPPORT_COMPOSITION
/* for Vista -- not needed because we don't use PFD_SUPPORT_GDI/BITMAP */
# define PFD_SUPPORT_COMPOSITION 0x00008000
#endif
/* TODO: move these statics into global structure */
static PFNWGLGETEXTENSIONSSTRINGARBPROC getExtensionsString = NULL;
static PFNWGLCHOOSEPIXELFORMATARBPROC choosePixelFormat;
static PFNWGLGETPIXELFORMATATTRIBIVARBPROC getPixelFormatAttribiv;
static PFNWGLCREATEPBUFFERARBPROC createPbuffer = NULL;
static PFNWGLDESTROYPBUFFERARBPROC destroyPbuffer = NULL;
static PFNWGLGETPBUFFERDCARBPROC getPbufferDC = NULL;
static PFNWGLRELEASEPBUFFERDCARBPROC releasePbufferDC = NULL;
static PFNWGLQUERYPBUFFERARBPROC queryPbuffer = NULL;
static int hasMultisampling = FALSE;
static int hasPbuffer = FALSE;
static int hasARBPbuffer = FALSE;
static HWND
toglCreateTestWindow(HWND parent)
{
static char ClassName[] = "ToglTestWindow";
WNDCLASS wc;
HINSTANCE instance = GetModuleHandle(NULL);
HWND wnd;
HDC dc;
PIXELFORMATDESCRIPTOR pfd;
int pixelFormat;
wc.style = CS_OWNDC;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = ClassName;
if (!RegisterClass(&wc)) {
DWORD err = GetLastError();
if (err != ERROR_CLASS_ALREADY_EXISTS) {
fprintf(stderr, "Unable to register Togl Test Window class\n");
return NULL;
}
}
wnd = CreateWindow(ClassName, "test OpenGL capabilities",
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0, 0, 1, 1, parent, NULL, instance, NULL);
if (wnd == NULL) {
fprintf(stderr, "Unable to create temporary OpenGL window\n");
return NULL;
}
dc = GetDC(wnd);
memset(&pfd, 0, sizeof pfd);
pfd.nSize = sizeof pfd;
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 3;
pfd.iLayerType = PFD_MAIN_PLANE;
pixelFormat = ChoosePixelFormat(dc, &pfd);
if (pixelFormat == 0) {
fprintf(stderr, "Unable to choose simple pixel format\n");
ReleaseDC(wnd, dc);
return NULL;
}
if (!SetPixelFormat(dc, pixelFormat, &pfd)) {
fprintf(stderr, "Unable to set simple pixel format\n");
ReleaseDC(wnd, dc);
return NULL;
}
ShowWindow(wnd, SW_HIDE); // make sure it's hidden
ReleaseDC(wnd, dc);
return wnd;
}
struct FBInfo
{
int stereo;
int acceleration;
int colors;
int depth;
int samples;
int pixelFormat;
};
typedef struct FBInfo FBInfo;
static int FBAttribs[] = {
/* must match order in FBInfo structure */
WGL_STEREO_ARB,
WGL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB,
WGL_DEPTH_BITS_ARB,
WGL_SAMPLES_ARB,
};
#define NUM_FBAttribs (sizeof FBAttribs / sizeof FBAttribs[0])
static int
FBInfoCmp(const void *a, const void *b)
{
/*
* 1. stereo is better
* 2. full acceleration is better
* 3. greater color bits is better
* 4. greater depth bits is better
* 5. more multisampling is better
*/
const FBInfo *x = (const FBInfo *) a;
const FBInfo *y = (const FBInfo *) b;
if (x->stereo != y->stereo)
return y->stereo - x->stereo;
if (x->acceleration != y->acceleration)
return y->acceleration - x->acceleration;
if (x->colors != y->colors)
return y->colors - x->colors;
if (x->depth != y->depth)
return y->depth - x->depth;
if (x->samples != y->samples)
return y->samples - x->samples;
return 0;
}
static int
togl_pixelFormat(Togl *togl, HWND hwnd)
{
/* return 0 when pixel format is unavailable. */
int pixelformat = 0;
static int loadedOpenGL = FALSE;
int formats[256];
UINT numFormats;
FBInfo *info;
UINT i;
int attribs[128];
int na = 0;
if (!loadedOpenGL) {
HWND test = NULL;
HDC dc;
HGLRC rc;
if (wglGetCurrentContext() != NULL) {
dc = wglGetCurrentDC();
} else {
/* HWND hwnd = Tk_GetHWND(Tk_WindowId(togl->TkWin)); */
test = toglCreateTestWindow(hwnd);
if (test == NULL) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "can't create dummy OpenGL window",
TCL_STATIC);
return 0;
}
dc = GetDC(test);
rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
}
loadedOpenGL = TRUE;
/*
* Now that we have an OpenGL window, we can initialize all
* OpenGL information and figure out if multisampling is supported.
*/
getExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)
wglGetProcAddress("wglGetExtensionsStringARB");
if (getExtensionsString == NULL)
getExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)
wglGetProcAddress("wglGetExtensionsStringEXT");
if (getExtensionsString) {
const char *extensions = getExtensionsString(dc);
if (strstr(extensions, "WGL_ARB_multisample") != NULL
|| strstr(extensions, "WGL_EXT_multisample") != NULL)
hasMultisampling = TRUE;
if (strstr(extensions, "WGL_ARB_pixel_format") != NULL) {
choosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)
wglGetProcAddress("wglChoosePixelFormatARB");
getPixelFormatAttribiv = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)
wglGetProcAddress("wglGetPixelFormatAttribivARB");
if (choosePixelFormat == NULL || getPixelFormatAttribiv == NULL) {
choosePixelFormat = NULL;
getPixelFormatAttribiv = NULL;
}
}
if (choosePixelFormat == NULL
&& strstr(extensions, "WGL_EXT_pixel_format") != NULL) {
choosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)
wglGetProcAddress("wglChoosePixelFormatEXT");
getPixelFormatAttribiv = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)
wglGetProcAddress("wglGetPixelFormatAttribivEXT");
if (choosePixelFormat == NULL || getPixelFormatAttribiv == NULL) {
choosePixelFormat = NULL;
getPixelFormatAttribiv = NULL;
}
}
if (createPbuffer == NULL
&& strstr(extensions, "WGL_ARB_pbuffer") != NULL) {
createPbuffer = (PFNWGLCREATEPBUFFERARBPROC)
wglGetProcAddress("wglCreatePbufferARB");
destroyPbuffer = (PFNWGLDESTROYPBUFFERARBPROC)
wglGetProcAddress("wglDestroyPbufferARB");
getPbufferDC = (PFNWGLGETPBUFFERDCARBPROC)
wglGetProcAddress("wglGetPbufferDCARB");
releasePbufferDC = (PFNWGLRELEASEPBUFFERDCARBPROC)
wglGetProcAddress("wglReleasePbufferDCARB");
queryPbuffer = (PFNWGLQUERYPBUFFERARBPROC)
wglGetProcAddress("wglQueryPbufferARB");
if (createPbuffer == NULL || destroyPbuffer == NULL
|| getPbufferDC == NULL || releasePbufferDC == NULL
|| queryPbuffer == NULL) {
createPbuffer = NULL;
destroyPbuffer = NULL;
getPbufferDC = NULL;
releasePbufferDC = NULL;
queryPbuffer = NULL;
} else {
hasPbuffer = TRUE;
hasARBPbuffer = TRUE;
}
}
if (createPbuffer == NULL
&& strstr(extensions, "WGL_EXT_pbuffer") != NULL) {
createPbuffer = (PFNWGLCREATEPBUFFERARBPROC)
wglGetProcAddress("wglCreatePbufferEXT");
destroyPbuffer = (PFNWGLDESTROYPBUFFERARBPROC)
wglGetProcAddress("wglDestroyPbufferEXT");
getPbufferDC = (PFNWGLGETPBUFFERDCARBPROC)
wglGetProcAddress("wglGetPbufferDCEXT");
releasePbufferDC = (PFNWGLRELEASEPBUFFERDCARBPROC)
wglGetProcAddress("wglReleasePbufferDCEXT");
queryPbuffer = (PFNWGLQUERYPBUFFERARBPROC)
wglGetProcAddress("wglQueryPbufferEXT");
if (createPbuffer == NULL || destroyPbuffer == NULL
|| getPbufferDC == NULL || releasePbufferDC == NULL
|| queryPbuffer == NULL) {
createPbuffer = NULL;
destroyPbuffer = NULL;
getPbufferDC = NULL;
releasePbufferDC = NULL;
queryPbuffer = NULL;
} else {
hasPbuffer = TRUE;
}
}
}
/* No need to confirm multisampling is in glGetString(GL_EXTENSIONS)
* because OpenGL driver is local */
if (test != NULL) {
/* cleanup by removing temporary OpenGL window */
wglMakeCurrent(NULL, NULL);
wglDeleteContext(rc);
ReleaseDC(test, dc);
DestroyWindow(test);
}
}
if (togl->MultisampleFlag && !hasMultisampling) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "multisampling not supported", TCL_STATIC);
return 0;
}
if (togl->PbufferFlag && !hasPbuffer) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "pbuffers are not supported", TCL_STATIC);
return 0;
}
if (choosePixelFormat == NULL) {
PIXELFORMATDESCRIPTOR pfd;
/* Don't have the great wglChoosePixelFormatARB() function, so do it
* the old way. */
if (togl->MultisampleFlag) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "multisampling not supported", TCL_STATIC);
return 0;
}
memset(&pfd, 0, sizeof pfd);
pfd.nSize = sizeof pfd;
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL
| PFD_SUPPORT_COMPOSITION;
if (togl->DoubleFlag) {
pfd.dwFlags |= PFD_DOUBLEBUFFER;
}
if (togl->Stereo == TOGL_STEREO_NATIVE) {
pfd.dwFlags |= PFD_STEREO;
}
pfd.iPixelType = togl->RgbaFlag ? PFD_TYPE_RGBA : PFD_TYPE_COLORINDEX;
pfd.cColorBits = togl->RgbaRed + togl->RgbaGreen + togl->RgbaBlue;
/* Alpha bitplanes are not supported in the current generic OpenGL
* implementation, but may be supported by specific hardware devices. */
pfd.cAlphaBits = togl->AlphaFlag ? togl->AlphaSize : 0;
pfd.cAccumBits = togl->AccumFlag ? (togl->AccumRed + togl->AccumGreen +
togl->AccumBlue + togl->AccumAlpha) : 0;
pfd.cDepthBits = togl->DepthFlag ? togl->DepthSize : 0;
pfd.cStencilBits = togl->StencilFlag ? togl->StencilSize : 0;
/* Auxiliary buffers are not supported in the current generic OpenGL
* implementation, but may be supported by specific hardware devices. */
pfd.cAuxBuffers = togl->AuxNumber;
pfd.iLayerType = PFD_MAIN_PLANE;
if ((pixelformat = ChoosePixelFormat(togl->tglGLHdc, &pfd)) == 0) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "couldn't choose pixel format", TCL_STATIC);
return 0;
}
/* double check that we got the stereo format we requested */
if (togl->Stereo == TOGL_STEREO_NATIVE) {
DescribePixelFormat(togl->tglGLHdc, pixelformat, sizeof (pfd),
&pfd);
if ((pfd.dwFlags & PFD_STEREO) == 0) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "couldn't choose stereo pixel format",
TCL_STATIC);
return 0;
}
}
return pixelformat;
}
// We have the new wglChoosePixelFormat!!
if (togl->MultisampleFlag && !hasMultisampling) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "multisampling not supported", TCL_STATIC);
return 0;
}
if (togl->PbufferFlag)
attribs[na++] = WGL_DRAW_TO_PBUFFER_ARB;
else
attribs[na++] = WGL_DRAW_TO_WINDOW_ARB;
attribs[na++] = GL_TRUE;
attribs[na++] = WGL_SUPPORT_OPENGL_ARB;
attribs[na++] = GL_TRUE;
attribs[na++] = WGL_PIXEL_TYPE_ARB;
if (!togl->RgbaFlag) {
attribs[na++] = WGL_TYPE_COLORINDEX_ARB;
} else {
attribs[na++] = WGL_TYPE_RGBA_ARB;
attribs[na++] = WGL_RED_BITS_ARB;
attribs[na++] = togl->RgbaRed;
attribs[na++] = WGL_GREEN_BITS_ARB;
attribs[na++] = togl->RgbaGreen;
attribs[na++] = WGL_BLUE_BITS_ARB;
attribs[na++] = togl->RgbaBlue;
if (togl->AlphaFlag) {
attribs[na++] = WGL_ALPHA_BITS_ARB;
attribs[na++] = togl->AlphaSize;
}
}
if (togl->DepthFlag) {
attribs[na++] = WGL_DEPTH_BITS_ARB;
attribs[na++] = togl->DepthSize;
}
if (togl->DoubleFlag) {
attribs[na++] = WGL_DOUBLE_BUFFER_ARB;
attribs[na++] = GL_TRUE;
}
if (togl->StencilFlag) {
attribs[na++] = WGL_STENCIL_BITS_ARB;
attribs[na++] = togl->StencilSize;
}
if (togl->AccumFlag) {
attribs[na++] = WGL_ACCUM_RED_BITS_ARB;
attribs[na++] = togl->AccumRed;
attribs[na++] = WGL_ACCUM_GREEN_BITS_ARB;
attribs[na++] = togl->AccumGreen;
attribs[na++] = WGL_ACCUM_BLUE_BITS_ARB;
attribs[na++] = togl->AccumBlue;
if (togl->AlphaFlag) {
attribs[na++] = WGL_ACCUM_ALPHA_BITS_ARB;
attribs[na++] = togl->AccumAlpha;
}
}
if (togl->Stereo == TOGL_STEREO_NATIVE) {
attribs[na++] = WGL_STEREO_ARB;
attribs[na++] = GL_TRUE;
}
if (togl->MultisampleFlag) {
attribs[na++] = WGL_SAMPLE_BUFFERS_ARB;
attribs[na++] = 1;
attribs[na++] = WGL_SAMPLES_ARB;
attribs[na++] = 2;
}
if (togl->AuxNumber) {
attribs[na++] = WGL_AUX_BUFFERS_ARB;
attribs[na++] = togl->AuxNumber;
}
attribs[na++] = 0; // must be last
if (!choosePixelFormat(togl->tglGLHdc, &attribs[0], NULL, 256, formats,
&numFormats) || numFormats == 0) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "couldn't choose pixel format", TCL_STATIC);
return 0;
}
/*
* Pick best format
*/
info = (FBInfo *) malloc(numFormats * sizeof (FBInfo));
for (i = 0; i != numFormats; ++i) {
info[i].pixelFormat = formats[i];
getPixelFormatAttribiv(togl->tglGLHdc, formats[i], 0,
NUM_FBAttribs, FBAttribs, &info[i].stereo);
/* revise attributes so larger is better */
if (!togl->DepthFlag)
info[i].depth = -info[i].depth;
if (!togl->MultisampleFlag)
info[i].samples = -info[i].samples;
if (togl->Stereo != TOGL_STEREO_NATIVE)
info[i].stereo = -info[i].stereo;
}
qsort(info, numFormats, sizeof info[0], FBInfoCmp);
pixelformat = info[0].pixelFormat;
/* double check that we got the stereo format we requested */
if (togl->Stereo == TOGL_STEREO_NATIVE && !info[0].stereo) {
Tcl_SetResult(togl->Interp,
TCL_STUPID "couldn't choose stereo pixel format", TCL_STATIC);
free(info);
return 0;
}
free(info);
return pixelformat;
}
static int
togl_describePixelFormat(Togl *togl)
{
if (getPixelFormatAttribiv == NULL) {
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(togl->tglGLHdc, (int) togl->PixelFormat,
sizeof (pfd), &pfd);
/* fill in flags normally passed in that affect behavior */
togl->RgbaFlag = pfd.iPixelType == PFD_TYPE_RGBA;
togl->DoubleFlag = (pfd.dwFlags & PFD_DOUBLEBUFFER) != 0;
togl->DepthFlag = (pfd.cDepthBits != 0);
togl->AccumFlag = (pfd.cAccumBits != 0);
togl->AlphaFlag = (pfd.cAlphaBits != 0);
togl->StencilFlag = (pfd.cStencilBits != 0);
if ((pfd.dwFlags & PFD_STEREO) != 0)
togl->Stereo = TOGL_STEREO_NATIVE;
else
togl->Stereo = TOGL_STEREO_NONE;
} else {
static int attribs[] = {
WGL_PIXEL_TYPE_ARB,
WGL_DOUBLE_BUFFER_ARB,
WGL_DEPTH_BITS_ARB,
WGL_ACCUM_RED_BITS_ARB,
WGL_ALPHA_BITS_ARB,
WGL_STENCIL_BITS_ARB,
WGL_STEREO_ARB,
WGL_SAMPLES_ARB
};
#define NUM_ATTRIBS (sizeof attribs / sizeof attribs[0])
int info[NUM_ATTRIBS];
getPixelFormatAttribiv(togl->tglGLHdc, (int) togl->PixelFormat, 0,
NUM_ATTRIBS, attribs, info);
#undef NUM_ATTRIBS
togl->RgbaFlag = info[0];
togl->DoubleFlag = info[1];
togl->DepthFlag = (info[2] != 0);
togl->AccumFlag = (info[3] != 0);
togl->AlphaFlag = (info[4] != 0);
togl->StencilFlag = (info[5] != 0);
togl->Stereo = info[6] ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE;
togl->MultisampleFlag = (info[7] != 0);
}
return True;
}
static HPBUFFERARB
togl_createPbuffer(Togl *togl)
{
int attribs[32];
int na = 0;
HPBUFFERARB pbuf;
if (togl->LargestPbufferFlag) {
attribs[na++] = WGL_PBUFFER_LARGEST_ARB;
attribs[na++] = 1;
}
attribs[na] = 0;
pbuf = createPbuffer(togl->tglGLHdc, (int) togl->PixelFormat, togl->Width,
togl->Height, attribs);
if (pbuf && togl->LargestPbufferFlag) {
queryPbuffer(pbuf, WGL_PBUFFER_WIDTH_ARB, &togl->Width);
queryPbuffer(pbuf, WGL_PBUFFER_HEIGHT_ARB, &togl->Height);
}
return pbuf;
}
static void
togl_destroyPbuffer(Togl *togl)
{
destroyPbuffer(togl->pbuf);
}
#if 0
// From nvidia.com
Multisampling requires WGL_ARB_extension_string and WGL_ARB_pixel_format
wglGetProcAddress("wglGetExtensionsStringARB")
// From msdn.microsoft.com, various ways to enumerate PixelFormats
void CPixForm::OnClickedLastPfd()
{
COpenGL gl;
PIXELFORMATDESCRIPTOR pfd;
//
// Get the hwnd of the view window.
//
HWND hwndview = GetViewHwnd();
//
// Get a DC associated with the view window.
//
HDC dc =::GetDC(hwndview);
int nID = (m_nNextID > 1) ? m_nNextID-- : 1;
//
// Get a description of the pixel format. If it is valid, then go and
// update the controls in the dialog box, otherwise do nothing.
//
if (gl.DescribePixelFormat(dc, nID, sizeof (PIXELFORMATDESCRIPTOR), &pfd))
UpdateDlg(&pfd);
//
// Release the DC.
//
::ReleaseDC(hwndview, dc);
}
----------------------
// local variables
int iMax;
PIXELFORMATDESCRIPTOR pfd;
int iPixelFormat;
// initialize a pixel format index variable
iPixelFormat = 1;
// keep obtaining and examining pixel format data...
do {
// try to obtain some pixel format data
iMax = DescribePixelFormat(dc, iPixelFormat, sizeof (pfd), &pfd);
// if there was some problem with that...
if (iMax == 0)
// return indicating failure
return (FALSE);
// we have successfully obtained pixel format data
// let's examine the pixel format data...
myPixelFormatExaminer(&pfd);
}
// ...until we've looked at all the device context's pixel formats
while (++iPixelFormat <= iMax);
#endif