Jump to content
  • Advertisement
Sign in to follow this  
Windhawk

2D rotation in SDL

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

From this topic (it was retired so I started a new one, hope this is ok): http://www.gamedev.net/community/forums/topic.asp?topic_id=417217 I've been playing around with the code suggested by TheAdmiral and am encountering a number of problems. The height and width of the new surface seem to be capable of being negative, which is not good for creating a surface of those dimensions. Past about 45degrees the surface seems not to be large enough for the original image, and with larger rotations the image seems to be getting cut off, and I can't work out why. Here's the code I've been using:
SDL_Surface *rotPlayer(int n, float angle) {
   
   SDL_Surface *result;
   SDL_Color c;
   float a = rads(angle); //convert from degrees
   
   int oW = pl[n].img->w;
   int oH = pl[n].img->h;
   
   int width = (oH*sin(a) + oW*cos(a));
   int height = (oH*cos(a) + oW*sin(a));
   
   result = SDL_CreateRGBSurface(SDL_SWSURFACE, width,height, 32, 0,0,0,0);
   
   int i0 = width/2;
   int j0 = height/2;
   
   for (int i = 0; i < width; ++i) {
      for(int j = 0; j < height; ++j) {
         float x = (float) (i - i0); // Translate
         float y = (float) (j - j0);
         
         float rx = x*cos(-a) - y*sin(-a); // Rotate
         float ry = x*sin(-a) + y*cos(-a);
         
         rx += i0; // Untranslate
         ry += j0;
         
         if (rx < 0 || rx > oW || ry < 0 || ry > oH) {
            c.r=0; c.g=0; c.b=0;
            putPixel(result, i,j, c);
            } else {
            c = bilinearSample(pl[n].img, rx,ry);
            putPixel(result, i,j, c);
         }
      }
   }
   return result;
}

I've also been wondering the effect of changing the origin of rotation, but am not entirely sure about how to go about doing so. It would be nice to get the original problems sorted out first though. Thanks for any help. Windhawk.

Share this post


Link to post
Share on other sites
Advertisement
I believe the formula you are using for the width and height of the new image will only work for rotations between 0 and 90 degrees, and that outside of that range the result will be incorrect. The formulas are assuming that the horizontal distance from the top left to the bottom right vertices determines the width, and the vertical distance from the bottom left to the top right vertices determine the height. Outside of the range of 0 to 90 degree rotations, this is incorrect and the formulas will start giving results smaller than the initial image's height and width, which is clearly wrong.

I think the height and width formulas could be fixed by using the absolute values of the sine and cosine instead (not entirely sure about this).

The other problem is in the loop where the transformations are handled. The purpose of the loop is to look at each pixel in the new image and calculate the corresponding position in the old image to get the color for that pixel in the new image. The first translation gives the displacement of the pixel in the new image from the center of the new image. This is what we need, since we're rotating the image about the center. So that displacement is rotated to give the displacement from the center of the unrotated image.

Now here's the problem. The centers of the new and old image are different. We have the displacement from the center, so that displacement has to be added to the center of the old image to get the coordinates in the old image. The second translation should therefore be using half the width and half the height of the original image, oW/2 and oH/2.

There's another minor problem dealing with how the floating point numbers are rounded. Currently the code is implicitly converting some floats to ints, which is truncating them. This could result in the top and right edges of the rotated image being slightly cut off. It may be better to treat the new width and height as floats, and using their ceiling to get the integer heights to use for creating the new surface.

Share this post


Link to post
Share on other sites
Thanks for the help. :)

There's probably a better way of doing this, but I changed the width and height forumla to work out the values for both sets of corners, and then take the largest value. This means the new surface is always large enough for the rotation. (Well, I think so. It seems to work anyway).

I've put in the ceiling values for changing from floats to ints and changed the second translation, and that all works nicely. :)

[Edit:] Something rather odd is still happening at a rotation angle of exactly 180degs, however, in that the center of the image seems like it's been eaten away in a kind of symmetrical pattern. This isn't a problem for me, since I only want a rotations of 0 to 90 and 270 to 360, just a rather strange phenomenon!

Here's the new code:


SDL_Surface *rotPlayer(int n, float angle) {

SDL_Surface *temp;
SDL_Surface *result;
SDL_Color c;
int width, height;

float oW = pl[n].img->w;
float oH = pl[n].img->h;

float a = rads(angle);
float a2 = rads(angle + 90);

float wid1 = oW*cos(a) + oH*sin(a);
float wid2 = oH*cos(a2) + oW*sin(a2);
float hgt1 = oH*cos(a) + oW*sin(a);
float hgt2 = oW*cos(a2) + oH*sin(a2);

wid1 = wid1>0 ? wid1 : -wid1;
wid2 = wid2>0 ? wid2 : -wid2;
hgt1 = hgt1>0 ? hgt1 : -hgt1;
hgt2 = hgt2>0 ? hgt2 : -hgt2;

if (wid1>wid2) {
width = ceil(wid1);
height = ceil(hgt1);
} else {
width = ceil(wid2);
height = ceil(hgt2);
}

temp = SDL_CreateRGBSurface(SDL_SWSURFACE, width,height, 32, 0,0,0,0);

int i0 = width/2;
int j0 = height/2;

for (int i = 0; i!=width; ++i) {
for (int j = 0; j!=height; ++j) {
float x = (float) (i - i0);
float y = (float) (j - j0);

float rx = x*cos(-a) - y*sin(-a);
float ry = x*sin(-a) + y*cos(-a);

rx += oW/2;
ry += oH/2;

if (rx < 0 || rx > oW || ry < 0 || ry > oH) {
c.r=0; c.g=0; c.b=0;
putPixel(temp, i,j, c);
} else {
c = bilinearSample(pl[n].img, rx,ry);
putPixel(temp, i,j, c);
}
}
}
return temp;
}




I do seem to get a few strange coloured pixels appearing along the bottom edge, but changing the condition ry > oH to ry > oH-1 seems to solve this. At the origin of rotation there is also one black pixel, but this should be easy to deal with.

Share this post


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