Bitmap Draw Stacking
InfiniteLoop

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

#SelectExpand
1void Sprite::draw() 2 { 3 //calculate source frame location 4 int fx = (this->curframe % this->animcolumns) * this->width; 5 int fy = (this->curframe / this->animcolumns) * this->height; 6 7 //draw the sprite frame 8 al_draw_bitmap_region(this->image, fx, fy, this->width, this->height, this->getX(), this->getY(), 0); 9 }

I would like to expand the capabilities, but I am coming up short

Trent Gamblin

Allegro has transforms as well. See http://docs.liballeg.org. You could use the code you have with some added transformations.

Edgar Reynaldo
InfiniteLoop

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?

Trent Gamblin

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.

Edgar Reynaldo
InfiniteLoop

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

Trent Gamblin

Yes, you have to use al_use_transform. You have to save the old transformation if you want to reset back to it.

InfiniteLoop

OK, I am using the following code and my image is going nuts:

#SelectExpand
1 2void Sprite::transform() 3 { 4 ALLEGRO_TRANSFORM T; 5 al_identity_transform(&T); 6 al_scale_transform(&T, this->getScale(), this->getScale()); 7 al_rotate_transform(&T, this->getRotation()); 8 al_use_transform(&T); 9 } 10 11 void Sprite::draw() 12 { 13 //calculate source frame location 14 int fx = (this->curframe % this->animcolumns) * this->width; 15 int fy = (this->curframe / this->animcolumns) * this->height; 16 17 //draw the sprite frame 18 this->transform(); 19 al_draw_bitmap_region(this->image, fx, fy, this->width, this->height, this->getX(), this->getY(), 0); 20 }

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?

Edgar Reynaldo

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

InfiniteLoop

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.

#SelectExpand
1ALLEGRO_TRANSFORM T; 2 al_identity_transform(&T); 3 al_translate_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2); 4 al_scale_transform(&T, this->getScale(), this->getScale()); 5 al_rotate_transform(&T, this->getRotation()); 6 al_translate_transform(&T, -this->width / 2, -this->height / 2); 7 al_use_transform(&T);

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.

#SelectExpand
1ALLEGRO_TRANSFORM T; 2 al_identity_transform(&T); 3 al_translate_transform(&T, this->getX() + this->width / 2, this->getY() + this->height / 2); 4 al_scale_transform(&T, this->getScale(), this->getScale()); 5 al_rotate_transform(&T, this->getRotation()); 6 al_translate_transform(&T, g_engine->getScreenWidth() / 2, g_engine->getScreenHeight() / 2); 7 al_use_transform(&T);

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

SiegeLord

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

InfiniteLoop

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.

SiegeLord

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.

InfiniteLoop

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.

Edgar Reynaldo

One question - are your coordinates in real space or screen space?

#SelectExpand
1 2int cx = this->x + this->width/2;// for screen space coordinates 3int cy = this->y + this->height/2;// for screen space coordinates 4 5int cx = (this->x + this->width)/2 - camera_x;// for real space coordinates 6int cy = (this->y + this->height)/2 - camera_y;// for real space coordinates 7 8ALLEGRO_TRANSFORM t; 9al_identity_transform(&t); 10//al_translate_transform(&t , -cx , -cy); 11al_translate_transform(&t , -this->width/2 , -this->height/2); 12al_scale_transform(&t , scale , scale); 13al_rotate_transform(&t , angle); 14al_translate_transform(&t , cx , cy); 15al_use_transform(&t);

Nevermind - I guess Siegelord is right.

InfiniteLoop

Edgar, I believe they are screen coordinates. I do everything relative to the upper left hand corner (0, 0).

Edgar Reynaldo

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.

Elias

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?

SiegeLord

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

Edgar Reynaldo

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

SiegeLord

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   /

InfiniteLoop

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.

Edgar Reynaldo
SiegeLord said:

Right, except there are two sets of translations. Here's what happens:

Fine, except for that's not what your code example shows.

SiegeLord

How not?

Edgar Reynaldo
SiegeLord said:

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.

Trent Gamblin
Elias said:

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.

SiegeLord

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);
al_draw_bitmap(bmp, -this->width / 2, -this->height / 2, 0, 0);

Edgar Reynaldo

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.

SiegeLord

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

Elias

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

Thread #608379. Printed from Allegro.cc