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

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

wxOgre

The following code is a bare-bones quick'n'dirty nearly-drop-in-ready control for use with the GUI toolkit wxWidgets.
It has to be adjusted slightly (include paths, for example) and doesn't really deserve a prize for being clean code :-) But it's a start.

Requirements

Limitations

  • Currently supports only D3D9 renderer.


-> Can quite easily be made independent of the D3D9 plugin by applying two patches to the OGRE 1.0.1 code base as well as including small interface changes (for resizing, mainly).

  • Doesn't support multi-window rendering (yet).

Code

wxOgre.h

#ifndef WX_OGRE_H
#define WX_OGRE_H

namespace Ogre {
    class Root;
    class RenderWindow;
    class D3D9RenderSystem;
    class D3D9RenderWindow;
    class SceneManager;
    class Camera;
    class Viewport; 
}

class wxOgreView : public wxControl
{
    DECLARE_CLASS(wxOgreView)
public:
    wxOgreView(    wxWindow* parent, 
                wxWindowID id = wxID_ANY,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize, 
                long style = 0 );
    ~wxOgreView();

    // Replaces wxWindow::Create functionality, since
    // we need to use a different window class
    bool Create(    wxWindow *parent, 
                    wxWindowID id,
                    const wxPoint& pos, 
                    const wxSize& size,
                    long style);

    void startRendering( const preferredTimeStep = 100 ); // in milliseconds

    Ogre::RenderWindow* _getRenderWindow() const;

    ED_SIGNAL( Resize, void(const unsigned short, const unsigned short), public );

    unsigned short getRenderWindowWidth() const;
    unsigned short getRenderWindowHeight() const;
protected:
    DECLARE_EVENT_TABLE()

    void OnSize(wxSizeEvent& event);
    void OnPaint(wxPaintEvent& event);
    void OnEraseBackground( wxEraseEvent& );
    void OnRenderTimer(wxTimerEvent& event);

private:
    wxTimer                    mTimer;

    // win32 stuff
    HWND                    m_hwnd;

    // Ogre Stuff
    void cleanupOgre();

    Ogre::Root*                mRoot;
    Ogre::D3D9RenderSystem*    mD3DRenderSystem;
    Ogre::D3D9RenderWindow*    mD3DRenderWindow;
    Ogre::SceneManager*        mSceneMgr;
    Ogre::Camera*            mCamera;
    Ogre::Viewport*            mViewport; 

    bool                    mReady;
};

#endif

wxOgre.cpp

  1. include "pch.h"
  2. include "wx/module.h"
  3. include "wx/msw/private.h"
  4. include "wx/msw/palette.h"
  5. include "wxOgre.h"

  1. include "OgreNoMemoryMacros.h"
  2. include "Ogre.h"
  3. include "f:/dep/ogrenew/RenderSystems/Direct3D9/include/OgreD3D9RenderSystem.h"
  4. include "f:/dep/ogrenew/RenderSystems/Direct3D9/include/OgreD3D9RenderWindow.h"
  5. include "OgreNoMemoryMacros.h"

  1. ifdef _MSC_VER
  2. ifdef _DEBUG
  3. pragma comment( lib, "ogremain_d" )
  4. else
  5. pragma comment( lib, "ogremain" )
  6. endif
  7. endif

  1. ifdef WIN32
  2. ifdef _DEBUG
  3. define new DEBUG_NEW
  4. endif
  5. endif


// ----------------------------
static const wxChar *wxOgreViewClassName = wxT("wxOgreViewClass");
static const wxChar *wxOgreViewClassNameNoRedraw = wxT("wxOgreViewClassNR");

LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message,

WPARAM wParam, LPARAM lParam);

class wxOgreModule : public wxModule
{
public:

bool OnInit() { return true; }

void OnExit() { UnregisterClasses(); }

// register the classes if not done yet, return true if ok, false if

// registration failed

static bool RegisterClasses();

// unregister the classes, done automatically on program termination

static void UnregisterClasses();

private:

// wxOgreView is only used from the main thread so this is MT-ok

static bool ms_registeredOgreClasses;

DECLARE_DYNAMIC_CLASS(wxOgreModule)
};

IMPLEMENT_DYNAMIC_CLASS(wxOgreModule, wxModule)

bool wxOgreModule::ms_registeredOgreClasses = false;

