2D sprites rotation...

Started by
12 comments, last by smr 18 years, 10 months ago
Ok, so I'm using SDL and I'm creating a little sprite class, that takes care of rendering them into the screen, etc..., I'm trying to make a rotate method, that will....well, rotate the sprite... I found a few tutorials on how to do it after googling for a while...., but since I haven't tried them yet..., does anyone know of a good tutorial about it???? Thanks in advance! Haora
Advertisement
Don't know of any good tutorials on it. There is an SDL library called rotozoom (I think) that will rotate and zoom SDL surfaces. If you're not rotating by 90 degree increments, you better program it really clever or you're going to take a big performance hit if you're rotating in real time. I looked into rotating once upon a time, and decided not to do it in 2d... until I started thinking of OpenGL as a viable way to do 2d. So easy with that. So fast with that.

I've actually put my projects on hiatus so I can learn OpenGL and do all my 2d graphics from there. Horribly fun.

***EDIT*** Not saying you shouldn't do it, just that the algorithm is a greedy hungry one that'll eat your resources if you're not careful.
----------------------------------------------------------No matter how eloquently you state your argument, the fact remains that the toilet seat is a bistable device. Therefore it's natural position is no more down than it is up.[SDL Smooth Tile Scrolling]
You might be better off photoshopping (or whatever) 16 or so rotations and having some kind of mapping function that takes an angle and tells you which position to use... Hey, if you're any good at gimp, you can probably write a srcipt that will do it all for you, so you can automate it for all your sprites that require it :). I'll probably end up learning the D3D way of doing 2D, since it's probably a lot smoother that way.
Well, actually, I did it!!!

Yes!!!

I'm still in the optimization stage..., but once I'm done, I'll post the code if anyone is interested.....

I know there is someone out there doing a SDL Sprite library..., that code could be usefull to him/her/them....

Haora
Sure I'd love to see it! I'm not using SDL, but I've used it in the past, and I am still interested. Is it fast (i know it's pre-optimization, but still hehe).
There IS an SDL library that will do this... It's called SDL_graphics or SDL_gfx, I don't remember which. It has LOTS of nice functions; beyond rotozoom, there's also anti-aliasing and basic vector-graphics drawing as well. I used it to whip up a simple program so that, instead of photoshopping 16 or 64 rotations of a sprite, I just feed the initial sprite to the program and it spits out all the rotations. It's pretty straightforward stuff.
-----http://alopex.liLet's Program: http://youtube.com/user/icefox192
Icefox: Well, I downloaded that library, and compiled it, with VC7, but couldn´t compile the examples...lots of link errors..., anyway..., here is my code..., I couldn´t optimize it very good..., hopefully, someone out there will be able to:

