Wednesday, November 19, 2008

QT and Ogre

So, during my time off, I've started looking at QT (something I used at Radical a ways back ... when it was at 3.3 or so) and Ogre. True to what I was just recently doing at my last job, I thought I'd give a go at using both for a world editor. So, I've been used to using wxWidgets for the last couple of years. There's things I like about it (Free, source code included and DialogBlocks is a relatively nice/non-retarded gui builder) and some things I don't like about it (it's free, so there's little support for it, subclassing compound widgets is a royal pain in the tuckas ). Anyway, what I wanted to start with was a simple window, with a menu, a status bar and a widget that encompassed a render context. So, I crack open the designer tool and go to work building a simple app. I create a mainwindow gui. This is nice because it comes with a menu, docking toolbar and status bar all ready to go. Then I add a widget (just a simple one) to the main window and then try to get it to fit completely in the layout. This is a nightmare, because I'm used to how Dialogblocks works (you know, in a way that makes sense). Apparently you have to click on the main background and do some fancy 'layout fitting' garbage. Completely non-intuitive. Not a good start, if you ask me. Anyway, I work a little more, trying to figure out how to use the Designer to create a subclass of a QWidget that would represent my new render window. Not easy, but significantly less tricky than layout. Now that's done, I start trying to bind Ogre to the window. This is where it all falls apart. Like a lot of these SDKs, the documentation is woefully inadequate. As soon as I get the widget ready to start rendering my window, I get nothing. Until I resize my window. Then I get this god-awful tearing of the widget and the Ogre based render. So, this leads me to believe that I've improperly overridden the paint method of the QWidget class. I dig and I dig and I dig, but everything I read about subclassing widgets tells me that I'm doing the right thing. That is until I read somewhere on a web site that in QT 4+, each widget automatically double buffers. So, somewhere in the deep internals of QT, they have a hidden swap chain that I have no idea how to override. So, after lots and lots of digging, I finally figure out what I need to set as part of the derived objects construction attributes. So, for a class like so:
#ifndef __RENDERWINDOW_H__
#define __RENDERWINDOW_H__
#include 
#include "context.h"
QT_BEGIN_NAMESPACE
/// *********************************************************************
/// @brief A RenderWindow is a Widget that is used for rendering
/// The RenderContext performs our rendering, but this class manages
/// all the window level refreshing.
/// 
/// *********************************************************************
class RenderWindow :    public QWidget
{
public:
    RenderWindow(void);
    RenderWindow ( QWidget * parent = 0, Qt::WindowFlags f = 0 );
    ~RenderWindow(void);
    virtual void showEvent( QShowEvent * event );
    virtual void paintEvent( QPaintEvent * event );
    virtual void resizeEvent(QResizeEvent *evt);
    void Init( void );
    void Update( void );
protected:
    RenderContext   m_RenderContext;
    bool            m_WindowInitialized : 1;
  };
QT_END_NAMESPACE
#endif
 // __RENDERWINDOW_H__
And where RenderContext is a class I've derived for doing the actual binding to Ogre, we need a constructor that looks like this:
/// *********************************************************************
/// @brief unparameterized Constructor for the Renderwindow
/// Note that we have to set the following attributes to disable QT's
/// double buffering, which was causing all sorts of flickering
/// 
/// *********************************************************************
RenderWindow::RenderWindow(void)   : m_WindowInitialized( false )
{
    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_OpaquePaintEvent);
    setAttribute(Qt::WA_NoSystemBackground);
    setAutoFillBackground( false );
}

/// *********************************************************************
/// @brief Parameterized Constructor for the RenderWindow
/// Note that we have to set the following attributes to disable QT's
/// double buffering, which was causing all sorts of flickering
/// 
/// @param parent Parent Window
/// @param f      Additional window creation flags
/// *********************************************************************
RenderWindow::RenderWindow( QWidget * parent, Qt::WindowFlags f ): QWidget( parent, f ),  m_WindowInitialized( false )
{
    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_OpaquePaintEvent);
    setAttribute(Qt::WA_NoSystemBackground);
    setAutoFillBackground( false );
}
As well, make sure you override the painting method as well:
/// *********************************************************************
/// @brief Handler for the paint event (overridden from the virutal base)
/// In this, we're simply calling the RenderWindow's Update Method.
/// 
/// @param event the event to process (which we don't)
/// *********************************************************************
void RenderWindow::paintEvent( QPaintEvent * event )
{
    Update();
}