/* static */
bool wxOgreModule::RegisterClasses()
{

if (ms_registeredOgreClasses)

return true;

WNDCLASSEX wndclass;

wndclass.cbSize = sizeof(WNDCLASSEX);

wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC|CS_DBLCLKS; // Redraw all when resized, OWNER DC for speed

wndclass.lpfnWndProc = (WNDPROC)wxWndProc; // what is this DWORD used for?

wndclass.cbClsExtra = 0;

wndclass.cbWndExtra = sizeof( DWORD );

wndclass.hInstance = wxhInstance;

wndclass.hIcon = NULL;

wndclass.hIconSm = NULL;

wndclass.hCursor = ::LoadCursor(NULL,IDC_ARROW);

wndclass.hbrBackground = NULL;

wndclass.lpszMenuName = NULL;

wndclass.lpszClassName = wxOgreViewClassName;

if ( !::RegisterClassEx(&wndclass) )

{

wxLogLastError(wxT("RegisterClassEx(wxOgreViewClass)"));

return false;

}

wndclass.style &= ~ (CS_HREDRAW|CS_VREDRAW);

wndclass.lpszClassName = wxOgreViewClassNameNoRedraw;

if ( !::RegisterClassEx(&wndclass) )

{

::UnregisterClass(wxOgreViewClassName, wxhInstance);

wxLogLastError(wxT("RegisterClassEx(wxOgreViewClassNR)"));

return false;

}

ms_registeredOgreClasses = true;

return true;
}

/* static */
void wxOgreModule::UnregisterClasses()
{

// we need to unregister the classes in case we're in a DLL which is

// unloaded and then loaded again because if we don't, the registration is

// going to fail in wxOgreView::Create() the next time we're loaded

if ( ms_registeredOgreClasses )

{

::UnregisterClass(wxOgreViewClassName, wxhInstance);

::UnregisterClass(wxOgreViewClassNameNoRedraw, wxhInstance);

ms_registeredOgreClasses = false;

}
}

// ----------------------------
const long ID_RENDERTIMER = wxNewId();

IMPLEMENT_CLASS(wxOgreView, wxControl)

BEGIN_EVENT_TABLE(wxOgreView, wxControl)

EVT_SIZE(wxOgreView::OnSize)

//EVT_PAINT(wxOgreView::OnPaint) // don't try this :-(

EVT_ERASE_BACKGROUND( wxOgreView::OnEraseBackground )

EVT_TIMER( ID_RENDERTIMER, wxOgreView::OnRenderTimer )
END_EVENT_TABLE()

wxOgreView::wxOgreView( wxWindow* parent,

wxWindowID id /* = wxID_ANY */,

const wxPoint& pos /* = wxDefaultPosition */,

const wxSize& size /* = wxDefaultSize */,

long style /* = 0 */ ) :

wxControl(),

mRoot( 0 ),

mD3DRenderSystem( 0 ),

mD3DRenderWindow( 0 ),

mSceneMgr( 0 ),

mCamera( 0 ),

mViewport( 0 ),

mReady( false ),

m_hwnd( 0 ),

mTimer(this, ID_RENDERTIMER)
{

bool ret = Create(parent, id, pos, size, style);

//if ( ret )

// SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));

//m_hwnd = (HWND)GetHWND();
}
wxOgreView::~wxOgreView()
{

cleanupOgre();

m_hwnd = 0;
}
bool wxOgreView::Create( wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
{

wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") );

style |= wxFULL_REPAINT_ON_RESIZE;

if ( !wxOgreModule::RegisterClasses() )

{

wxLogError(_("Failed to register wxOgreView window class."));

return false;

}

if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, "OgreView") )

return false;

parent->AddChild(this);

DWORD msflags = WS_CHILD | WS_VISIBLE;

WXDWORD exStyle = 0;

bool ret = MSWCreate(wxOgreViewClassName, NULL, pos, size, msflags, exStyle);

wxCHECK_MSG( ret, false, wxT("Could not create window. MSWCreate() failed") );

if (!ret)

return false;

m_hwnd = HWND( GetHWND() );

// Now on to OGRE

using namespace Ogre;

try

