animat 2D sprites with Irrlicht

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

animat 2D sprites with Irrlicht

Post by Emil_halim »

Hi All

here is a CAnimSprite class for the users who love to animat 2D sprites with Irrlicht.
it allows you play the 2D sprite animation forward or backward,controling the speed of
animation, draw specific frame ,automatically set the colorkey,automatically adjest the
size of it's 3D coordenats relative to Screen dimantion so it will appears as it's orignal
size,and more .......,check out the class.


CAnimSprite.cpp
============

Code: Select all

class TAnimSprite : public ISceneNode
  {
     private:
           core::aabbox3d<f32> Box; 
           video::S3DVertex    Vertices[4];
           u16                 Indices[12]; 
           video::SMaterial    Material; 
           video::ITexture*    Texture;
           f32                 fWidth,fHeight;
           s32                 crntFrm,TotalFrm;
           s32                 stepww,stephh;
           BOOL                forward;
           DWORD               time;
           DWORD               oldtick;
           
           
      public: 
          TAnimSprite(ISceneNode* parent, ISceneManager* mgr, s32 id): ISceneNode(parent, mgr, id)
           {      
                  Material.Wireframe = false;
                  Material.Lighting = false;
                  
                  u16 ind[] = { 0,1,3, 3,1,2, 1,0,2, 2,0,3 };
                  memcpy(Indices,ind,sizeof(u16)*12);                         
           }     
          
          virtual void Load(char* filename,s32 frmWidth,s32 frmHeight)
           {
               IVideoDriver* driver = SceneManager->getVideoDriver();
               dimension2d<s32> Screensize = driver->getScreenSize(); 
               float x = (float)frmWidth/(float)Screensize.Width;
               float y = (float)frmHeight/(float)Screensize.Height;
               Vertices[0] = S3DVertex(-x,-y,0, 0,0,0,SColor(255,255,255,255),0,1);
               Vertices[1] = S3DVertex( x,-y,0, 0,0,0,SColor(255,255,255,255),1,1); 
               Vertices[2] = S3DVertex( x, y,0, 0,0,0,SColor(255,255,255,255),1,0);
               Vertices[3] = S3DVertex(-x, y,0, 0,0,0,SColor(255,255,255,255),0,0);
               
               Box.reset(Vertices[0].Pos);
               for (s32 i=1; i<4; ++i)  Box.addInternalPoint(Vertices[i].Pos);
                  
               Texture = driver->getTexture(filename);
               driver->makeColorKeyTexture(Texture,position2d<s32>(0,0));
                
               Material.Texture1 = Texture;
               Material.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL;
               
               dimension2d<s32> size = Texture->getOriginalSize();
               fWidth  = (float)frmWidth/(float)size.Width;
               fHeight = (float)frmHeight/(float)size.Height;
               crntFrm = 0;
               stepww = size.Width / frmWidth;
               stephh = size.Height / frmHeight;
               TotalFrm = stepww * stephh;
               forward = TRUE;
                             
               Vertices[0].TCoords.X = 0; 
               Vertices[0].TCoords.Y = fHeight;
               Vertices[1].TCoords.X = fWidth; 
               Vertices[1].TCoords.Y = fHeight;
               Vertices[2].TCoords.X = fWidth; 
               Vertices[2].TCoords.Y = 0;
               Vertices[3].TCoords.X = 0; 
               Vertices[3].TCoords.Y = 0;
           }
           
          virtual void PlayForward() {forward = TRUE;}
          virtual void PlayBackward() {forward = FALSE;} 
          virtual void SetSpeed(int spd)  {time = spd;}
           
          virtual void OnPreRender()
           { 
              if (IsVisible)    SceneManager->registerNodeForRendering(this);
              ISceneNode::OnPreRender();
           }
           
          virtual void setFrame(int n)
           {
               float x = (n % stepww)*fWidth;
               float y = (n / stepww)*fHeight; 
               Vertices[0].TCoords.X = x; 
               Vertices[0].TCoords.Y = y+fHeight;
               Vertices[1].TCoords.X = x+fWidth; 
               Vertices[1].TCoords.Y = y+fHeight;
               Vertices[2].TCoords.X = x+fWidth; 
               Vertices[2].TCoords.Y = y;
               Vertices[3].TCoords.X = x; 
               Vertices[3].TCoords.Y = y;
               
           }
          
          virtual void Update()
           {
              if(GetTickCount()-oldtick > time)
              {  
                   oldtick = GetTickCount();
                   if (forward) 
                   {
                       crntFrm++; 
                       if (crntFrm > TotalFrm-1)crntFrm = 0;
                   }
                   else
                   {
                       crntFrm--;
                       if (crntFrm < 0 )crntFrm = TotalFrm-1;
                   }      
                   float x = (crntFrm % stepww)*fWidth;
                   float y = (crntFrm / stepww)*fHeight; 
                   Vertices[0].TCoords.X = x; 
                   Vertices[0].TCoords.Y = y+fHeight;
                   Vertices[1].TCoords.X = x+fWidth; 
                   Vertices[1].TCoords.Y = y+fHeight;
                   Vertices[2].TCoords.X = x+fWidth; 
                   Vertices[2].TCoords.Y = y;
                   Vertices[3].TCoords.X = x; 
                   Vertices[3].TCoords.Y = y;
              }     
           }     
          
          virtual s32 GetMaxFrames() { return TotalFrm; }
            
          virtual void render()
           {  
               
              IVideoDriver* driver = SceneManager->getVideoDriver();
              driver->setMaterial(Material);
              driver->setTransform(ETS_WORLD, AbsoluteTransformation);
              driver->drawIndexedTriangleList(&Vertices[0], 4, &Indices[0], 4);
           }
 
          virtual const aabbox3d<f32>& getBoundingBox() const 
           { 
              return Box; 
           }
  
          virtual s32 getMaterialCount()
           {
              return 1;
           }
  
          virtual SMaterial& getMaterial(s32 i)
           {
             return Material;
           } 
           
  }; 
