WxOgre for OGRE v1.2         A simple rendering widget for wxWindows (OGRE V1.2)

This document is outdated, make sure you read WxOgre for OGRE v1.4

wxOgre for OGRE v1.2

This article provides an example of how OGRE and wxWidgets can work together in one application. This can be very useful when writing tools such as 3D world editors or 3D viewers.
The following C++ class is a quick and simple wxControl derived object that can be easily used with the GUI toolkit wxWidgets. It is defined as singleton and currently only supports a single OGRE window.

Originally posted here.

OGRE Forums Thread here.

Requirements

  • OGRE 1.2 Dagon
  • wxWidgets

Limitations

  • Supports a single OGRE window only
  • Fully tested on Windows OS. Linux support is there but needs testing (if somebody was able to get this to work on MacOS please add your code).

Memory Manager Note

Just like Ogre, wxWidgets uses a memory manager. To avoid collision between the two memory managers, you can do one of the following:

  • Recompile Ogre from source without the debug memory manager: Add #define OGRE_DEBUG_MEMORY_MANAGER 0 in OgreConfig.h and build Ogre.
  • If you want to avoid recompiling and use the precompiled OgreSDK, follow these steps:
    • In OgreMemoryManager.h comment out these lines (lines 353-373)



inline void *operator new(size_t reportedSize)
{
    if( !gProcessID )
        gProcessID = Ogre::MemoryManager::instance()._getProcessID();
    return Ogre::MemoryManager::instance().op_new_sc( reportedSize, gProcessID );
}
inline void *operator new[](size_t reportedSize)
{
    if( !gProcessID )
        gProcessID = Ogre::MemoryManager::instance()._getProcessID();
    return Ogre::MemoryManager::instance().op_new_vc( reportedSize, gProcessID );
}

inline void operator delete(void *reportedAddress)
{
    Ogre::MemoryManager::instance().op_del_sc( reportedAddress, gProcessID );    
}
inline void operator delete[](void *reportedAddress)
{
    Ogre::MemoryManager::instance().op_del_vc( reportedAddress, gProcessID );
}
    • And replace the line 381



#include "OgreMemoryMacros.h"

with

#include "OgreNoMemoryMacros.h"

Code

wxOgre.h

#ifndef __WXOGRE_H__
#define __WXOGRE_H__

#include "Ogre.h"
#include "wx/wx.h"
#include <OgreNoMemoryMacros.h>
#include <OgreMemoryMacros.h>

    /**
    @brief WX widget for and Ogre rendering window

    This WX widget is a self-contained SINGLETON Ogre rendering window; 
    meaning it contains all Ogre objects necessary to create a rendering 
    window and currently supports only one rendering window at a time!
    This is due to the limitation of the self contained class.
    
    @usage Simply create a new wxOgre object and pass a wxFrame as its 
    parent window. Then work with it just like ay other wxControl object.
    It can even be passed to an wxAUI pane.
    */
    class wxOgre : 
        public wxControl, 
        public Ogre::Singleton<wxOgre>
    {
        DECLARE_CLASS(wxOgre)
    public:
        /** A new wxOgre must receive a parent frame to which to attach 
        itself to */
        wxOgre (wxFrame* parent);
        ~wxOgre();

        /** Renders a single Ogre frame */
        void update();

        /** Returns the currently used camera */
        inline Ogre::Camera* getCamera(){return mCamera;}
        /** Sets a new camera for rendering */
        inline void setCamera(Ogre::Camera* camera){mCamera = camera;}

    protected:
        DECLARE_EVENT_TABLE()

    private:
        /** Creates an ogre rendering window and all other default objects
        such as the Ogre Root, default camera, default scene manager etc */ 
        void createOgreRenderWindow();
        /** Toggles the rendering timer on/off */
        void toggleTimerRendering();

        /** Callback function to a window resize event */
        void OnSize(wxSizeEvent& event);
        /** Callback function to a window paint event */
        void OnPaint(wxPaintEvent& event);
        /** Callback function to an EraseBackground event */
        void OnEraseBackground( wxEraseEvent& );
        /** Callback function to a timer "tick" event */
        void OnRenderTimer(wxTimerEvent& event);

        /* WX members */
        /** Rendering timer */
        wxTimer    mTimer;

        /* Ogre members */
        /** Local Ogre::Root pointer */
        Ogre::Root* mRoot;
        /** Local Ogre::Viewport pointer */
        Ogre::Viewport* mViewPort;
        /** Local Ogre::Camera pointer */
        Ogre::Camera* mCamera;
        /** Local Ogre::SceneManager pointer */
        Ogre::SceneManager* mSceneMgr;
        /** Local Ogre::RenderWindow pointer */
        Ogre::RenderWindow* mRenderWindow;

    public:
        // *****************************************************

        // -----------------------------------------------------
        /**
        @remarks
        Why do we do this? Well, it's because the Singleton
        implementation is in a .h file, which means it gets compiled
        into anybody who includes it. This is needed for the
        Singleton template to work, but we actually only want it
        compiled into the implementation of the class based on the
        Singleton, not all of them. If we don't change this, we get
        link errors when trying to use the Singleton-based class from
        an outside dll.
        @par
        This method just delegates to the template version anyway,
        but the implementation stays in this single compilation unit,
        preventing link errors.
        */
        static wxOgre& getSingleton();
        /**
        @remarks
        Why do we do this? Well, it's because the Singleton
        implementation is in a .h file, which means it gets compiled
        into anybody who includes it. This is needed for the
        Singleton template to work, but we actually only want it
        compiled into the implementation of the class based on the
        Singleton, not all of them. If we don't change this, we get
        link errors when trying to use the Singleton-based class from
        an outside dll.
        @par
        This method just delegates to the template version anyway,
        but the implementation stays in this single compilation unit,
        preventing link errors.
        */
        static wxOgre* getSingletonPtr();

    };