{

mRoot = new Root("ogre_view_plugins.cfg");

// Verify that we found the D3D9 Render system

RenderSystemList *rl = Root::getSingleton().getAvailableRenderers();

RenderSystemList::iterator it = rl->begin();

if (rl->empty() || rl->size() > 1)

{

wxMessageBox("No RenderSystem found in wxOgreView configuration!", "Error");

cleanupOgre () ;

return false; //@todo throw ...

}

Root::getSingleton().setRenderSystem (*it) ;

mD3DRenderSystem = static_cast<D3D9RenderSystem*> (Root::getSingleton().getRenderSystem ()) ;

// Default options

mD3DRenderSystem->initConfigOptions();

// Fixed options

mD3DRenderSystem->setConfigOption("Full Screen", "No");

mD3DRenderSystem->setConfigOption("VSync", "No");

// Build up the resolution string

char buffer 1024 ;

// Woah! Now what's this?

// Needed for Ogre without D3D9 resizing patch (< 1.0.1 and maybe beyond)

//@fixme:

SetWindowPos(m_hwnd, NULL, 0, 0, size.GetWidth(), size.GetHeight(), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

sprintf (buffer, "%d x %d @ 32-bit colour", size.GetWidth(), size.GetHeight()) ;

mD3DRenderSystem->setConfigOption("Video Mode", buffer) ;

mD3DRenderSystem->setConfigOption("Anti aliasing", "None");

// Load resource paths from config file

ConfigFile cf;

cf.load("ogre_resources.cfg");

// Go through all settings in the file

ConfigFile::SectionIterator seci = cf.getSectionIterator();

Ogre::String secName, typeName, archName;

while (seci.hasMoreElements())

{

secName = seci.peekNextKey();

ConfigFile::SettingsMultiMap *settings = seci.getNext();

ConfigFile::SettingsMultiMap::iterator i;

for (i = settings->begin(); i != settings->end(); ++i)

{

typeName = i->first;

archName = i->second;

Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);

}

}

// Tell Ogre to not create it's own window

mD3DRenderWindow = static_cast<D3D9RenderWindow*>(mRoot->initialise(false)) ;

NameValuePairList miscParams;

//miscParams%22colourDepth%22 = StringConverter::toString(32);

//miscParams%22FSAA%22 = StringConverter::toString(0);

//miscParams%22FSAAQuality%22 = StringConverter::toString(0);

//miscParams%22vsync%22 = "false";

miscParams%22externalWindowHandle%22 = StringConverter::toString((size_t)m_hwnd);

mD3DRenderWindow = static_cast<D3D9RenderWindow*>(mRoot->createRenderWindow("OgreView",

size.GetWidth(), size.GetHeight(), false, &miscParams ));

// Do some one-time initializations

mSceneMgr = mRoot->getSceneManager (ST_GENERIC);

// Cameras, viewports, shadows etc are set up via yake::graphics::IWorld!

//mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);

//mSceneMgr->setShadowColour(ColourValue(1, 1, 1));

//mSceneMgr->setShadowFarDistance(300);

mSceneMgr->setAmbientLight(ColourValue(0.4, 0.3, 0.3));

TextureManager::getSingleton().setDefaultNumMipmaps(5);

LogManager::getSingletonPtr()->logMessage("wxOgreView -> default num nip map set to 5");

TextureManager::getSingleton().enable32BitTextures(true);

LogManager::getSingletonPtr()->logMessage("wxOgreView -> 32Bit textures : yes");

Animation::setDefaultInterpolationMode(Animation::IM_SPLINE);

LogManager::getSingletonPtr()->logMessage("wxOgreView -> default animation interpolation mode set to : IM_SPLINE");

MaterialManager::getSingletonPtr()->setDefaultAnisotropy(0);

LogManager::getSingletonPtr()->logMessage("wxOgreView -> default anisotropy level set to : 0");

MaterialManager::getSingletonPtr()->setDefaultTextureFiltering(FT_MIP, FO_LINEAR);

MaterialManager::getSingletonPtr()->setDefaultTextureFiltering(FT_MIN, FO_LINEAR);

MaterialManager::getSingletonPtr()->setDefaultTextureFiltering(FT_MAG, FO_LINEAR);

LogManager::getSingletonPtr()->logMessage("wxOgreView -> default texture filtering set to : FO_LINEAR");

mD3DRenderWindow->setActive(true);

}

catch (Ogre::Exception& e)

