Heat Shimmer Manager         A simple to use manager for adding a particle based heat shimmer effect
Image


Created for the purpose to easily create multiple heat shimmers either via scene loader or manually, and manage the shader in general.

Usage

Include HeatShimmerManager.h in your project, make sure the needed scripts are in place, create your manager object, and after the scene is loaded, call its initialize(bool) function.

If you use a scene loader, name your scene nodes where you want the heat shimmer effect to begin with "HeatSystem_". Same goes with bones on skeletal entities. If you use either, it would be best to call initialize(true), as that goes through and looks for those named elements.

HeatShimmerManager relies heavily on the systems being a certain name, and being a certain amount of systems. As such, there is not yet a remove function, however in case of changing scenes, the manager has a reset() function that will clear the -SceneManager of the particle system and the HeatShimmerManager of how many particle systems there are.

For manual adding of systems, there are add functions.

Code

HeatShimmerManager

/*
    -----------------------------------------------------------------------------
    Filename:    HeatShimmerManager.h
    -----------------------------------------------------------------------------
    
    This is the source file to create and manage multiple instances of the heat
    shimmer particle shader.
    
    Uses OGRE
    (Object-oriented Graphics Rendering Engine)
    
    Copyright (c) 2000-2009 The OGRE Team
    For the latest info, see http://www.ogre3d.org/
    
    While OGRE is under the LGPL, this file is in the public domain, and may be
    used for whatever you like.
    
    If your -SceneManager or loader can have user data attached to nodes,
    you can modify the addHeat and setHeatParticle functions to load user data
    to modify the particle system. I did not do this because from what I can tell,
    there are different user data systems that store it differently.
    
    By Shadowking97 @ Ogre 3d forums
    -----------------------------------------------------------------------------
    */
    
    #ifndef __HeatShimmerManager_H__
    #define __HeatShimmerManager_H__
    
    #include "Ogre.h"
    #include "OgreStringConverter.h"
    #include "OgreException.h"
    
    using namespace Ogre;
    
    //Renderable Listener combined with RenderTargetListener to black out non-heat related
    //renderables
    class HeatRenderableListener: public RenderTargetListener, RenderQueue::RenderableListener
    {
    private:
       SceneManager* mSceneMgr;
       Camera* cam;
       //Number of heat particle emmiters.
       int numParticles;
       RenderQueue* queue;
    public:
       HeatRenderableListener(SceneManager* scene, Camera* c, int p): cam(c)
       {
          numParticles = p;
          mSceneMgr = scene;
          queue = mSceneMgr->getRenderQueue();
       }
       void preRenderTargetUpdate(const RenderTargetEvent& evt)
       {
          // Todo: Set non-heat related soft particles to black while keeping alpha
          
          //set heat particle camera to the same loc/rot of the default camera
          mSceneMgr->getCamera("HeatCam")->setPosition(cam->getPosition());
          mSceneMgr->getCamera("HeatCam")->setOrientation(cam->getOrientation());
    
          //Set heat particles visible and set renderablelistener
          for(int i = 0;i<numParticles;i++)
                 mSceneMgr->getParticleSystem("HeatParticleSystem_"+StringConverter::toString(i))->setVisible(true);
          queue->setRenderableListener(this);
       }
       void postRenderTargetUpdate(const RenderTargetEvent& evt)
       {
          // Reset the heat particles and RenderableListener
          for(int i = 0;i<numParticles;i++)
             mSceneMgr->getParticleSystem("HeatParticleSystem_"+StringConverter::toString(i))->setVisible(false);
          queue->setRenderableListener(0);
       }
       bool renderableQueued(Renderable* rend, uint8 groupID,
                ushort priority, Technique** ppTech, RenderQueue* pQueue)
       {
          //If renderable's technique isn't the Technique from Particle/Heat,
          //Renderable is not a heat particle so set the technique to Darkness
          Technique* tech = *ppTech;   
          if(tech->getParent()->getName()!="Particle/Heat")
          {
             MaterialPtr mat = MaterialManager::getSingleton().getByName("Heat/Darkness");
             tech = mat->getTechnique(0);
             *ppTech = tech;
          }
          return true;
       }
    };
    
    //Main manager for heat particles.
    class HeatShimmerManager
    {
    private:
       //Main render camera
       Camera* mCamera;
       //Camera created for heat rendering
       Camera* mHeatCam;
       SceneManager* mSceneMgr;
       RenderTexture* heatRender;
       RenderWindow* mWindow;
       //Number of particle systems
       int particleCount;
    public:
       HeatShimmerManager(Camera* MainCam, SceneManager* MainScene, RenderWindow* win): mCamera(MainCam), mSceneMgr(MainScene), mWindow(win)
       {
          particleCount=0;
       }
       /*  HeatShimmerManager::Initialize(bool)
          
          Used to initialize Heat Shimmer Manager.
          Sets up the heat camera, viewport, render texture
          Adds Renderable/RenderTarget Lisstener to the texture
          Adds Heat Compositor
    
          Call this function after the scene has been set up inbetween the compositors
          you want the heat wave to go.
    
          @setUpParticles
          If true, calls functions to search for nodes and bones
          named for particle system attachment.      
       */
       void initialize(bool setUpParticles)
       {
          mHeatCam = mSceneMgr->createCamera("HeatCam");
          mHeatCam->setNearClipDistance(mCamera->getNearClipDistance());
          mHeatCam->setFarClipDistance(mCamera->getFarClipDistance());
          mHeatCam->setAspectRatio(mCamera->getAspectRatio());
          mHeatCam->setFOVy (mCamera->getFOVy());
          mHeatCam->setOrientation(mCamera->getOrientation());
          mHeatCam->setPosition(mCamera->getPosition());
    
          TexturePtr heatTex = TextureManager::getSingleton().createManual("heat",
             ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, mWindow->getWidth()/2,
             mWindow->getHeight()/2, 0, PF_R8G8B8, TU_RENDERTARGET);
          heatRender = heatTex->getBuffer()->getRenderTarget();
          heatRender->addViewport(mHeatCam);
          heatRender->getViewport(0)->setClearEveryFrame(true);
          heatRender->getViewport(0)->setBackgroundColour(ColourValue::Black);
          heatRender->getViewport(0)->setOverlaysEnabled(false);
          heatRender->setAutoUpdated(true);
         
          if(setUpParticles)
          {
             setHeatParticles(mSceneMgr->getRootSceneNode());
             setBonedHeatParticles();
          }
            
          HeatRenderableListener * heatListener = new HeatRenderableListener(mSceneMgr,mCamera,particleCount);
          heatRender->addListener(heatListener);
    
          CompositorManager::getSingleton().addCompositor(mCamera->getViewport(), "heat_waves");
             CompositorManager::getSingleton().setCompositorEnabled(mCamera->getViewport(),"heat_waves",true);
       }
    
       /*  HeatShimmerManager::reset()
              
          Clears the Heat Shimmer and -Scene Managers of all particle systems.
       */
       void reset()
       {
          for(int i = 0; i<particleCount;i++)
          {
             mSceneMgr->destroyParticleSystem("HeatParticleSystem_"+StringConverter::toString(i));
          }
          particleCount=0;
       }
    
       //Manual heat particle creation on SceneNode
       void addHeat(SceneNode* n)
       {
          ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat");
          particleCount++;
          n->attachObject(heat_Particles);
          heat_Particles->setVisible(false);
       }
       
       //Manual heat particle creation on SceneNode
       void addHeat(String sceneNodeName)
       {
          ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat");
          particleCount++;
          mSceneMgr->getSceneNode(sceneNodeName)->attachObject(heat_Particles);
          heat_Particles->setVisible(false);
       }
       
       //Manual heat particle creation on Entity bone
       void addHeatToBone(Bone* b, Entity* e)
       {
          ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat");
          particleCount++;
          e->attachObjectToBone(b->getName(),heat_Particles);
          heat_Particles->setVisible(false);
       }
       
       //Manual heat particle creation on Entity bone
       void addHeatToBone(String boneName, Entity* e)
       {
          ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat");
          particleCount++;
          e->attachObjectToBone(boneName,heat_Particles);
          heat_Particles->setVisible(false);
       }
    protected:
    
       /*
          setHeatParticles(SceneNode*)
          Goes through the scene recursivly via root scene node
          attaching ParticleSystems for heat to the apropriate
          nodes.
       */
       void setHeatParticles(SceneNode* node)
       {
          if(node->getName().find("HeatSystem_")!=-1)
          {
             ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat");
             particleCount++;
             node->attachObject(heat_Particles);
             heat_Particles->setVisible(false);
          }
          Node::ChildNodeIterator iterator = node->getChildIterator();
          while(iterator.hasMoreElements())
          {
             SceneNode* n = dynamic_cast<SceneNode*>(iterator.getNext());
             if(n)
                setHeatParticles(n);
          }
       }
    
       /*
          setBonedHeatParticles()
          Goes through Entities in a scene and attaches heat particles
          to the appropriate bones.
       */
       void setBonedHeatParticles()
       {
          SceneManager::MovableObjectIterator iterator = mSceneMgr->getMovableObjectIterator("Entity");
          while(iterator.hasMoreElements())
          {
             Entity* e = static_cast<Entity*>(iterator.getNext());
             if(e->hasSkeleton()&&e->isInScene())
             {
                SkeletonInstance* s = e->getSkeleton();
                Skeleton::BoneIterator bones = s->getBoneIterator();
                while(bones.hasMoreElements())
                {
                   Bone* bone = static_cast<Bone*>(bones.getNext());
                   if(bone->getName().find("HeatSystem_")!=-1)
                   {
                      ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat");
                      particleCount++;
                      e->attachObjectToBone(bone->getName(),heat_Particles);
                      heat_Particles->setVisible(false);
                   }
                }
             }
          }
       }
    };
    
    
    #endif

