tomwalters@397: // Copyright 2006, Willem van Engen tomwalters@397: // tomwalters@397: // AIM-C: A C++ implementation of the Auditory Image Model tomwalters@397: // http://www.acousticscale.org/AIMC tomwalters@397: // tomwalters@397: // Licensed under the Apache License, Version 2.0 (the "License"); tomwalters@397: // you may not use this file except in compliance with the License. tomwalters@397: // You may obtain a copy of the License at tomwalters@397: // tomwalters@397: // http://www.apache.org/licenses/LICENSE-2.0 tomwalters@397: // tomwalters@397: // Unless required by applicable law or agreed to in writing, software tomwalters@397: // distributed under the License is distributed on an "AS IS" BASIS, tomwalters@397: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. tomwalters@397: // See the License for the specific language governing permissions and tomwalters@397: // limitations under the License. tomwalters@397: tomwalters@397: /*! tomwalters@397: * \file tomwalters@397: * \brief Output device for output to a wxWidgets OpenGL canvas tomwalters@397: * tomwalters@397: * \author Willem van Engen tomwalters@397: * \date created 2006/09/21 tomwalters@397: * \version \$Id: $ tomwalters@397: */ tomwalters@397: tomwalters@397: #ifndef __GRAPHICS_OUTPUT_DEVICE_GL_CANVAS_H__ tomwalters@397: #define __GRAPHICS_OUTPUT_DEVICE_GL_CANVAS_H__ tomwalters@397: tomwalters@397: // Precompiled wxWidgets headers tomwalters@397: #include "stdwx.h" tomwalters@397: tomwalters@397: // Make sure GLCANVAS is compiled into wxWidgets tomwalters@397: #if !wxUSE_GLCANVAS tomwalters@398: # error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" tomwalters@397: #endif tomwalters@397: tomwalters@397: #if defined (_MACOSX) tomwalters@398: # include tomwalters@398: # include tomwalters@397: #elif defined (_WINDOWS) tomwalters@398: # include tomwalters@398: # define GL_GET_PROC_ADDRESS wglGetProcAddress tomwalters@397: #else tomwalters@398: # include tomwalters@398: # define GL_GET_PROC_ADDRESS(x) glXGetProcAddress((const GLubyte*)x) tomwalters@397: #endif tomwalters@397: /* Define them just ourselves, easiest way to get it working cross-platform tomwalters@397: * and -Mesa/OpenGL-version. */ tomwalters@397: #ifndef APIENTRY tomwalters@398: # define APIENTRY tomwalters@397: #endif tomwalters@397: typedef void (APIENTRY * LOCAL_PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); tomwalters@397: typedef void (APIENTRY * LOCAL_PFNGLUNLOCKARRAYSEXTPROC) (void); tomwalters@397: tomwalters@397: #ifdef FTGL_SUBDIR tomwalters@398: # include tomwalters@397: #else tomwalters@398: # include tomwalters@397: #endif tomwalters@397: tomwalters@397: #include "Output/GraphicsOutputDevice.h" tomwalters@397: tomwalters@397: // Use by default tomwalters@397: #define WITH_GL_VERTEX_ARRAYS tomwalters@397: tomwalters@397: /*! tomwalters@397: * \class GraphicsOutputDevicewxGLCanvas "Output/GraphicsOutputDevicewxGLCanvas.h" tomwalters@397: * \brief Output class for output to a wxWidgets OpenGL canvas tomwalters@397: * tomwalters@397: * On windows, OpenGL needs a different context when two different threads tomwalters@397: * want to issue OpenGL commands. This is handled automatically for two tomwalters@397: * wxThread s. tomwalters@397: */ tomwalters@397: class GraphicsOutputDevicewxGLCanvas : public wxGLCanvas, tomwalters@397: public GraphicsOutputDevice { tomwalters@397: public: tomwalters@397: GraphicsOutputDevicewxGLCanvas(Parameters *pParam, tomwalters@398: wxWindow *parent, tomwalters@397: wxWindowID id = wxID_ANY, tomwalters@397: const wxPoint& pos = wxDefaultPosition, tomwalters@397: const wxSize& size = wxDefaultSize, tomwalters@397: long style = 0, tomwalters@397: const wxString& name tomwalters@397: = _T("GraphicsOutputDeviceGLCanvas")); tomwalters@397: virtual ~GraphicsOutputDevicewxGLCanvas(); tomwalters@397: void OnPaint(wxPaintEvent& event); tomwalters@397: void OnSize(wxSizeEvent& event); tomwalters@397: void OnEraseBackground(wxEraseEvent& event); tomwalters@397: tomwalters@398: /*! \param iVerticesMax Maximum number of vertices to be draw inside a gBegin()..gEnd() tomwalters@398: * tomwalters@398: * When iVerticesMax is zero, this variable will not be updated. Note that is _has_ tomwalters@398: * to be set at least once before using this class. tomwalters@398: */ tomwalters@398: bool Initialize(unsigned int iVerticesMax); tomwalters@398: bool Initialize(); tomwalters@397: tomwalters@398: void Start(); tomwalters@397: tomwalters@397: void gGrab(); tomwalters@397: void gBeginLineStrip(); tomwalters@397: void gBeginQuadStrip(); tomwalters@398: using GraphicsOutputDevice::gVertex3f; // Because we overload it tomwalters@398: void gVertex3f(float x, float y, float z); tomwalters@398: void gColor3f(float r, float g, float b); tomwalters@397: void gEnd(); tomwalters@398: void gText3f(float x, float y, float z, const char *sStr, bool bRotated = false); tomwalters@398: void gRelease(); tomwalters@397: protected: tomwalters@398: /*! \brief Smarter SetCurrent() replacement tomwalters@398: * \return true on success, false on error tomwalters@398: * tomwalters@398: * This function tries GetContext() first. If that fails, it returns false. tomwalters@398: */ tomwalters@398: bool SetCurrent(); tomwalters@398: void Render(); tomwalters@397: tomwalters@398: /*! \brief Initialize the OpenGL environment. tomwalters@398: * tomwalters@398: * This must be called after the canvas is realized but before any other tomwalters@398: * OpenGL operation is done. Make sure to run SetCurrent() beforehand. tomwalters@398: * Usually only needed when m_init is false. tomwalters@398: */ tomwalters@397: void InitGL(); tomwalters@397: tomwalters@398: /*! \brief Handle a resize (notify OpenGL of the new area) tomwalters@398: * tomwalters@398: * This is a separate function, because in multi-threading environments tomwalters@398: * multiple contexts have to call it. tomwalters@398: */ tomwalters@398: void DoResize(); tomwalters@397: tomwalters@398: /*! \brief Only need to initialize OpenGL once. tomwalters@398: * tomwalters@398: * This is false at start and true when OpenGL has been initialized. tomwalters@398: * No mutex needed, since it's set once at InitGL() and only read afterwards. tomwalters@398: */ tomwalters@397: bool m_init; tomwalters@397: tomwalters@398: /*! \brief Vertex list for last drawing so it can be updated on repaint. tomwalters@398: * tomwalters@398: * No mutex needed, since it's set once at InitGL() and only read afterwards. tomwalters@398: */ tomwalters@397: GLuint m_gllist; tomwalters@397: tomwalters@398: //! \brief OpenGL context for worker thread, use when wxIsMainThread() returns false. tomwalters@398: wxGLContext *m_pWorkerContext; tomwalters@397: tomwalters@398: //! \brief Mutex for inter-thread communication tomwalters@398: wxMutex s_mutexOpenGL; tomwalters@397: tomwalters@398: //! \brief When true, OpenGL needs to be reinitialized (in the worker thread) tomwalters@398: bool s_bWorkerNeedsInit; tomwalters@397: tomwalters@398: //! \brief OpenGL attributes used for initialization. tomwalters@397: static int GLAttrlist[]; tomwalters@397: tomwalters@398: //! \brief Whether to use anti-aliasing or not tomwalters@398: bool m_bAntialiasing; tomwalters@397: tomwalters@398: //! \brief FTGL Font class tomwalters@398: FTFont *m_pFont; tomwalters@398: //! \brief Current font filename tomwalters@398: const char *m_sFontFile; tomwalters@398: //! \brief Current font size tomwalters@398: int m_iFontsize; tomwalters@397: tomwalters@397: #if defined(WITH_GL_VERTEX_ARRAYS) || defined(DOXYGEN) tomwalters@398: //! \brief OpenGL vertex type of the current m_pVertices, or 0xffff is outside gBegin()..gEnd() tomwalters@398: int m_iVertexType; tomwalters@398: //! \brief Maximum number of vertices begin gBegin()..gEnd() tomwalters@398: unsigned int m_iVerticesMax; tomwalters@398: //! \brief Vertex array to draw at gEnd(), this becomes m_pVertices[m_iVerticesMax*3] tomwalters@398: GLfloat *m_pVertices; tomwalters@398: //! \brief The current number of vertices inside m_pVertices tomwalters@398: unsigned int m_iVertexCount; tomwalters@397: tomwalters@398: /*! \brief Whether to use coloring in vertex lists tomwalters@398: * tomwalters@398: * This variable must not change after Initialize(), or the program may crash. tomwalters@398: * When this variable is true, color information is stored in vertex lists. If tomwalters@398: * it is false, only vertex data is stored. tomwalters@398: * tomwalters@398: * This variable exists for performance reasons, but is currently only set in tomwalters@398: * the constructor of this object. tomwalters@398: */ tomwalters@398: bool m_bStaticColor; tomwalters@398: //! \brief Current color for vertex list drawing tomwalters@398: float m_fCurColorR, m_fCurColorG, m_fCurColorB; tomwalters@397: tomwalters@398: //! \brief Whether to use vertex array locking or not tomwalters@398: bool m_bVertexArrayLock; tomwalters@398: //! \brief Pointer to vertex array locking function; can be NULL. tomwalters@398: LOCAL_PFNGLLOCKARRAYSEXTPROC m_glLockArraysEXT; tomwalters@398: //! \brief Pointer to vertex array unlocking function; can be NULL. tomwalters@398: LOCAL_PFNGLUNLOCKARRAYSEXTPROC m_glUnlockArraysEXT; tomwalters@397: #endif tomwalters@397: tomwalters@398: /*! \brief wxMutexGuiEnter() / wxMutexGuiLeave() wrapper tomwalters@398: * tomwalters@398: * This is a wxMutexLocker-alike for the main gui mutex. Any method that tomwalters@398: * is public, can be called from within another thread and does OpenGL or tomwalters@398: * other gui calls must use this. Example: tomwalters@398: * \code tomwalters@398: * void DoFoo() { tomwalters@398: * AimwxGuiLocker __lock__; tomwalters@398: * glAmazingMethod(); tomwalters@398: * } tomwalters@398: * \endcode tomwalters@398: * tomwalters@398: * It is mostly on X-Windows (Xorg/XFree86) that the gui mutex appears to tomwalters@398: * be needed. Otherwise the error "Xlib: unexpected async reply" can occur. tomwalters@398: * tomwalters@398: * On windows, the ui may occasionally lock up for a short while with these tomwalters@398: * mutexes. Since they aren't really needed on that platform, it's left out tomwalters@398: * alltogether. tomwalters@398: */ tomwalters@398: class AimwxGuiLocker { tomwalters@398: public: tomwalters@398: inline AimwxGuiLocker() { tomwalters@397: #ifndef _WINDOWS tomwalters@398: if (!wxIsMainThread()) wxMutexGuiEnter(); tomwalters@397: #endif tomwalters@398: } tomwalters@398: inline ~AimwxGuiLocker() { tomwalters@397: #ifndef _WINDOWS tomwalters@398: if (!wxIsMainThread()) wxMutexGuiLeave(); tomwalters@397: #endif tomwalters@398: } tomwalters@398: }; tomwalters@397: DECLARE_EVENT_TABLE() tomwalters@397: }; tomwalters@397: #endif /* __GRAPHICS_OUTPUT_DEVICE_GL_CANVAS_H__ */