{

wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception" );

cleanupOgre();

}

return true;
}
void wxOgreView::startRendering( const preferredTimeStep /* = 100 */)
{

if (mTimer.IsRunning())

mTimer.Stop();

mTimer.Start(preferredTimeStep);
}
void wxOgreView::cleanupOgre()
{

using namespace Ogre;

try {

if (mReady)

{

//clear scene @todo: move to clearScene()

MeshManager::getSingletonPtr()->removeAll();

SkeletonManager::getSingletonPtr()->removeAll();

MaterialManager::getSingletonPtr()->removeAll();

TextureManager::getSingletonPtr()->removeAll();

GpuProgramManager::getSingletonPtr()->removeAll();

mSceneMgr->removeAllEntities();

SceneNode *root = mSceneMgr->getRootSceneNode();

if (root)

{

if (root->numChildren())

root->removeAllChildren();

}

}

// destroy / close OGRE

delete mRoot;

mRoot = 0;

}

catch (Ogre::Exception& e)

{

wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception" );

cleanupOgre();

}
}
void wxOgreView::OnSize(wxSizeEvent& event)
{

try {

if (mD3DRenderWindow)

mD3DRenderWindow->windowMovedOrResized();

}

catch (Ogre::Exception& e)

{

wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception" );

cleanupOgre();

}

mSigResize( event.GetSize().GetWidth(), event.GetSize().GetHeight() );

Refresh();
}
void wxOgreView::OnRenderTimer(wxTimerEvent& event)
{

try {

if (mRoot)

{

if (!mRoot->renderOneFrame())

{

//@todo do something useful here...

}

}

}

catch (Ogre::Exception& e)

{

wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception" );

cleanupOgre();

}
}
void wxOgreView::OnPaint(wxPaintEvent& event)
{

  1. if 0 // DON'T use this. It doesn't work. Really. Strange effects.

try {

if (mRoot)

mRoot->renderOneFrame();

}

catch (Ogre::Exception& e)

{

wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception" );

cleanupOgre();

}

  1. endif

}
void wxOgreView::OnEraseBackground(wxEraseEvent&)
{
}
Ogre::RenderWindow* wxOgreView::_getRenderWindow() const
{

return mD3DRenderWindow;
}
unsigned short wxOgreView::getRenderWindowWidth() const
{

if (mD3DRenderWindow)

return mD3DRenderWindow->getWidth();

else

return 0;
}
unsigned short wxOgreView::getRenderWindowHeight() const
{

if (mD3DRenderWindow)

return mD3DRenderWindow->getHeight();

else

return 0;
}{CODE}

Usage

// create the rendering view
wxOgreView* ogreView = new wxOgreView( parent, // parent is of type 'wxWindow*'
                    wxID_ANY,
                    wxPoint(0,0),//or wxDefaultPosition,
                    wxSize(400,300), //or wxDefaultSize
                    0 );

// pass preferred minimum time for a single frame in milliseconds
ogreView->startRendering(10);

Picking, scene management etc

A controlling class is preferrable rather than embedding it into the Ogre view.
Here's a reduced sample interface (based on Yake's "storm" editor):

class wxYakeGraphics : public wxEvtHandler
{
    wxYakeGraphics(const wxYakeGraphics&);
    const wxYakeGraphics& operator = (const wxYakeGraphics&);
public:
    wxYakeGraphics( wxOgreView* ogreView );
    ~wxYakeGraphics();

    // various operations...
    void translateCam(const yake::math::Vector3& delta);
    void moveCamForward(const yake::real distance);
    void moveCamRight(const yake::real distance);
    void moveCamUp(const yake::real distance);
    void yawCam(const yake::real rad);
    void pitchCam(const yake::real rad);
    yake::math::Vector3 getCamRight() const;
    yake::math::Vector3 getCamLookAt() const;
    yake::math::Vector3 getCamUp() const;
    yake::math::Vector3 getCamPosition() const;
    yake::graphics::IWorld* getWorld() const;

    // signals
    ED_SIGNAL( BeginSelection, void, public )
    ED_SIGNAL( EndSelection, void, public )
    ED_SIGNAL( GraphicsEntitySelected, void(yake::graphics::IEntity*), public )
    ED_SIGNAL( GraphicsEntityDeselected, void(yake::graphics::IEntity*), public )
    // etc...