#endif // __WXOGRE_H__


wxOgre.cpp

#include "wxOgre.h"

#ifdef __WXGTK__
#include <gdk/gdk.h>
#include <gtk/gtk.h> // just this should suffice as it should include gdk.h itself
#endif

// Required for the timer
const long ID_RENDERTIMER = wxNewId();

// Required for WX
IMPLEMENT_CLASS(wxOgre, wxControl)

// Required for WX
BEGIN_EVENT_TABLE(wxOgre, wxControl)
    EVT_SIZE(wxOgre::OnSize)
    // EVT_PAINT(wxOgre::OnPaint) // Produces flickers and runs too fast!
    EVT_ERASE_BACKGROUND( wxOgre::OnEraseBackground )
    EVT_TIMER( ID_RENDERTIMER, wxOgre::OnRenderTimer )
END_EVENT_TABLE()

wxOgre::wxOgre(wxFrame* parent) : 
        wxControl(parent, -1),
        mTimer(this, ID_RENDERTIMER),
        mRoot(0),
        mViewPort(0),
        mCamera(0),
        mSceneMgr(0),
        mRenderWindow(0)
{
    // Create all Ogre objects
    createOgreRenderWindow();
    // Start the rendering timer
    toggleTimerRendering();
}

void wxOgre::createOgreRenderWindow()
{
    // Create a new Ogre ROOT
    mRoot = new Ogre::Root();
    Ogre::Root::getSingleton().restoreConfig();
    mRenderWindow = Ogre::Root::getSingleton().initialise(false);

    // --------------------
    // Create a new parameters list according to compiled OS
    Ogre::NameValuePairList params;
    Ogre::String handle;
#ifdef __WXMSW__
    handle = Ogre::StringConverter::toString((size_t)((HWND)GetHandle()));
#elif defined(__WXGTK__)
    // TODO: Someone test this. you might to use "parentWindowHandle" if this
    // does not work.  Ogre 1.2 + Linux + GLX platform wants a string of the
    // format display:screen:window, which has variable types ulong:uint:ulong.
    GtkWidget* widget = GetHandle();
    gtk_widget_realize( widget );    // Mandatory. Otherwise, a segfault happens.
    std::stringstream handleStream;
    Display* display = GDK_WINDOW_XDISPLAY( widget->window );
    Window wid = GDK_WINDOW_XWINDOW( widget->window );    // Window is a typedef for XID, which is a typedef for unsigned int
    /* Get the right display (DisplayString() returns ":display.screen") */
    std::string displayStr = DisplayString( display );
    displayStr = displayStr.substr( 1, ( displayStr.find( ".", 0 ) - 1 ) );
    /* Put all together */
    handleStream << displayStr << ':' << DefaultScreen( display ) << ':' << wid;
    handle = handleStream.str();
#else
    #error Not supported on this platform.
#endif
    params["externalWindowHandle"] = handle;

    // Get wx control window size
    int width;
    int height;
    GetSize(&width, &height);
    // Create the render window
    mRenderWindow = Ogre::Root::getSingleton().createRenderWindow("OgreRenderWindow", width, height, false, &params);

    // --------------------
    // Create the SceneManager, in this case a generic one
    mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC, "ExampleSMInstance");

    // --------------------
    // Create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");
    // Position it at 500 in Z direction
    mCamera->setPosition(Ogre::Vector3(0,0,500));
    // Look back along -Z
    mCamera->lookAt(Ogre::Vector3(0,0,-300));
    mCamera->setNearClipDistance(5);
    // Set the viewport
    mViewPort = mRenderWindow->addViewport(mCamera); 
    mViewPort->setBackgroundColour(Ogre::ColourValue(1.0f, 0.0f, 0.0f, 1.0f)); 
}

