FadeEffectOverlay         Create an overlay that lets you create fade in/out effects in your scene

Introduction

This article shows you how to use overlays to create an object you can use to make your scene fade in and out. This is useful for scene transitions, such as exiting a building or loading a new level. You will need to create an overlay, a material, and a class, all of which will be provided here. Basically, fading in will cause the screen to start out black and gradually start showing your scene until it's completely visible, and fading out will slowly cause everything to fade to black until there's nothing left on the screen. Think about scene transitions in a movie. This could be useful if you're character is transitioning from one level to the next, or a good fadeout at the end when the user quits.

Overlay

The overlay is simple. It's a panel that takes up the entire screen and uses the material we'll define in just a second.

Overlays/FadeInOut
 {
     zorder 650
 
     container Panel(FadeInOutPanel)
     {
         left 0
         top 0
         width 1
         height 1
         material Materials/OverlayMaterial
     }
 }

Material



The material is also pretty simple. It just contains a texture that is solid black. The size of the image is irrelevant since distortion won't be noticed.

material Materials/OverlayMaterial
 {
     technique
     {
         pass
         {
             scene_blend alpha_blend
 
             texture_unit
             {
                 texture black.png
             }
         }
     }
 }

Source Code



Here's where the magic happens. We're going to create a Fader class that will manage the fading in and out. All you'll have to do is instanciate the class and call the appropriate functions to fade in and out. We're also going to create a FaderCallback function that you can derive your function off of so that the Fader class can alert you to when it is done fading in and out. That way you can take certain actions when the fading is done.

Fader.h

#pragma once
 
 namespace Ogre {
     class TextureUnitState;
     class Overlay;
 }
 
 class FaderCallback
 {
 public:
     virtual void fadeInCallback(void) {}
     virtual void fadeOutCallback(void) {}
 };
 
 class Fader
 {
 public:
     Fader(const char *OverlayName, const char *MaterialName, FaderCallback *instance = 0);
     ~Fader(void);
 
     void startFadeIn(double duration = 1.0f);
     void startFadeOut(double duration = 1.0f);
     void fade(double timeSinceLastFrame);
 
 protected:
     double _alpha;
     double _current_dur;
     double _total_dur;
     FaderCallback *_inst;
     Ogre::TextureUnitState *_tex_unit;
     Ogre::Overlay *_overlay;
 
     enum _fadeop {
         FADE_NONE,
         FADE_IN,
         FADE_OUT,
     } _fadeop;
 };

Fader.cpp