    // event handlers which control scene navigation (movement, picking ...)
    void onOgreViewMouseWheelEvent(wxMouseEvent& event);
    void onOgreViewMouseEvent(wxMouseEvent& event);
    void onOgreViewMouseMoved(wxMouseEvent& event);
    void onOgreViewKeyDown(wxKeyEvent& event);
    void onOgreViewKeyUp(wxKeyEvent& event);
    void onUpdateTimer(wxTimerEvent& event);
private:
    void onOgreViewResized(const unsigned short w, const unsigned short h);
    // etc...

    enum MouseNavState
    {
        MS_NONE,
        MS_MOVE_XY,
        MS_MOVE_XZ,
        MS_MOVE_XZ_AND_TURN,
        MS_FREE_LOOK
    };
    MouseNavState        mMouseState;
};

wxOgreControl

By Clay Larabie (Falagard)

I had problems with the wxOgre implementation above so looked into my own implementation. The problem I had was that I was getting an overrun caused by the window proc going into an infinite loop. After looking into wxWidgets some more I found that the wxOgre implementation above is doing a lot of unnecessary things. To subclass wxControl you shouldn't be calling MS Windows specific functions like MSWCreate, etc. Doing so basically means that you are limiting the control to only working in MS Windows, and also has the problem of the infinite loop overrun I described above. Using this page as an example http://www.wxwidgets.org/wiki/index.php/Subclassing_WxControl I created my own custom control for rendering Ogre in wxWidgets. I've pasted it here because it seemed like the best place to put it.

Please note that I had problems with Ogre's memory manager and had to compile Ogre with the memory manager turned off by going into OgreConfig.h and changing this line:

#define OGRE_DEBUG_MEMORY_MANAGER 0

wxOgreControl.h

#ifndef __wxOgreControl_H
#define __wxOgreControl_H

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

namespace Ogre {
    class Root;
    class RenderWindow;
    class RenderSystem;
    class RenderWindow;
    class SceneManager;
    class Camera;
    class Viewport; 
}

class wxOgreControl : public wxControl
{
public:
    
    DECLARE_DYNAMIC_CLASS(wxOgreControl);

    wxOgreControl () {}
    wxOgreControl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxPanelNameStr);
    virtual ~wxOgreControl();
    
protected:
    
    DECLARE_EVENT_TABLE()

    void OnSize(wxSizeEvent& evt);
    void OnEraseBackground(wxEraseEvent& evt);
    void OnRenderTimer(wxTimerEvent& evt);

private:

    void setupOgre();
    void cleanupOgre();

    wxTimer* mTimer;
    Ogre::Root* mRoot;
    Ogre::RenderSystem* mRenderSystem;
    Ogre::RenderWindow* mRenderWindow;
    Ogre::SceneManager* mSceneMgr;
    Ogre::Camera* mCamera;
    Ogre::Viewport* mViewport; 
    bool mOgreReady;
    bool mOgreAbandoned;

};

#endif

wxOgreControl.cpp

#include "OgreNoMemoryMacros.h"
#include "wx/module.h"
#include "wxOgreControl.h"
#include "Ogre.h"

using namespace Ogre;

const long ID_RENDERTIMER = wxNewId();

IMPLEMENT_DYNAMIC_CLASS(wxOgreControl, wxControl)

BEGIN_EVENT_TABLE(wxOgreControl, wxControl)
    EVT_SIZE(wxOgreControl::OnSize)
    EVT_ERASE_BACKGROUND(wxOgreControl::OnEraseBackground)
    EVT_TIMER(ID_RENDERTIMER, wxOgreControl::OnRenderTimer)
END_EVENT_TABLE()

wxOgreControl::wxOgreControl(wxWindow* parent, wxWindowID id, const wxPoint& pos /* = wxDefaultPosition */, const wxSize& size /* = wxDefaultSize */, long style /* = 0 */, const wxValidator& validator /* = wxDefaultValidator */, const wxString& name /* = wxPanelNameStr */)
: wxControl(parent, id, pos, size, style, validator, name)
{
    mTimer = new wxTimer(this, ID_RENDERTIMER);
    mTimer->Start(100);
    mRoot = NULL;
    mRenderSystem = NULL;
    mRenderWindow = NULL;
    mSceneMgr = NULL;
    mCamera = NULL;
    mViewport = NULL;
    mOgreReady = false;
    mOgreAbandoned = false;
}
wxOgreControl::~wxOgreControl()
{
    cleanupOgre();
    delete mTimer;
}

