(C++) GUI viewport class

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

(C++) GUI viewport class

Post by vitek »

Render an entire scene into a gui window. I think it's simpler and more useful than the IGUIMeshViewer. It also allows you to interact with the camera inside the viewport...

Code: Select all

   // add a viewport that displays the scene
   IGUIViewport* viewport = env->addViewport(core::rect<s32>(10, 30, 290, 290));
   if (viewport)
   {
      //
      // create a scene for the viewport
      //
      ISceneManager* vsmgr = smgr->createNewSceneManager();
         viewport->setSceneManager(vsmgr);
      vsmgr->drop();

      // set the viewport background color
      //viewport->setOverrideColor(video::SColor(255, 160, 180, 255));

      // add stuff to viewports scene manager

      // add a camera for the viewport
      vsmgr->addCameraSceneNodeMaya();
   }

Code: Select all

#ifndef __I_GUI_VIEWPORT_H_INCLUDED__
#define __I_GUI_VIEWPORT_H_INCLUDED__

#include "IGUIElement.h"
#include "SColor.h"

namespace irr
{

namespace scene
{
   class ISceneManager;
}

namespace gui
{

   //! 3d scene GUI element.
   class IGUIViewport : public IGUIElement
   {
   public:

      //! constructor
      IGUIViewport(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect<s32> rectangle)
         : IGUIElement(EGUIET_MESH_VIEWER, environment, parent, id, rectangle)
      {
      }

      //! sets the scene to be shown
      virtual void setSceneManager(scene::ISceneManager* scene) = 0;

      //! gets the scene to be shown
      virtual scene::ISceneManager* getSceneManager() const = 0;

      //! sets the viewport fill color
      virtual void setOverrideColor(video::SColor color) = 0;

      //! enable or disable the viewport fill color
      virtual void enableOverrideColor(bool override) = 0;
   };


} // end namespace gui
} // end namespace irr

#endif

Code: Select all

#ifndef __C_GUI_VIEWPORT_H_INCLUDED__
#define __C_GUI_VIEWPORT_H_INCLUDED__

#include "IGUIViewport.h"

namespace irr
{

namespace gui
{

   class CGUIViewport : public IGUIViewport
   {
   public:

      //! constructor
      CGUIViewport(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect<s32> rectangle);

      //! destructor
      virtual ~CGUIViewport();

      //! called if an event happened.
      virtual bool OnEvent(SEvent event);

      //! draws the element and its children
      virtual void draw();

      //! sets the scene to be shown
      virtual void setSceneManager(scene::ISceneManager* scene);

      //! gets the scene to be shown
      virtual scene::ISceneManager* getSceneManager() const;

      //! sets the viewport fill color
      virtual void setOverrideColor(video::SColor color);

      //! enable or disable the viewport fill color
      virtual void enableOverrideColor(bool override);

   private:
      scene::ISceneManager* SceneManager;

      bool OverrideColorEnabled;
      video::SColor OverrideColor;
   };


} // end namespace gui
} // end namespace irr

#endif

Code: Select all

#include "CGUIViewport.h"
#include "IGUIEnvironment.h"
#include "ISceneManager.h"
#include "ICameraSceneNode.h"
#include "IVideoDriver.h"
#include "IGUISkin.h"