and here is a simple demo that showing you how to use it

Code: Select all

#include "irrlicht.h"
// Irrlicht Namespaces
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#include "CAnimSprite.cpp"

void main()
  {
      IrrlichtDevice* irrDevice = createDevice(EDT_DIRECTX9,dimension2d<s32>(640,480),32,false,false,false,0);
      IVideoDriver*   irrVideo  = irrDevice->getVideoDriver();
      ISceneManager*  irrSceneMgr = irrDevice->getSceneManager();
      
      TAnimSprite* myNode = new TAnimSprite(irrSceneMgr->getRootSceneNode(), irrSceneMgr, 666);
      myNode->Load("sonwalk.bmp",54,54);
      myNode->SetSpeed(100);
     //myNode->PlayBackward();
      
      While  (irrDevice->run())
        {
            irrVideo->beginScene(true, true, SColor(0,200,200,200));
            myNode->Update(); 
            //myNode->setFrame(5);
            irrSceneMgr->drawAll();
            irrVideo->endScene();
        }
      irrDevice->drop();
      
      return 0;
  }             
i hope that,it is useful for someone here.
alelink
Posts: 52
Joined: Tue Jan 20, 2004 8:32 pm
Location: Italy
Contact:

Post by alelink »

hi,
first thanks for this class, it will be very useful for all want to make 2d games using irrlicht.
Can you add some comment to your code or post the img you use in the sample code?

thanks in advance
Alessandro
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

sorry i have no website to send the image to the forum,the forum does not allow posting the image.so i will explain it.

1- create a sprite

TAnimSprite* myNode = new TAnimSprite(irrSceneMgr->getRootSceneNode(), irrSceneMgr, 666);

2- load the image

as you know the 2D animated sprites consiste of small images that display in screen frame after frame,and most animated sprite contain the frames in only one image.

Code: Select all

-----------------------------------
|........|........|........|.......|
|...f1...|...f2...|...f3...|...f4..|
|........|........|........|.......|
-----------------------------------
|........|........|........|.......|
|...f5...|...f6...|...f7...|...f8..|
|........|........|........|.......|
-----------------------------------
this is a the image that contain 8 frames, f1 is the first frame, f2 is the socnd one and so on...

we use the load member function to load the animated sprite

myNode->Load("sonwalk.bmp",54,54);

the first parametre is the name of our sprite,then comes the frame width and height , so in our example the frame width = 54
alos the frame height = 54

you must specify the frame width and height that the image will be divided into.


3- then if you want to set the speed of animating use

myNode->SetSpeed(100);

and if you want to play tha animation in backword use

myNode->PlayBackward();


4- render the sprite in irrlicht loop

use myNode->Update() to update the animation to display the next frame

you can control the frame that will render if you want,for example, use myNode->setFrame(5) to display frame number 5 of
our animation ,you can use it to play some frames like this

int frm;
myNode->setFrame(frm);
frm++;
if (frm > 6) frm = 3;


i hope that it is easy now.
alelink
Posts: 52
Joined: Tue Jan 20, 2004 8:32 pm
Location: Italy
Contact:

Post by alelink »

Hi, thanks for your reply, it was very useful.

I've modded your class to loop between two frames stored in an image beacuse Irrlicht works better with ^2 dimension images, so we can have an image with some blank frames.

I've update the Update() method and added 2 variable to control animation start and end.

this is the modded class:

Code: Select all

#include <irrlicht.h>
#include <windows.h>//for DWORD and others..

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

class TAnimSprite : public ISceneNode
{
	private:
	core::aabbox3d<f32> Box;
	video::S3DVertex Vertices[4];
	u16 Indices[12];
	video::SMaterial Material;
	video::ITexture* Texture;
	f32 fWidth,fHeight;
	s32 crntFrm,TotalFrm;
	s32 stepww,stephh;
	bool forward;
	DWORD time;
	DWORD oldtick;
	
	
	public:

	//for the animation loop between this frames
	s32 startFrame, endFrame;

	TAnimSprite(ISceneNode* parent, ISceneManager* mgr, s32 id):ISceneNode(parent, mgr, id)
	{
		Material.Wireframe = false;
		Material.Lighting = false;
		u16 ind[] = { 0,1,3, 3,1,2, 1,0,2, 2,0,3 };
		memcpy(Indices,ind,sizeof(u16)*12);
   	}

	virtual void Load(char* filename,s32 frmWidth,s32 frmHeight)
	{
		IVideoDriver* driver = SceneManager->getVideoDriver();
		dimension2d<s32> Screensize = driver->getScreenSize();
		float x = (float)frmWidth/(float)Screensize.Width;
		float y = (float)frmHeight/(float)Screensize.Height;
		Vertices[0] = S3DVertex(-x,-y,0,
		0,0,0,SColor(255,255,255,255),0,1);
		Vertices[1] = S3DVertex( x,-y,0,
		0,0,0,SColor(255,255,255,255),1,1);
		Vertices[2] = S3DVertex( x, y,0,
		0,0,0,SColor(255,255,255,255),1,0);
		Vertices[3] = S3DVertex(-x, y,0,
		0,0,0,SColor(255,255,255,255),0,0);
		Box.reset(Vertices[0].Pos);
		for (s32 i=1; i<4; ++i)
		Box.addInternalPoint(Vertices[i].Pos);
		Texture = driver->getTexture(filename);
		driver->makeColorKeyTexture(Texture,position2d<s32>(0,0));
		Material.Texture1 = Texture;
		Material.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL;
		dimension2d<s32> size = Texture->getOriginalSize();
		fWidth = (float)frmWidth/(float)size.Width;
		fHeight = (float)frmHeight/(float)size.Height;
		crntFrm = 0;
		stepww = size.Width / frmWidth;
		stephh = size.Height / frmHeight;
		TotalFrm = stepww * stephh;
		forward = TRUE;
		Vertices[0].TCoords.X = 0;
		Vertices[0].TCoords.Y = fHeight;
		Vertices[1].TCoords.X = fWidth;
		Vertices[1].TCoords.Y = fHeight;
		Vertices[2].TCoords.X = fWidth;
		Vertices[2].TCoords.Y = 0;
		Vertices[3].TCoords.X = 0;
		Vertices[3].TCoords.Y = 0;
	}

	virtual void PlayForward() {forward = TRUE;}

	virtual void PlayBackward() {forward = FALSE;}