#include "fader.h"
 #include "windows.h"    // for MessageBox
 #include "OgreMaterialManager.h"
 #include "OgreOverlayManager.h"
 #include "OgreTechnique.h"
 #include "OgreBlendMode.h"
 
 using namespace Ogre;
 
 Fader::Fader(const char *OverlayName, const char *MaterialName, FaderCallback *instance)
 {
     try
     {
         _fadeop = FADE_NONE;
         _alpha = 0.0;
         _inst = instance;
 
         // Get the material by name
         Ogre::ResourcePtr resptr = Ogre::MaterialManager::getSingleton().getByName(MaterialName);
         Ogre::Material * mat = dynamic_cast<Ogre::Material*>(resptr.getPointer());
 
         Ogre::Technique *tech = mat->getTechnique(0);    // Get the technique
         Ogre::Pass *pass = tech->getPass(0);            // Get the pass
         _tex_unit = pass->getTextureUnitState(0);        // Get the texture_unit state
 
         // Get the _overlay
         _overlay = Ogre::OverlayManager::getSingleton().getByName(OverlayName);
         _overlay->hide();
 
     } catch(Ogre::Exception e) {
         MessageBox(NULL, e.getFullDescription().c_str(), "Fader Exception", MB_OK | MB_ICONERROR | MB_TASKMODAL);
     } catch(...) {
         MessageBox(NULL, "An unknown exception has occured while setting up the fader.  Scene fading will not be supported.", "Fader Exception", MB_OK | MB_ICONERROR | MB_TASKMODAL);
     }
 }
 
 Fader::~Fader(void)
 {
 }
 
 void Fader::startFadeIn(double duration )
 {
     if( duration < 0 )
         duration = -duration;
     if( duration < 0.000001 )
         duration = 1.0;
 
     _alpha = 1.0;
     _total_dur = duration;
     _current_dur = duration;
     _fadeop = FADE_IN;
     _overlay->show();
 }
 
 void Fader::startFadeOut(double duration )
 {
     if( duration < 0 )
         duration = -duration;
     if( duration < 0.000001 )
         duration = 1.0;
 
     _alpha = 0.0;
     _total_dur = duration;
     _current_dur = 0.0;
     _fadeop = FADE_OUT;
     _overlay->show();
 }
 
 void Fader::fade(double timeSinceLastFrame)
 {
     if( _fadeop != FADE_NONE && _tex_unit )
     {
         // Set the _alpha value of the _overlay
         _tex_unit->setAlphaOperation(LBX_MODULATE, LBS_MANUAL, LBS_TEXTURE, _alpha);    // Change the _alpha operation
 
         // If fading in, decrease the _alpha until it reaches 0.0
         if( _fadeop == FADE_IN )
         {
             _current_dur -= timeSinceLastFrame;
             _alpha = _current_dur / _total_dur;
             if( _alpha < 0.0 )
             {
                 _overlay->hide();
                 _fadeop = FADE_NONE;
                 if( _inst )
                     _inst->fadeInCallback();
             }
         }
 
         // If fading out, increase the _alpha until it reaches 1.0
         else if( _fadeop == FADE_OUT )
         {
             _current_dur += timeSinceLastFrame;
             _alpha = _current_dur / _total_dur;
             if( _alpha > 1.0 )
             {
                 _fadeop = FADE_NONE;
                 if( _inst )
                     _inst->fadeOutCallback();
             }
         }
     }
 }

So let's break it down. Starting in the constructor, we look up the material and overlay by name. Our goal with the material is to get a pointer to the TextureUnitState which represents the texture. That way we can easily change in alpha value later. This is all wrapped up in a try block, just incase your MaterialName and OverlayName are incorrect. Note that if you're not using Windows you'll have to change the MessageBox lines. You may also notice that we're just getting the first technique and pass (0). The material script only has one technique and pass, so this is okay. If you end up doing something funky with your material and have multiple techniques and/or passes, you'll need to update that.

The startFadeIn() and startFadeOut() functions start the process of fading in and out. You can pass a double specifying how long the fade should take. Note that it has to be positive, and if the duration is 0 then it will be reset to 1 to avoid divide by 0 errors.

The fade() function should be called from within your frameStarted() function in your frame listener, passing the time since the last frame was rendered. What happens is if it's fading, it will update the alpha value of the texture and then check to see if it's finished fading in or out. If so, it stops (_fadeop = FADE_NONE) and calls the appropriate callback function if an instance of the FaderCallback class was passed in with the constructor.

So how does this look in practice? You should create a pointer to the Fader class as a member of your frame listener. In your frame listener's constructor, add the following code to instanciate the Fader:

fader = new Fader("Overlays/FadeInOut", "Materials/OverlayMaterial", this);

Don't forget to delete the fader, for example in the frame listener's destructor:

delete fader;


Anytime you want to fade in or out, you would call the startFadeIn() or startFadeOut() function with the duration, like so:

fader->startFadeIn(2.5);

Finally, in order for this to work, you must call the fade() function inside your frameStarted() function, like so:

bool frameStarted(const FrameEvent &evt)
     {
         if( !ExampleFrameListener::frameStarted(evt) )
             return false;
 
         // Fade in/out
         fader->fade(evt.timeSinceLastFrame);
         ...

Don't worry about unncessary overhead from calling fade() every single frame. If you look back at the fade() function, it only does anything if it should be fading in or out at the time. Otherwise it just returns. So give it a shot, and if you've found some way to improve on it then please leave a comment in the discussion for this page. Enjoy!