Page 1 of 2

animat 2D sprites with Irrlicht

Posted: Mon Apr 18, 2005 6:02 pm
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.

Posted: Mon Apr 18, 2005 8:37 pm
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

Posted: Mon Apr 18, 2005 10:52 pm
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.

Posted: Tue Apr 19, 2005 6:24 pm
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

Posted: Tue Apr 19, 2005 6:25 pm
by alelink
...

Posted: Tue Apr 19, 2005 7:31 pm
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

Posted: Fri Apr 22, 2005 7:06 pm
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. ;)

Posted: Fri Apr 22, 2005 7:21 pm
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.

Posted: Fri Apr 22, 2005 7:54 pm
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

Posted: Wed Apr 27, 2005 5:43 am
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

Posted: Wed Apr 27, 2005 3:23 pm
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!!!!!!!!!!??????????

Posted: Wed Apr 27, 2005 6:27 pm
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(); 
  }

Full code?

Posted: Mon May 09, 2005 12:41 pm
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

Posted: Mon May 09, 2005 3:41 pm
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

Posted: Mon May 09, 2005 3:41 pm
by Emil_halim