Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Bitmap Draw Stacking

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Bitmap Draw Stacking
InfiniteLoop
Member #8,434
March 2007

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
Member #261
April 2000
avatar

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

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

InfiniteLoop
Member #8,434
March 2007

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
Member #261
April 2000
avatar

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
Major Reynaldo
May 2007
avatar

InfiniteLoop
Member #8,434
March 2007

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
Member #261
April 2000
avatar

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

InfiniteLoop
Member #8,434
March 2007

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
Major Reynaldo
May 2007
avatar

InfiniteLoop
Member #8,434
March 2007

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
Member #7,827
October 2006
avatar

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

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

InfiniteLoop
Member #8,434
March 2007

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
Member #7,827
October 2006
avatar

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.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

InfiniteLoop
Member #8,434
March 2007

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
Major Reynaldo
May 2007
avatar

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
Member #8,434
March 2007

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

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Elias
Member #358
May 2000

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?

--
"Either help out or stop whining" - Evert

SiegeLord
Member #7,827
October 2006
avatar

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

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

SiegeLord
Member #7,827
October 2006
avatar

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   /

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

InfiniteLoop
Member #8,434
March 2007

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
Major Reynaldo
May 2007
avatar

SiegeLord
Member #7,827
October 2006
avatar

How not?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

 1   2 


Go to: