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 }