1 /////////////////////////////////////////////////////////////////////////////
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 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "glcanvas.h"
16 #include "wx/wxprec.h"
18 #if defined(__BORLANDC__)
30 #include <wx/msw/private.h>
31 #include <wx/settings.h>
34 #include <wx/glcanvas.h>
36 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
37 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
39 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
40 WPARAM wParam
, LPARAM lParam
);
43 * GLContext implementation
46 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
50 m_hDC
= win
->GetHDC();
52 m_glContext
= wglCreateContext((HDC
) m_hDC
);
53 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
55 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
58 wxGLContext::wxGLContext(
59 bool isRGB
, wxGLCanvas
*win
,
60 const wxPalette
& palette
,
61 const wxGLContext
*other
/* for sharing display lists */
66 m_hDC
= win
->GetHDC();
68 m_glContext
= wglCreateContext((HDC
) m_hDC
);
69 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
72 wglShareLists( other
->m_glContext
, m_glContext
);
74 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
77 wxGLContext::~wxGLContext()
81 wglMakeCurrent(NULL
, NULL
);
82 wglDeleteContext(m_glContext
);
86 void wxGLContext::SwapBuffers()
90 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
91 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
95 void wxGLContext::SetCurrent()
99 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
103 setupPixelFormat(hDC);
108 void wxGLContext::SetColour(const char *colour
)
113 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
116 r
= (float)(col
->Red()/256.0);
117 g
= (float)(col
->Green()/256.0);
118 b
= (float)(col
->Blue()/256.0);
125 * wxGLCanvas implementation
128 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
130 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
131 EVT_SIZE(wxGLCanvas::OnSize
)
132 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
133 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
136 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
137 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
138 int *attribList
, const wxPalette
& palette
) : wxWindow()
140 m_glContext
= (wxGLContext
*) NULL
;
142 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
146 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
147 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
150 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
152 SetupPixelFormat(attribList
);
153 SetupPalette(palette
);
155 m_glContext
= new wxGLContext(TRUE
, this, palette
);
158 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
159 const wxGLContext
*shared
, wxWindowID id
,
160 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
161 int *attribList
, const wxPalette
& palette
)
164 m_glContext
= (wxGLContext
*) NULL
;
166 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
170 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
171 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
174 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
176 SetupPixelFormat(attribList
);
177 SetupPalette(palette
);
179 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
182 // Not very useful for wxMSW, but this is to be wxGTK compliant
184 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
185 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
186 int *attribList
, const wxPalette
& palette
):
189 m_glContext
= (wxGLContext
*) NULL
;
191 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
195 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
196 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
199 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
201 SetupPixelFormat(attribList
);
202 SetupPalette(palette
);
204 wxGLContext
*sharedContext
=0;
205 if (shared
) sharedContext
=shared
->GetContext();
206 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
209 wxGLCanvas::~wxGLCanvas()
214 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
217 // Replaces wxWindow::Create functionality, since we need to use a different
219 bool wxGLCanvas::Create(wxWindow
*parent
,
224 const wxString
& name
)
226 static bool s_registeredGLCanvasClass
= FALSE
;
228 // We have to register a special window class because we need
229 // the CS_OWNDC style for GLCanvas.
232 From Angel Popov <jumpo@bitex.com>
234 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
235 how this problem can be fixed:
237 "There are 5 common DCs available in Win95. These are aquired when you call
238 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
239 OWNDC flagged windows do not get their DC from the common DC pool, the issue
240 is they require 800 bytes each from the limited 64Kb local heap for GDI."
242 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
243 do), Win95 will actually "steal" it from you. MakeCurrent fails,
244 apparently, because Windows re-assigns the HDC to a different window. The
245 only way to prevent this, the only reliable means, is to set CS_OWNDC."
248 if (!s_registeredGLCanvasClass
)
252 // the fields which are common to all classes
253 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
254 wndclass
.cbClsExtra
= 0;
255 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
256 wndclass
.hInstance
= wxhInstance
;
257 wndclass
.hIcon
= (HICON
) NULL
;
258 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
259 wndclass
.lpszMenuName
= NULL
;
261 // Register the GLCanvas class name
262 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
263 wndclass
.lpszClassName
= wxGLCanvasClassName
;
264 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
266 if ( !::RegisterClass(&wndclass
) )
268 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
272 // Register the GLCanvas class name for windows which don't do full repaint
274 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
275 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
277 if ( !::RegisterClass(&wndclass
) )
279 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
281 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
286 s_registeredGLCanvasClass
= TRUE
;
289 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
291 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
294 parent
->AddChild(this);
297 if ( style
& wxBORDER
)
298 msflags
|= WS_BORDER
;
299 if ( style
& wxTHICK_FRAME
)
300 msflags
|= WS_THICKFRAME
;
303 A general rule with OpenGL and Win32 is that any window that will have a
304 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
305 You can find references about this within the knowledge base and most OpenGL
306 books that contain the wgl function descriptions.
309 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
312 WXDWORD exStyle
= Determine3DEffects(WS_EX_CLIENTEDGE
, &want3D
);
314 // Even with extended styles, need to combine with WS_BORDER
315 // for them to look right.
316 if ( want3D
|| (m_windowStyle
& wxSIMPLE_BORDER
) || (m_windowStyle
& wxRAISED_BORDER
) ||
317 (m_windowStyle
& wxSUNKEN_BORDER
) || (m_windowStyle
& wxDOUBLE_BORDER
))
319 msflags
|= WS_BORDER
;
322 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
325 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
328 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
329 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
333 while( (attribList
[arg
]!=0) )
335 switch( attribList
[arg
++] )
338 pfd
.iPixelType
= PFD_TYPE_RGBA
;
340 case WX_GL_BUFFER_SIZE
:
341 pfd
.cColorBits
= attribList
[arg
++];
344 // this member looks like it may be obsolete
345 if (attribList
[arg
] > 0) {
346 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
347 } else if (attribList
[arg
] < 0) {
348 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
350 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
354 case WX_GL_DOUBLEBUFFER
:
355 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
358 pfd
.dwFlags
|= PFD_STEREO
;
360 case WX_GL_AUX_BUFFERS
:
361 pfd
.cAuxBuffers
= attribList
[arg
++];
364 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
366 case WX_GL_MIN_GREEN
:
367 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
370 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
372 case WX_GL_MIN_ALPHA
:
373 // doesn't count in cColorBits
374 pfd
.cAlphaBits
= attribList
[arg
++];
376 case WX_GL_DEPTH_SIZE
:
377 pfd
.cDepthBits
= attribList
[arg
++];
379 case WX_GL_STENCIL_SIZE
:
380 pfd
.cStencilBits
= attribList
[arg
++];
382 case WX_GL_MIN_ACCUM_RED
:
383 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
385 case WX_GL_MIN_ACCUM_GREEN
:
386 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
388 case WX_GL_MIN_ACCUM_BLUE
:
389 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
391 case WX_GL_MIN_ACCUM_ALPHA
:
392 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
401 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
404 PIXELFORMATDESCRIPTOR pfd
= {
405 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
409 PFD_DOUBLEBUFFER
, /* support double-buffering */
410 PFD_TYPE_RGBA
, /* color type */
411 16, /* prefered color depth */
412 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
413 0, /* no alpha buffer */
414 0, /* alpha bits (ignored) */
415 0, /* no accumulation buffer */
416 0, 0, 0, 0, /* accum bits (ignored) */
417 16, /* depth buffer */
418 0, /* no stencil buffer */
419 0, /* no auxiliary buffers */
420 PFD_MAIN_PLANE
, /* main layer */
422 0, 0, 0, /* no layer, visible, damage masks */
425 AdjustPFDForAttributes(pfd
, attribList
);
427 pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
428 if (pixelFormat
== 0) {
429 wxLogWarning(_("ChoosePixelFormat failed."));
432 if (SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) != TRUE
) {
433 wxLogWarning(_("SetPixelFormat failed."));
438 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
440 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
441 PIXELFORMATDESCRIPTOR pfd
;
443 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
445 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
455 if ( !m_palette
.Ok() )
457 m_palette
= CreateDefaultPalette();
462 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
463 RealizePalette((HDC
) m_hDC
);
467 wxPalette
wxGLCanvas::CreateDefaultPalette()
469 PIXELFORMATDESCRIPTOR pfd
;
471 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
473 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
475 paletteSize
= 1 << pfd
.cColorBits
;
478 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
479 pPal
->palVersion
= 0x300;
480 pPal
->palNumEntries
= paletteSize
;
482 /* build a simple RGB color palette */
484 int redMask
= (1 << pfd
.cRedBits
) - 1;
485 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
486 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
489 for (i
=0; i
<paletteSize
; ++i
) {
490 pPal
->palPalEntry
[i
].peRed
=
491 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
492 pPal
->palPalEntry
[i
].peGreen
=
493 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
494 pPal
->palPalEntry
[i
].peBlue
=
495 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
496 pPal
->palPalEntry
[i
].peFlags
= 0;
500 HPALETTE hPalette
= CreatePalette(pPal
);
504 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
509 void wxGLCanvas::SwapBuffers()
512 m_glContext
->SwapBuffers();
515 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
519 void wxGLCanvas::SetCurrent()
523 m_glContext
->SetCurrent();
527 void wxGLCanvas::SetColour(const char *colour
)
530 m_glContext
->SetColour(colour
);
533 // TODO: Have to have this called by parent frame (?)
534 // So we need wxFrame to call OnQueryNewPalette for all children...
535 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
537 /* realize palette if this is the current window */
538 if ( GetPalette()->Ok() ) {
539 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
540 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
541 ::RealizePalette((HDC
) GetHDC());
543 event
.SetPaletteRealized(TRUE
);
546 event
.SetPaletteRealized(FALSE
);
549 // I think this doesn't have to be propagated to child windows.
550 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
552 /* realize palette if this is *not* the current window */
554 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
556 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
557 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
558 ::RealizePalette((HDC
) GetHDC());
563 /* Give extensions proper function names. */
565 /* EXT_vertex_array */
566 void glArrayElementEXT(GLint i
)
570 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
574 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
576 #ifdef GL_EXT_vertex_array
577 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
581 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
585 (* proc
) (mode
, first
, count
);
589 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
593 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
597 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
601 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
603 #ifdef GL_EXT_vertex_array
604 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
608 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
612 (* proc
) (type
, stride
, count
, pointer
);
616 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
620 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
622 #ifdef GL_EXT_vertex_array
623 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
627 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
630 (* proc
) (size
, type
, stride
, count
, pointer
);
634 /* EXT_color_subtable */
635 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
639 /* EXT_color_table */
640 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
644 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
648 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
652 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
656 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
660 /* SGI_compiled_vertex_array */
661 void glLockArraysSGI(GLint first
, GLsizei count
)
665 void glUnlockArraysSGI()
670 /* SGI_cull_vertex */
671 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
675 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
680 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
684 /* SGI_index_material */
685 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
690 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
695 //---------------------------------------------------------------------------
697 //---------------------------------------------------------------------------
699 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
701 bool wxGLApp::InitGLVisual(int *attribList
)
704 PIXELFORMATDESCRIPTOR pfd
= {
705 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
709 PFD_DOUBLEBUFFER
, /* support double-buffering */
710 PFD_TYPE_RGBA
, /* color type */
711 16, /* prefered color depth */
712 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
713 0, /* no alpha buffer */
714 0, /* alpha bits (ignored) */
715 0, /* no accumulation buffer */
716 0, 0, 0, 0, /* accum bits (ignored) */
717 16, /* depth buffer */
718 0, /* no stencil buffer */
719 0, /* no auxiliary buffers */
720 PFD_MAIN_PLANE
, /* main layer */
722 0, 0, 0, /* no layer, visible, damage masks */
725 AdjustPFDForAttributes(pfd
, attribList
);
727 // use DC for whole (root) screen, since no windows have yet been created
728 pixelFormat
= ChoosePixelFormat((HDC
) ::GetDC(NULL
), &pfd
);
730 if (pixelFormat
== 0) {
731 wxLogError(_("Failed to initialize OpenGL"));