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__)
26 #define wxUSE_GLCANVAS 1
31 #include "wx/settings.h"
36 #include "wx/msw/private.h"
38 #include "myglcanvas.h"
40 const wxChar
* wxGLCanvasName
= wxT("GLcanvas");
41 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
42 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
44 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
45 WPARAM wParam
, LPARAM lParam
);
48 * GLContext implementation
51 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
55 m_hDC
= win
->GetHDC();
57 m_glContext
= wglCreateContext((HDC
) m_hDC
);
58 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
60 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
63 wxGLContext::wxGLContext(
64 bool isRGB
, wxGLCanvas
*win
,
65 const wxPalette
& palette
,
66 const wxGLContext
*other
/* for sharing display lists */
71 m_hDC
= win
->GetHDC();
73 m_glContext
= wglCreateContext((HDC
) m_hDC
);
74 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
77 wglShareLists( other
->m_glContext
, m_glContext
);
79 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
82 wxGLContext::~wxGLContext()
86 wglMakeCurrent(NULL
, NULL
);
87 wglDeleteContext(m_glContext
);
91 void wxGLContext::SwapBuffers()
95 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
96 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
100 void wxGLContext::SetCurrent()
104 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
108 setupPixelFormat(hDC);
113 void wxGLContext::SetColour(const wxChar
*colour
)
118 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
121 r
= (float)(col
->Red()/256.0);
122 g
= (float)(col
->Green()/256.0);
123 b
= (float)(col
->Blue()/256.0);
130 * wxGLCanvas implementation
133 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
135 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
136 EVT_SIZE(wxGLCanvas::OnSize
)
137 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
138 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
141 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
142 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
143 int *attribList
, const wxPalette
& palette
) : wxWindow()
145 m_glContext
= (wxGLContext
*) NULL
;
147 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
151 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
152 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
155 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
157 SetupPixelFormat(attribList
);
158 SetupPalette(palette
);
160 m_glContext
= new wxGLContext(TRUE
, this, palette
);
163 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
164 const wxGLContext
*shared
, wxWindowID id
,
165 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
166 int *attribList
, const wxPalette
& palette
)
169 m_glContext
= (wxGLContext
*) NULL
;
171 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
175 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
176 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
179 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
181 SetupPixelFormat(attribList
);
182 SetupPalette(palette
);
184 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
187 // Not very useful for wxMSW, but this is to be wxGTK compliant
189 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
190 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
191 int *attribList
, const wxPalette
& palette
):
194 m_glContext
= (wxGLContext
*) NULL
;
196 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
200 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
201 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
204 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
206 SetupPixelFormat(attribList
);
207 SetupPalette(palette
);
209 wxGLContext
*sharedContext
=0;
210 if (shared
) sharedContext
=shared
->GetContext();
211 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
214 wxGLCanvas::~wxGLCanvas()
219 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
222 // Replaces wxWindow::Create functionality, since we need to use a different
224 bool wxGLCanvas::Create(wxWindow
*parent
,
229 const wxString
& name
)
231 static bool s_registeredGLCanvasClass
= FALSE
;
233 // We have to register a special window class because we need
234 // the CS_OWNDC style for GLCanvas.
237 From Angel Popov <jumpo@bitex.com>
239 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
240 how this problem can be fixed:
242 "There are 5 common DCs available in Win95. These are aquired when you call
243 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
244 OWNDC flagged windows do not get their DC from the common DC pool, the issue
245 is they require 800 bytes each from the limited 64Kb local heap for GDI."
247 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
248 do), Win95 will actually "steal" it from you. MakeCurrent fails,
249 apparently, because Windows re-assigns the HDC to a different window. The
250 only way to prevent this, the only reliable means, is to set CS_OWNDC."
253 if (!s_registeredGLCanvasClass
)
257 // the fields which are common to all classes
258 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
259 wndclass
.cbClsExtra
= 0;
260 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
261 wndclass
.hInstance
= wxhInstance
;
262 wndclass
.hIcon
= (HICON
) NULL
;
263 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
264 wndclass
.lpszMenuName
= NULL
;
266 // Register the GLCanvas class name
267 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
268 wndclass
.lpszClassName
= wxGLCanvasClassName
;
269 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
271 if ( !::RegisterClass(&wndclass
) )
273 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
277 // Register the GLCanvas class name for windows which don't do full repaint
279 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
280 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
282 if ( !::RegisterClass(&wndclass
) )
284 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
286 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
291 s_registeredGLCanvasClass
= TRUE
;
294 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
296 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
299 parent
->AddChild(this);
302 if ( style
& wxBORDER
)
303 msflags
|= WS_BORDER
;
304 if ( style
& wxTHICK_FRAME
)
305 msflags
|= WS_THICKFRAME
;
308 A general rule with OpenGL and Win32 is that any window that will have a
309 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
310 You can find references about this within the knowledge base and most OpenGL
311 books that contain the wgl function descriptions.
314 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
317 WXDWORD exStyle
= Determine3DEffects(WS_EX_CLIENTEDGE
, &want3D
);
319 // Even with extended styles, need to combine with WS_BORDER
320 // for them to look right.
321 if ( want3D
|| (m_windowStyle
& wxSIMPLE_BORDER
) || (m_windowStyle
& wxRAISED_BORDER
) ||
322 (m_windowStyle
& wxSUNKEN_BORDER
) || (m_windowStyle
& wxDOUBLE_BORDER
))
324 msflags
|= WS_BORDER
;
327 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
330 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
333 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
334 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
338 while( (attribList
[arg
]!=0) )
340 switch( attribList
[arg
++] )
343 pfd
.iPixelType
= PFD_TYPE_RGBA
;
345 case WX_GL_BUFFER_SIZE
:
346 pfd
.cColorBits
= attribList
[arg
++];
349 // this member looks like it may be obsolete
350 if (attribList
[arg
] > 0) {
351 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
352 } else if (attribList
[arg
] < 0) {
353 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
355 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
359 case WX_GL_DOUBLEBUFFER
:
360 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
363 pfd
.dwFlags
|= PFD_STEREO
;
365 case WX_GL_AUX_BUFFERS
:
366 pfd
.cAuxBuffers
= attribList
[arg
++];
369 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
371 case WX_GL_MIN_GREEN
:
372 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
375 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
377 case WX_GL_MIN_ALPHA
:
378 // doesn't count in cColorBits
379 pfd
.cAlphaBits
= attribList
[arg
++];
381 case WX_GL_DEPTH_SIZE
:
382 pfd
.cDepthBits
= attribList
[arg
++];
384 case WX_GL_STENCIL_SIZE
:
385 pfd
.cStencilBits
= attribList
[arg
++];
387 case WX_GL_MIN_ACCUM_RED
:
388 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
390 case WX_GL_MIN_ACCUM_GREEN
:
391 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
393 case WX_GL_MIN_ACCUM_BLUE
:
394 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
396 case WX_GL_MIN_ACCUM_ALPHA
:
397 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
406 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
408 PIXELFORMATDESCRIPTOR pfd
= {
409 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
413 PFD_DOUBLEBUFFER
, /* support double-buffering */
414 PFD_TYPE_RGBA
, /* color type */
415 16, /* prefered color depth */
416 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
417 0, /* no alpha buffer */
418 0, /* alpha bits (ignored) */
419 0, /* no accumulation buffer */
420 0, 0, 0, 0, /* accum bits (ignored) */
421 16, /* depth buffer */
422 0, /* no stencil buffer */
423 0, /* no auxiliary buffers */
424 PFD_MAIN_PLANE
, /* main layer */
426 0, 0, 0, /* no layer, visible, damage masks */
429 AdjustPFDForAttributes(pfd
, attribList
);
431 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
432 if (pixelFormat
== 0) {
433 wxLogLastError(_T("ChoosePixelFormat"));
436 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
437 wxLogLastError(_T("SetPixelFormat"));
442 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
444 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
445 PIXELFORMATDESCRIPTOR pfd
;
447 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
449 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
459 if ( !m_palette
.Ok() )
461 m_palette
= CreateDefaultPalette();
466 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
467 RealizePalette((HDC
) m_hDC
);
471 wxPalette
wxGLCanvas::CreateDefaultPalette()
473 PIXELFORMATDESCRIPTOR pfd
;
475 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
477 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
479 paletteSize
= 1 << pfd
.cColorBits
;
482 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
483 pPal
->palVersion
= 0x300;
484 pPal
->palNumEntries
= paletteSize
;
486 /* build a simple RGB color palette */
488 int redMask
= (1 << pfd
.cRedBits
) - 1;
489 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
490 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
493 for (i
=0; i
<paletteSize
; ++i
) {
494 pPal
->palPalEntry
[i
].peRed
=
495 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
496 pPal
->palPalEntry
[i
].peGreen
=
497 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
498 pPal
->palPalEntry
[i
].peBlue
=
499 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
500 pPal
->palPalEntry
[i
].peFlags
= 0;
504 HPALETTE hPalette
= CreatePalette(pPal
);
508 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
513 void wxGLCanvas::SwapBuffers()
516 m_glContext
->SwapBuffers();
519 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
523 void wxGLCanvas::SetCurrent()
527 m_glContext
->SetCurrent();
531 void wxGLCanvas::SetColour(const wxChar
*colour
)
534 m_glContext
->SetColour(colour
);
537 // TODO: Have to have this called by parent frame (?)
538 // So we need wxFrame to call OnQueryNewPalette for all children...
539 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
541 /* realize palette if this is the current window */
542 if ( GetPalette()->Ok() ) {
543 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
544 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
545 ::RealizePalette((HDC
) GetHDC());
547 event
.SetPaletteRealized(TRUE
);
550 event
.SetPaletteRealized(FALSE
);
553 // I think this doesn't have to be propagated to child windows.
554 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
556 /* realize palette if this is *not* the current window */
558 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
560 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
561 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
562 ::RealizePalette((HDC
) GetHDC());
567 /* Give extensions proper function names. */
569 /* EXT_vertex_array */
570 void glArrayElementEXT(GLint i
)
574 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
578 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
580 #ifdef GL_EXT_vertex_array
581 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
585 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
589 (* proc
) (mode
, first
, count
);
593 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
597 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
601 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
605 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
607 #ifdef GL_EXT_vertex_array
608 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
612 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
616 (* proc
) (type
, stride
, count
, pointer
);
620 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
624 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
626 #ifdef GL_EXT_vertex_array
627 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
631 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
634 (* proc
) (size
, type
, stride
, count
, pointer
);
638 /* EXT_color_subtable */
639 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
643 /* EXT_color_table */
644 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
648 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
652 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
656 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
660 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
664 /* SGI_compiled_vertex_array */
665 void glLockArraysSGI(GLint first
, GLsizei count
)
669 void glUnlockArraysSGI()
674 /* SGI_cull_vertex */
675 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
679 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
684 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
688 /* SGI_index_material */
689 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
694 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
699 //---------------------------------------------------------------------------
701 //---------------------------------------------------------------------------
703 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
705 bool wxGLApp::InitGLVisual(int *attribList
)
708 PIXELFORMATDESCRIPTOR pfd
= {
709 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
713 PFD_DOUBLEBUFFER
, /* support double-buffering */
714 PFD_TYPE_RGBA
, /* color type */
715 16, /* prefered color depth */
716 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
717 0, /* no alpha buffer */
718 0, /* alpha bits (ignored) */
719 0, /* no accumulation buffer */
720 0, 0, 0, 0, /* accum bits (ignored) */
721 16, /* depth buffer */
722 0, /* no stencil buffer */
723 0, /* no auxiliary buffers */
724 PFD_MAIN_PLANE
, /* main layer */
726 0, 0, 0, /* no layer, visible, damage masks */
729 AdjustPFDForAttributes(pfd
, attribList
);
731 // use DC for whole (root) screen, since no windows have yet been created
732 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
734 if (pixelFormat
== 0) {
735 wxLogError(_("Failed to initialize OpenGL"));