Is it possible to draw a rotated, scaled bitmap region? I am looking for functionality similar to DirectX's transforms. Basically, I am doing something like this now
I would like to expand the capabilities, but I am coming up short
Allegro has transforms as well. See http://docs.liballeg.org. You could use the code you have with some added transformations.
Thanks guys. Edgar, that would work but I need to draw regions.
Trent, can you offer any insight on how I would use Allegro transforms to handle scaling and rotation?
They work exactly like OpenGL transforms, except the first one you specify is applied first not last.
Example
ALLEGRO_TRANSFORM t; al_copy_transform(&t, al_get_current_transform); al_scale_transform(&t, scaleX, scaleY); al_rotate_transform(&t, someAngle); // scales about Z axis
All transforms in Allegro presently are 2D.
Thanks again guys.
After performing scaling and rotating transforms, do I need to apply with al_use_transform?
Also, since this is only for a single sprite, how do I prevent the transform from applying to other sprites? How do I reset it?
Thanks
Yes, you have to use al_use_transform. You have to save the old transformation if you want to reset back to it.
OK, I am using the following code and my image is going nuts:
Scale is 1 and rotation is based on the system clock. I want to image to spin in place, instead it is flying all around the screen
EDIT: actually, I see that it is rotating about the top left corner. How do I make it rotate about the middle?
You'll have to add a few translate transforms in there.
identity
translate (to origin, center on the middle of your region)
scale
rotate
translate (back to where your sprite was)
use transform
I am sorry for my density, but I am a little lost at this point. I have no idea how to structure my transform statements. I see where you told my to translate to "origin" and the center of my "region" but I am not sure what that means. Could you help me with some sample code maybe? I tried the following, but it didn't work.
EDIT: am I assuming the camera is looking right at the center of my screen? Then what, I have to tell the camera to move where?
EDIT AGAIN: this is the code I am now using. The image rotates about the center of the screen, but in a wide arc. I would like the image to rotate in place.
Also, is the following acceptable for resetting the transform?
const ALLEGRO_TRANSFORM *current = al_get_current_transform(); //draw the sprite frame this->beginTransform(); al_draw_bitmap_region(this->image, fx, fy, this->width, this->height, this->getX(), this->getY(), 0); al_use_transform(current);
Try:
ALLEGRO_TRANSFORM T; al_identity_transform(&T); al_translate_transform(&T, -this->width / 2, -this->height / 2); al_scale_transform(&T, this->getScale(), this->getScale()); al_rotate_transform(&T, this->getRotation()); al_translate_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2); al_use_transform(&T);
Also, is the following acceptable for resetting the transform?
No, you need to copy it elsewhere:
ALLEGRO_TRANSFORM old; al_copy_transform(&old, al_get_current_transform()); this->beginTransform(); al_draw_bitmap_region(this->image, fx, fy, this->width, this->height, this->getX(), this->getY(), 0); al_use_transform(&old);
Thank you siege, it is much more tame now, but it is still rotating about some point just past the upper left hand corner of the image. What is doing the "looking"? What do I need to be "looking" at? I get rotation and scaling, but when you throw in translation I am lost.
Oh, I missed one bit. With the set of transforms I suggested you need to draw like this:
al_draw_bitmap_region(this->image, fx, fy, this->width, this->height, 0, 0, 0);
Note the 0's in the destination: the transformation takes entirely of the positioning.
My God! You sir are a gentleman and a scholar! That works brilliantly. Would it be too much to ask (and it doesn't have to be now) for you to explain why that worked? I am not sure how I would draw it at 0, 0 and have it appear where I want.
One question - are your coordinates in real space or screen space?
Nevermind - I guess Siegelord is right.
Edgar, I believe they are screen coordinates. I do everything relative to the upper left hand corner (0, 0).
Okay, well if you ever use a camera, just remember that you need to subtract the camera position from the object position to get its screen coordinates.
Why don't we have an al_draw_rotated_scaled_bitmap_region function? The function name is long, yes - and you can do the same thing with transformations. But we have all the other variants for doing it in a single function call so why not this?
Would it be too much to ask (and it doesn't have to be now) for you to explain why that worked? I am not sure how I would draw it at 0, 0 and have it appear where I want.
Well, thing is, (0, 0) is the initial pair of coordinates that you specify before they get transformed. The full explanation probably requires pictures, but I don't feel like drawing right now. Let me try to explain in words then. Let's go through the list of transformations and see how it affects the output of al_draw_bitmap(bmp, 0, 0, 0):
1. al_identity_transform(&T);
After this call al_draw_bitmap draws like usual, with the top-left corner at (0, 0).
2. al_translate_transform(&T, -this->width / 2, -this->height / 2);
After this call the al_draw_bitmap call will be offset, so that the top left corner will now be at (-this->width / 2, -this->height / 2). We do this so that the middle of the bitmap is at (0, 0). Note that this step (and this step alone) can be removed by doing al_draw_bitmap(bmp, -this->width / 2, -this->height / 2, 0).
3. al_scale_transform(&T, this->getScale(), this->getScale());
After this call the top left corner will be at (this->getScale() * -this->width / 2, this->getScale() * -this->height / 2) and the bitmap will also be drawn bigger. The critical bit here (and why we did step #2) is that the center of the bitmap doesn't move with scaling. If the center was not at (0, 0) it would be scaled, and we'd have a mess.
4. al_rotate_transform(&T, this->getRotation());
After this call the bitmap will be rotated about (0, 0). Since we took care to place the bitmap's center at (0, 0) it will be rotated around its center.
5. al_translate_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2);
After this call the bitmap's center will move to (this->getX() + this->width / 2, this->getY() + this->height / 2).
If we had no rotation and scaling, then we'd just have this:
ALLEGRO_TRANSFORM T; al_identity_transform(&T); al_translate_transform(&T, -this->width / 2, -this->height / 2); al_translate_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2); al_use_transform(&T); al_draw_bitmap(bmp, 0, 0, 0);
Translations simply add, so if you followed the steps above it should be clear to see how this code is exactly equivalent to:
al_draw_bitmap(bmp, this->getX(), this->getY(), 0);
Lastly, there is a function to simplify this process a bit:
ALLEGRO_TRANSFORM T; al_build_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2, this->getScale(), this->getScale(), this->getRotation()); al_use_transform(&T); al_draw_bitmap(bmp, -this->width / 2, -this->height / 2, 0, 0);
I don't think al_build_transform would work in this case, because it does the scaling and rotation before the translation (according to the manual).
I don't think al_build_transform would work in this case, because it does the scaling and rotation before the translation (according to the manual).
Right, except there are two sets of translations. Here's what happens:
Original set of transformations | New set of transformations Initial location: 0, 0 Initial location: -w/2, -h/2 Translate(-w/2, -h/2) ---------------------------------^ moved to here Scale \ Rotate |-------------------> combined into al_build_transform Translate /
Thank you, that helps a lot. After a nights sleep, I went back and looked at the order I was calling the functions you gave me and I figured it out (by drawing on a piece of paper). What you said has confirmed my suspicions.
Thank you all again for your help. It is truly appreciated.
Right, except there are two sets of translations. Here's what happens:
Fine, except for that's not what your code example shows.
How not?
Lastly, there is a function to simplify this process a bit:
ALLEGRO_TRANSFORM T; al_build_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2, this->getScale(), this->getScale(), this->getRotation()); al_use_transform(&T); al_draw_bitmap(bmp, -this->width / 2, -this->height / 2, 0, 0);
It's missing the first translation, that's all. So it actually wouldn't work, because al_build_transform sets the identity first.
Why don't we have an al_draw_rotated_scaled_bitmap_region function? The function name is long, yes - and you can do the same thing with transformations. But we have all the other variants for doing it in a single function call so why not this?
I think it would be good, too. It's a fairly common thing to want to do, so it's a reasonable thing to add. If it weren't for the new test cases needed I could add it tomorrow. I haven't looked at adding test cases yet so I have no idea if it's a big deal. I might try it anyway if I remember this thread... but that's not a promise.
It's missing the first translation, that's all.
No, it's not. As my diagram showed, it got transfered to the coordinates passed to al_draw_bitmap:
ALLEGRO_TRANSFORM T; al_build_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2, this->getScale(), this->getScale(), this->getRotation()); al_use_transform(&T);
Wouldn't you have to modify the final translation in al_draw_bitmap to account for the scaling performed in al_build_transform though? And what about the rotation? It's scaled, then rotated about its top left corner, which is not what you want.
final translation in al_draw_bitmap
The translation in al_draw_bitmap is applied first, and then it is transformed using the current transformation. Doing it the other way wouldn't make any sense (it would make it clunky to use the transformations for a camera system, for example).
I think it would be good, too. It's a fairly common thing to want to do, so it's a reasonable thing to add. If it weren't forithe new test cases needed I could add it tomorrow. I haven't looked at adding test cases yet so I have no idea if it's a big deal. I might try it anyway if I remember this thread... but that'snot a promise.
I can also add it, just could have been that there's a reason against.
And yeah, we probably should make unit tests mandatory for any new additions