Mercurial > hg > aimc
comparison trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicewxGLCanvas.cc @ 397:7a573750b186
- First add of a lot of graphics code from the old version. Not working yet, not even compiling yet.
author | tomwalters |
---|---|
date | Fri, 15 Oct 2010 05:40:53 +0000 |
parents | |
children | 3ee03a6b95a0 |
comparison
equal
deleted
inserted
replaced
396:06a26f5cdad7 | 397:7a573750b186 |
---|---|
1 // Copyright 2006, Willem van Engen | |
2 // | |
3 // AIM-C: A C++ implementation of the Auditory Image Model | |
4 // http://www.acousticscale.org/AIMC | |
5 // | |
6 // Licensed under the Apache License, Version 2.0 (the "License"); | |
7 // you may not use this file except in compliance with the License. | |
8 // You may obtain a copy of the License at | |
9 // | |
10 // http://www.apache.org/licenses/LICENSE-2.0 | |
11 // | |
12 // Unless required by applicable law or agreed to in writing, software | |
13 // distributed under the License is distributed on an "AS IS" BASIS, | |
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 // See the License for the specific language governing permissions and | |
16 // limitations under the License. | |
17 | |
18 /*! | |
19 * \file | |
20 * \brief Output device for output to a wxWidgets OpenGL canvas | |
21 * | |
22 * \author Willem van Engen <cnbh@willem.engen.nl> | |
23 * \date created 2006/09/21 | |
24 * \version \$Id: $ | |
25 */ | |
26 | |
27 #include "Support/Common.h" | |
28 | |
29 /*! \class GraphicsOutputDevicewxGLCanvas | |
30 * | |
31 * Graphics output takes a large part of the application's performance at the | |
32 * moment when it is inline with the Process() loop. Much is gained by | |
33 * putting it in a separate thread, which can be done using ShotTargetThreaded. | |
34 * | |
35 * OpenGL-related documents: | |
36 * - http://www.opengl.org/ | |
37 * - http://www.sgi.com/products/software/opengl/ | |
38 * - http://developer.apple.com/graphicsimaging/opengl/ | |
39 * - http://developer.nvidia.com/page/documentation.html | |
40 * - Vertex arrays | |
41 * - http://www.opengl.org/registry/specs/EXT/vertex_array.txt | |
42 * - http://www.awprofessional.com/articles/article.asp?p=461848&seqNum=2&rl=1 | |
43 * - http://jdobry.webpark.cz/opengl/opengl_maximum_performance.html | |
44 * - Fonts and OpenGL | |
45 * - http://gltt.sourceforge.net/ | |
46 */ | |
47 | |
48 // And finally our own | |
49 #include "Support/util.h" | |
50 #include "Output/GraphicsOutputDevice.h" | |
51 #include "Output/GraphicsOutputDevicewxGLCanvas.h" | |
52 | |
53 BEGIN_EVENT_TABLE(GraphicsOutputDevicewxGLCanvas, wxGLCanvas) | |
54 EVT_SIZE(GraphicsOutputDevicewxGLCanvas::OnSize) | |
55 EVT_PAINT(GraphicsOutputDevicewxGLCanvas::OnPaint) | |
56 EVT_ERASE_BACKGROUND(GraphicsOutputDevicewxGLCanvas::OnEraseBackground) | |
57 END_EVENT_TABLE() | |
58 | |
59 // wxGLCanvas attributes | |
60 int GraphicsOutputDevicewxGLCanvas::GLAttrlist[] = { | |
61 WX_GL_RGBA, 1, | |
62 WX_GL_DOUBLEBUFFER, 1, | |
63 WX_GL_MIN_RED, 5, | |
64 WX_GL_MIN_GREEN, 5, | |
65 WX_GL_MIN_BLUE, 5, | |
66 WX_GL_MIN_ALPHA, 3, | |
67 WX_GL_DEPTH_SIZE, 16, | |
68 0 | |
69 }; | |
70 | |
71 // OpenGL get procaddress function pointer, differs across platforms | |
72 typedef void (*(*glGetProcAddressPtr_t)(const char*))(); | |
73 | |
74 GraphicsOutputDevicewxGLCanvas::GraphicsOutputDevicewxGLCanvas(Parameters *pParam, | |
75 wxWindow *parent, | |
76 wxWindowID id, | |
77 const wxPoint& pos, | |
78 const wxSize& size, | |
79 long style, | |
80 const wxString& name) | |
81 : wxGLCanvas(parent, (wxGLCanvas*) NULL, id, pos, size, | |
82 style|wxFULL_REPAINT_ON_RESIZE, name, GLAttrlist), | |
83 GraphicsOutputDevice(pParam) { | |
84 m_init = false; | |
85 m_gllist = 0; | |
86 m_pWorkerContext = NULL; | |
87 m_bAntialiasing = true; | |
88 m_pFont = NULL; | |
89 m_sFontFile = NULL; | |
90 m_iFontsize = -1; | |
91 #if !defined(_MACOSX) | |
92 s_bWorkerNeedsInit = false; | |
93 #endif | |
94 | |
95 #ifdef WITH_GL_VERTEX_ARRAYS | |
96 m_iVertexType = 0xffff; // no gBegin() has happened yet | |
97 m_bStaticColor = false; | |
98 m_pVertices = NULL; | |
99 m_iVerticesMax = 0; | |
100 // Enable vertex arrays if possible | |
101 #ifdef _MACOSX | |
102 m_glLockArraysEXT = ::glLockArraysEXT; | |
103 m_glUnlockArraysEXT = ::glUnlockArraysEXT; | |
104 m_bVertexArrayLock = true; | |
105 #else | |
106 m_bVertexArrayLock = false; | |
107 // OpenGL command needed to fetch entry point, do it in InitGL() | |
108 #endif /* _MACOSX */ | |
109 #endif /* WITH_GL_VERTEX_ARRAYS */ | |
110 } | |
111 | |
112 GraphicsOutputDevicewxGLCanvas::~GraphicsOutputDevicewxGLCanvas() { | |
113 // Cleanup OpenGL display list | |
114 if (m_init) { | |
115 glDeleteLists(m_gllist, 1); | |
116 } | |
117 DELETE_IF_NONNULL(m_pWorkerContext); | |
118 DELETE_IF_NONNULL(m_pFont); | |
119 #ifdef WITH_GL_VERTEX_ARRAYS | |
120 DELETE_ARRAY_IF_NONNULL(m_pVertices); | |
121 #endif | |
122 } | |
123 | |
124 void GraphicsOutputDevicewxGLCanvas::Start() { | |
125 // This seems to be needed to prevent a crash on windows, but why???? | |
126 SetCurrent(); | |
127 return GraphicsOutputDevice::Start(); | |
128 } | |
129 | |
130 bool GraphicsOutputDevicewxGLCanvas::Initialize(unsigned int iVerticesMax) { | |
131 AIM_ASSERT(m_pParam); | |
132 // Give a chance to update anti-aliasing settings | |
133 if (m_bAntialiasing != m_pParam->GetBool("output.antialias")) { | |
134 m_bAntialiasing = m_pParam->GetBool("output.antialias"); | |
135 if (SetCurrent()) { | |
136 InitGL(); | |
137 #if !defined(_MACOSX) | |
138 { | |
139 wxMutexLocker lock(s_mutexOpenGL); | |
140 s_bWorkerNeedsInit = true; | |
141 } | |
142 #endif | |
143 } | |
144 } | |
145 | |
146 #ifdef WITH_GL_VERTEX_ARRAYS | |
147 // Re-allocate vertices | |
148 if (iVerticesMax > m_iVerticesMax) { | |
149 DELETE_IF_NONNULL(m_pVertices); | |
150 m_iVerticesMax = iVerticesMax; | |
151 // If color is static, we need not store the color | |
152 if (m_bStaticColor) | |
153 m_pVertices = new GLfloat[(iVerticesMax+1)*3]; | |
154 else | |
155 m_pVertices = new GLfloat[(iVerticesMax+1)*6]; | |
156 } | |
157 #endif | |
158 | |
159 // Change font if requested | |
160 const char *sFontFile = m_pParam->GetString("output.gl.fontfile"); | |
161 unsigned int iFontsize = m_pParam->GetUInt("output.fontsize"); | |
162 if (!m_sFontFile | |
163 || !strcmp(m_sFontFile,sFontFile)==0 | |
164 || m_iFontsize!=(int)iFontsize) { | |
165 wxMutexLocker lock(s_mutexOpenGL); | |
166 DELETE_IF_NONNULL(m_pFont); | |
167 wxString sWorkingFontFilename = wxString::FromAscii(sFontFile); | |
168 if (!wxFileExists(sWorkingFontFilename)) { | |
169 sWorkingFontFilename = wxString::FromAscii(aimDataDir()); | |
170 sWorkingFontFilename += _T("/"); | |
171 sWorkingFontFilename += wxString::FromAscii(sFontFile); | |
172 } | |
173 //if (!wxFileExists(sWorkingFontFilename)) | |
174 //sWorkingFontFilename.replace("Font:").append(sFontFile); | |
175 m_pFont = static_cast<FTFont*>(new FTGLBitmapFont(sWorkingFontFilename.fn_str())); | |
176 if (!m_pFont || m_pFont->Error()) { | |
177 aimERROR(_T("Couldn't load font '%s'"), sFontFile); | |
178 DELETE_IF_NONNULL(m_pFont); | |
179 } else { | |
180 // Display lists don't mix with our own usage :( | |
181 // May not be needed for a Bitmap font | |
182 //m_pFont->UseDisplayList(false); | |
183 if ( !m_pFont->FaceSize(iFontsize) ) { | |
184 AIM_ERROR(_T("Couldn't select font size %u on font '%s'"), iFontsize, sFontFile); | |
185 DELETE_IF_NONNULL(m_pFont); | |
186 } | |
187 } | |
188 m_sFontFile = sFontFile; | |
189 m_iFontsize = iFontsize; | |
190 } | |
191 return true; | |
192 } | |
193 bool GraphicsOutputDevicewxGLCanvas::Initialize() { | |
194 return Initialize(0); | |
195 } | |
196 | |
197 void GraphicsOutputDevicewxGLCanvas::Render() { | |
198 wxPaintDC dc(this); | |
199 // We want to initialize first from main thread. | |
200 if (!m_init) { | |
201 if (!SetCurrent()) return; | |
202 InitGL(); | |
203 } | |
204 // Render saved list only if not animating (redrawn anyway in that case) | |
205 if (!m_bRunning) { | |
206 if (!SetCurrent()) { | |
207 return; | |
208 } | |
209 glClear(GL_COLOR_BUFFER_BIT/*|GL_DEPTH_BUFFER_BIT*/); | |
210 glCallList(m_gllist); | |
211 SwapBuffers(); | |
212 } | |
213 } | |
214 | |
215 void GraphicsOutputDevicewxGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { | |
216 Render(); | |
217 } | |
218 | |
219 void GraphicsOutputDevicewxGLCanvas::OnSize(wxSizeEvent& event) { | |
220 // this is also necessary to update the context on some platforms | |
221 wxGLCanvas::OnSize(event); | |
222 | |
223 // set GL viewport | |
224 // (not called by wxGLCanvas::OnSize on all platforms...) | |
225 if (SetCurrent()) { | |
226 DoResize(); | |
227 // It is only sensible to update the other thread when it's running | |
228 // Don't acquire the mutex when s_bWorkerNeedsInit already to avoid deadlock | |
229 if (/*m_bRunning &&*/ !s_bWorkerNeedsInit) { | |
230 wxMutexLocker lock(s_mutexOpenGL); | |
231 s_bWorkerNeedsInit = true; | |
232 } | |
233 } | |
234 } | |
235 | |
236 void GraphicsOutputDevicewxGLCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event)) { | |
237 } | |
238 | |
239 bool GraphicsOutputDevicewxGLCanvas::SetCurrent() { | |
240 bool bRet=true; | |
241 | |
242 #ifndef __WXMOTIF__ | |
243 bRet = (GetContext()!=NULL); | |
244 if (bRet) | |
245 #endif | |
246 { | |
247 wxGLCanvas::SetCurrent(); | |
248 } | |
249 return bRet; | |
250 } | |
251 | |
252 void GraphicsOutputDevicewxGLCanvas::DoResize() { | |
253 int w, h; | |
254 GetClientSize(&w, &h); | |
255 glViewport(0, 0, (GLint)w, (GLint)h); | |
256 } | |
257 | |
258 void GraphicsOutputDevicewxGLCanvas::InitGL() { | |
259 /* No SetCurrent() here, because this can be called from different GL contexts. | |
260 * Convenient for multi-threaded operation. */ | |
261 //aimERROR(_T("InitGL Called")); | |
262 #if defined(WITH_GL_VERTEX_ARRAYS) && !defined(_MACOSX) | |
263 if (!m_init) { | |
264 /* This needs to be done here, because OpenGL commands may need SetCurrent() | |
265 * and an already shown window. */ | |
266 char *extensions = (char *)glGetString(GL_EXTENSIONS); | |
267 if (!extensions) { | |
268 AIM_INFO(_T("Could not query OpenGL extensions, vertex arrays disabled")); | |
269 } else if (strstr(extensions, "GL_EXT_compiled_vertex_array")) { | |
270 m_glLockArraysEXT = (LOCAL_PFNGLLOCKARRAYSEXTPROC)GL_GET_PROC_ADDRESS("glLockArraysEXT"); | |
271 m_glUnlockArraysEXT = (LOCAL_PFNGLUNLOCKARRAYSEXTPROC)GL_GET_PROC_ADDRESS("glUnlockArraysEXT"); | |
272 if(!m_glLockArraysEXT || !m_glUnlockArraysEXT) | |
273 AIM_ERROR(_T("OpenGL error on GL_EXT_compiled_vertex_array")); | |
274 else | |
275 m_bVertexArrayLock = true; | |
276 } | |
277 } | |
278 #endif | |
279 DoResize(); | |
280 glClearColor(0, 0, 0, 1); | |
281 glMatrixMode( GL_PROJECTION ); | |
282 glLoadIdentity( ); | |
283 | |
284 glEnable(GL_VERTEX_ARRAY); | |
285 | |
286 // Window limits in OpenGL co-ordiantes | |
287 //! \todo Make this configurable, or change and document fixed values | |
288 glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0); | |
289 glTranslatef(0.0, 0.0, 0.0); | |
290 | |
291 if (m_bAntialiasing) { | |
292 glEnable(GL_LINE_SMOOTH); | |
293 glEnable(GL_BLEND); | |
294 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
295 //glBlendFunc(GL_ONE, GL_ONE); | |
296 glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); | |
297 } else { | |
298 glDisable(GL_LINE_SMOOTH); | |
299 glDisable(GL_BLEND); | |
300 } | |
301 glLineWidth(1.0); | |
302 | |
303 // Get a free display list only the first time | |
304 if (!m_init) { | |
305 #if !defined(_MACOSX) | |
306 // Windows and Linux need a separate worker context | |
307 aimASSERT(wxIsMainThread()); | |
308 #if wxCHECK_VERSION(2,8,0) | |
309 m_pWorkerContext = new wxGLContext(this, m_glContext); | |
310 #else | |
311 m_pWorkerContext = new wxGLContext(true, | |
312 this, | |
313 wxNullPalette, | |
314 m_glContext); | |
315 #endif | |
316 aimASSERT(m_pWorkerContext); | |
317 s_bWorkerNeedsInit = true; | |
318 #endif | |
319 m_gllist = glGenLists(1); | |
320 aimASSERT(m_gllist); | |
321 // Empty window at start | |
322 glNewList(m_gllist, GL_COMPILE_AND_EXECUTE); | |
323 glEndList(); | |
324 m_init = true; | |
325 } | |
326 } | |
327 | |
328 // Call before any other render* functions | |
329 void GraphicsOutputDevicewxGLCanvas::gGrab() { | |
330 AimwxGuiLocker __lock__; | |
331 #if !defined(_MACOSX) | |
332 // Detect if we're the main thread or not. | |
333 if (!wxIsMainThread()) { | |
334 // We're called by a worker thread, make sure there's a right context | |
335 AIM_ASSERT(m_pWorkerContext); | |
336 #if wxCHECK_VERSION(2,8,0) | |
337 m_pWorkerContext->SetCurrent(*this); | |
338 #else | |
339 m_pWorkerContext->SetCurrent(); | |
340 #endif | |
341 // Update OpenGL settings if needed | |
342 wxMutexLocker lock(s_mutexOpenGL); | |
343 if (s_bWorkerNeedsInit) { | |
344 InitGL(); | |
345 s_bWorkerNeedsInit = false; | |
346 } | |
347 } else | |
348 #endif | |
349 { | |
350 // Either called by main thread, or we need no special worker glContext | |
351 if (!SetCurrent()) { | |
352 return; | |
353 } | |
354 // Init OpenGL once, but after SetCurrent | |
355 if (!m_init) { | |
356 InitGL(); | |
357 } | |
358 } | |
359 glClear(GL_COLOR_BUFFER_BIT); | |
360 | |
361 // Start and store in a display list for redrawing | |
362 glNewList(m_gllist, GL_COMPILE); | |
363 } | |
364 | |
365 void GraphicsOutputDevicewxGLCanvas::gBeginLineStrip() { | |
366 #ifdef WITH_GL_VERTEX_ARRAYS | |
367 aimASSERT(m_iVertexType == 0xffff); // Previous gBegin*() must be gEnd()ed | |
368 // New lines vertex array | |
369 m_iVertexCount = 0; | |
370 m_iVertexType = GL_LINE_STRIP; | |
371 #else | |
372 AimwxGuiLocker __lock__; | |
373 glBegin(GL_LINE_STRIP); | |
374 #endif | |
375 } | |
376 | |
377 void GraphicsOutputDevicewxGLCanvas::gBeginQuadStrip() { | |
378 #ifdef WITH_GL_VERTEX_ARRAYS | |
379 aimASSERT(m_iVertexType == 0xffff); // Previous gBegin*() must be gEnd()ed | |
380 // New quads vertex array | |
381 m_iVertexCount = 0; | |
382 m_iVertexType = GL_QUAD_STRIP; | |
383 #else | |
384 AimwxGuiLocker __lock__; | |
385 glBegin(GL_QUAD_STRIP); | |
386 #endif | |
387 } | |
388 | |
389 void GraphicsOutputDevicewxGLCanvas::gVertex3f(float x, float y, float z) { | |
390 #ifdef WITH_GL_VERTEX_ARRAYS | |
391 aimASSERT(m_iVertexType != 0xffff); // Must be inside gBegin*() | |
392 if (m_iVertexCount>=m_iVerticesMax) { | |
393 static bool errShown=false; | |
394 if (!errShown) { | |
395 aimERROR(_T("Error: max vertex count reached: %d"), m_iVertexCount); | |
396 errShown=true; | |
397 } | |
398 return; | |
399 } | |
400 if (m_bStaticColor) { | |
401 m_pVertices[m_iVertexCount*3+0] = x; | |
402 m_pVertices[m_iVertexCount*3+1] = y; | |
403 m_pVertices[m_iVertexCount*3+2] = z; | |
404 } else { | |
405 m_pVertices[m_iVertexCount*6+0] = m_fCurColorR; | |
406 m_pVertices[m_iVertexCount*6+1] = m_fCurColorG; | |
407 m_pVertices[m_iVertexCount*6+2] = m_fCurColorB; | |
408 m_pVertices[m_iVertexCount*6+3] = x; | |
409 m_pVertices[m_iVertexCount*6+4] = y; | |
410 m_pVertices[m_iVertexCount*6+5] = z; | |
411 } | |
412 m_iVertexCount++; | |
413 #else | |
414 AimwxGuiLocker __lock__; | |
415 glVertex3f(x,y,z); | |
416 #endif | |
417 } | |
418 | |
419 void GraphicsOutputDevicewxGLCanvas::gColor3f(float r, float g, float b) { | |
420 #ifdef WITH_GL_VERTEX_ARRAYS | |
421 if (m_iVertexType==0xffff || m_bStaticColor) { | |
422 // If not inside vertex array run, use the ordinary command | |
423 glColor3f(r, g, b); | |
424 } | |
425 if (!m_bStaticColor) { | |
426 // Set current color for vertex array usage | |
427 m_fCurColorR = r; | |
428 m_fCurColorG = g; | |
429 m_fCurColorB = b; | |
430 } | |
431 #else | |
432 AimwxGuiLocker __lock__; | |
433 glColor3f(r, g, b); | |
434 #endif | |
435 } | |
436 | |
437 void GraphicsOutputDevicewxGLCanvas::gEnd() { | |
438 #ifdef WITH_GL_VERTEX_ARRAYS | |
439 aimASSERT(m_iVertexType != 0xffff); // Must be inside gBegin*() | |
440 AimwxGuiLocker __lock__; | |
441 | |
442 // Draw the vertex array | |
443 glEnableClientState(GL_VERTEX_ARRAY); | |
444 | |
445 // Draw vertices | |
446 if (m_bStaticColor) | |
447 glVertexPointer(3, GL_FLOAT, 0, m_pVertices); | |
448 else | |
449 glInterleavedArrays(GL_C3F_V3F, 0, m_pVertices); | |
450 if (m_bVertexArrayLock) m_glLockArraysEXT(0, m_iVertexCount); | |
451 glDrawArrays(m_iVertexType, 0, m_iVertexCount); | |
452 if (m_bVertexArrayLock) m_glUnlockArraysEXT(); | |
453 | |
454 glDisableClientState(GL_VERTEX_ARRAY); | |
455 | |
456 // Remember we're outside a gBegin()..gEnd() loop | |
457 m_iVertexType = 0xffff; | |
458 #else | |
459 AimwxGuiLocker __lock__; | |
460 glEnd(); | |
461 #endif | |
462 } | |
463 | |
464 void GraphicsOutputDevicewxGLCanvas::gText3f(float x, | |
465 float y, | |
466 float z, | |
467 const char *sStr, | |
468 bool bRotated) { | |
469 #ifdef WITH_GL_VERTEX_ARRAYS | |
470 aimASSERT(m_iVertexType == 0xffff); // Must be outside gBegin*() | |
471 #endif | |
472 | |
473 if (!m_pFont) | |
474 return; | |
475 | |
476 //! \todo make rotation work | |
477 if (bRotated) | |
478 return; | |
479 | |
480 { | |
481 AimwxGuiLocker __lock__; | |
482 /* | |
483 if (bRotated) { | |
484 glPushMatrix(); | |
485 glTranslatef(x,y,z); | |
486 glRotatef(90.0f, 0, 0, 1.0f); | |
487 glRasterPos3f(0,0,0); | |
488 m_pFont->Render(sStr); | |
489 glPopMatrix(); | |
490 } else { | |
491 */ | |
492 glRasterPos3f(x, y, z); | |
493 m_pFont->Render(sStr); | |
494 } | |
495 } | |
496 | |
497 void GraphicsOutputDevicewxGLCanvas::gRelease() { | |
498 #ifdef WITH_GL_VERTEX_ARRAYS | |
499 aimASSERT(m_iVertexType == 0xffff); // Must be gEnd()ed | |
500 #endif | |
501 AimwxGuiLocker __lock__; | |
502 glEndList(); | |
503 glCallList(m_gllist); | |
504 //glFlush(); | |
505 SwapBuffers(); // Doesn't matter in what context | |
506 } |