   Handling lifetime of renderables
  I've got myself a nice little explosion animation all coded up. Currently, it is contained in an "image" class that I created and added animation handling to. When my animation is done, I am unsure what to do. I have it stop calulating which frame to render and it just sits at the last frame.There are plenty of options I am sure.What I am wondering is what you guys do when you plan on just rendering something for a little bit, like an animation. How do you go about determining when it is all done and stop rendering it?I am trying to do this in a logical manner. It isn't just for a game that works one way only, but for my engine. Polling it "are you done?" in the render loop, every frame isn't attractive.It's almost as if I need to have the animation register itself with some kind of render queue, and signal the render queue when it is done playing, for removal. I am not sure how I'd do that.What do you guys do, if say you want to replace a ship with a 2D image that contains 16 frames of explosion, then remove it?Here is my image code:#ifndef IMAGE2D_H#define IMAGE2D_H// EngineX Includes#include "Graphics/3D/InputLayoutManager.h"#include "Graphics/Textures/TextureManager.h"#include "Graphics/Effects/EffectManager.h"#include "Graphics/3D/PolygonSet.h"// Common Lib Includes#include "Timer.h"// DirectX Includes#include <d3d10.h>#include <d3dx10.h>// Standard Includes#include <vector>#include <string>//----------------------------------------------------------------------------/*****/class Image2D{public:   /**   * Defines the current frame index used each Render call   **/   enum AnimationMode   { ANIMATION_MODE_NONE = 0,   // Animation is not automatic, user may set current frame index themselves ANIMATION_MODE_ONCE,  // Animation counts up from start index to end index and stops ANIMATION_MODE_LOOP,  // Animation counts up from start index to end index, then starts again ANIMATION_MODE_BOUNCE,// Animation counts up from start index to end index, then counts back down   }; /**   *   **/   Image2D(ID3D10Device & device,  InputLayoutManager & inputLayoutManager, TextureManager & textureManager,  EffectManager & effectManager, const std::string & textureFilePath, unsigned rows, unsigned cols, float depth); /**   *   **/   ~Image2D();   /**   * Starts an animation   **/   void StartAnimation(unsigned startFrameIndex, unsigned endFrameIndex, AnimationMode animationMode, double fps);   /**   * Stops an animation   **/   void StopAnimation();   /**   * Gets the number of frames the current image contains   *   * @return unsigned - Number of frames the current image contains   **/   unsigned GetNumFrames() const;   /**   * Gets which frame of the image will be rendered   *   * @return unsigned - Frame index (zero based) that will be rendered   **/   unsigned GetCurrentFrameIndex() const;   /**   * Sets which frame of the image will be rendered   *   * @param index - Frame index (zero based) to render   *    * @throws BaseException - If the frame doesn not exist   **/   void SetCurrentFrameIndex(unsigned index);   /**   * Renders the Image   **/   void Render();protected:   /**   * Calculates which frame of an animation is the current frame to render   **/   void Image2D::CalculateAnimationFrame();   ID3D10Device &  m_device;    // DirectX device   InputLayoutManager & m_inputLayoutManager;  // Contains and manages the lifetime of input layouts   TextureManager &m_textureManager; // Contains and manages the lifetime of textures   EffectManager & m_effectManager;  // Contains and manages the lifetime of effects std::vector<Buffer::SharedPtr> m_quadBuffers;    // Vertex buffers containing quads to render image frame to   ID3D10InputLayout *  m_inputLayout;    // DirectX description of the vertex buffers   std::vector<unsigned>m_strides;   // Number of bytes of an element of a vertex buffer. 1 per buffer   std::stringm_textureName;    // Name of the texture that contains the image frames   D3DXMATRIX m_world;// Transform to apply to the quad   std::auto_ptr<Material>   m_material;  // Effect variable states to use when rendering   float m_width;// Width of each frame, in pixels   float m_height;    // Height of each frame, in pixels   float m_depth;// 0 to 1, depth to render the frame AnimationMode   m_animationMode;  // If and how to set the frame index every Render call   bool  m_animationDirection;  // 0 - currently counting up. 1 - currently counting down   common_lib_cpisz::Timer   m_animationTimer; // Keeps track of time passed between frames   unsigned   m_numFrames; // Number of animation frames the image contains   unsigned   m_startFrameIndex;// Start frame of an animation   unsigned   m_endFrameIndex;  // End frame of an animation   unsigned   m_currentFrameIndex;   // Current frame to be rendered   doublem_fps;  // Frames per second of the animation};#endif // IMAGE2D#include "Image2D.h"// Common Lib Includes#include "BaseException.h"#include "StringUtil.h"// Standard Includes#include <sstream>using namespace common_lib_cpisz;//-------------------------------------------------------------------Image2D::Image2D(ID3D10Device & device,   InputLayoutManager & inputLayoutManager,  TextureManager & textureManager,   EffectManager & effectManager,  const std::string & textureFilePath,  unsigned rows,  unsigned cols,  float depth)   :   m_device(device),   m_inputLayoutManager(inputLayoutManager),   m_textureManager(textureManager),   m_effectManager(effectManager),   m_inputLayout(NULL),   m_width(0),   m_height(0),   m_depth(depth),   m_animationMode(ANIMATION_MODE_NONE),   m_animationDirection(0),   m_numFrames(rows * cols),   m_startFrameIndex(0),   m_endFrameIndex(0),m_currentFrameIndex(0),   m_fps(0){   // SNIP - long exception string// Load the texture   RemoveExtFromFilename(textureFilePath, m_textureName);   Texture * texture = NULL; try   { texture = &(m_textureManager.CreateTextureFromFile(m_textureName, textureFilePath));   }   catch(BaseException & e)   { throw e;   }   m_width  = static_cast<float>(texture->GetWidth() / cols);   m_height = static_cast<float>(texture->GetHeight() / rows);   if(texture->GetWidth() % cols || texture->GetHeight() % rows)   { exception.m_msg = textureFilePath + "is not evenly divisible into the requested number of frames"; throw exception;   }   // Create the effect   Effect *    effect    = NULL;   Technique * technique = NULL;   Pass * pass = NULL; try   { effect    = &(m_effectManager.CreateChildEffect("image", "image.fx")); technique = &(effect->GetTechnique("RenderWithAlpha")); pass = &(technique->GetPass(0));   }   catch(BaseException & e)   { throw e;   }   // Create a material   try   { m_material = effect->CreateMaterial(); m_material->SetBool("diffuseMapped", true); m_material->SetTextureName("diffuseTexture", m_textureName);   }   catch(BaseException & e)   { throw e;   }   // Create a transform   D3DXMatrixIdentity(&m_world);   // Create a quad to texture with the image   std::vector<Position>quadPositions;   std::vector<TexCoord2D>   quadTexCoords;   D3DXVECTOR2 positionOffset;   positionOffset.x = 0.5f * m_width;  // Distance from origin on x axis used to generate vertex positions for a quad   positionOffset.y = 0.5f * m_height; // Distance from origin on y axis used to generate vertex positions for a quad   D3DXVECTOR2 textureOffset;   textureOffset.x = 1.0f / static_cast<float>(cols);// How much to shift the texture coords for each frame horizontally   textureOffset.y = 1.0f / static_cast<float>(rows);// How much to shift the texture coords for each frame verticallyfor(unsigned currentRow = 0; currentRow < rows; ++currentRow){for(unsigned currentColumn = 0; currentColumn < cols; ++currentColumn){    quadPositions.push_back(Position( -positionOffset.x, -positionOffset.y, m_depth));    quadTexCoords.push_back(TexCoord2D( currentColumn * textureOffset.x, (currentRow + 1) * textureOffset.y));    quadPositions.push_back(Position( -positionOffset.x,  positionOffset.y, m_depth));    quadTexCoords.push_back(TexCoord2D( currentColumn * textureOffset.x, currentRow * textureOffset.y));    quadPositions.push_back(Position(  positionOffset.x, -positionOffset.y, m_depth));    quadTexCoords.push_back(TexCoord2D( (currentColumn + 1) * textureOffset.x, (currentRow + 1) * textureOffset.y));    quadPositions.push_back(Position(  positionOffset.x,  positionOffset.y, m_depth));    quadTexCoords.push_back(TexCoord2D( (currentColumn + 1) * textureOffset.x, currentRow * textureOffset.y));}}   Buffer::SharedPtr quadPositionBuffer(new Buffer(m_device, POSITION, quadPositions, false));   Buffer::SharedPtr quadTexCoordBuffer(new Buffer(m_device, TEXCOORD2D, quadTexCoords, false));   m_quadBuffers.push_back(quadPositionBuffer);   m_quadBuffers.push_back(quadTexCoordBuffer);   // Create an input layout   std::vector<InputElementDescription> inputElementDescs;   std::vector<InputElementDescription> thisElementDesc;   InputElementDescription::GetInputElementDesc(thisElementDesc,   m_quadBuffers[0]->GetContentType(),    0,   0,   m_quadBuffers[0]->IsPerInstance());   inputElementDescs.insert(inputElementDescs.end(), thisElementDesc.begin(), thisElementDesc.end());   InputElementDescription::GetInputElementDesc(thisElementDesc,   m_quadBuffers[1]->GetContentType(),    0,   1,   m_quadBuffers[1]->IsPerInstance());   inputElementDescs.insert(inputElementDescs.end(), thisElementDesc.begin(), thisElementDesc.end());   m_inputLayout = m_inputLayoutManager.GetInputLayout(inputElementDescs, *pass);   // Calculate the strides   m_strides.push_back(GetStride(m_quadBuffers[0]->GetContentType()));   m_strides.push_back(GetStride(m_quadBuffers[1]->GetContentType()));}//-------------------------------------------------------------------Image2D::~Image2D(){}//-------------------------------------------------------------------unsigned Image2D::GetNumFrames() const{   return m_numFrames;}//-------------------------------------------------------------------void Image2D::StartAnimation(unsigned startFrameIndex, unsigned endFrameIndex, AnimationMode animationMode, double fps){   // SNIP - long exception string   if( startFrameIndex < 0 || startFrameIndex >= m_numFrames ||  endFrameIndex   < 0 || endFrameIndex   >= m_numFrames )   { std::stringstream msg; msg << "Invalid start index: " << startFrameIndex << " or end index: " << endFrameIndex << ". "; msg << "Valid frame indices are 0 to " << (m_numFrames - 1);  exception.m_msg  = msg.str(); throw exception;   }   m_startFrameIndex   = startFrameIndex;   m_currentFrameIndex = startFrameIndex;   m_endFrameIndex= endFrameIndex;   m_animationMode= animationMode;   m_fps= fps;   m_animationTimer.Start();}//-------------------------------------------------------------------void Image2D::StopAnimation(){   m_startFrameIndex   = 0;   m_currentFrameIndex = 0;   m_endFrameIndex= 0;   m_animationMode= ANIMATION_MODE_NONE;   m_fps= 0;   m_animationTimer.Stop();}//-------------------------------------------------------------------unsigned Image2D::GetCurrentFrameIndex() const{   return m_currentFrameIndex;}//-------------------------------------------------------------------void Image2D::SetCurrentFrameIndex(unsigned index){   // SNIP - long exception string   if( index < 0 || index >= m_numFrames )   { std::stringstream msg; msg << "Frame index " << index << "does not exist. Valid frame indices are 0 to " << (m_numFrames - 1);  exception.m_msg  = msg.str(); throw exception;   }   m_currentFrameIndex = index;}//-------------------------------------------------------------------void Image2D::Render(){   // Get the original matrices   D3DXMATRIX origProjection;   D3DXMATRIX origView;   m_effectManager.GetViewMatrix(origView);   m_effectManager.GetProjectionMatrix(origProjection);   // Create new matrices for rendering 2D   D3DXMATRIXnewProjection;D3DXMATRIXnewView;   D3DXMATRIXworld;   UINT numViewports = 1;   D3D10_VIEWPORT viewport;   m_device.RSGetViewports(&numViewports, &viewport);   D3DXMatrixOrthoLH(&newProjection, static_cast<float>(viewport.Width), static_cast<float>(viewport.Height), 0.0f, 1.0f);   D3DXMatrixIdentity(&newView);   m_effectManager.SetProjectionMatrix(newProjection);   m_effectManager.SetViewMatrix(newView);   // Set the world matrix and material   Effect *    effect    = NULL;   Technique * technique = NULL;   Pass * pass = NULL;   try   { effect    = &(m_effectManager.GetChildEffect("image")); technique = &(effect->GetTechnique("RenderWithAlpha")); pass = &(technique->GetPass(0)); effect->SetWorldMatrix(m_world); effect->SetMaterial(*m_material);   }   catch(BaseException & e)   { throw e;   }   // Bind the input layout   m_device.IASetInputLayout(m_inputLayout);   m_device.IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);   // Bind the vertex buffers   std::vector<ID3D10Buffer *> vertexBuffers;   vertexBuffers.push_back( m_quadBuffers[0]->GetD3DBuffer() );   vertexBuffers.push_back( m_quadBuffers[1]->GetD3DBuffer() );   std::vector<unsigned> offsets;   offsets.push_back(0);   offsets.push_back(0);   m_device.IASetVertexBuffers(0, static_cast<UINT>(vertexBuffers.size()), &vertexBuffers[0],  &m_strides[0],  &offsets[0]);   // Calculate the frame to be rendered   CalculateAnimationFrame();   unsigned startVertex = (m_currentFrameIndex) * 4;   // Apply the pass   pass->Apply();   // Draw   m_device.Draw(4, startVertex);   // Restore the original matrices   m_effectManager.SetProjectionMatrix(origProjection);   m_effectManager.SetViewMatrix(origView);}//-------------------------------------------------------------------void Image2D::CalculateAnimationFrame(){   // Check if an animation is playing   if( !m_animationTimer.IsStarted() )   { return;   }   // Make the calculation   double   frameInterval= 1.0 / m_fps;   double   elapsedTime  = m_animationTimer.GetElapsedTime();   unsigned framesToAdvance   = static_cast<unsigned>(elapsedTime / frameInterval);   unsigned framesInAnimation = m_endFrameIndex - m_startFrameIndex + 1;   switch(m_animationMode)   { case ANIMATION_MODE_ONCE : {    if( framesToAdvance > m_endFrameIndex )    {  m_currentFrameIndex = m_endFrameIndex;  m_animationMode= ANIMATION_MODE_NONE;  m_animationTimer.Stop();    }    else    {  m_currentFrameIndex = m_startFrameIndex + framesToAdvance;    }    break; } case ANIMATION_MODE_LOOP : {    m_currentFrameIndex = m_startFrameIndex + (framesToAdvance % framesInAnimation);   break; }  case ANIMATION_MODE_BOUNCE : {    // if start = 0 and end = 9    // frames in animation = 10    // 0 to 9 foward    // 10 is end - 1    // 11 is end - 2    // ...    // 18 is end - 9 = start    //    bool backward = ( (framesToAdvance / (framesInAnimation - 1)) % 2 != 0 );    if( !backward )    {  m_currentFrameIndex = m_startFrameIndex + (framesToAdvance % (framesInAnimation - 1));    }    else    {  m_currentFrameIndex = m_endFrameIndex - (framesToAdvance % (framesInAnimation - 1));    }    break; } case ANIMATION_MODE_NONE : default: {    break; }   }}and how I have it rigged into the main test application right now://----------------------------------------------------------------------------void TestApp::HandleKeyboardInput(){   // SNIP   // Space   if( m_keystates[VK_SPACE] )   { m_image->StartAnimation(0, 15, Image2D::ANIMATION_MODE_ONCE, 30.0);   }}//----------------------------------------------------------------------------void TestApp::Render(){   //--------   // TODO - Alot of the following should belong in a renderqueue class   // Bind the view and projection matrices from the camera   m_effectManager->BindCamera(m_camera);    // Bind the lights   m_effectManager->SetAmbientLight(m_ambientLight);   m_effectManager->SetDirectionalLight(m_directionalLight, 0);   // Render Opaque objects first// m_skyBox->Render();// m_nebula_tl->Render();// m_nebula_tr->Render();// m_nebula_bl->Render();// m_nebula_br->Render();// m_ship->Render();   m_image->Render();// m_lensFlare->Render();   // Render Transparent objects second// m_nebula_stars->Render();}Lucky for me, the first frame is all alpha, but it's no good to _always_ display it even if it is completely transparent.EDIT: wish they had an option to automatically wrap code blocks :( Working on it.... took out long exception strings, it still seems to through in an extra 20 chars of white space or so for no reason. Let me know if anyone has advise on posting copy pasted code in a more readable fashion. (aside from coding in 80 char lines in my editor :/ )[Edited by - brekehan on May 17, 2009 7:25:40 PM]