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