	virtual void SetSpeed(int spd) {time = spd;}

	virtual void OnPreRender()
	{
		if (IsVisible) SceneManager->registerNodeForRendering(this);
		ISceneNode::OnPreRender();
	}

	virtual void setFrame(int n)
	{
		float x = (n % stepww)*fWidth;
		float y = (n / stepww)*fHeight;
		Vertices[0].TCoords.X = x;
		Vertices[0].TCoords.Y = y+fHeight;
		Vertices[1].TCoords.X = x+fWidth;
		Vertices[1].TCoords.Y = y+fHeight;
		Vertices[2].TCoords.X = x+fWidth;
		Vertices[2].TCoords.Y = y;
		Vertices[3].TCoords.X = x;
		Vertices[3].TCoords.Y = y;
	}

	//set the animation between 2 frames and update it
	virtual void Update()
	{
		//if it's time to update...
		if(GetTickCount()-oldtick > time)
		{
			oldtick = GetTickCount();
			if (forward)//if we must go forward in animation
			{
				crntFrm++;
				if (crntFrm > endFrame) crntFrm = startFrame;
			}
			else //we go backward
				{
				crntFrm--;
				if (crntFrm < startFrame )crntFrm = endFrame;
				}

			float x = (crntFrm % stepww)*fWidth;
			float y = (crntFrm / stepww)*fHeight;
			Vertices[0].TCoords.X = x;
			Vertices[0].TCoords.Y = y+fHeight;
			Vertices[1].TCoords.X = x+fWidth;
			Vertices[1].TCoords.Y = y+fHeight;
			Vertices[2].TCoords.X = x+fWidth;
			Vertices[2].TCoords.Y = y;
			Vertices[3].TCoords.X = x;
			Vertices[3].TCoords.Y = y;
		}
	}

	virtual s32 GetMaxFrames() { return TotalFrm; }

	virtual void render()
	{
		IVideoDriver* driver = SceneManager->getVideoDriver();
		driver->setMaterial(Material);
		driver->setTransform(ETS_WORLD, AbsoluteTransformation);
		driver->drawIndexedTriangleList(&Vertices[0], 4, &Indices[0],4);
	}

	virtual const aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}

	virtual s32 getMaterialCount()
	{
		return 1;
	}

	virtual SMaterial& getMaterial(s32 i)
	{
		return Material;
	}
};
You know any tips for collision detection?
I see that the image is "mounted" over a polygon,
so it could be processed by a triangleSelector?

thanks again for this class! :D
alelink
Posts: 52
Joined: Tue Jan 20, 2004 8:32 pm
Location: Italy
Contact:

Post by alelink »

...
Last edited by alelink on Tue Apr 19, 2005 8:01 pm, edited 2 times in total.
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

Hi, thanks for your good addations.

i think that you must put the 2 var(startFrame,endFrame) in the privet declration
of the class so it must be look like this

Code: Select all

private: 
   core::aabbox3d<f32> Box; 
   video::S3DVertex Vertices[4]; 
   u16 Indices[12]; 
   video::SMaterial Material; 
   video::ITexture* Texture; 
   f32 fWidth,fHeight; 
   s32 crntFrm,TotalFrm; 
   s32 stepww,stephh; 
   bool forward; 
   DWORD time; 
   DWORD oldtick; 
   s32  startFrame,endFrame;
and in Load function we must add this

Code: Select all

startFrame = 0;
endFrame   = TotalFrm;
and we must put this Helper function

Code: Select all

virtual void setStartEndFrame( s32 st, s32 ed)
  {
       startFrame = st;
       endFrame   = ed;
  }
i think that irrlicht could handel the collision detection easlly as it does with any model

thanks for your suggestion
aradu
Posts: 5
Joined: Wed Apr 20, 2005 3:31 pm
Location: Sweden
Contact:

Post by aradu »

When I compiled this I got a few errors and I can't make it work. I managed to get the error down to 2, but I can't get any further.

Code: Select all

--------------------Configuration: anim2d - Win32 Debug--------------------
Linking...
main.obj : error LNK2001: unresolved external symbol __imp__createDevice
Debug/anim2d.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

