# Rotating sprites in homemade "2d engine"

## Recommended Posts

h3ro    161
Hallo everyone. Im working on a small project where I am trying to make a simple 2d game using a very limited API. The API only gives you the ability to create a window and return a pointer for all the pixels in the window. Then it is up to me to manipulate that pointer in order to create something useful. So far everything is good, but I am having troubles with getting my sprite rotation to work. The problem is that a screen pixel has a x,y integer value, and the rotated sprite has a x,y float value for all its pixels. Therefor roundoff errors cause some pixels to be on to of each other leaving some blank spaces where I am drawing. Does anyone know of a way to fix this? Here is a picture of the problem [url=http://imageshack.us][img=http://img257.imageshack.us/img257/7503/spritetroublecf2.jpg][/url] And here is the code
void rotatPixelList(std::vector<pixel> &list, float rotation)
{
float PI =  3.14159265;

float r = rotation * PI / 180.0;

for(int i = 0; i < list.size(); i++)
{
float x = list[i].xPos;
float y = list[i].yPos;

list[i].xPos = x * cos(r) - y * sin(r);
list[i].yPos = y * cos(r) + x * sin(r);
}
}

void movePixelList(std::vector<pixel> &list, float x, float y)
{
for(int i = 0; i < list.size(); i++)
{
list[i].xPos += x;
list[i].yPos += y;
}
}

void Visualisation::setPixel(std::vector<pixel> list)
{
for(int i = 0; i < list.size(); i++)
{
int offset = 4 * (((int)list[i].yPos * screenRec.getWidth()) + (int)list[i].xPos);

screenPnt[offset+0] = list[i].b;
screenPnt[offset+1] = list[i].g;
screenPnt[offset+2] = list[i].r;
}
}



##### Share on other sites
trippytarka    156
Instead of rotating every pixel, you should rotate the sprites corners and use scanline rendering (http://en.wikipedia.org/wiki/Scanline_rendering) to display it on the screen.

##### Share on other sites
Stonemonkey    142
Instead of calculating where the pixels of the sprite image are in the screenbuffer, I'd fill the region on the screenbuffer and look up the sprite image to find what colour each pixel should be filled with.

For sprites I'd probably go for 2 triangles with affine texture mapping and possibly bi linear filtering.

##### Share on other sites
h3ro    161
Thats sounded like a lot of work.

So basically I have to rewrite my "engine" to a scanline render? As it is now I dont have polygons at all and I am writing directly to the screen (I have a rectangle for each sprite, but the sprite is not really inside it, its just to pass position values around)

Thanks for the replies

##### Share on other sites
if dont want a scan line renderer, you could always use this approach, although i don't recommend it - scan line rendering would be much better.

rotate corners of sprite, calculate min-max along x and y directions for an axis aligned bounding box. then run through all the pixels in this box, determine whether pixel is inside the actual sprite rotated box or not, and if it is. look up what is the nearest pixel in the sprite image to this point.

##### Share on other sites
h3ro    161
Quote:
 Original post by luca-deltodescoif dont want a scan line renderer, you could always use this approach, although i don't recommend it - scan line rendering would be much better.

Its for a introduction to games programming class which im not even taking, so I think that would be a bit over my current skill level. I might look into it though, I find rendering quite interesting.

Quote:
 Original post by luca-deltodescorotate corners of sprite, calculate min-max along x and y directions for an axis aligned bounding box. then run through all the pixels in this box, determine whether pixel is inside the actual sprite rotated box or not, and if it is. look up what is the nearest pixel in the sprite image to this point.

Not sure if I fully understand what do to here.

1. Rotate the sprite using one of the corners (Does it only work using corners?)
2. Create a new boundingBox (The bounding box should not be rotated, but be aligned to the axis)
3. Not sure what do to here

##### Share on other sites
heres an image to help describe what i mean:
http://www.ngup.net/view?file=527

the black box is your rotated sprite. the red box is the axis aligned bounding box for it calculated from the min-max values of the sprite's corners. the circles are the pixels.

from here, easiest way is that for each pixel, do the opposite rotation to take you into the sprite's coordinate system - this way its very easy to check whether the pixel is within the black sprite box or not, if it is, then you also have the coordinate to use for the texture look-up.

##### Share on other sites
h3ro    161
Again, thank you.

Just let me see if I have gotten this right:

1. Rotate the texture
2. Create the bounding box
3. For each pixel(screen pixel, right?) inside the bounding box: Rotate back to get to texture space and find the right pixel in the texture.

##### Share on other sites
h3ro    161
After fooling around for a while I got it working, but the problem is that its dead slow. I currently get 8 fps (140fps with a blank screen) when I try to rotate a texture.

My problem is that I loop trough the data to many times, but im not sure how to make it faster. Assembly is not an alternative for me :P

Here is the current Code
// Create bounding box// Rotate the bounding box// Move the bounding box// Create a axis aligned bounding box// Transform the bounding box so that one of the vertex are at 0,0// For each pixel in the aliignedBox go back to the texture and see if there is a pixel there.// if it is, copy the color of itfor(int y = lowestY; y < higestY; y++){	for(int x = lowestX; x < highestX; x++)	{		// Check if the pixel is inside the texture		//---------------------------------------------		// Move the point from screen space to texture space		float newX = x - moveX;		float newY = y - moveY;		// See if the point is inside the texture		Vector3D c = Vector3D(boundingBox[1].x,boundingBox[1].y, 0);		Vector3D p = Vector3D(newX, newY, 0);					Vector3D v = p - c;		Vector3D v1 = Vector3D(boundingBox[0].x,boundingBox[0].y, 0);		Vector3D v2 = Vector3D(boundingBox[3].x,boundingBox[3].y, 0);		    		if( ( 0 <= Dot(v,v1) && Dot(v,v1) <= Dot(v1,v1) ) && 			( 0 <= Dot(v,v2) && Dot(v,v2) <= Dot(v2,v2) )    )		{			// Find the closest texture pixel			// ----------------------------------			// Rotate the point back, to find the correct pixel			float newXtemp = newX;			float newYtemp = newY;			newX = abs(newXtemp * cos(-r) - newY * sin(-r));			newY = abs(newY * cos(-r) + newXtemp * sin(-r));				// Loop through the pixelList and find the pixel with the same position value			for(int i = 0; i < curSprite.pixelList.size(); i++)			{				if( ((int)newX == (int)curSprite.pixelList[i].xPos) && ((int)newY == (int)curSprite.pixelList[i].yPos) )				{					// Copy the colors of the pixel into the screenPointer					int offset = 4 * (((int)y * screenRec.getWidth()) + (int)x);					screenPnt[offset+0] = curSprite.pixelList[i].b;					screenPnt[offset+1] = curSprite.pixelList[i].g;					screenPnt[offset+2] = curSprite.pixelList[i].r;				}				}		}	}}

Thanks,

##### Share on other sites
DeathRay2K    100
I think you're doing unnecessary work there.
All you need to do is perform the opposite of the rotation for every pixel in the bounding box, then use that coordinate to get the colour from the texture. You certainly shouldn't have to loop through the texture pixels. Unless they are in a random order, surely you can just use (y * texwidth + x)

I made a fairly decent 2D engine myself, but I quit before I finished rotation. :P

##### Share on other sites
h3ro    161
Quote:
 Original post by DeathRay2KI think you're doing unnecessary work there.All you need to do is perform the opposite of the rotation for every pixel in the bounding box, then use that coordinate to get the colour from the texture. You certainly shouldn't have to loop through the texture pixels. Unless they are in a random order, surely you can just use (y * texwidth + x)I made a fairly decent 2D engine myself, but I quit before I finished rotation. :P

Thanks for your reply. You mean using (newY * texwidth + newX)? I keep "getting vector out off range" by doing so. Cant really understand why.

Im starting to think that doing rotation with this API is not possible. With your fix I only get 6fps more, which brings me up to 14. 14fps is kind of low considering that I only are trying to draw a single 64x64 sprite.

EDIT:
Changing to release mode gave me a lot better frame rate. I think its the actually part where I copy information into the screen buffer that is the slow part, becasue it seems like the fps relates haveliy to how many pixels im drawing.

[Edited by - h3ro on February 21, 2008 9:49:25 PM]

##### Share on other sites
ViperG    206
Quote:
Original post by h3ro
Quote:
 Original post by DeathRay2KI think you're doing unnecessary work there.All you need to do is perform the opposite of the rotation for every pixel in the bounding box, then use that coordinate to get the colour from the texture. You certainly shouldn't have to loop through the texture pixels. Unless they are in a random order, surely you can just use (y * texwidth + x)I made a fairly decent 2D engine myself, but I quit before I finished rotation. :P

Thanks for your reply. You mean using (newY * texwidth + newX)? I keep "getting vector out off range" by doing so. Cant really understand why.

Im starting to think that doing rotation with this API is not possible. With your fix I only get 6fps more, which brings me up to 14. 14fps is kind of low considering that I only are trying to draw a single 64x64 sprite.

EDIT:
Changing to release mode gave me a lot better frame rate. I think its the actually part where I copy information into the screen buffer that is the slow part, becasue it seems like the fps relates haveliy to how many pixels im drawing.

You would be suprised this is usually the bottleneck in most games, hence running the lowest resolution provides the best increase in performance.

When your in software mode, most games use pre-made sprites and specific angles and use that, like in starcraft and other such games, because rotating and updating per pixel manually is slow and takes too much time.

It's always better to use opengl/directx to do rotations for full 360 degree's.

##### Share on other sites
h3ro    161
Quote:
 Original post by ViperGYou would be suprised this is usually the bottleneck in most games, hence running the lowest resolution provides the best increase in performance.When your in software mode, most games use pre-made sprites and specific angles and use that, like in starcraft and other such games, because rotating and updating per pixel manually is slow and takes too much time. It's always better to use opengl/directx to do rotations for full 360 degree's.

Do you know how many rotation angles each sprite normally have?
How would it be saved? One sprite sheet for each rotation or one massive?

I think I will have the character that is controlled by the player fully rotatable and use pre made rotation for enemies.

##### Share on other sites
DeathRay2K    100
Well, developers don't usually save all possible rotations (That'd be impossible) if they do it that way, they generally save 8 to 32 different angles, and just use the closest one to the actual angle.

##### Share on other sites
Fingers_    410
A couple of years ago, I released the source code for Strange Adventures in Infinite Space (click on "files" on the website). You should find the file "sprites.cpp" and function "ik_drsprite" educational. Moving as much code out of the horizontal scanline loop as possible is the key. That code is executed per-pixel, so all you want to do there is a couple of additions. All real math is outside.

Even though the rotated sprite functions aren't as optimized as I could make them now (you can get rid of the branching) the game ran OK on ~300MHz Pentium2 boxes at 640x480. Assuming you want a resolution about four times higher, CPU's 1.2GHz and above should be enough.

Saving multiple frames for rotations is unnecessary unless you're doing it for an isometric perspective like in an RTS game.

## Create an account

Register a new account