Mercurial > hg > aimc
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicewxGLCanvas.cc Fri Oct 15 05:40:53 2010 +0000 @@ -0,0 +1,506 @@ +// Copyright 2006, Willem van Engen +// +// AIM-C: A C++ implementation of the Auditory Image Model +// http://www.acousticscale.org/AIMC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * \file + * \brief Output device for output to a wxWidgets OpenGL canvas + * + * \author Willem van Engen <cnbh@willem.engen.nl> + * \date created 2006/09/21 + * \version \$Id: $ + */ + +#include "Support/Common.h" + +/*! \class GraphicsOutputDevicewxGLCanvas + * + * Graphics output takes a large part of the application's performance at the + * moment when it is inline with the Process() loop. Much is gained by + * putting it in a separate thread, which can be done using ShotTargetThreaded. + * + * OpenGL-related documents: + * - http://www.opengl.org/ + * - http://www.sgi.com/products/software/opengl/ + * - http://developer.apple.com/graphicsimaging/opengl/ + * - http://developer.nvidia.com/page/documentation.html + * - Vertex arrays + * - http://www.opengl.org/registry/specs/EXT/vertex_array.txt + * - http://www.awprofessional.com/articles/article.asp?p=461848&seqNum=2&rl=1 + * - http://jdobry.webpark.cz/opengl/opengl_maximum_performance.html + * - Fonts and OpenGL + * - http://gltt.sourceforge.net/ + */ + +// And finally our own +#include "Support/util.h" +#include "Output/GraphicsOutputDevice.h" +#include "Output/GraphicsOutputDevicewxGLCanvas.h" + +BEGIN_EVENT_TABLE(GraphicsOutputDevicewxGLCanvas, wxGLCanvas) + EVT_SIZE(GraphicsOutputDevicewxGLCanvas::OnSize) + EVT_PAINT(GraphicsOutputDevicewxGLCanvas::OnPaint) + EVT_ERASE_BACKGROUND(GraphicsOutputDevicewxGLCanvas::OnEraseBackground) +END_EVENT_TABLE() + +// wxGLCanvas attributes +int GraphicsOutputDevicewxGLCanvas::GLAttrlist[] = { + WX_GL_RGBA, 1, + WX_GL_DOUBLEBUFFER, 1, + WX_GL_MIN_RED, 5, + WX_GL_MIN_GREEN, 5, + WX_GL_MIN_BLUE, 5, + WX_GL_MIN_ALPHA, 3, + WX_GL_DEPTH_SIZE, 16, + 0 +}; + +// OpenGL get procaddress function pointer, differs across platforms +typedef void (*(*glGetProcAddressPtr_t)(const char*))(); + +GraphicsOutputDevicewxGLCanvas::GraphicsOutputDevicewxGLCanvas(Parameters *pParam, + wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) + : wxGLCanvas(parent, (wxGLCanvas*) NULL, id, pos, size, + style|wxFULL_REPAINT_ON_RESIZE, name, GLAttrlist), + GraphicsOutputDevice(pParam) { + m_init = false; + m_gllist = 0; + m_pWorkerContext = NULL; + m_bAntialiasing = true; + m_pFont = NULL; + m_sFontFile = NULL; + m_iFontsize = -1; +#if !defined(_MACOSX) + s_bWorkerNeedsInit = false; +#endif + +#ifdef WITH_GL_VERTEX_ARRAYS + m_iVertexType = 0xffff; // no gBegin() has happened yet + m_bStaticColor = false; + m_pVertices = NULL; + m_iVerticesMax = 0; + // Enable vertex arrays if possible +#ifdef _MACOSX + m_glLockArraysEXT = ::glLockArraysEXT; + m_glUnlockArraysEXT = ::glUnlockArraysEXT; + m_bVertexArrayLock = true; +#else + m_bVertexArrayLock = false; + // OpenGL command needed to fetch entry point, do it in InitGL() +#endif /* _MACOSX */ +#endif /* WITH_GL_VERTEX_ARRAYS */ +} + +GraphicsOutputDevicewxGLCanvas::~GraphicsOutputDevicewxGLCanvas() { + // Cleanup OpenGL display list + if (m_init) { + glDeleteLists(m_gllist, 1); + } + DELETE_IF_NONNULL(m_pWorkerContext); + DELETE_IF_NONNULL(m_pFont); +#ifdef WITH_GL_VERTEX_ARRAYS + DELETE_ARRAY_IF_NONNULL(m_pVertices); +#endif +} + +void GraphicsOutputDevicewxGLCanvas::Start() { + // This seems to be needed to prevent a crash on windows, but why???? + SetCurrent(); + return GraphicsOutputDevice::Start(); +} + +bool GraphicsOutputDevicewxGLCanvas::Initialize(unsigned int iVerticesMax) { + AIM_ASSERT(m_pParam); + // Give a chance to update anti-aliasing settings + if (m_bAntialiasing != m_pParam->GetBool("output.antialias")) { + m_bAntialiasing = m_pParam->GetBool("output.antialias"); + if (SetCurrent()) { + InitGL(); +#if !defined(_MACOSX) + { + wxMutexLocker lock(s_mutexOpenGL); + s_bWorkerNeedsInit = true; + } +#endif + } + } + +#ifdef WITH_GL_VERTEX_ARRAYS + // Re-allocate vertices + if (iVerticesMax > m_iVerticesMax) { + DELETE_IF_NONNULL(m_pVertices); + m_iVerticesMax = iVerticesMax; + // If color is static, we need not store the color + if (m_bStaticColor) + m_pVertices = new GLfloat[(iVerticesMax+1)*3]; + else + m_pVertices = new GLfloat[(iVerticesMax+1)*6]; + } +#endif + + // Change font if requested + const char *sFontFile = m_pParam->GetString("output.gl.fontfile"); + unsigned int iFontsize = m_pParam->GetUInt("output.fontsize"); + if (!m_sFontFile + || !strcmp(m_sFontFile,sFontFile)==0 + || m_iFontsize!=(int)iFontsize) { + wxMutexLocker lock(s_mutexOpenGL); + DELETE_IF_NONNULL(m_pFont); + wxString sWorkingFontFilename = wxString::FromAscii(sFontFile); + if (!wxFileExists(sWorkingFontFilename)) { + sWorkingFontFilename = wxString::FromAscii(aimDataDir()); + sWorkingFontFilename += _T("/"); + sWorkingFontFilename += wxString::FromAscii(sFontFile); + } + //if (!wxFileExists(sWorkingFontFilename)) + //sWorkingFontFilename.replace("Font:").append(sFontFile); + m_pFont = static_cast<FTFont*>(new FTGLBitmapFont(sWorkingFontFilename.fn_str())); + if (!m_pFont || m_pFont->Error()) { + aimERROR(_T("Couldn't load font '%s'"), sFontFile); + DELETE_IF_NONNULL(m_pFont); + } else { + // Display lists don't mix with our own usage :( + // May not be needed for a Bitmap font + //m_pFont->UseDisplayList(false); + if ( !m_pFont->FaceSize(iFontsize) ) { + AIM_ERROR(_T("Couldn't select font size %u on font '%s'"), iFontsize, sFontFile); + DELETE_IF_NONNULL(m_pFont); + } + } + m_sFontFile = sFontFile; + m_iFontsize = iFontsize; + } + return true; +} +bool GraphicsOutputDevicewxGLCanvas::Initialize() { + return Initialize(0); +} + +void GraphicsOutputDevicewxGLCanvas::Render() { + wxPaintDC dc(this); + // We want to initialize first from main thread. + if (!m_init) { + if (!SetCurrent()) return; + InitGL(); + } + // Render saved list only if not animating (redrawn anyway in that case) + if (!m_bRunning) { + if (!SetCurrent()) { + return; + } + glClear(GL_COLOR_BUFFER_BIT/*|GL_DEPTH_BUFFER_BIT*/); + glCallList(m_gllist); + SwapBuffers(); + } +} + +void GraphicsOutputDevicewxGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + Render(); +} + +void GraphicsOutputDevicewxGLCanvas::OnSize(wxSizeEvent& event) { + // this is also necessary to update the context on some platforms + wxGLCanvas::OnSize(event); + + // set GL viewport + // (not called by wxGLCanvas::OnSize on all platforms...) + if (SetCurrent()) { + DoResize(); + // It is only sensible to update the other thread when it's running + // Don't acquire the mutex when s_bWorkerNeedsInit already to avoid deadlock + if (/*m_bRunning &&*/ !s_bWorkerNeedsInit) { + wxMutexLocker lock(s_mutexOpenGL); + s_bWorkerNeedsInit = true; + } + } +} + +void GraphicsOutputDevicewxGLCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event)) { +} + +bool GraphicsOutputDevicewxGLCanvas::SetCurrent() { + bool bRet=true; + +#ifndef __WXMOTIF__ + bRet = (GetContext()!=NULL); + if (bRet) +#endif + { + wxGLCanvas::SetCurrent(); + } + return bRet; +} + +void GraphicsOutputDevicewxGLCanvas::DoResize() { + int w, h; + GetClientSize(&w, &h); + glViewport(0, 0, (GLint)w, (GLint)h); +} + +void GraphicsOutputDevicewxGLCanvas::InitGL() { + /* No SetCurrent() here, because this can be called from different GL contexts. + * Convenient for multi-threaded operation. */ + //aimERROR(_T("InitGL Called")); +#if defined(WITH_GL_VERTEX_ARRAYS) && !defined(_MACOSX) + if (!m_init) { + /* This needs to be done here, because OpenGL commands may need SetCurrent() + * and an already shown window. */ + char *extensions = (char *)glGetString(GL_EXTENSIONS); + if (!extensions) { + AIM_INFO(_T("Could not query OpenGL extensions, vertex arrays disabled")); + } else if (strstr(extensions, "GL_EXT_compiled_vertex_array")) { + m_glLockArraysEXT = (LOCAL_PFNGLLOCKARRAYSEXTPROC)GL_GET_PROC_ADDRESS("glLockArraysEXT"); + m_glUnlockArraysEXT = (LOCAL_PFNGLUNLOCKARRAYSEXTPROC)GL_GET_PROC_ADDRESS("glUnlockArraysEXT"); + if(!m_glLockArraysEXT || !m_glUnlockArraysEXT) + AIM_ERROR(_T("OpenGL error on GL_EXT_compiled_vertex_array")); + else + m_bVertexArrayLock = true; + } + } +#endif + DoResize(); + glClearColor(0, 0, 0, 1); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity( ); + + glEnable(GL_VERTEX_ARRAY); + + // Window limits in OpenGL co-ordiantes + //! \todo Make this configurable, or change and document fixed values + glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0); + glTranslatef(0.0, 0.0, 0.0); + + if (m_bAntialiasing) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glBlendFunc(GL_ONE, GL_ONE); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + } else { + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } + glLineWidth(1.0); + + // Get a free display list only the first time + if (!m_init) { +#if !defined(_MACOSX) + // Windows and Linux need a separate worker context + aimASSERT(wxIsMainThread()); +#if wxCHECK_VERSION(2,8,0) + m_pWorkerContext = new wxGLContext(this, m_glContext); +#else + m_pWorkerContext = new wxGLContext(true, + this, + wxNullPalette, + m_glContext); +#endif + aimASSERT(m_pWorkerContext); + s_bWorkerNeedsInit = true; +#endif + m_gllist = glGenLists(1); + aimASSERT(m_gllist); + // Empty window at start + glNewList(m_gllist, GL_COMPILE_AND_EXECUTE); + glEndList(); + m_init = true; + } +} + +// Call before any other render* functions +void GraphicsOutputDevicewxGLCanvas::gGrab() { + AimwxGuiLocker __lock__; +#if !defined(_MACOSX) + // Detect if we're the main thread or not. + if (!wxIsMainThread()) { + // We're called by a worker thread, make sure there's a right context + AIM_ASSERT(m_pWorkerContext); +#if wxCHECK_VERSION(2,8,0) + m_pWorkerContext->SetCurrent(*this); +#else + m_pWorkerContext->SetCurrent(); +#endif + // Update OpenGL settings if needed + wxMutexLocker lock(s_mutexOpenGL); + if (s_bWorkerNeedsInit) { + InitGL(); + s_bWorkerNeedsInit = false; + } + } else +#endif + { + // Either called by main thread, or we need no special worker glContext + if (!SetCurrent()) { + return; + } + // Init OpenGL once, but after SetCurrent + if (!m_init) { + InitGL(); + } + } + glClear(GL_COLOR_BUFFER_BIT); + + // Start and store in a display list for redrawing + glNewList(m_gllist, GL_COMPILE); +} + +void GraphicsOutputDevicewxGLCanvas::gBeginLineStrip() { +#ifdef WITH_GL_VERTEX_ARRAYS + aimASSERT(m_iVertexType == 0xffff); // Previous gBegin*() must be gEnd()ed + // New lines vertex array + m_iVertexCount = 0; + m_iVertexType = GL_LINE_STRIP; +#else + AimwxGuiLocker __lock__; + glBegin(GL_LINE_STRIP); +#endif +} + +void GraphicsOutputDevicewxGLCanvas::gBeginQuadStrip() { +#ifdef WITH_GL_VERTEX_ARRAYS + aimASSERT(m_iVertexType == 0xffff); // Previous gBegin*() must be gEnd()ed + // New quads vertex array + m_iVertexCount = 0; + m_iVertexType = GL_QUAD_STRIP; +#else + AimwxGuiLocker __lock__; + glBegin(GL_QUAD_STRIP); +#endif +} + +void GraphicsOutputDevicewxGLCanvas::gVertex3f(float x, float y, float z) { +#ifdef WITH_GL_VERTEX_ARRAYS + aimASSERT(m_iVertexType != 0xffff); // Must be inside gBegin*() + if (m_iVertexCount>=m_iVerticesMax) { + static bool errShown=false; + if (!errShown) { + aimERROR(_T("Error: max vertex count reached: %d"), m_iVertexCount); + errShown=true; + } + return; + } + if (m_bStaticColor) { + m_pVertices[m_iVertexCount*3+0] = x; + m_pVertices[m_iVertexCount*3+1] = y; + m_pVertices[m_iVertexCount*3+2] = z; + } else { + m_pVertices[m_iVertexCount*6+0] = m_fCurColorR; + m_pVertices[m_iVertexCount*6+1] = m_fCurColorG; + m_pVertices[m_iVertexCount*6+2] = m_fCurColorB; + m_pVertices[m_iVertexCount*6+3] = x; + m_pVertices[m_iVertexCount*6+4] = y; + m_pVertices[m_iVertexCount*6+5] = z; + } + m_iVertexCount++; +#else + AimwxGuiLocker __lock__; + glVertex3f(x,y,z); +#endif +} + +void GraphicsOutputDevicewxGLCanvas::gColor3f(float r, float g, float b) { +#ifdef WITH_GL_VERTEX_ARRAYS + if (m_iVertexType==0xffff || m_bStaticColor) { + // If not inside vertex array run, use the ordinary command + glColor3f(r, g, b); + } + if (!m_bStaticColor) { + // Set current color for vertex array usage + m_fCurColorR = r; + m_fCurColorG = g; + m_fCurColorB = b; + } +#else + AimwxGuiLocker __lock__; + glColor3f(r, g, b); +#endif +} + +void GraphicsOutputDevicewxGLCanvas::gEnd() { +#ifdef WITH_GL_VERTEX_ARRAYS + aimASSERT(m_iVertexType != 0xffff); // Must be inside gBegin*() + AimwxGuiLocker __lock__; + + // Draw the vertex array + glEnableClientState(GL_VERTEX_ARRAY); + + // Draw vertices + if (m_bStaticColor) + glVertexPointer(3, GL_FLOAT, 0, m_pVertices); + else + glInterleavedArrays(GL_C3F_V3F, 0, m_pVertices); + if (m_bVertexArrayLock) m_glLockArraysEXT(0, m_iVertexCount); + glDrawArrays(m_iVertexType, 0, m_iVertexCount); + if (m_bVertexArrayLock) m_glUnlockArraysEXT(); + + glDisableClientState(GL_VERTEX_ARRAY); + + // Remember we're outside a gBegin()..gEnd() loop + m_iVertexType = 0xffff; +#else + AimwxGuiLocker __lock__; + glEnd(); +#endif +} + +void GraphicsOutputDevicewxGLCanvas::gText3f(float x, + float y, + float z, + const char *sStr, + bool bRotated) { +#ifdef WITH_GL_VERTEX_ARRAYS + aimASSERT(m_iVertexType == 0xffff); // Must be outside gBegin*() +#endif + + if (!m_pFont) + return; + + //! \todo make rotation work + if (bRotated) + return; + + { + AimwxGuiLocker __lock__; + /* + if (bRotated) { + glPushMatrix(); + glTranslatef(x,y,z); + glRotatef(90.0f, 0, 0, 1.0f); + glRasterPos3f(0,0,0); + m_pFont->Render(sStr); + glPopMatrix(); + } else { + */ + glRasterPos3f(x, y, z); + m_pFont->Render(sStr); + } +} + +void GraphicsOutputDevicewxGLCanvas::gRelease() { +#ifdef WITH_GL_VERTEX_ARRAYS + aimASSERT(m_iVertexType == 0xffff); // Must be gEnd()ed +#endif + AimwxGuiLocker __lock__; + glEndList(); + glCallList(m_gllist); + //glFlush(); + SwapBuffers(); // Doesn't matter in what context +}