void wxOgreControl::setupOgre()
{
    if(mOgreReady)
        return;

    void* windowHandle = GetHandle();

    try
    {
        mRoot = new Root("Plugins.cfg");

        RenderSystemList *rl = Root::getSingleton().getAvailableRenderers();
        RenderSystemList::iterator it = rl->begin();
        if (rl->empty()) 
        {
            wxMessageBox("No render systems found", "Error");
            cleanupOgre() ;
            mOgreAbandoned = true;
            return;
        }

        mRoot->setRenderSystem (*it) ;

        mRenderSystem = mRoot->getRenderSystem ();
        mRenderSystem->setConfigOption("Full Screen", "No");
        mRenderSystem->setConfigOption("VSync", "No");

        char buffer [1024] ;

        wxSize size = wxWindow::GetSize();

        sprintf(buffer, "%d x %d @ 32-bit colour", size.GetWidth(), size.GetHeight()) ;
        mRenderSystem->setConfigOption("Video Mode", buffer) ;

        ConfigFile cf;
        cf.load("Resources.cfg");

        ConfigFile::SectionIterator seci = cf.getSectionIterator();

        Ogre::String secName, typeName, archName;
        while(seci.hasMoreElements())
        {
            secName = seci.peekNextKey();
            ConfigFile::SettingsMultiMap *settings = seci.getNext();
            ConfigFile::SettingsMultiMap::iterator i;
            for(i = settings->begin(); i != settings->end(); ++i)
            {
                typeName = i->first;
                archName = i->second;
                Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
            }
        }
        NameValuePairList miscParams;
        miscParams["externalWindowHandle"] = StringConverter::toString((size_t)windowHandle);
        mRoot->initialise(false);
        mRenderWindow = mRoot->createRenderWindow("OgreView", size.GetWidth(), size.GetHeight(), false, &miscParams );

        mSceneMgr = mRoot->createSceneManager("DefaultSceneManager", "DefaultSceneManager1");

        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

        mSceneMgr->setAmbientLight(ColourValue(0.4, 0.3, 0.3));        
        TextureManager::getSingleton().setDefaultNumMipmaps(5);

        mCamera = mSceneMgr->createCamera("PlayerCam");
        mCamera->setPosition(Vector3(0,0,500));
        mCamera->lookAt(Vector3(0,0,-300));
        mCamera->setNearClipDistance(5);

        mViewport = mRenderWindow->addViewport(mCamera);
        mViewport->setBackgroundColour(ColourValue(0,0,0));

        mCamera->setAspectRatio(Real(mViewport->getActualWidth()) / Real(mViewport->getActualHeight()));

        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
        mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 50 );
        mRenderWindow->setActive(true);
    }
    catch(Ogre::Exception& e)
    {
        wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception" );
        cleanupOgre();
        mOgreAbandoned = true;
        return;
    }
    mOgreReady = true;
}

void wxOgreControl::cleanupOgre()
{
    delete mRoot;
    mRoot = NULL;
    mOgreReady = false;
}

void wxOgreControl::OnSize(wxSizeEvent& event)
{
    try 
    {
        if(mRenderWindow)
            mRenderWindow->windowMovedOrResized();

        if(mCamera)
        {
            mCamera->setAspectRatio(Real(mViewport->getActualWidth()) / Real(mViewport->getActualHeight()));
        }
    }
    catch(Ogre::Exception& e)
    {
        wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception");
        cleanupOgre();
    }

    Refresh();
}

void wxOgreControl::OnRenderTimer(wxTimerEvent& event)
{
    if(!mOgreReady && !mOgreAbandoned)
        setupOgre();

    try 
    {
        if(mRoot) 
        {
            if(!mRoot->renderOneFrame())
            {
                cleanupOgre();    
            }
        }
    }
    catch(Ogre::Exception& e)
    {
        wxMessageBox(e.getFullDescription().c_str(), "OGRE Exception");
        cleanupOgre();
    }
}

void wxOgreControl::OnEraseBackground(wxEraseEvent&)
{
}