Particles


particle_system Render/Heat
    {
       quota   1500
       material   Particle/Heat
       particle_width   1
       particle_height   1
       cull_each   true
       renderer   billboard
       billboard_type   point
    
       emitter Box
       {
          angle   73.17
          colour   1 1 1 1
          colour_range_start   1 1 1 1
          colour_range_end   1 1 1 1
          direction   0 0 1
          emission_rate   500
          position   0 0 0
          velocity   8
          velocity_min   8
          velocity_max   8
          time_to_live   1.5
          time_to_live_min   1.5
          time_to_live_max   1.5
          duration   0
          duration_min   0
          duration_max   0
          repeat_delay   0
          repeat_delay_min   0
          repeat_delay_max   0
          width   10
          height   10
          depth   10
       }
    
       affector LinearForce
       {
          force_vector   0 9.8 0
          force_application   add
       }
    
       affector ColourFader
       {
          red   -0.2967
          green   -0.2768
          blue   -0.7465
          alpha   0
       }
    }

Materials


material Particle/Heat
    {
       technique
       {
          pass
          {
             lighting off
             depth_write off
             scene_blend add
    
             texture_unit
             {
                texture heat_sprite.png
             }
          }
       }
    }
    
    material Heat/Darkness
    {
       technique
       {
          pass
          {
             lighting off
    
             texture_unit
             {
                texture darkness.png
             }
          }
       }
    }
    
    fragment_program Distortion/Heat_ps cg
    {
       source heat_Distortion.cg
       entry_point main
       profiles ps_2_0 arbfp1
       default_params
       {
          param_named distortionScale float 2
       }
    }
    
    material Ogre/Compositor/Heat_wave
    {
       technique
       {
          pass
          {
             depth_check off
             vertex_program_ref Ogre/Compositor/StdQuad_Cg_vp
             {
             }
             fragment_program_ref Distortion/Heat_ps
             {
             }
    
             texture_unit Texture0
             {
                    tex_coord_set 0
                tex_address_mode clamp
                filtering linear linear linear
             }
             texture_unit Texture1
             {
             texture heat
                    tex_coord_set 1
                tex_address_mode clamp
                filtering linear linear linear
             }
             
          }
       }
    }

Compositor


compositor heat_waves
    {
       technique
       {
          texture main target_width target_height PF_R8G8B8
    
          target main
          {
             input previous
          }
    
          target_output
          {
             input none
             pass render_quad
             {
                material Ogre/Compositor/Heat_wave
                input 0 main
             }
          }
       }
    }

heat_Distortion.cg


sampler Texture0 : register(s0);
    sampler Texture1 : register(s1);
    float distortionScale;
    
    float4 main(float2 texCoord : TEXCOORD0) : COLOR
    {
       float2 distort = tex2D(Texture1,texCoord).xy;
       
       return tex2D(Texture0, texCoord - distortionScale*(distort/100*(texCoord-0.5)));
    }

Images Used



darkness.png
heat_sprite.png