anim2d.exe - 2 error(s), 0 warning(s)
Otherwise it looks exactly like something I need for my own project. ;)
darkraven
Posts: 29
Joined: Sat Mar 06, 2004 5:54 am
Location: Florida
Contact:

Post by darkraven »

aradu wrote:When I compiled this I got a few errors and I can't make it work. I managed to get the error down to 2, but I can't get any further.

Code: Select all

--------------------Configuration: anim2d - Win32 Debug--------------------
Linking...
main.obj : error LNK2001: unresolved external symbol __imp__createDevice
Debug/anim2d.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

anim2d.exe - 2 error(s), 0 warning(s)
Otherwise it looks exactly like something I need for my own project. ;)

Not totally sure about this but you may need to check to see if the path to the irrlicht lib file is set in the project properties so the linker knows to link to it. I've gotten this error before when the linker can't find a lib file. Again this may not be the problem but it is something I would suggest looking into.
aradu
Posts: 5
Joined: Wed Apr 20, 2005 3:31 pm
Location: Sweden
Contact:

Post by aradu »

darkraven: Thanks a lot! Exactly what the problem was.

I added:

Code: Select all

#pragma comment(lib, "Irrlicht.lib")
and it worked.

I still haven't got the play to work, but I can set different frames. I'll try some more.

EDIT: The play frames is working fine. Thanks! :D
Beam

Post by Beam »

I add a node and it seemes to work. I add the update in the loop, but nothing is drawn? What could be wrong?

Also, can I rotate and scale the node dereived from the pic as a normal node?

Beam
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

can you post your code and your pic ,to allow us to help you please.

ofcaurse you can Scale,set the position,and rotate the node as usual, but to be honest,
the rotaion did not work as i want,till now i am tring to understand how absolute rotation
could be done in irrlicht.it makes me crazy.

here is the code to scale and set position of node

Code: Select all

While  (irrDevice->run()) 
  { 
      irrVideo->beginScene(true, true, SColor(0,200,200,200)); 
            myNode->setPosition(vector3df(-0.6,0.5,0)); 
            myNode->setScale(vector3df(2,2,2));  
            myNode->Update();   
            irrSceneMgr->drawAll(); 
            irrVideo->endScene(); 
  } 
the help file says that aboute setRotation

This only modifies the relative rotation of the node

so relative to what, and how the hell absolute rotation could be done!!!!!!!!!!??????????
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

finaly,it works as expected. the Rotation work correctly now

here is the new render function,replace it in your class

Code: Select all

virtual void render()
           {      
              IVideoDriver* driver = SceneManager->getVideoDriver();
              driver->setMaterial(Material);  
              matrix4 Trns,Scl,Rot,wrld;
              Trns.makeIdentity(); 
              Scl.makeIdentity();
              Rot.makeIdentity();
              Trns.setTranslation(RelativeTranslation);
              Scl.setScale(RelativeScale);
              Rot.setRotationRadians(RelativeRotation);
              wrld = Trns * Rot * Scl;
              driver->setTransform(ETS_WORLD, wrld);
              driver->drawIndexedTriangleList(&Vertices[0], 4, &Indices[0], 4);
           }
and here is the demo

Code: Select all

While  (irrDevice->run())
  {
       irrVideo->beginScene(true, true, SColor(0,200,200,200));
                  static float rt;
                  rt += 0.001;
                  Sprite->setPosition(vector3df(-0.5,0.1,0));
                  Sprite->setRotation(vector3df(0,0,rt));   
                  Sprite->setScale(vector3df(2,2,0));    
                  Sprite->Update(); 
                  irrSceneMgr->drawAll();
       irrVideo->endScene(); 
  }
Benjamin

Full code?

Post by Benjamin »

I tried to use the class, but there is nothing rendered, no matter what i try. Could you please post the full code?

Thanks,

Benjamin
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

see this tutorial it contains a full code example, if it does not work
please tell me.
http://www.irrforge.org/index.php/Pla ... h_Irrlicht
Last edited by Emil_halim on Mon May 09, 2005 3:50 pm, edited 1 time in total.
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

Last edited by Emil_halim on Mon May 09, 2005 3:45 pm, edited 1 time in total.
Post Reply