WxOgreRenderWindow for Eihort         Heavily modified version of wxOgre, that decouples the most possible wxWidgets from Ogre

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:

  1. There can be any number of wxOgreRenderWindows existing at the same time (no singleton class)
  2. 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, &params);

    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