1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWindows under MS Windows
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "glcanvas.h"
16 #include "wx/wxprec.h"
18 #if defined(__BORLANDC__)
26 #include "wx/settings.h"
32 #include "wx/msw/private.h"
34 // DLL options compatibility check:
36 WX_CHECK_BUILD_OPTIONS("wxGL")
38 #include "wx/glcanvas.h"
41 The following two compiler directives are specific to the Microsoft Visual
42 C++ family of compilers
44 Fundementally what they do is instruct the linker to use these two libraries
45 for the resolution of symbols. In essence, this is the equivalent of adding
46 these two libraries to either the Makefile or project file.
48 This is NOT a recommended technique, and certainly is unlikely to be used
49 anywhere else in wxWindows given it is so specific to not only wxMSW, but
50 also the VC compiler. However, in the case of opengl support, it's an
51 applicable technique as opengl is optional in setup.h This code (wrapped by
52 wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
53 setup.h rather than by having to modify either the project or DSP fle.
55 See MSDN for further information on the exact usage of these commands.
58 # pragma comment( lib, "opengl32" )
59 # pragma comment( lib, "glu32" )
63 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
64 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
66 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
67 WPARAM wParam
, LPARAM lParam
);
70 * GLContext implementation
73 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
77 m_hDC
= win
->GetHDC();
79 m_glContext
= wglCreateContext((HDC
) m_hDC
);
80 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
82 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
85 wxGLContext::wxGLContext(
86 bool isRGB
, wxGLCanvas
*win
,
87 const wxPalette
& palette
,
88 const wxGLContext
*other
/* for sharing display lists */
93 m_hDC
= win
->GetHDC();
95 m_glContext
= wglCreateContext((HDC
) m_hDC
);
96 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
99 wglShareLists( other
->m_glContext
, m_glContext
);
101 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
104 wxGLContext::~wxGLContext()
108 wglMakeCurrent(NULL
, NULL
);
109 wglDeleteContext(m_glContext
);
113 void wxGLContext::SwapBuffers()
117 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
118 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
122 void wxGLContext::SetCurrent()
126 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
130 setupPixelFormat(hDC);
135 void wxGLContext::SetColour(const wxChar
*colour
)
140 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
143 r
= (float)(col
->Red()/256.0);
144 g
= (float)(col
->Green()/256.0);
145 b
= (float)(col
->Blue()/256.0);
152 * wxGLCanvas implementation
155 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
157 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
158 EVT_SIZE(wxGLCanvas::OnSize
)
159 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
160 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
163 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
164 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
165 int *attribList
, const wxPalette
& palette
) : wxWindow()
167 m_glContext
= (wxGLContext
*) NULL
;
169 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
173 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
174 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
177 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
179 SetupPixelFormat(attribList
);
180 SetupPalette(palette
);
182 m_glContext
= new wxGLContext(TRUE
, this, palette
);
185 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
186 const wxGLContext
*shared
, wxWindowID id
,
187 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
188 int *attribList
, const wxPalette
& palette
)
191 m_glContext
= (wxGLContext
*) NULL
;
193 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
197 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
198 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
201 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
203 SetupPixelFormat(attribList
);
204 SetupPalette(palette
);
206 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
209 // Not very useful for wxMSW, but this is to be wxGTK compliant
211 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
212 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
213 int *attribList
, const wxPalette
& palette
):
216 m_glContext
= (wxGLContext
*) NULL
;
218 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
222 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
223 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
226 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
228 SetupPixelFormat(attribList
);
229 SetupPalette(palette
);
231 wxGLContext
*sharedContext
=0;
232 if (shared
) sharedContext
=shared
->GetContext();
233 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
236 wxGLCanvas::~wxGLCanvas()
241 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
244 // Replaces wxWindow::Create functionality, since we need to use a different
246 bool wxGLCanvas::Create(wxWindow
*parent
,
251 const wxString
& name
)
253 static bool s_registeredGLCanvasClass
= FALSE
;
255 // We have to register a special window class because we need
256 // the CS_OWNDC style for GLCanvas.
259 From Angel Popov <jumpo@bitex.com>
261 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
262 how this problem can be fixed:
264 "There are 5 common DCs available in Win95. These are aquired when you call
265 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
266 OWNDC flagged windows do not get their DC from the common DC pool, the issue
267 is they require 800 bytes each from the limited 64Kb local heap for GDI."
269 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
270 do), Win95 will actually "steal" it from you. MakeCurrent fails,
271 apparently, because Windows re-assigns the HDC to a different window. The
272 only way to prevent this, the only reliable means, is to set CS_OWNDC."
275 if (!s_registeredGLCanvasClass
)
279 // the fields which are common to all classes
280 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
281 wndclass
.cbClsExtra
= 0;
282 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
283 wndclass
.hInstance
= wxhInstance
;
284 wndclass
.hIcon
= (HICON
) NULL
;
285 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
286 wndclass
.lpszMenuName
= NULL
;
288 // Register the GLCanvas class name
289 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
290 wndclass
.lpszClassName
= wxGLCanvasClassName
;
291 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
293 if ( !::RegisterClass(&wndclass
) )
295 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
299 // Register the GLCanvas class name for windows which don't do full repaint
301 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
302 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
304 if ( !::RegisterClass(&wndclass
) )
306 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
308 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
313 s_registeredGLCanvasClass
= TRUE
;
316 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
318 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
321 parent
->AddChild(this);
326 A general rule with OpenGL and Win32 is that any window that will have a
327 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
328 You can find references about this within the knowledge base and most OpenGL
329 books that contain the wgl function descriptions.
333 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
334 msflags
|= MSWGetStyle(style
, & exStyle
) ;
336 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
339 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
342 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
343 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
347 while( (attribList
[arg
]!=0) )
349 switch( attribList
[arg
++] )
352 pfd
.iPixelType
= PFD_TYPE_RGBA
;
354 case WX_GL_BUFFER_SIZE
:
355 pfd
.cColorBits
= attribList
[arg
++];
358 // this member looks like it may be obsolete
359 if (attribList
[arg
] > 0) {
360 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
361 } else if (attribList
[arg
] < 0) {
362 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
364 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
368 case WX_GL_DOUBLEBUFFER
:
369 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
372 pfd
.dwFlags
|= PFD_STEREO
;
374 case WX_GL_AUX_BUFFERS
:
375 pfd
.cAuxBuffers
= attribList
[arg
++];
378 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
380 case WX_GL_MIN_GREEN
:
381 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
384 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
386 case WX_GL_MIN_ALPHA
:
387 // doesn't count in cColorBits
388 pfd
.cAlphaBits
= attribList
[arg
++];
390 case WX_GL_DEPTH_SIZE
:
391 pfd
.cDepthBits
= attribList
[arg
++];
393 case WX_GL_STENCIL_SIZE
:
394 pfd
.cStencilBits
= attribList
[arg
++];
396 case WX_GL_MIN_ACCUM_RED
:
397 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
399 case WX_GL_MIN_ACCUM_GREEN
:
400 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
402 case WX_GL_MIN_ACCUM_BLUE
:
403 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
405 case WX_GL_MIN_ACCUM_ALPHA
:
406 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
415 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
417 PIXELFORMATDESCRIPTOR pfd
= {
418 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
422 PFD_DOUBLEBUFFER
, /* support double-buffering */
423 PFD_TYPE_RGBA
, /* color type */
424 16, /* prefered color depth */
425 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
426 0, /* no alpha buffer */
427 0, /* alpha bits (ignored) */
428 0, /* no accumulation buffer */
429 0, 0, 0, 0, /* accum bits (ignored) */
430 16, /* depth buffer */
431 0, /* no stencil buffer */
432 0, /* no auxiliary buffers */
433 PFD_MAIN_PLANE
, /* main layer */
435 0, 0, 0, /* no layer, visible, damage masks */
438 AdjustPFDForAttributes(pfd
, attribList
);
440 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
441 if (pixelFormat
== 0) {
442 wxLogLastError(_T("ChoosePixelFormat"));
445 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
446 wxLogLastError(_T("SetPixelFormat"));
451 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
453 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
454 PIXELFORMATDESCRIPTOR pfd
;
456 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
458 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
468 if ( !m_palette
.Ok() )
470 m_palette
= CreateDefaultPalette();
475 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
476 RealizePalette((HDC
) m_hDC
);
480 wxPalette
wxGLCanvas::CreateDefaultPalette()
482 PIXELFORMATDESCRIPTOR pfd
;
484 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
486 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
488 paletteSize
= 1 << pfd
.cColorBits
;
491 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
492 pPal
->palVersion
= 0x300;
493 pPal
->palNumEntries
= paletteSize
;
495 /* build a simple RGB color palette */
497 int redMask
= (1 << pfd
.cRedBits
) - 1;
498 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
499 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
502 for (i
=0; i
<paletteSize
; ++i
) {
503 pPal
->palPalEntry
[i
].peRed
=
504 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
505 pPal
->palPalEntry
[i
].peGreen
=
506 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
507 pPal
->palPalEntry
[i
].peBlue
=
508 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
509 pPal
->palPalEntry
[i
].peFlags
= 0;
513 HPALETTE hPalette
= CreatePalette(pPal
);
517 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
522 void wxGLCanvas::SwapBuffers()
525 m_glContext
->SwapBuffers();
528 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
532 void wxGLCanvas::SetCurrent()
536 m_glContext
->SetCurrent();
540 void wxGLCanvas::SetColour(const wxChar
*colour
)
543 m_glContext
->SetColour(colour
);
546 // TODO: Have to have this called by parent frame (?)
547 // So we need wxFrame to call OnQueryNewPalette for all children...
548 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
550 /* realize palette if this is the current window */
551 if ( GetPalette()->Ok() ) {
552 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
553 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
554 ::RealizePalette((HDC
) GetHDC());
556 event
.SetPaletteRealized(TRUE
);
559 event
.SetPaletteRealized(FALSE
);
562 // I think this doesn't have to be propagated to child windows.
563 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
565 /* realize palette if this is *not* the current window */
567 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
569 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
570 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
571 ::RealizePalette((HDC
) GetHDC());
576 /* Give extensions proper function names. */
578 /* EXT_vertex_array */
579 void glArrayElementEXT(GLint i
)
583 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
587 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
589 #ifdef GL_EXT_vertex_array
590 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
594 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
598 (* proc
) (mode
, first
, count
);
602 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
606 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
610 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
614 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
616 #ifdef GL_EXT_vertex_array
617 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
621 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
625 (* proc
) (type
, stride
, count
, pointer
);
629 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
633 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
635 #ifdef GL_EXT_vertex_array
636 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
640 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
643 (* proc
) (size
, type
, stride
, count
, pointer
);
647 /* EXT_color_subtable */
648 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
652 /* EXT_color_table */
653 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
657 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
661 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
665 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
669 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
673 /* SGI_compiled_vertex_array */
674 void glLockArraysSGI(GLint first
, GLsizei count
)
678 void glUnlockArraysSGI()
683 /* SGI_cull_vertex */
684 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
688 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
693 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
697 /* SGI_index_material */
698 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
703 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
708 //---------------------------------------------------------------------------
710 //---------------------------------------------------------------------------
712 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
714 bool wxGLApp::InitGLVisual(int *attribList
)
717 PIXELFORMATDESCRIPTOR pfd
= {
718 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
722 PFD_DOUBLEBUFFER
, /* support double-buffering */
723 PFD_TYPE_RGBA
, /* color type */
724 16, /* prefered color depth */
725 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
726 0, /* no alpha buffer */
727 0, /* alpha bits (ignored) */
728 0, /* no accumulation buffer */
729 0, 0, 0, 0, /* accum bits (ignored) */
730 16, /* depth buffer */
731 0, /* no stencil buffer */
732 0, /* no auxiliary buffers */
733 PFD_MAIN_PLANE
, /* main layer */
735 0, 0, 0, /* no layer, visible, damage masks */
738 AdjustPFDForAttributes(pfd
, attribList
);
740 // use DC for whole (root) screen, since no windows have yet been created
741 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
743 if (pixelFormat
== 0) {
744 wxLogError(_("Failed to initialize OpenGL"));