namespace irr
{

namespace gui
{


//! constructor
CGUIViewport::CGUIViewport(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect<s32> rectangle)
   : IGUIViewport(environment, parent, id, rectangle)
   , SceneManager(0)
   , OverrideColorEnabled(false)
   , OverrideColor(255, 0, 0, 0)
{
   #ifdef _DEBUG
   setDebugName("CGUIViewport");
   #endif
}



//! destructor
CGUIViewport::~CGUIViewport()
{
   if (SceneManager)
      SceneManager->drop();
}



//! called if an event happened.
bool CGUIViewport::OnEvent(SEvent event)
{
   bool absorbed = false;

   if (SceneManager)
      absorbed = SceneManager->postEventFromUser(event);

   if (!absorbed && Parent)
      absorbed = Parent->OnEvent(event);

   return absorbed;
}



//! draws the element and its children
void CGUIViewport::draw()
{
   if (!IsVisible)
      return;

   video::IVideoDriver* driver = Environment->getVideoDriver();

   // draw the background and frame

   core::rect<s32> frameRect(AbsoluteRect);
   if (OverrideColorEnabled)
      driver->draw2DRectangle(OverrideColor, frameRect, &AbsoluteClippingRect);

   IGUISkin* skin = Environment->getSkin();

   frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + 1;
   driver->draw2DRectangle(skin->getColor(EGDC_3D_SHADOW), frameRect, &AbsoluteClippingRect);

   frameRect.LowerRightCorner.Y = AbsoluteRect.LowerRightCorner.Y;
   frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + 1;
   driver->draw2DRectangle(skin->getColor(EGDC_3D_SHADOW), frameRect, &AbsoluteClippingRect);

   frameRect = AbsoluteRect;
   frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - 1;
   driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), frameRect, &AbsoluteClippingRect);

   frameRect = AbsoluteRect;
   frameRect.UpperLeftCorner.Y = AbsoluteRect.LowerRightCorner.Y - 1;
   frameRect.LowerRightCorner.Y = AbsoluteRect.LowerRightCorner.Y;
   driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), frameRect, &AbsoluteClippingRect);

   // draw the scene

   if (SceneManager)
   {
      core::rect<s32> viewPort = AbsoluteRect;
      viewPort.LowerRightCorner.X -= 1;
      viewPort.LowerRightCorner.Y -= 1;
      viewPort.UpperLeftCorner.X += 1;
      viewPort.UpperLeftCorner.Y += 1;

      viewPort.clipAgainst(AbsoluteClippingRect);

      core::rect<s32> screenRect;
      screenRect.UpperLeftCorner.X = 0;
      screenRect.UpperLeftCorner.Y = 0;
      screenRect.LowerRightCorner.X = driver->getScreenSize().Width;
      screenRect.LowerRightCorner.Y = driver->getScreenSize().Height;

      viewPort.clipAgainst(screenRect);

      if ( !(viewPort.getWidth() < 1 || viewPort.getHeight() < 1))
      {
         scene::ICameraSceneNode* cam = SceneManager->getActiveCamera();

         if (cam)
         {
            core::rect<s32> oldViewPort = driver->getViewPort();
            driver->setViewPort(viewPort);

            driver->clearZBuffer();

            f32 oldAspectRatio = cam->getAspectRatio();
            cam->setAspectRatio(1.f * viewPort.getWidth() / viewPort.getHeight());

            f32 oldFieldOfView = cam->getFOV();
            cam->setFOV(oldFieldOfView * viewPort.getHeight() / (AbsoluteRect.getHeight() - 2));

            SceneManager->drawAll();

            cam->setFOV(oldFieldOfView);

            cam->setAspectRatio(oldAspectRatio);

            driver->setViewPort(oldViewPort);
         }
      }
   }

   IGUIElement::draw();
}


//! sets the scene to be shown
void CGUIViewport::setSceneManager(scene::ISceneManager* scene)
{
   if (SceneManager)
      SceneManager->drop();

   SceneManager = scene;

   if (SceneManager)
      SceneManager->grab();
}



//! gets the scene to be shown
scene::ISceneManager* CGUIViewport::getSceneManager() const
{
   return SceneManager;
}



//! sets the viewport fill color
void CGUIViewport::setOverrideColor(video::SColor color)
{
   OverrideColor = color;
   enableOverrideColor(true);
}



//! enable or disable the viewport fill color
void CGUIViewport::enableOverrideColor(bool override)
{
   OverrideColorEnabled = override;
}



} // end namespace gui
} // end namespace irr
Last edited by vitek on Sat Oct 21, 2006 6:44 am, edited 3 times in total.
Mloren
Posts: 114
Joined: Mon Aug 07, 2006 2:30 am
Location: Australia
Contact:

