]> git.saurik.com Git - wxWidgets.git/blob - src/msw/glcanvas.cpp
78c1a4bd2d90e46728d5ad4e0d250adc99a49e85
[wxWidgets.git] / src / msw / glcanvas.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWindows under MS Windows
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "glcanvas.h"
14 #endif
15
16 #include "wx/wxprec.h"
17
18 #if defined(__BORLANDC__)
19 #pragma hdrstop
20 #endif
21
22 #include <wx/setup.h>
23
24 #if wxUSE_GLCANVAS
25
26 #ifndef WX_PRECOMP
27 #include <wx/frame.h>
28 #endif
29
30 #include <wx/msw/private.h>
31 #include <wx/settings.h>
32 #include <wx/log.h>
33
34 #include <wx/glcanvas.h>
35
36 wxChar wxGLCanvasClassName[] = wxT("wxGLCanvasClass");
37
38 LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message,
39 WPARAM wParam, LPARAM lParam);
40
41 /*
42 * GLContext implementation
43 */
44
45 wxGLContext::wxGLContext(bool isRGB, wxGLCanvas *win, const wxPalette& palette)
46 {
47 m_window = win;
48
49 m_hDC = win->GetHDC();
50
51 m_glContext = wglCreateContext((HDC) m_hDC);
52 wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGl context") );
53
54 wglMakeCurrent((HDC) m_hDC, m_glContext);
55 }
56
57 wxGLContext::wxGLContext(
58 bool isRGB, wxGLCanvas *win,
59 const wxPalette& palette,
60 const wxGLContext *other /* for sharing display lists */
61 )
62 {
63 m_window = win;
64
65 m_hDC = win->GetHDC();
66
67 m_glContext = wglCreateContext((HDC) m_hDC);
68 wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGl context") );
69
70 if( other != 0 )
71 wglShareLists( other->m_glContext, m_glContext );
72
73 wglMakeCurrent((HDC) m_hDC, m_glContext);
74 }
75
76 wxGLContext::~wxGLContext()
77 {
78 if (m_glContext)
79 {
80 wglMakeCurrent(NULL, NULL);
81 wglDeleteContext(m_glContext);
82 }
83 }
84
85 void wxGLContext::SwapBuffers()
86 {
87 if (m_glContext)
88 {
89 wglMakeCurrent((HDC) m_hDC, m_glContext);
90 ::SwapBuffers((HDC) m_hDC); //blits the backbuffer into DC
91 }
92 }
93
94 void wxGLContext::SetCurrent()
95 {
96 if (m_glContext)
97 {
98 wglMakeCurrent((HDC) m_hDC, m_glContext);
99 }
100
101 /*
102 setupPixelFormat(hDC);
103 setupPalette(hDC);
104 */
105 }
106
107 void wxGLContext::SetColour(const char *colour)
108 {
109 float r = 0.0;
110 float g = 0.0;
111 float b = 0.0;
112 wxColour *col = wxTheColourDatabase->FindColour(colour);
113 if (col)
114 {
115 r = (float)(col->Red()/256.0);
116 g = (float)(col->Green()/256.0);
117 b = (float)(col->Blue()/256.0);
118 glColor3f( r, g, b);
119 }
120 }
121
122
123 /*
124 * wxGLCanvas implementation
125 */
126
127 IMPLEMENT_CLASS(wxGLCanvas, wxScrolledWindow)
128
129 BEGIN_EVENT_TABLE(wxGLCanvas, wxScrolledWindow)
130 EVT_SIZE(wxGLCanvas::OnSize)
131 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged)
132 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette)
133 END_EVENT_TABLE()
134
135 wxGLCanvas::wxGLCanvas(wxWindow *parent, wxWindowID id,
136 const wxPoint& pos, const wxSize& size, long style, const wxString& name,
137 int *attribList /* not used yet! */, const wxPalette& palette):
138 wxScrolledWindow()
139 {
140 m_glContext = (wxGLContext*) NULL;
141
142 bool ret = Create(parent, id, pos, size, style, name);
143
144 if ( ret )
145 {
146 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
147 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
148 }
149
150 m_hDC = (WXHDC) ::GetDC((HWND) GetHWND());
151
152 SetupPixelFormat();
153 SetupPalette(palette);
154
155 m_glContext = new wxGLContext(TRUE, this, palette);
156 }
157
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 )
162 : wxScrolledWindow()
163 {
164 m_glContext = (wxGLContext*) NULL;
165
166 bool ret = Create(parent, id, pos, size, style, name);
167
168 if ( ret )
169 {
170 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
171 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
172 }
173
174 m_hDC = (WXHDC) ::GetDC((HWND) GetHWND());
175
176 SetupPixelFormat();
177 SetupPalette(palette);
178
179 m_glContext = new wxGLContext(TRUE, this, palette, shared );
180 }
181
182 // Not very useful for wxMSW, but this is to be wxGTK compliant
183
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 ):
187 wxScrolledWindow()
188 {
189 m_glContext = (wxGLContext*) NULL;
190
191 bool ret = Create(parent, id, pos, size, style, name);
192
193 if ( ret )
194 {
195 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
196 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
197 }
198
199 m_hDC = (WXHDC) ::GetDC((HWND) GetHWND());
200
201 SetupPixelFormat();
202 SetupPalette(palette);
203
204 wxGLContext *sharedContext=0;
205 if (shared) sharedContext=shared->GetContext();
206 m_glContext = new wxGLContext(TRUE, this, palette, sharedContext );
207 }
208
209 wxGLCanvas::~wxGLCanvas()
210 {
211 if (m_glContext)
212 delete m_glContext;
213
214 ::ReleaseDC((HWND) GetHWND(), (HDC) m_hDC);
215 }
216
217 // Replaces wxWindow::Create functionality, since we need to use a different window class
218 bool wxGLCanvas::Create(wxWindow *parent, wxWindowID id,
219 const wxPoint& pos, const wxSize& size, long style, const wxString& name)
220 {
221 /* Suggestion from Kelly Brock <kbrock@8cs.com> (not yet implemented):
222
223 OpenGL corruption fix is simple assuming it doesn't screw anything else
224 up. Add the following line to the top of the create function:
225 wxSize parentSize = GetClientSize();
226 All locations within the function that use 'size' are changed to
227 'parentSize'.
228 The above corrects the initial display corruption with the GeForce and
229 TNT2, not sure about other NVidia cards yet.
230 */
231
232 static bool registeredGLCanvasClass = FALSE;
233
234 // We have to register a special window class because we need
235 // the CS_OWNDC style for GLCanvas.
236
237 /*
238 From Angel Popov <jumpo@bitex.com>
239
240 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
241 how this problem can be fixed:
242
243 "There are 5 common DCs available in Win95. These are aquired when you call
244 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
245 OWNDC flagged windows do not get their DC from the common DC pool, the issue
246 is they require 800 bytes each from the limited 64Kb local heap for GDI."
247
248 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
249 do), Win95 will actually "steal" it from you. MakeCurrent fails,
250 apparently, because Windows re-assigns the HDC to a different window. The
251 only way to prevent this, the only reliable means, is to set CS_OWNDC."
252 */
253
254 if (!registeredGLCanvasClass)
255 {
256 WNDCLASS wndclass;
257
258 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
259
260 // the fields which are common to all classes
261 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
262 wndclass.cbClsExtra = 0;
263 wndclass.cbWndExtra = sizeof( DWORD ); // VZ: what is this DWORD used for?
264 wndclass.hInstance = wxhInstance;
265 wndclass.hIcon = (HICON) NULL;
266 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
267 wndclass.lpszMenuName = NULL;
268
269 // Register the GLCanvas class name
270 wndclass.hbrBackground = (HBRUSH)NULL;
271 wndclass.lpszClassName = wxGLCanvasClassName;
272 wndclass.style = styleNormal;
273
274 if ( !RegisterClass(&wndclass) )
275 {
276 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
277
278 return FALSE;
279 }
280 registeredGLCanvasClass = TRUE;
281 }
282
283 wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") );
284
285 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
286 return FALSE;
287
288 parent->AddChild(this);
289
290 DWORD msflags = 0;
291 if ( style & wxBORDER )
292 msflags |= WS_BORDER;
293 if ( style & wxTHICK_FRAME )
294 msflags |= WS_THICKFRAME;
295
296 /*
297 A general rule with OpenGL and Win32 is that any window that will have a
298 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
299 You can find references about this within the knowledge base and most OpenGL
300 books that contain the wgl function descriptions.
301 */
302
303 msflags |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
304 // if ( style & wxCLIP_CHILDREN )
305 // msflags |= WS_CLIPCHILDREN;
306 msflags |= WS_CLIPCHILDREN;
307
308 bool want3D;
309 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
310
311 // Even with extended styles, need to combine with WS_BORDER
312 // for them to look right.
313 if ( want3D || (m_windowStyle & wxSIMPLE_BORDER) || (m_windowStyle & wxRAISED_BORDER ) ||
314 (m_windowStyle & wxSUNKEN_BORDER) || (m_windowStyle & wxDOUBLE_BORDER))
315 {
316 msflags |= WS_BORDER;
317 }
318
319 // calculate the value to return from WM_GETDLGCODE handler
320 if ( GetWindowStyleFlag() & wxWANTS_CHARS )
321 {
322 // want everything: i.e. all keys and WM_CHAR message
323 m_lDlgCode = DLGC_WANTARROWS | DLGC_WANTCHARS |
324 DLGC_WANTTAB | DLGC_WANTMESSAGE;
325 }
326
327 MSWCreate(m_windowId, parent, wxGLCanvasClassName, this, NULL,
328 pos.x, pos.y,
329 WidthDefault(size.x), HeightDefault(size.y),
330 msflags, NULL, exStyle);
331
332 return TRUE;
333
334 }
335
336 void wxGLCanvas::SetupPixelFormat() // (HDC hDC)
337 {
338 PIXELFORMATDESCRIPTOR pfd = {
339 sizeof(PIXELFORMATDESCRIPTOR), /* size */
340 1, /* version */
341 PFD_SUPPORT_OPENGL |
342 PFD_DRAW_TO_WINDOW |
343 PFD_DOUBLEBUFFER, /* support double-buffering */
344 PFD_TYPE_RGBA, /* color type */
345 16, /* prefered color depth */
346 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
347 0, /* no alpha buffer */
348 0, /* alpha bits (ignored) */
349 0, /* no accumulation buffer */
350 0, 0, 0, 0, /* accum bits (ignored) */
351 16, /* depth buffer */
352 0, /* no stencil buffer */
353 0, /* no auxiliary buffers */
354 PFD_MAIN_PLANE, /* main layer */
355 0, /* reserved */
356 0, 0, 0, /* no layer, visible, damage masks */
357 };
358 int pixelFormat;
359
360 pixelFormat = ChoosePixelFormat((HDC) m_hDC, &pfd);
361 if (pixelFormat == 0) {
362 MessageBox(WindowFromDC((HDC) m_hDC), wxT("ChoosePixelFormat failed."), wxT("Error"),
363 MB_ICONERROR | MB_OK);
364 exit(1);
365 }
366
367 if (SetPixelFormat((HDC) m_hDC, pixelFormat, &pfd) != TRUE) {
368 MessageBox(WindowFromDC((HDC) m_hDC), wxT("SetPixelFormat failed."), wxT("Error"),
369 MB_ICONERROR | MB_OK);
370 exit(1);
371 }
372 }
373
374 void wxGLCanvas::SetupPalette(const wxPalette& palette)
375 {
376 int pixelFormat = GetPixelFormat((HDC) m_hDC);
377 PIXELFORMATDESCRIPTOR pfd;
378
379 DescribePixelFormat((HDC) m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
380
381 if (pfd.dwFlags & PFD_NEED_PALETTE)
382 {
383 }
384 else
385 {
386 return;
387 }
388
389 m_palette = palette;
390
391 if ( !m_palette.Ok() )
392 {
393 m_palette = CreateDefaultPalette();
394 }
395
396 if (m_palette.Ok())
397 {
398 SelectPalette((HDC) m_hDC, (HPALETTE) m_palette.GetHPALETTE(), FALSE);
399 RealizePalette((HDC) m_hDC);
400 }
401 }
402
403 wxPalette wxGLCanvas::CreateDefaultPalette()
404 {
405 PIXELFORMATDESCRIPTOR pfd;
406 int paletteSize;
407 int pixelFormat = GetPixelFormat((HDC) m_hDC);
408
409 DescribePixelFormat((HDC) m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
410
411 paletteSize = 1 << pfd.cColorBits;
412
413 LOGPALETTE* pPal =
414 (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
415 pPal->palVersion = 0x300;
416 pPal->palNumEntries = paletteSize;
417
418 /* build a simple RGB color palette */
419 {
420 int redMask = (1 << pfd.cRedBits) - 1;
421 int greenMask = (1 << pfd.cGreenBits) - 1;
422 int blueMask = (1 << pfd.cBlueBits) - 1;
423 int i;
424
425 for (i=0; i<paletteSize; ++i) {
426 pPal->palPalEntry[i].peRed =
427 (((i >> pfd.cRedShift) & redMask) * 255) / redMask;
428 pPal->palPalEntry[i].peGreen =
429 (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask;
430 pPal->palPalEntry[i].peBlue =
431 (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask;
432 pPal->palPalEntry[i].peFlags = 0;
433 }
434 }
435
436 HPALETTE hPalette = CreatePalette(pPal);
437 free(pPal);
438
439 wxPalette palette;
440 palette.SetHPALETTE((WXHPALETTE) hPalette);
441
442 return palette;
443 }
444
445 void wxGLCanvas::SwapBuffers()
446 {
447 if (m_glContext)
448 m_glContext->SwapBuffers();
449 }
450
451 void wxGLCanvas::OnSize(wxSizeEvent& event)
452 {
453 int width, height;
454 GetClientSize(& width, & height);
455
456 if (m_glContext)
457 {
458 m_glContext->SetCurrent();
459
460 glViewport(0, 0, (GLint)width, (GLint)height);
461 glMatrixMode(GL_PROJECTION);
462 glLoadIdentity();
463 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 15.0 );
464 glMatrixMode(GL_MODELVIEW);
465 }
466 }
467
468 void wxGLCanvas::SetCurrent()
469 {
470 if (m_glContext)
471 {
472 m_glContext->SetCurrent();
473 }
474 }
475
476 void wxGLCanvas::SetColour(const char *colour)
477 {
478 if (m_glContext)
479 m_glContext->SetColour(colour);
480 }
481
482 // TODO: Have to have this called by parent frame (?)
483 // So we need wxFrame to call OnQueryNewPalette for all children...
484 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
485 {
486 /* realize palette if this is the current window */
487 if ( GetPalette()->Ok() ) {
488 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
489 ::SelectPalette((HDC) GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
490 ::RealizePalette((HDC) GetHDC());
491 Refresh();
492 event.SetPaletteRealized(TRUE);
493 }
494 else
495 event.SetPaletteRealized(FALSE);
496 }
497
498 // I think this doesn't have to be propagated to child windows.
499 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
500 {
501 /* realize palette if this is *not* the current window */
502 if ( GetPalette() &&
503 GetPalette()->Ok() && (this != event.GetChangedWindow()) )
504 {
505 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
506 ::SelectPalette((HDC) GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
507 ::RealizePalette((HDC) GetHDC());
508 Refresh();
509 }
510 }
511
512 /* Give extensions proper function names. */
513
514 /* EXT_vertex_array */
515 void glArrayElementEXT(GLint i)
516 {
517 }
518
519 void glColorPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
520 {
521 }
522
523 void glDrawArraysEXT(GLenum mode, GLint first, GLsizei count)
524 {
525 #ifdef GL_EXT_vertex_array
526 static PFNGLDRAWARRAYSEXTPROC proc = 0;
527
528 if ( !proc )
529 {
530 proc = (PFNGLDRAWARRAYSEXTPROC) wglGetProcAddress("glDrawArraysEXT");
531 }
532
533 if ( proc )
534 (* proc) (mode, first, count);
535 #endif
536 }
537
538 void glEdgeFlagPointerEXT(GLsizei stride, GLsizei count, const GLboolean *pointer)
539 {
540 }
541
542 void glGetPointervEXT(GLenum pname, GLvoid* *params)
543 {
544 }
545
546 void glIndexPointerEXT(GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
547 {
548 }
549
550 void glNormalPointerEXT(GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
551 {
552 #ifdef GL_EXT_vertex_array
553 static PFNGLNORMALPOINTEREXTPROC proc = 0;
554
555 if ( !proc )
556 {
557 proc = (PFNGLNORMALPOINTEREXTPROC) wglGetProcAddress("glNormalPointerEXT");
558 }
559
560 if ( proc )
561 (* proc) (type, stride, count, pointer);
562 #endif
563 }
564
565 void glTexCoordPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
566 {
567 }
568
569 void glVertexPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
570 {
571 #ifdef GL_EXT_vertex_array
572 static PFNGLVERTEXPOINTEREXTPROC proc = 0;
573
574 if ( !proc )
575 {
576 proc = (PFNGLVERTEXPOINTEREXTPROC) wglGetProcAddress("glVertexPointerEXT");
577 }
578
579 if ( proc )
580 (* proc) (size, type, stride, count, pointer);
581 #endif
582 }
583
584 /* EXT_color_subtable */
585 void glColorSubtableEXT(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *table)
586 {
587 }
588
589 /* EXT_color_table */
590 void glColorTableEXT(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table)
591 {
592 }
593
594 void glCopyColorTableEXT(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width)
595 {
596 }
597
598 void glGetColorTableEXT(GLenum target, GLenum format, GLenum type, GLvoid *table)
599 {
600 }
601
602 void glGetColorTableParamaterfvEXT(GLenum target, GLenum pname, GLfloat *params)
603 {
604 }
605
606 void glGetColorTavleParameterivEXT(GLenum target, GLenum pname, GLint *params)
607 {
608 }
609
610 /* SGI_compiled_vertex_array */
611 void glLockArraysSGI(GLint first, GLsizei count)
612 {
613 }
614
615 void glUnlockArraysSGI()
616 {
617 }
618
619
620 /* SGI_cull_vertex */
621 void glCullParameterdvSGI(GLenum pname, GLdouble* params)
622 {
623 }
624
625 void glCullParameterfvSGI(GLenum pname, GLfloat* params)
626 {
627 }
628
629 /* SGI_index_func */
630 void glIndexFuncSGI(GLenum func, GLclampf ref)
631 {
632 }
633
634 /* SGI_index_material */
635 void glIndexMaterialSGI(GLenum face, GLenum mode)
636 {
637 }
638
639 /* WIN_swap_hint */
640 void glAddSwapHintRectWin(GLint x, GLint y, GLsizei width, GLsizei height)
641 {
642 }
643
644 #endif
645 // wxUSE_GLCANVAS