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
- OGRE 1.0.1 (See WxOgre for OGRE v1.2 for more recent versions)
- this patch
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
- include "pch.h"
- include "wx/module.h"
- include "wx/msw/private.h"
- include "wx/msw/palette.h"
- include "wxOgre.h"
- include "OgreNoMemoryMacros.h"
- include "Ogre.h"
- include "f:/dep/ogrenew/RenderSystems/Direct3D9/include/OgreD3D9RenderSystem.h"
- include "f:/dep/ogrenew/RenderSystems/Direct3D9/include/OgreD3D9RenderWindow.h"
- include "OgreNoMemoryMacros.h"
- ifdef _MSC_VER
- ifdef _DEBUG
- pragma comment( lib, "ogremain_d" )
- else
- pragma comment( lib, "ogremain" )
- endif
- endif
- ifdef WIN32
- ifdef _DEBUG
- define new DEBUG_NEW
- endif
- 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)
{
- 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();
}
- 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&) { }