Briefing
Here is a heavily modified version of wxOgre, that decouples the most possible wxWidgets from Ogre. The idea behind this class is to provide a widget that only manages a single render window/target. The pros of this approach are mainly two:
- There can be any number of wxOgreRenderWindows existing at the same time (no singleton class)
- Gives the user total freedom on how to manage Ogre (scene type, viewports, etc.)
It provides the common behavior for events such as timer ticks, painting or resizing. As for the input handling, it can register mouse event handler callback functions to change its behavior in run time, as well as avoiding the need to refine the class in order to do custom processing of the events.
It has the same requirements as wxOgre, and has been tested under Eihort, wxWidgets 2.8 and Ubuntu 7.04. Windows is untested yet, but the Windows-related source code has been copied straight from the original wxOgre.
Code
wxOgreRenderWindow.h
#ifndef WXOGRERENDERWINDOW_H #define WXOGRERENDERWINDOW_H #include "Ogre.h" #include "OgreNoMemoryMacros.h" #include "wx/wx.h" #include "OgreMemoryMacros.h" /** wxWidgets Ogre render window widget. Strongly based on the existing wxOgre widget implementation, this one isolates the wx component from Ogre, acting as a simple bind to render inside a wxWidgets window. @author Jesús Alonso Abad 'Kencho', Other contributors (original wxOgre). */ class wxOgreRenderWindow : public wxControl { DECLARE_CLASS (wxOgreRenderWindow) DECLARE_EVENT_TABLE () // Type definitions ------------------------------------------------------------ public: /** Mouse events callback type. Any static function with the signature of a mouse events handler does the work. */ typedef void (*MouseEventsCallback)(wxMouseEvent &); // Attributes ------------------------------------------------------------------ protected: /// A shared reference to the Ogre root. static Ogre::Root *msOgreRoot; /// This control's own render window reference. Ogre::RenderWindow *mRenderWindow; /// Timer to sync the rendering to a "constant" frame rate. wxTimer *mRenderTimer; /// The Id of the next render window static unsigned int msNextRenderWindowId; // Registered callbacks /// Callback for mouse events. MouseEventsCallback mMouseEventsCallback; // Methods --------------------------------------------------------------------- public: /** wx-like Constructor. @param parent The parent wxWindow component. @param id The control id. @param pos The default position. @param size The default size. @param style The default style for this component. @param validator A default validator for the component. */ wxOgreRenderWindow (wxWindow *parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxSUNKEN_BORDER, const wxValidator &validator = wxDefaultValidator); /** Default constructor. Allows the "standard" wxWidgets' two-step construction. */ wxOgreRenderWindow (); /** Creation method (for the two-step construction). @param parent The parent wxWindow component. @param id The control id. @param pos The default position. @param size The default size. @param style The default style for this component. @param validator A default validator for the component. */ bool Create (wxWindow *parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxSUNKEN_BORDER, const wxValidator &validator = wxDefaultValidator); /** Virtual destructor. */ virtual ~wxOgreRenderWindow (); /** Initialisation method. */ virtual void Init (); /** Overrides the default implementation. This override is here for convenience. Returns a symbolic 320x240px size. @return A size of 320x240 (just a symbolic 4:3 size). */ virtual wxSize DoGetBestSize () const; /** Gets the current Ogre root reference associated. @return The current reference to Ogre's Root. */ static Ogre::Root *GetOgreRoot (); /** Sets the Ogre root for all the wxOgreRenderWindow instances. @param root The new OgreRoot. */ static void SetOgreRoot (Ogre::Root *root); /** Gets the associated Ogre render window. @return The render window used to paint this control. */ Ogre::RenderWindow *GetRenderWindow () const; /** Sets the render timer period. @param period The number of milliseconds before the next notification. A negative or zero value will stop the timer. */ void SetRenderTimerPeriod (int period); /** Sets the mouse events callback. @param callback The callback function. */ void SetMouseEventsCallback (MouseEventsCallback callback); /** Updating function. */ virtual void Update (); /** Painting event callback. @param evt Data regarding the painting event. */ virtual void OnPaint (wxPaintEvent &evt); /** Render timer event callback. @param evt Data regarding the timer event. */ virtual void OnRenderTimer (wxTimerEvent &evt); /** Resizing events callback. @param evt Data regarding the resize event. */ virtual void OnSize (wxSizeEvent &evt); /** Mouse events callback. @remarks Note this will call the specified callback function to process the event. @param evt Data regarding the mouse event. */ virtual void OnMouseEvents (wxMouseEvent &evt); protected: /** Creates an Ogre render window for this widget. */ virtual void CreateRenderWindow (); /** Gets the handle for the render window. @return The render window handle. */ virtual Ogre::String GetOgreHandle () const; }; #endif // WXOGRERENDERWINDOW_H
wxOgreRenderWindow.cpp
#include "wxOgreRenderWindow.h" #ifdef __WXGTK__ #include <gdk/gdk.h> #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <wx/gtk/win_gtk.h> #include <GL/glx.h> #endif const wxWindowID ID_RENDERTIMER = wxNewId (); IMPLEMENT_CLASS (wxOgreRenderWindow, wxControl) BEGIN_EVENT_TABLE (wxOgreRenderWindow, wxControl) #ifndef __WXMSW__ EVT_PAINT (wxOgreRenderWindow::OnPaint) #endif EVT_TIMER (ID_RENDERTIMER, wxOgreRenderWindow::OnRenderTimer) EVT_SIZE (wxOgreRenderWindow::OnSize) EVT_MOUSE_EVENTS (wxOgreRenderWindow::OnMouseEvents) END_EVENT_TABLE () Ogre::Root *wxOgreRenderWindow::msOgreRoot = 0; //------------------------------------------------------------------------------ unsigned int wxOgreRenderWindow::msNextRenderWindowId = 1; //------------------------------------------------------------------------------ wxOgreRenderWindow::wxOgreRenderWindow (wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxValidator &validator) { Init (); Create (parent, id, pos, size, style, validator); } //------------------------------------------------------------------------------ wxOgreRenderWindow::wxOgreRenderWindow () { Init (); } //------------------------------------------------------------------------------ bool wxOgreRenderWindow::Create (wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxValidator &validator) { // Error creating the base class if (!wxControl::Create (parent, id, pos, size, style, validator)) return false; // Use the told size or let the sizers pick one. SetInitialSize (size); SetBackgroundStyle(wxBG_STYLE_CUSTOM); if (msOgreRoot) CreateRenderWindow (); return true; } //------------------------------------------------------------------------------ wxOgreRenderWindow::~wxOgreRenderWindow () { if (mRenderWindow && msOgreRoot) msOgreRoot->detachRenderTarget (mRenderWindow); mRenderWindow = 0; if (mRenderTimer) delete mRenderTimer; } //------------------------------------------------------------------------------ void wxOgreRenderWindow::Init () { mRenderWindow = 0; // Callbacks mMouseEventsCallback = 0; mRenderTimer = new wxTimer (this, ID_RENDERTIMER); mRenderTimer->Start (10); } //------------------------------------------------------------------------------ inline wxSize wxOgreRenderWindow::DoGetBestSize () const { return wxSize (320, 240); } //------------------------------------------------------------------------------ Ogre::Root *wxOgreRenderWindow::GetOgreRoot () { return msOgreRoot; } //------------------------------------------------------------------------------ void wxOgreRenderWindow::SetOgreRoot (Ogre::Root *root) { msOgreRoot = root; } //------------------------------------------------------------------------------ Ogre::RenderWindow *wxOgreRenderWindow::GetRenderWindow () const { return mRenderWindow; } //------------------------------------------------------------------------------ void wxOgreRenderWindow::SetRenderTimerPeriod (int period) { if (!mRenderTimer) return; if (period <= 0) mRenderTimer->Stop (); else mRenderTimer->Start (period); } //------------------------------------------------------------------------------ void wxOgreRenderWindow::Update () { if (msOgreRoot) msOgreRoot->renderOneFrame (); } //------------------------------------------------------------------------------ void wxOgreRenderWindow::SetMouseEventsCallback (MouseEventsCallback callback) { mMouseEventsCallback = callback; } //------------------------------------------------------------------------------ void wxOgreRenderWindow::OnPaint (wxPaintEvent &evt) { Update (); } //------------------------------------------------------------------------------ void wxOgreRenderWindow::OnRenderTimer (wxTimerEvent &evt) { Update (); } //------------------------------------------------------------------------------ void wxOgreRenderWindow::OnSize (wxSizeEvent &evt) { if (mRenderWindow) { // Setting new size; int width; int height; wxSize size = evt.GetSize (); width = size.GetWidth (); height = size.GetHeight (); // Cocoa will break the window here if the panel docks using an Advanced UI Manager // because wxWidgets actually destroys the floating panel. // wxWidgets handles the width and height of the window anyway // and Ogre recognises that in windowMovedOrResized(). // I am not sure if windows requires this, but it works fine with it. #if defined(__WXGTK__) || defined(__WXMSW__) mRenderWindow->resize (width, height); #endif // Letting Ogre know the window has been resized; mRenderWindow->windowMovedOrResized (); } Update (); } //------------------------------------------------------------------------------ void wxOgreRenderWindow::OnMouseEvents (wxMouseEvent &evt) { if (mMouseEventsCallback) (*mMouseEventsCallback)(evt); } //------------------------------------------------------------------------------ void wxOgreRenderWindow::CreateRenderWindow () { Ogre::NameValuePairList params; params["externalWindowHandle"] = GetOgreHandle (); // Need to tell Ogre3D that we are using a cocoa window // if wx is using it. // and that wxWidgets uses an NSView* as the handle #ifdef __WXCOCOA__ params["macAPI"] = "cocoa"; params["macAPICocoaUseNSView"] = "true"; #endif // Get wx control window size int width; int height; GetSize (&width, &height); // Create the render window mRenderWindow = Ogre::Root::getSingleton ().createRenderWindow ( Ogre::String ("OgreRenderWindow") + Ogre::StringConverter::toString (msNextRenderWindowId++), width, height, false, ¶ms); mRenderWindow->setActive (true); } //------------------------------------------------------------------------------ Ogre::String wxOgreRenderWindow::GetOgreHandle () const { Ogre::String handle; #ifdef __WXMSW__ // Handle for Windows systems handle = Ogre::StringConverter::toString((size_t)((HWND)GetHandle())); #elif defined(__WXCOCOA__) handle = Ogre::StringConverter::toString((size_t)((NSView*)(GetHandle())); #elif defined(__WXGTK__) // Handle for GTK-based systems GtkWidget *widget = m_wxwindow; gtk_widget_set_double_buffered (widget, FALSE); gtk_widget_realize( widget ); // Grab the window object GdkWindow *gdkWin = GTK_PIZZA (widget)->bin_window; Display* display = GDK_WINDOW_XDISPLAY(gdkWin); Window wid = GDK_WINDOW_XWINDOW(gdkWin); std::stringstream str; // Display str << (unsigned long)display << ':'; // Screen (returns "display.screen") std::string screenStr = DisplayString(display); std::string::size_type dotPos = screenStr.find("."); screenStr = screenStr.substr(dotPos+1, screenStr.size()); str << screenStr << ':'; // XID str << wid << ':'; // Retrieve XVisualInfo int attrlist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 16, GLX_STENCIL_SIZE, 8, None }; XVisualInfo* vi = glXChooseVisual(display, DefaultScreen(display), attrlist); str << (unsigned long)vi; handle = str.str(); #else // Any other unsupported system #error Not supported on this platform. #endif return handle; }
Closing words
Thanks to the authors of the original wxOgre for giving me the base to work here. My lack of knowledge on wxWidgets, GTK etc. is a shame!
Kencho
25th of June 2013
I fixed a bug to do with using the AUIManager, Cocoa and the RenderWindow. Hopefully someone finds this helpful. Big thanks to Kencho and original authors.
Blader