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
, wxScrolledWindow
)
130 BEGIN_EVENT_TABLE(wxGLCanvas
, wxScrolledWindow
)
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
) : wxScrolledWindow()
140 m_glContext
= (wxGLContext
*) NULL
;
142 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
146 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
147 SetFont(wxSystemSettings::GetSystemFont(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::GetSystemColour(wxSYS_COLOUR_3DFACE
));
171 SetFont(wxSystemSettings::GetSystemFont(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::GetSystemColour(wxSYS_COLOUR_3DFACE
));
196 SetFont(wxSystemSettings::GetSystemFont(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 // calculate the value to return from WM_GETDLGCODE handler
323 if ( GetWindowStyleFlag() & wxWANTS_CHARS
)
325 // want everything: i.e. all keys and WM_CHAR message
326 m_lDlgCode
= DLGC_WANTARROWS
| DLGC_WANTCHARS
|
327 DLGC_WANTTAB
| DLGC_WANTMESSAGE
;
330 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
333 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
336 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
337 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
341 while( (attribList
[arg
]!=0) )
343 switch( attribList
[arg
++] )
346 pfd
.iPixelType
= PFD_TYPE_RGBA
;
348 case WX_GL_BUFFER_SIZE
:
349 pfd
.cColorBits
= attribList
[arg
++];
352 // this member looks like it may be obsolete
353 if (attribList
[arg
] > 0) {
354 pfd
.iLayerType
= PFD_OVERLAY_PLANE
;
355 } else if (attribList
[arg
] < 0) {
356 pfd
.iLayerType
= PFD_UNDERLAY_PLANE
;
358 pfd
.iLayerType
= PFD_MAIN_PLANE
;
362 case WX_GL_DOUBLEBUFFER
:
363 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
366 pfd
.dwFlags
|= PFD_STEREO
;
368 case WX_GL_AUX_BUFFERS
:
369 pfd
.cAuxBuffers
= attribList
[arg
++];
372 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
374 case WX_GL_MIN_GREEN
:
375 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
378 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
380 case WX_GL_MIN_ALPHA
:
381 // doesn't count in cColorBits
382 pfd
.cAlphaBits
= attribList
[arg
++];
384 case WX_GL_DEPTH_SIZE
:
385 pfd
.cDepthBits
= attribList
[arg
++];
387 case WX_GL_STENCIL_SIZE
:
388 pfd
.cStencilBits
= attribList
[arg
++];
390 case WX_GL_MIN_ACCUM_RED
:
391 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
393 case WX_GL_MIN_ACCUM_GREEN
:
394 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
396 case WX_GL_MIN_ACCUM_BLUE
:
397 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
399 case WX_GL_MIN_ACCUM_ALPHA
:
400 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
409 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
412 PIXELFORMATDESCRIPTOR pfd
= {
413 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
417 PFD_DOUBLEBUFFER
, /* support double-buffering */
418 PFD_TYPE_RGBA
, /* color type */
419 16, /* prefered color depth */
420 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
421 0, /* no alpha buffer */
422 0, /* alpha bits (ignored) */
423 0, /* no accumulation buffer */
424 0, 0, 0, 0, /* accum bits (ignored) */
425 16, /* depth buffer */
426 0, /* no stencil buffer */
427 0, /* no auxiliary buffers */
428 PFD_MAIN_PLANE
, /* main layer */
430 0, 0, 0, /* no layer, visible, damage masks */
433 AdjustPFDForAttributes(pfd
, attribList
);
435 pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
436 if (pixelFormat
== 0) {
437 wxLogWarning(_("ChoosePixelFormat failed."));
440 if (SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) != TRUE
) {
441 wxLogWarning(_("SetPixelFormat failed."));
446 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
448 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
449 PIXELFORMATDESCRIPTOR pfd
;
451 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
453 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
463 if ( !m_palette
.Ok() )
465 m_palette
= CreateDefaultPalette();
470 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
471 RealizePalette((HDC
) m_hDC
);
475 wxPalette
wxGLCanvas::CreateDefaultPalette()
477 PIXELFORMATDESCRIPTOR pfd
;
479 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
481 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
483 paletteSize
= 1 << pfd
.cColorBits
;
486 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
487 pPal
->palVersion
= 0x300;
488 pPal
->palNumEntries
= paletteSize
;
490 /* build a simple RGB color palette */
492 int redMask
= (1 << pfd
.cRedBits
) - 1;
493 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
494 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
497 for (i
=0; i
<paletteSize
; ++i
) {
498 pPal
->palPalEntry
[i
].peRed
=
499 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
500 pPal
->palPalEntry
[i
].peGreen
=
501 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
502 pPal
->palPalEntry
[i
].peBlue
=
503 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
504 pPal
->palPalEntry
[i
].peFlags
= 0;
508 HPALETTE hPalette
= CreatePalette(pPal
);
512 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
517 void wxGLCanvas::SwapBuffers()
520 m_glContext
->SwapBuffers();
523 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
526 GetClientSize(& width
, & height
);
530 m_glContext
->SetCurrent();
532 glViewport(0, 0, (GLint
)width
, (GLint
)height
);
533 glMatrixMode(GL_PROJECTION
);
535 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 15.0 );
536 glMatrixMode(GL_MODELVIEW
);
540 void wxGLCanvas::SetCurrent()
544 m_glContext
->SetCurrent();
548 void wxGLCanvas::SetColour(const char *colour
)
551 m_glContext
->SetColour(colour
);
554 // TODO: Have to have this called by parent frame (?)
555 // So we need wxFrame to call OnQueryNewPalette for all children...
556 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
558 /* realize palette if this is the current window */
559 if ( GetPalette()->Ok() ) {
560 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
561 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
562 ::RealizePalette((HDC
) GetHDC());
564 event
.SetPaletteRealized(TRUE
);
567 event
.SetPaletteRealized(FALSE
);
570 // I think this doesn't have to be propagated to child windows.
571 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
573 /* realize palette if this is *not* the current window */
575 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
577 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
578 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
579 ::RealizePalette((HDC
) GetHDC());
584 /* Give extensions proper function names. */
586 /* EXT_vertex_array */
587 void glArrayElementEXT(GLint i
)
591 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
595 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
597 #ifdef GL_EXT_vertex_array
598 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
602 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
606 (* proc
) (mode
, first
, count
);
610 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
614 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
618 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
622 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
624 #ifdef GL_EXT_vertex_array
625 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
629 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
633 (* proc
) (type
, stride
, count
, pointer
);
637 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
641 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
643 #ifdef GL_EXT_vertex_array
644 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
648 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
651 (* proc
) (size
, type
, stride
, count
, pointer
);
655 /* EXT_color_subtable */
656 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
660 /* EXT_color_table */
661 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
665 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
669 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
673 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
677 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
681 /* SGI_compiled_vertex_array */
682 void glLockArraysSGI(GLint first
, GLsizei count
)
686 void glUnlockArraysSGI()
691 /* SGI_cull_vertex */
692 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
696 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
701 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
705 /* SGI_index_material */
706 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
711 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
716 //---------------------------------------------------------------------------
718 //---------------------------------------------------------------------------
720 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
722 bool wxGLApp::InitGLVisual(int *attribList
)
725 PIXELFORMATDESCRIPTOR pfd
= {
726 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
730 PFD_DOUBLEBUFFER
, /* support double-buffering */
731 PFD_TYPE_RGBA
, /* color type */
732 16, /* prefered color depth */
733 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
734 0, /* no alpha buffer */
735 0, /* alpha bits (ignored) */
736 0, /* no accumulation buffer */
737 0, 0, 0, 0, /* accum bits (ignored) */
738 16, /* depth buffer */
739 0, /* no stencil buffer */
740 0, /* no auxiliary buffers */
741 PFD_MAIN_PLANE
, /* main layer */
743 0, 0, 0, /* no layer, visible, damage masks */
746 AdjustPFDForAttributes(pfd
, attribList
);
748 // use DC for whole (root) screen, since no windows have yet been created
749 pixelFormat
= ChoosePixelFormat((HDC
) ::GetDC(NULL
), &pfd
);
751 if (pixelFormat
== 0) {
752 wxLogError(_("Failed to initialize OpenGL"));