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
33 #include <wx/msw/private.h>
34 #include <wx/settings.h>
37 #include "myglcanvas.h"
39 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
40 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
42 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
43 WPARAM wParam
, LPARAM lParam
);
46 * GLContext implementation
49 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
53 m_hDC
= win
->GetHDC();
55 m_glContext
= wglCreateContext((HDC
) m_hDC
);
56 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
58 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
61 wxGLContext::wxGLContext(
62 bool isRGB
, wxGLCanvas
*win
,
63 const wxPalette
& palette
,
64 const wxGLContext
*other
/* for sharing display lists */
69 m_hDC
= win
->GetHDC();
71 m_glContext
= wglCreateContext((HDC
) m_hDC
);
72 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
75 wglShareLists( other
->m_glContext
, m_glContext
);
77 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
80 wxGLContext::~wxGLContext()
84 wglMakeCurrent(NULL
, NULL
);
85 wglDeleteContext(m_glContext
);
89 void wxGLContext::SwapBuffers()
93 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
94 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
98 void wxGLContext::SetCurrent()
102 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
106 setupPixelFormat(hDC);
111 void wxGLContext::SetColour(const char *colour
)
116 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
119 r
= (float)(col
->Red()/256.0);
120 g
= (float)(col
->Green()/256.0);
121 b
= (float)(col
->Blue()/256.0);
128 * wxGLCanvas implementation
131 IMPLEMENT_CLASS(wxGLCanvas
, wxScrolledWindow
)
133 BEGIN_EVENT_TABLE(wxGLCanvas
, wxScrolledWindow
)
134 EVT_SIZE(wxGLCanvas::OnSize
)
135 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
136 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
139 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
140 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
141 int *attribList
, const wxPalette
& palette
) : wxScrolledWindow()
143 m_glContext
= (wxGLContext
*) NULL
;
145 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
149 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
150 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
));
153 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
155 SetupPixelFormat(attribList
);
156 SetupPalette(palette
);
158 m_glContext
= new wxGLContext(TRUE
, this, palette
);
161 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
162 const wxGLContext
*shared
, wxWindowID id
,
163 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
164 int *attribList
, const wxPalette
& palette
)
167 m_glContext
= (wxGLContext
*) NULL
;
169 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
173 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
174 SetFont(wxSystemSettings::GetSystemFont(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
, shared
);
185 // Not very useful for wxMSW, but this is to be wxGTK compliant
187 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
188 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
189 int *attribList
, const wxPalette
& palette
):
192 m_glContext
= (wxGLContext
*) NULL
;
194 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
198 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
199 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
));
202 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
204 SetupPixelFormat(attribList
);
205 SetupPalette(palette
);
207 wxGLContext
*sharedContext
=0;
208 if (shared
) sharedContext
=shared
->GetContext();
209 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
212 wxGLCanvas::~wxGLCanvas()
217 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
220 // Replaces wxWindow::Create functionality, since we need to use a different
222 bool wxGLCanvas::Create(wxWindow
*parent
,
227 const wxString
& name
)
229 static bool s_registeredGLCanvasClass
= FALSE
;
231 // We have to register a special window class because we need
232 // the CS_OWNDC style for GLCanvas.
235 From Angel Popov <jumpo@bitex.com>
237 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
238 how this problem can be fixed:
240 "There are 5 common DCs available in Win95. These are aquired when you call
241 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
242 OWNDC flagged windows do not get their DC from the common DC pool, the issue
243 is they require 800 bytes each from the limited 64Kb local heap for GDI."
245 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
246 do), Win95 will actually "steal" it from you. MakeCurrent fails,
247 apparently, because Windows re-assigns the HDC to a different window. The
248 only way to prevent this, the only reliable means, is to set CS_OWNDC."
251 if (!s_registeredGLCanvasClass
)
255 // the fields which are common to all classes
256 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
257 wndclass
.cbClsExtra
= 0;
258 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
259 wndclass
.hInstance
= wxhInstance
;
260 wndclass
.hIcon
= (HICON
) NULL
;
261 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
262 wndclass
.lpszMenuName
= NULL
;
264 // Register the GLCanvas class name
265 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
266 wndclass
.lpszClassName
= wxGLCanvasClassName
;
267 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
269 if ( !::RegisterClass(&wndclass
) )
271 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
275 // Register the GLCanvas class name for windows which don't do full repaint
277 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
278 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
280 if ( !::RegisterClass(&wndclass
) )
282 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
284 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
289 s_registeredGLCanvasClass
= TRUE
;
292 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
294 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
297 parent
->AddChild(this);
300 if ( style
& wxBORDER
)
301 msflags
|= WS_BORDER
;
302 if ( style
& wxTHICK_FRAME
)
303 msflags
|= WS_THICKFRAME
;
306 A general rule with OpenGL and Win32 is that any window that will have a
307 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
308 You can find references about this within the knowledge base and most OpenGL
309 books that contain the wgl function descriptions.
312 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
315 WXDWORD exStyle
= Determine3DEffects(WS_EX_CLIENTEDGE
, &want3D
);
317 // Even with extended styles, need to combine with WS_BORDER
318 // for them to look right.
319 if ( want3D
|| (m_windowStyle
& wxSIMPLE_BORDER
) || (m_windowStyle
& wxRAISED_BORDER
) ||
320 (m_windowStyle
& wxSUNKEN_BORDER
) || (m_windowStyle
& wxDOUBLE_BORDER
))
322 msflags
|= WS_BORDER
;
325 // calculate the value to return from WM_GETDLGCODE handler
326 if ( GetWindowStyleFlag() & wxWANTS_CHARS
)
328 // want everything: i.e. all keys and WM_CHAR message
329 m_lDlgCode
= DLGC_WANTARROWS
| DLGC_WANTCHARS
|
330 DLGC_WANTTAB
| DLGC_WANTMESSAGE
;
333 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
336 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
339 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
340 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
344 while( (attribList
[arg
]!=0) )
346 switch( attribList
[arg
++] )
349 pfd
.iPixelType
= PFD_TYPE_RGBA
;
351 case WX_GL_BUFFER_SIZE
:
352 pfd
.cColorBits
= attribList
[arg
++];
355 // this member looks like it may be obsolete
356 if (attribList
[arg
] > 0) {
357 pfd
.iLayerType
= PFD_OVERLAY_PLANE
;
358 } else if (attribList
[arg
] < 0) {
359 pfd
.iLayerType
= PFD_UNDERLAY_PLANE
;
361 pfd
.iLayerType
= PFD_MAIN_PLANE
;
365 case WX_GL_DOUBLEBUFFER
:
366 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
369 pfd
.dwFlags
|= PFD_STEREO
;
371 case WX_GL_AUX_BUFFERS
:
372 pfd
.cAuxBuffers
= attribList
[arg
++];
375 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
377 case WX_GL_MIN_GREEN
:
378 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
381 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
383 case WX_GL_MIN_ALPHA
:
384 // doesn't count in cColorBits
385 pfd
.cAlphaBits
= attribList
[arg
++];
387 case WX_GL_DEPTH_SIZE
:
388 pfd
.cDepthBits
= attribList
[arg
++];
390 case WX_GL_STENCIL_SIZE
:
391 pfd
.cStencilBits
= attribList
[arg
++];
393 case WX_GL_MIN_ACCUM_RED
:
394 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
396 case WX_GL_MIN_ACCUM_GREEN
:
397 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
399 case WX_GL_MIN_ACCUM_BLUE
:
400 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
402 case WX_GL_MIN_ACCUM_ALPHA
:
403 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
412 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
415 PIXELFORMATDESCRIPTOR pfd
= {
416 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
420 PFD_DOUBLEBUFFER
, /* support double-buffering */
421 PFD_TYPE_RGBA
, /* color type */
422 16, /* prefered color depth */
423 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
424 0, /* no alpha buffer */
425 0, /* alpha bits (ignored) */
426 0, /* no accumulation buffer */
427 0, 0, 0, 0, /* accum bits (ignored) */
428 16, /* depth buffer */
429 0, /* no stencil buffer */
430 0, /* no auxiliary buffers */
431 PFD_MAIN_PLANE
, /* main layer */
433 0, 0, 0, /* no layer, visible, damage masks */
436 AdjustPFDForAttributes(pfd
, attribList
);
438 pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
439 if (pixelFormat
== 0) {
440 wxLogWarning(_("ChoosePixelFormat failed."));
443 if (SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) != TRUE
) {
444 wxLogWarning(_("SetPixelFormat failed."));
449 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
451 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
452 PIXELFORMATDESCRIPTOR pfd
;
454 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
456 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
466 if ( !m_palette
.Ok() )
468 m_palette
= CreateDefaultPalette();
473 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
474 RealizePalette((HDC
) m_hDC
);
478 wxPalette
wxGLCanvas::CreateDefaultPalette()
480 PIXELFORMATDESCRIPTOR pfd
;
482 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
484 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
486 paletteSize
= 1 << pfd
.cColorBits
;
489 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
490 pPal
->palVersion
= 0x300;
491 pPal
->palNumEntries
= paletteSize
;
493 /* build a simple RGB color palette */
495 int redMask
= (1 << pfd
.cRedBits
) - 1;
496 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
497 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
500 for (i
=0; i
<paletteSize
; ++i
) {
501 pPal
->palPalEntry
[i
].peRed
=
502 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
503 pPal
->palPalEntry
[i
].peGreen
=
504 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
505 pPal
->palPalEntry
[i
].peBlue
=
506 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
507 pPal
->palPalEntry
[i
].peFlags
= 0;
511 HPALETTE hPalette
= CreatePalette(pPal
);
515 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
520 void wxGLCanvas::SwapBuffers()
523 m_glContext
->SwapBuffers();
526 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
529 GetClientSize(& width
, & height
);
533 m_glContext
->SetCurrent();
535 glViewport(0, 0, (GLint
)width
, (GLint
)height
);
536 glMatrixMode(GL_PROJECTION
);
538 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 15.0 );
539 glMatrixMode(GL_MODELVIEW
);
543 void wxGLCanvas::SetCurrent()
547 m_glContext
->SetCurrent();
551 void wxGLCanvas::SetColour(const char *colour
)
554 m_glContext
->SetColour(colour
);
557 // TODO: Have to have this called by parent frame (?)
558 // So we need wxFrame to call OnQueryNewPalette for all children...
559 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
561 /* realize palette if this is the current window */
562 if ( GetPalette()->Ok() ) {
563 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
564 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
565 ::RealizePalette((HDC
) GetHDC());
567 event
.SetPaletteRealized(TRUE
);
570 event
.SetPaletteRealized(FALSE
);
573 // I think this doesn't have to be propagated to child windows.
574 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
576 /* realize palette if this is *not* the current window */
578 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
580 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
581 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
582 ::RealizePalette((HDC
) GetHDC());
587 /* Give extensions proper function names. */
589 /* EXT_vertex_array */
590 void glArrayElementEXT(GLint i
)
594 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
598 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
600 #ifdef GL_EXT_vertex_array
601 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
605 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
609 (* proc
) (mode
, first
, count
);
613 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
617 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
621 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
625 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
627 #ifdef GL_EXT_vertex_array
628 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
632 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
636 (* proc
) (type
, stride
, count
, pointer
);
640 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
644 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
646 #ifdef GL_EXT_vertex_array
647 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
651 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
654 (* proc
) (size
, type
, stride
, count
, pointer
);
658 /* EXT_color_subtable */
659 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
663 /* EXT_color_table */
664 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
668 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
672 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
676 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
680 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
684 /* SGI_compiled_vertex_array */
685 void glLockArraysSGI(GLint first
, GLsizei count
)
689 void glUnlockArraysSGI()
694 /* SGI_cull_vertex */
695 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
699 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
704 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
708 /* SGI_index_material */
709 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
714 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
719 //---------------------------------------------------------------------------
721 //---------------------------------------------------------------------------
723 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
725 bool wxGLApp::InitGLVisual(int *attribList
)
728 PIXELFORMATDESCRIPTOR pfd
= {
729 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
733 PFD_DOUBLEBUFFER
, /* support double-buffering */
734 PFD_TYPE_RGBA
, /* color type */
735 16, /* prefered color depth */
736 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
737 0, /* no alpha buffer */
738 0, /* alpha bits (ignored) */
739 0, /* no accumulation buffer */
740 0, 0, 0, 0, /* accum bits (ignored) */
741 16, /* depth buffer */
742 0, /* no stencil buffer */
743 0, /* no auxiliary buffers */
744 PFD_MAIN_PLANE
, /* main layer */
746 0, 0, 0, /* no layer, visible, damage masks */
749 AdjustPFDForAttributes(pfd
, attribList
);
751 // use DC for whole (root) screen, since no windows have yet been created
752 pixelFormat
= ChoosePixelFormat((HDC
) ::GetDC(NULL
), &pfd
);
754 if (pixelFormat
== 0) {
755 wxLogError(_("Failed to initialize OpenGL"));