Post by Mloren »

Code: Select all

IGUIViewport* viewport = env->addViewport(core::rect<s32>(10, 30, 290, 290)); 
what is "env"? I couldnt find an addViewport() function anywhere.
Saku
Posts: 158
Joined: Wed Jan 05, 2005 8:48 am
Location: Denmark

Post by Saku »

Im guessing the 'env' is the GUIEnvironment ;)
Call me Wice, Miami Wice!
afecelis
Admin
Posts: 3075
Joined: Sun Feb 22, 2004 10:44 pm
Location: Colombia
Contact:

Post by afecelis »

right, you have to set it as a pointer:

Code: Select all

IGUIEnvironment* env = device->getGUIEnvironment();
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I added an interface to the gui environment for creating these viewports in my custom build of Irrlicht. If you just used the code I pasted above, you would need to write

Code: Select all

IGUIViewport* viewport = new CGUIViewport(env, env->getRootGUIElement(), 0, core::rect<s32>(10, 10, 290, 290));
Another thing to note is that you must tell the camera the aspect ratio of the viewport or you'll get a distorted view.

Code: Select all

ISceneManager* vsmgr = smgr->createNewSceneManager();
  viewport->setSceneManager(vsmgr);
vsmgr->drop();

ICameraSceneNode* camera = vsmgr->addCameraSceneNodeFPS();
camera->setAspectRatio(1.f * viewport->getRelativePosition().Width / viewport->getRelativePosition().Height);
eviral
Posts: 91
Joined: Mon Oct 25, 2004 10:25 am

totally separated Scene Manager ?

Post by eviral »

Hi Vitek,

Your GUIViewport class is really really nice !

Just a question : how can i use a totally separated Scene Manager in my GUI window viewport ?

I'm using CreateNewSceneManager() as in your sample but when i rotate the object displayed in my GUI window it intersects with the walls of my scene under the GUI window ?

Thanks a lot

Eviral
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I'm using CreateNewSceneManager() as in your sample but when i rotate the object displayed in my GUI window it intersects with the walls of my scene under the GUI window ?
I'm not sure exactly what you mean. Could you post a screenshot or maybe describe the problem better?
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I see the problem... Looks like the viewport is not clearing the z-buffer, so you will see things in the viewport being obscured by objects that are being rendered by the other scene manager. The fix is to clear the z-buffer before rendering the new scene. Here's the updated block...

Code: Select all

   core::rect<s32> oldViewPort = driver->getViewPort();
   driver->setViewPort(viewPort);
   
->   driver->clearZBuffer();
->    
   SceneManager->drawAll();
   
   driver->setViewPort(oldViewPort);
   }
I'll fix the code above to reflect the solution.
eviral
Posts: 91
Joined: Mon Oct 25, 2004 10:25 am

3D Object mouse rollo over to display their name

Post by eviral »

Hello Vitek !

I'm trying to add a 3D object rollover function with objects displayed in your GuiViewport.

When the mouse cursor roll over the mesh, i would like to display its name.

I have a lot of problem with detecting when my mouse cursor is over a 3D object.

I spent hours and i'm desperate...


Please help !


Thanks a lot

Eviral
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I've never tried that, but you should be able to use the collision manager.

Code: Select all

ISceneCollisionManager* coll = viewport->getSceneManager()->getSceneCollisionManager();
ISceneNode* node = coll->getSceneNodeFromScreenCoordinatesBB(mousePos);
if (node)
{
  // something is under the mouse
}
Magnet
Posts: 101
Joined: Wed Sep 27, 2006 7:32 pm

Re: totally separated Scene Manager ?

Post by Magnet »