/* * Rotates a sprite * @src = sprite to be rotated * @dest = the destination surface  * @angle = the angle to rotate * @ xf, yf = the final coordinates of the sprite, in the screen  */void draw_rotated_sprite(SDL_Surface *src, SDL_Surface *dest,int angle,int xf, int yf){	Uint8 r = 0,g = 0,b = 0;	Uint16 *bufp;	int xrot,yrot;	int haldWidth = src->w/2;	int halfHeight  = src->h/2;	Uint32 rmask, gmask, bmask, amask;	float incX = 1.0/src->w;	float incY = 1.0/src->h;	int x1rot, x2rot, x3rot, x4rot;	int y1rot, y2rot, y3rot, y4rot;#if SDL_BYTEORDER == SDL_BIG_ENDIAN    rmask = 0xff000000;    gmask = 0x00000000;    bmask = 0x0000ff00;    amask = 0x000000ff;#else    rmask = 0x000000ff;    gmask = 0x00000000;    bmask = 0x00ff0000;    amask = 0xff000000;#endif	int max = (src->w > src->h)?src->w:src->h;	        SDL_Surface *tmp = SDL_CreateRGBSurface(SDL_HWSURFACE, max, max, 16,                                   rmask, gmask, bmask, amask);	SDL_FillRect(tmp,NULL,SDL_MapRGB(tmp->format,255,0,255));	SDL_SetColorKey(tmp,SDL_SRCCOLORKEY,SDL_MapRGB(tmp->format,255,0,255));	Trig *oTrig = Trig::Instance(); //singleton that contains a look-up table, with the cosine, and sine allready calculated	int xminusCenter, yminusCenter;	float cosin, sin;	sLock(tmp);	sLock(src);			for(register int y = 0; y < src->h; y++){		for(register int x = 0; x < src->w; x++){			xminusCenter = x-haldWidth;			yminusCenter = y-halfHeight;			cosin = oTrig->_Cos(angle); //this only access an array with the precalculated value			sin = oTrig->_Sin(angle);			xrot = (int)(((cosin * (xminusCenter)) - (sin*(yminusCenter))));			yrot = (int)(((sin * (xminusCenter)) + (cosin*(yminusCenter))));					if(!((xrot+haldWidth)< src->w && (xrot+haldWidth) > 0) || !( (yrot+halfHeight) > 0 && (yrot+halfHeight) < src->h)) {				continue;			}else{									/* get pixel */				bufp = (Uint16 *)src->pixels + ((yrot+halfHeight)*src->pitch/2 +(xrot+haldWidth));				SDL_GetRGB((Uint32)*bufp,src->format,&r,&g,&b);				if(!(r == 255 && g == 0 && b == 255)){					PutPixel(tmp,x,y,r,g,b);								}										}		}		}	suLock(src);	suLock(tmp);	SDL_Rect rectDest;	rectDest.x = xf;	rectDest.y = yf;	SDL_BlitSurface(tmp,NULL,dest,&rectDest);	SDL_FreeSurface(tmp);}


The main problem with this code, is that it does lots of calculations for every pixel...,I´ve tried other ideas, like only rotating the corners, and then interpolate the rest of the pixels, but I can´t seem to get it working..., this code is the only one that works....

If anyone has any thoughts on how to optimize it, I would be REEEEEAAAALLLY thankfull!!! [grin]

Haora
You can download a small tutorial which takes the 2d rotation algorithm from Tricks of the Game Programming Guru's and attempts to speed it up:

http://www.programmersheaven.com/zone10/cat345/15441.htm

I haven't tried it out, so I'm not sure about its performance.
Well, since rotation and scaling amounts to texture mapping without the Z coordinate to worry about (although this does shear as well), check out Michael Abrash's Graphics Programming Black Book (http://www.gamedev.net/reference/articles/article1698.asp). Chapter 58 claims to be able to do non-perspective texture mapping in 5.5 cycles per pixel. Of course since this is 2D non-perspective is perfect. 5.5 cycles is for an algorithm written in assembler and run on a Pentium (original - this was more than 10 years ago). It does draw pixels in columns rather than rows but that's because of the way VGA works and should certainly not be done today.
Well..., last night, after sending the message I found a tutorial, that explained how to optimize my code using fixed point math..., I did that, and the code got pretty faster..., but still, with like 20 sprites rotating at the same time, the game gets really slow..., it's probably because of my computer, but I still will try to use some of the tutorials you just mentioned....

Any way..., here is the code I came up with...

void draw_rotated_sprite(SDL_Surface *src, SDL_Surface *dest,int angle,int xf, int yf,bool rotate){   Uint8 r = 0,g = 0,b = 0;   Uint16 *bufp;   int xrot,yrot;   Uint32 rmask, gmask, bmask, amask;#if SDL_BYTEORDER == SDL_BIG_ENDIAN   rmask = 0xff000000;   gmask = 0x00000000;   bmask = 0x0000ff00;   amask = 0x000000ff;#else   rmask = 0x000000ff;   gmask = 0x00000000;   bmask = 0x00ff0000;   amask = 0xff000000;#endif   int max = (src->w > src->h)?src->w:src->h;   SDL_Surface *tmp = SDL_CreateRGBSurface(SDL_HWSURFACE, max, max, 16,                                  rmask, gmask, bmask, amask);   SDL_FillRect(tmp,NULL,SDL_MapRGB(tmp->format,255,0,255));   SDL_SetColorKey(tmp,SDL_SRCCOLORKEY,SDL_MapRGB(tmp->format,255,0,255));   Trig *oTrig = Trig::Instance();   long cosin = oTrig->_Cos(angle);   long sin   = oTrig->_Sin(angle);   long ys;   long yc;   sLock(tmp);   sLock(src);                  for(register int y = -src->h/2; y < src->h/2; y++){	   ys = (y*sin)>>16;	   yc = (y*cosin)>>16;	   for(register int x = -src->w/2; x < src->w/2; x++){	       xrot = ( ((x*cosin)>>16) - ys);	       yrot = ( ((x*sin)>>16) + yc);	       if(xrot>-tmp->w/2 && xrot < tmp->w/2  && yrot > -tmp->h/2 && yrot<tmp->h/2){		   bufp = (Uint16 *)src->pixels + ((yrot+src->h/2)*src->pitch/2 +(xrot+src->w/2)); /*get pixel */		   SDL_GetRGB((Uint32)*bufp,src->format,&r,&g,&b);		   if(!(r == 255 && g == 0 && b == 255)){		       PutPixel(tmp,x+src->w/2,y+src->h/2,r,g,b);                                  }                                               }	   }          }   suLock(src);   suLock(tmp);   SDL_Rect rectDest;   rectDest.x = xf;   rectDest.y = yf;   FillRect(src,&rectNegro,SDL_MapRGB(src->format,255,0,255));   SDL_BlitSurface(tmp,NULL,dest,&rectDest);   SDL_FreeSurface(tmp);}

Now, in the look-up tables, I multiply sin and cosin by 2^16.....

I'll let you know, if any of the other methods works out...

Thanks again!

This topic is closed to new replies.

Advertisement