• Advertisement
Sign in to follow this  

2D sprites rotation...

This topic is 4607 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

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
Well I don't know too much about SDL but I can suggest two obvious things: first - get those multiplications out of the inner loop. You should be able to get by with just additions and bit shifts. Second - it will be a LOT fater if you can get a pointer to the pixels on the surface and manipulate them directly instead of working through SDL_GetRGB and PutPixel. If you're working in more than one colour depth this will mean more than one version of the function but it won't exactly kill you - it only depends on the number of bits per pixel not the format.

Share this post


Link to post
Share on other sites
Quote:
Original post by haora
...but still, with like 20 sprites rotating at the same time, the game gets really slow...


Here's a suggestion: Don't rotate 20 sprites at the same time. Instead, make an array of surfaces for each sprite, each element being a sprite rotated X degrees. Something like:


SDL_surface* rotated_foo_sprite[64];

void init_foo_sprite()
{
int i;
/* Pretend I want to bother writing code to initialize rotated_foo_sprite to be
* full of surfaces of the appropriate size here... */

for( i = 0; i &lt; 64; i++ )
{
draw_rotated_sprite( foo_sprite, rotated_foo_sprite[i], (int) (i * 5.625), 0, 0, true );
}
}



Classic trade-off of speed for memory. Of course, I don't know what exactly you're trying to do with this, so this may not be appropriate.

Sorry the SDL library didn't work out; I'm not an expert on VC, but surely if it compiled it should link fine... what kind of errors were you having?

Share this post


Link to post
Share on other sites
Quote:

Here's a suggestion: Don't rotate 20 sprites at the same time. Instead, make an array of surfaces for each sprite, each element being a sprite rotated X degrees.


Thanks, that's an idea..., I could do that....

About the linking errors.., well I don't remember them right now, but they were the "undefined reference to..." type of errors...


I'm actually having another problem now..., I don't know if anybody noticed, but the "draw_rotated_sprite" function only work with square sprites..., if the sprite is rectangular, the whole thing (the sprite) gets cut down....

Any ideas on how could I fix that???

Thanks again!

Share this post


Link to post
Share on other sites

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))));



You can remove all of this code from the inner loop, or at least simplify it. Since your angle never changes, there's no need to get it for every pixel. Just do it once outside both of your loops. Also I'm not sure there's a need for using lookup tables for your sine and cosines. Modern processors are pretty darn fast at floating point nowadays. You can remove the multiplications by changing them to simple floating point additions. Just calculate your x increment and your y increment, then add them to the current x and current y for each pixel. You're done with the row when x >= the width of the sprite, and you're done with the sprite when y >= the hieght.

The calls to getpixel and putpixel are also showstoppers. You'll never get good performance with these. They don't inline well because they both do a great deal of setup each time you call them that could be done just once if you implemented them yourself... which is no trivial task to boot.

Share this post


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

  • Advertisement