eviral wrote:Just a question : how can i use a totally separated Scene Manager in my GUI window viewport ?
I'm using CreateNewSceneManager() as in your sample but when i rotate the object displayed in my GUI window it intersects with the walls of my scene under the GUI window ?
I have this problem to.
My scene intersects with CGUIViewport scene.
My scene has a big textured object.
CGUIViewport scene has one "fishing rod" object.
If I move my camera on scene and approached to object on scene, objects in CGUIViewport and my scene are intersets :-(

CGUIViewport with override color:
Image

CGUIViewport without override color:
Image
Magnet
Posts: 101
Joined: Wed Sep 27, 2006 7:32 pm

Post by Magnet »

I have found several errors.
If GUIWindow moved outside of screen, Viewport is disabled and all scene draws to default viewport.

if part of CGUIViewport moved outside of screen, Viewport is distorted.
Image
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I think your first error is already fixed. You need to clear the z-buffer before rendering the scene again, otherwise the objects in your scene will obscure objects in the viewport, even though they aren't visible in the viewport.

Not sure what to do about the changing fov problem. The issue is that the IVideoDriver::setViewport() method clips the view to the screen size. The viewport attempts to maintain the aspect ratio you specify within the clipped view area and that makes the view look funny. To fix it I would have to change the camera target and fov, and I don't really want to do that.

The last issue is related, but a simple fix. The setViewport method does not actually set the view area if the clipped view rect doesn't have a size, so the old viewport is used. I can fix this easily by clipping the view to the window size before calling setViewPort and if the view does not have a size then just not render anything.

Code: Select all

core::rect<s32> screenRect;
screenRect.UpperLeftCorner.X = 0;
screenRect.UpperLeftCorner.Y = 0;
screenRect.LowerRightCorner.X = driver->getScreenSize().Width;
screenRect.LowerRightCorner.Y = driver->getScreenSize().Height;

viewPort.clipAgainst(screenRect);

if ( !(viewPort.getWidth() < 1 || viewPort.getHeight() < 1))
{
   core::rect<s32> oldViewPort = driver->getViewPort(); 
   driver->setViewPort(viewPort); 

   driver->clearZBuffer(); 

   SceneManager->drawAll(); 

   driver->setViewPort(oldViewPort); 
}
The easy thing is just to keep the viewport on the screen. Then you have no problem. :)
Magnet
Posts: 101
Joined: Wed Sep 27, 2006 7:32 pm

Post by Magnet »

vitek wrote:I think your first error is already fixed. You need to clear the z-buffer before rendering the scene again, otherwise the objects in your scene will obscure objects in the viewport, even though they aren't visible in the viewport.
No. This is not fix problem.
I fix this promlem by set material with flag Z_BUFFER=false
It is correct?
vitek wrote:The last issue is related, but a simple fix. The setViewport method does not actually set the view area if the clipped view rect doesn't have a size, so the old viewport is used. I can fix this easily by clipping the view to the window size before calling setViewPort and if the view does not have a size then just not render anything.
Hm. Then how to draw and rotate 3D object on GUIWindow.
For this task I use this viewport. But it distort my scene.
Can fix this problem?
vitek wrote:Not sure what to do about the changing fov problem. The issue is that the IVideoDriver::setViewport() method clips the view to the screen size. The viewport attempts to maintain the aspect ratio you specify within the clipped view area and that makes the view look funny.
I am undestand this. But I can't fix this problem.
vitek wrote:To fix it I would have to change the camera target and fov, and I don't really want to do that.
I am try to fix it. But not take any results. :-(

Please help me. :wink:
vitek wrote:The easy thing is just to keep the viewport on the screen. Then you have no problem. :)

And then I can't move my window ^-) It's not solve this problem.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

The only thing that I can think of is that you are rendering something to the main viewport after you render the gui viewport. You should render everything in the normal scene first, then the GUI.

Code: Select all

if (driver->beginRender(...))
{
  smgr->drawAll();

  gui->drawAll();

  driver->endRender();
}
To figure it out I'd probably have to see your code, and I'm not really interested in debugging it, so don't post it. Setting the z-buffer off will work, but it is not the right fix. It will cause problems if you want to render other objects to the viewport.

The fix to the view distortion problem is rather simple actually. There is probably a better way, but this seems to work just fine. I'm going to just update the original source above so that users can copy/paste it directly into their projects.

Travis
Post Reply