/// *********************************************************************
/// @brief Update our RenderWindow/// We update one frame of the rendercontext
/// 
/// *********************************************************************void RenderWindow::Update( void )
{
    g_SceneManager->GetSceneRoot()->_fireFrameStarted();
    m_RenderContext.Update( );
    g_SceneManager->GetSceneRoot()->_fireFrameEnded();
}
And that seems to work fine. As I progress more with this, I'll update with new details.

13 comments:

ion_pulse said...

Cool that you're playing around with Ogre. I've used it for a number of projects over the last few years. I've had this idea in my head about making tools with it + wxWidgets myself, but I look forward to seeing how you fare with Qt!

Ash said...

I had this wacky thought about reformatting my computer. And I did. Now I'm in the process of rebuilding QT (I had last build with Dev Studio 2008 ... Yeah, don't use it ... what a pig). So I'm a couple of days away from having something to show.

artm said...

Hi Ash

Thanks for sharing, your post helped me to start my own qt application embedding an ogre widget.

a comment on your implementation though. One of the things I wish to use Qt for is building an interface "around" ogre, with several embedded "ogre views". The way you fire frame started / ended in widget Update() they'll happen again and again for each render window, which isn't exactly what you want (well, I should say "what I want", but suppose you want multiple views as well). So I ended up writing a special method that fires frame start, updates all ogre widgets, then fires frame end. It isn't very flexible, but OK for the current state of my application... Just my two cents.

Ash said...

Hey Art,
I should have been a little more clear with my design strategy for for the view. My intent was to have several views, instead of one large viewport that I would break up into several views. Each 'View' would eventually have a reference to an associated scene graph. As I update it more, I'll post this update.

artm said...

Ah, but then you probably don't want to fire frame start / frame end per view either - otherwise they won't be in sync. Say you do physics in frame listener and you drop an apple. then in each consequtive view the apple will fall further (because physics will be called again and again). With my approach physics will be called once per "logical" frame, then the scene will be rendered by each view in exactly the same state. (physics is just an example, could be procedural animation or what not).

Ash said...

Not really. In my experience I usually want each frame (remember, this is an editor) I don't want live updating. When you get into a very complex scene, that tends to be very, very expensive (computationally). Essentially, when I'm in a selected viewport, I want that view to be live.

artm said...

ah, inetersting consideration.

I'm thinking - if I have several panes that show the scene from different angles, then my intention is to see what I'm doing, what I'm changing in the scene, from different angles as I do it... So I intend to have all panes as close to live as possible. Probably you're right though, I don't want "as close as possible" become "constantly updating everything"... I guess I'll have to see how it goes as my application (and scenes I deal with) grow.

Ash said...

Well, one of the things that you could do (and something I was considering) was having each pane have an "update in Realtime" facility. That would be controlled by the application (not the panes). On a per tick basis, the application could force each pane to update. That seems to me to be a more appropriate solution.

artm said...

Yep, that's more or less what I'm doing at the moment - updating everything on timer ticks. I have only two hardcoded ogre panes at the moment and don't have much content in them, so it doesn't bite me yet.

Now I want to play with some multipane level editor and try to guess how they handle view updates :-)

rafa.gdev said...

Hey! That was exactly what I needed, thanks SO much! =D

rafa.gdev said...

Hey! That was exactly what I needed, thanks SO much! =D

Pier Lorenzo said...

Please, i am trying to use qt to host ogre and i tried to use the examples i found but no luck.
I am using ogre 1.6.1 and qt 4.5.1
on visual studio 2008 in windows XP

Can you please tell me where to find a working simple .cpp and .h
to try it ?
Thanks
Lorenzo

Pier Lorenzo said...

Sorry I forgot to check for an email follow-up to my address on replay to this post, so i insert this new post.
Lorenzo