void wxOgre::toggleTimerRendering()
{
    // Toggle Start/Stop
    if (mTimer.IsRunning())
        mTimer.Stop();
    mTimer.Start(10);
}

wxOgre::~wxOgre()
{
    // destroy Viewport and RenderWindow
    if (mViewPort)
    {
        mRenderWindow->removeViewport(mViewPort->getZOrder());
        mViewPort = 0;
    }

    Ogre::Root::getSingleton().detachRenderTarget(mRenderWindow);
    mRenderWindow = 0;
}

void wxOgre::OnSize(wxSizeEvent& event)
{
    // Setting new size;
    int width;
    int height;
    GetSize(&width, &height);
    mRenderWindow->resize( width, height );
    // Letting Ogre know the window has been resized;
    mRenderWindow->windowMovedOrResized();
    // Set the aspect ratio for the new size;
    if (mCamera)
        mCamera->setAspectRatio(Ogre::Real(width) / Ogre::Real(height));

    update();
}
void wxOgre::OnPaint(wxPaintEvent& event)
{
//    update(); // Produces flickers and runs too fast!
}
void wxOgre::OnEraseBackground( wxEraseEvent& )
{
    update();
}
void wxOgre::OnRenderTimer(wxTimerEvent& event)
{
    update();
}

void wxOgre::update()
{
    // ****************************************************
    // TODO: REMOVE THESE LINES! These are merely for test!
    static float redTone = 0;
    redTone += 0.01;
    if(redTone>1.0)
        redTone=0;
    // ****************************************************

    mViewPort->setBackgroundColour(Ogre::ColourValue(redTone, 0.0f, 0.0f, 1.0f)); 
    Ogre::Root::getSingletonPtr()->renderOneFrame();
}

template<> wxOgre* Ogre::Singleton<wxOgre>::ms_Singleton = 0;
wxOgre& wxOgre::getSingleton()
{
    return ( *ms_Singleton );
}

wxOgre* wxOgre::getSingletonPtr()
{
    return ms_Singleton;
}

Usage - Example with wxAUI

MainFrame.h

#ifndef __MAINFRAME_H__
#define __MAINFRAME_H__

#include "wx/wx.h"
#include "wx/aui/aui.h"
#include "wxOgre.h"

class MainFrame : public wxFrame
{
public:
    MainFrame(wxWindow* parent) : wxFrame(parent, -1, _("wxAUI Test"),
        wxDefaultPosition, wxSize(800,600),
        wxDEFAULT_FRAME_STYLE)                              
    {
        // notify wxAUI which frame to use
        m_mgr.SetFrame(this);

        // create several text controls
        wxTextCtrl* text1 = new wxTextCtrl(this, -1, _("Pane 1 - sample text"),
            wxDefaultPosition, wxSize(200,150),
            wxNO_BORDER | wxTE_MULTILINE);

        wxTextCtrl* text2 = new wxTextCtrl(this, -1, _("Pane 2 - sample text"),
            wxDefaultPosition, wxSize(200,150),
            wxNO_BORDER | wxTE_MULTILINE);

        wxTextCtrl* text3 = new wxTextCtrl(this, -1, _("Main content window"),
            wxDefaultPosition, wxSize(200,150),
            wxNO_BORDER | wxTE_MULTILINE);

        // add the panes to the manager
        m_mgr.AddPane(text1, wxLEFT, wxT("Pane Number One"));
        m_mgr.AddPane(text2, wxBOTTOM, wxT("Pane Number Two"));
        m_mgr.AddPane(text3, wxTOP);

        // ************************
        wxOgrePane = new wxOgre(this);
        m_mgr.AddPane(wxOgrePane, wxCENTER, wxT("Ogre Pane"));

        // tell the manager to "commit" all the changes just made
        m_mgr.Update();
    }

    ~MainFrame()
    {
        // deinitialize the frame manager
        m_mgr.UnInit();
    }

    void UpdateOgre()
    {
        wxOgrePane->update();
    }

    

private:
    wxAuiManager m_mgr;
    wxOgre* wxOgrePane;
};

// our normal wxApp-derived class, as usual
class MyApp : public wxApp
{
public:
    MainFrame* frame;
    bool OnInit()
    {
        frame = new MainFrame(NULL);
        SetTopWindow(frame);
        frame->Show();
        frame->UpdateOgre();
        return true;                    
    }
};

DECLARE_APP(MyApp);
IMPLEMENT_APP(MyApp);

#endif //__MAINFRAME_H__