Jump to content
  • Advertisement
Sign in to follow this  
brekehan

Handling lifetime of renderables

This topic is 3493 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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::string                    m_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
   double                         m_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 vertically

	for(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
   D3DXMATRIX     newProjection;	
	D3DXMATRIX     newView;
   D3DXMATRIX     world;
   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]

Share this post


Link to post
Share on other sites
Advertisement
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!