liballeg / allegro5

The official Allegro 5 git repository. Pull requests welcome!
https://liballeg.org
Other
1.9k stars 286 forks source link

The axis from al_draw is not aligned #974

Open saisilcastro opened 5 years ago

saisilcastro commented 5 years ago

I did a plugin in order to start allegro. Almost everything works perfectly but the axis. But it just happens when I zoom the image. In other case it works fine.

void place_draw_allegro_choice(CHAINED * user, B16U choice) {
    if (place && place->machine && bit_is_on(place->machine->status, FOCUS_ON) && place->map && user && bit_is_on(USER_C(user->it)->object->status, OBJECT_VISIBLE)) {
        BP32 dx = (USER_C(user->it)->object->x_route - place->map->x) + USER_C(user->it)->object->x_axis;
        BP32 dy = (USER_C(user->it)->object->y_route - place->map->y) + USER_C(user->it)->object->y_axis;
        switch (choice) {
            case 0:
            {
                al_draw_rotated_bitmap(ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis, dx, dy, USER_C(user->it)->object->angle * ((M_PI + M_PI) / 360), USER_C(user->it)->object->flip);
            }
                break;
            case 1:
            {
                al_draw_scaled_rotated_bitmap(ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis, dx, dy, USER_C(user->it)->object->zw, USER_C(user->it)->object->zh, USER_C(user->it)->object->angle * ((M_PI + M_PI) / 360), USER_C(user->it)->object->flip);
            }
                break;
            case 2:
            {
                al_draw_tinted_rotated_bitmap(ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory, al_map_rgba(USER_C(user->it)->object->of->color->r, USER_C(user->it)->object->of->color->g, USER_C(user->it)->object->of->color->b, USER_C(user->it)->object->of->color->a), USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis, dx, dy, USER_C(user->it)->object->angle * ((M_PI + M_PI) / 360), USER_C(user->it)->object->flip);
            }
                break;
            case 3:
            {
                al_draw_tinted_scaled_rotated_bitmap_region(ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->left, USER_C(user->it)->object->top, USER_C(user->it)->object->width, USER_C(user->it)->object->height, al_map_rgba(USER_C(user->it)->object->of->color->r, USER_C(user->it)->object->of->color->g, USER_C(user->it)->object->of->color->b, USER_C(user->it)->object->of->color->a), USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis, dx, dy, USER_C(user->it)->object->zw, USER_C(user->it)->object->zh, USER_C(user->it)->object->angle * ((M_PI + M_PI) / 360), USER_C(user->it)->object->flip); // flag = ALLEGRO_FLIP_VERTICAL
            }
                break;
        }
    }
}

PLACE_CALL STATUS PLACE_TYPE place_user_draw(CHAINED * user) {
    if (place && place->machine && bit_is_on(place->machine->status, FOCUS_ON) && place->map && user && bit_is_on(USER_C(user->it)->object->status, OBJECT_VISIBLE)) {
        if (place->machine->what.b16 & SDL_SYS) {
            if (place->machine->plugin && SDL_C(place->machine->plugin)->pallet) {
                if (USER_C(user->it)->object->of->image && SDLIMG_C(USER_C(user->it)->object->of->image)->memory) {
                    SDL_Rect src = {(B32) USER_C(user->it)->object->left, (B32) USER_C(user->it)->object->top, (B32) USER_C(user->it)->object->width, (B32) USER_C(user->it)->object->height};
                    SDL_Rect dst = {USER_C(user->it)->object->x_route - place->map->x, USER_C(user->it)->object->y_route - place->map->y, USER_C(user->it)->object->width * USER_C(user->it)->object->zw, USER_C(user->it)->object->height * USER_C(user->it)->object->zh};
                    SDL_Point center = {USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis};
                    SDL_SetTextureAlphaMod(SDLIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->of->color->a);
                    SDL_RenderCopyEx(SDL_C(place->machine->plugin)->pallet, SDLIMG_C(USER_C(user->it)->object->of->image)->memory, &src, &dst, USER_C(user->it)->object->angle, &center, USER_C(user->it)->object->flip);
                    return (On);
                } else {
                    printf("no image\n");
                }
            }
        }
        if (place->machine->what.b16 & ALLEGRO_SYS) {
            if (place->machine->plugin) {
                if (USER_C(user->it)->object->of->image && ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory) {
                    place_draw_allegro_choice(user, 3);
                    return (On);
                }
            }
        }
    }
    return (Off);
}

This is the code I am using to draw and here is main code.

void user_start_up(PLACE * where, B8U * name, B64U id) {
    if (where) {
        where->next_front(where->set(where->object_set(id, name, 32, 112, 1, 1, 150, 50, 15, 15, where->image_select(WARRIOR_RUN_1), NULL), NULL, NULL));
    }
}

void user_align_up(PLACE * where, B64U id, STATUS left) {
    if (where) {
        BP32 screen_w = where->machine->width, screen_h = where->machine->height;
        B64U last = 0, max = 1;
        CHAINED * object[max];
        while (last < max) {
            object[last] = where->select(id + last);
            if (object[last]) {
                switch (last) {
                    case 0: // character
                    {
                        //printf("w:%.2f h:%.2f",where->object_get(object[last])->width,where->object_get(object[last])->height);
                        where->image_zoom_fit(where->image_get(object[last]), screen_w * .1, screen_h * .4, &where->object_get(object[last])->zw, &where->object_get(object[last])->zh);
                        BP32 h_s = where->height_get(object[last]);
                        //where->x_set(object[last], h_s + 30);
                        //where->y_set(object[last], h_s);
                        //printf("y: %.2f",where->y_get(object[last]));
                        where->x_axis_set(object[last], where->width_get(object[last]) * 0.5);
                        where->y_axis_set(object[last], h_s);
                    }
                        break;
                }
            }
            last++;
        }
    }
}

When I try to zoom fit it goes out of axis. Here is the repository with the complete code

https://github.com/saisilcastro/Place

Thanks

fatcerberus commented 5 years ago

Not sure what you mean by "axis" - cx/cy specify a specific point to rotate about, it's not an axis vector like glRotate (in fact in 2D there's only one axis you can rotate about--the Z axis).

https://liballeg.org/a5docs/trunk/graphics.html#al_draw_rotated_bitmap

saisilcastro commented 5 years ago

Here is the deal. I made an engine which can alternate between allegro and sdl2. With the very same code you can do the samething in both. The only thing you have to change is the flag in the initialization. In sdl2 it words properly, but in allegro it doesn't. The test was the following, I did an animation of a rpg game that I am doing, and when someone defeat the other, the object has to rotate from 0 to 90 or from 360 to 270 degrees . In order to do that I have to set the "axis", or cy to the foot of the image. Here starts the problem, the point y(y_route) changes when I try to add the height to that cy. When we look at the animation, it looks like the object's not really falling. I don't know if I made it clear. But that is it

fatcerberus commented 5 years ago

The documentation makes it very clear what these functions are meant to do:

The point at cx/cy in the bitmap will be drawn at dx/dy and the bitmap is rotated and scaled around this point.

If you expect anything other than that, I don’t know what to say other than “that’s just not how the function works”. If the behavior differs from that described in the quote above, then there is indeed a bug to fix. But I can’t figure out whether that’s the case or not just from your description of the problem...

SiegeLord commented 5 years ago

I guess it would help if you provided a description (image? gif?) of how you expect it to work. Even if Allegro's (default) way isn't what you expect, we can still give you a piece of code that'll do what you want using a few extra instructions.

saisilcastro commented 5 years ago

I am gonna do the following, I am going to record a short video showing the test with both, sdl and allegro working, so you'll can see by ourselves what I am talkin about. All right? Hold on just a short time

saisilcastro commented 5 years ago

Hey guys, I made a short video. First I've started the engine with SDL2 as you can see in command line. This is the expected behavior for the rpg. After I started with allegro5 almost everything works perfectly but the axis. I mean, when the character is falling, it does not start from the foot as in the SDL2. I don't know if it is a bug, If it is not, please, explain me how to code in my engine the right code in order to adjust that. I really like the allegro library, and wish it to work in my projects. Thanks Here is the video link: https://youtu.be/lnbqiIWCcJQ

dos1 commented 5 years ago

It's not a bug. You simply have to carefully read the docs and what @fatcerberus already told you.

dx and dy in al_draw_rotated_bitmap aren't a position of top-left corner of the image, but the position of cx and cy point. You have to fix your code to set those correctly, as right now Allegro does exactly what you tell it to do - it's just not what you want it to do :P

fatcerberus commented 5 years ago

This is neither here nor there but personally I feel that Allegro’s behavior makes more sense - as in a rotation, (cx,cy) is the only fixed point and therefore the only one you can place reliably. I can see how it’d be unintuitive to someone used to SDL’s API (among others), but from a mathematical point of view Allegro’s behavior is very elegant. :smiley:

dos1 commented 5 years ago

What I find less elegant is that al_draw_scaled_* functions sometimes take scaling factor, and sometimes take scaled dimensions... but that's another thing. I agree that rotation in Allegro makes sense ;)

fatcerberus commented 5 years ago

@saisilcastro To be concrete, you want your characters to “fall backwards” by rotating on their feet. So to solve that, you would ask “where do I need to put the foot on the screen?” and specify that as the value of dx/dy and then for cx/cy you’d give the position of the foot within the bitmap.

If you do that correctly, the only value you should ever have to change between frames is the angle of rotation.

saisilcastro commented 5 years ago

I have tried many ways to figure out on how to do that. I've tried to look at api, but there is no reference to al_draw_tinted_scaled_rotated_bitmap_region, and I don't know what to do anymore in order to solve that. This function is necessary in order for increase or decrease the health point, magic point and experience of the character, but even so I've tried to change for another and wasn't sucessfull. If you please can you give me a short example on how to do that I'd appreciate?

fatcerberus commented 5 years ago

cx,cy - center of rotation/zoom center (relative to bitmap dimensions, this is the fixed point) dx,dx - place on screen to put the aforementioned point, scaling/rotation will be centered at this location region - rectangular portion of source image to draw

If you can’t figure out what to do from there, I don’t know what else I can say to make it clearer.

dos1 commented 5 years ago

If you're looking at docs on allegro.cc, then don't - they're outdated.

https://liballeg.org/a5docs/trunk/graphics.html#al_draw_tinted_scaled_rotated_bitmap_region

SiegeLord commented 5 years ago

I actually played around with both APIs, I think the only difference is that when using Allegro you need to add cx and cy to dx and dy respectively. On my system, this leads to identical outputs.

What confuses me on your video is that it appears as if your axis of rotation is different between Allegro and SDL, which shouldn't really happen if I understand the APIs correctly. Both should be rotation around the same axis of the destination rectangle, but differ in where the axis is placed.

fatcerberus commented 5 years ago

Oh, good point - I forgot the outdated documentation is the first thing in a Google search. I wish there was something we could do about that.

saisilcastro commented 5 years ago

I actually played around with both APIs, I think the only difference is that when using Allegro you need to add cx and cy to dx and dy respectively. On my system, this leads to identical outputs.

What confuses me on your video is that it appears as if your axis of rotation is different between Allegro and SDL, which shouldn't really happen if I understand the APIs correctly. Both should be rotation around the same axis of the destination rectangle, but differ in where the axis is placed.

But the code between then is exactly the same. I followed the same steps in both, and could reach a satisfied result just in one.

saisilcastro commented 5 years ago

I saw the link @dos1 docs posted, but I can't see what happens inside the function, because there's just another call of it, as if it was made in assembly and called by some object file.

PLACE_CALL STATUS PLACE_TYPE place_user_draw(CHAINED * user) {
    if (this && this->machine && bit_is_on(this->machine->status, FOCUS_ON) && this->map && user && bit_is_on(USER_C(user->it)->object->status, OBJECT_VISIBLE)) {
        if (this->machine->what.b16 & SDL_SYS) {
            if (this->machine->plugin && SDL_C(this->machine->plugin)->pallet) {
                if (USER_C(user->it)->object->of->image && SDLIMG_C(USER_C(user->it)->object->of->image)->memory) {
                    SDL_Rect src = {(B32) USER_C(user->it)->object->left, (B32) USER_C(user->it)->object->top, (B32) USER_C(user->it)->object->width, (B32) USER_C(user->it)->object->height};
                    SDL_Rect dst = {USER_C(user->it)->object->x_route - this->map->x, USER_C(user->it)->object->y_route - this->map->y, USER_C(user->it)->object->width * USER_C(user->it)->object->zw, USER_C(user->it)->object->height * USER_C(user->it)->object->zh};
                    SDL_Point center = {USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis};
                    SDL_SetTextureAlphaMod(SDLIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->of->color->a);
                    SDL_RenderCopyEx(SDL_C(this->machine->plugin)->pallet, SDLIMG_C(USER_C(user->it)->object->of->image)->memory, &src, &dst, USER_C(user->it)->object->angle, &center, USER_C(user->it)->object->flip);
                    return (On);
                } else {
                    printf("no image\n");
                }
            }
        }
        if (this->machine->what.b16 & ALLEGRO_SYS) {
            if (this->machine->plugin) {
                if (USER_C(user->it)->object->of->image && ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory) {
                    BP32 dx = (USER_C(user->it)->object->x_route - this->map->x); // + USER_C(user->it)->object->x_axis;
                    BP32 dy = (USER_C(user->it)->object->y_route - this->map->y); // + USER_C(user->it)->object->y_axis;
                    al_draw_tinted_scaled_rotated_bitmap_region(ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->left, USER_C(user->it)->object->top, USER_C(user->it)->object->width, USER_C(user->it)->object->height, al_map_rgba(USER_C(user->it)->object->of->color->r, USER_C(user->it)->object->of->color->g, USER_C(user->it)->object->of->color->b, USER_C(user->it)->object->of->color->a), USER_C(user->it)->object->x_axis, USER_C(user->it)->object->y_axis, dx, dy, USER_C(user->it)->object->zw, USER_C(user->it)->object->zh, USER_C(user->it)->object->angle * ((M_PI + M_PI) / 360), USER_C(user->it)->object->flip); // flag = ALLEGRO_FLIP_VERTICAL
                    return (On);
                }
            }
        }
    }
    return (Off);
}

If you see this code, there is a commentary, this is what I have made in order to align the x axis and y axis. But I wasn't sucessfull.

fatcerberus commented 5 years ago

The point is that you don’t have to align the axis at all - cx/cy specifies where in the bitmap the axis is, and dx/dy is where to place that axis on the screen. It’s only necessary for you to do that alignment manually in SDL because they do it relative to the top-left, where Allegro is relative to the center of rotation.

SiegeLord commented 5 years ago

Hmm, your comments appear to be the right solution (i.e. the dx += USER_C(user->it)->object->x_axis;), I'm surprised to hear that it didn't work for you.

saisilcastro commented 5 years ago

Hmm, your comments appear to be the right solution (i.e. the dx += USER_C(user->it)->object->x_axis;), I'm surprised to hear that it didn't work for you.

@SiegeLord when I mark this, the character goes out of place. The dx does not stay in the same place.

fatcerberus commented 5 years ago

Are you sure you’re not swapping the cx/cy with dx/dy?

saisilcastro commented 5 years ago

Are you sure you’re not swapping the cx/cy with dx/dy?

I am pretty sure of it. I think I understand how the function works(only think), The x_route is the destiny of the object, the map->x is the scroll of the map, and x_axis is the place where it starts to rotate the object. Am I telling something wrong?

SiegeLord commented 5 years ago

Ok, I think I figured it out. Here's what Allegro does to draw the image (starting with the image drawn at top left corner):

  1. Scale
  2. Shift by -cx xscale, -cy yscale
  3. Rotate
  4. Shift by dx, dy

And here's what SDL does:

  1. Scale
  2. Shift by (-cx, -cy)
  3. Rotate
  4. Shift by (dx + cx, dy + cy)

So the issue is that the rotation axes are in different spots (step 1) in addition to the different shifts (step 4). So what you need to do is (for Allegro):

  dx += cx;
  dy += cy;

  cx /= xscale;
  cy /= yscale;
fatcerberus commented 5 years ago

That SDL behavior seems kind of weird. I like Allegro’s approach as it makes sense mathematically (logically: translate -> rotate -> scale -> untranslate); I can’t even begin to see where SDL’s logic comes from.

saisilcastro commented 5 years ago

Ok, I think I figured it out. Here's what Allegro does to draw the image (starting with the image drawn at top left corner):

  1. Scale
  2. Shift by -cx xscale, -cy yscale
  3. Rotate
  4. Shift by dx, dy

And here's what SDL does:

  1. Scale
  2. Shift by (-cx, -cy)
  3. Rotate
  4. Shift by (dx + cx, dy + cy)

So the issue is that the rotation axes are in different spots (step 1) in addition to the different shifts (step 4). So what you need to do is (for Allegro):

  dx += cx;
  dy += cy;

  cx /= xscale;
  cy /= yscale;

Thanks for that man. I am gonna give it a try. :)

fatcerberus commented 5 years ago

I think I see - in matrix terms, SDL does scale ->translate->rotate whereas Allegro does the translate first, which is IMO more intuitive but will indeed produce incorrect results if you assume the SDL behavior.

saisilcastro commented 5 years ago

@SiegeLord You're my hero man. That fixed my problem.

That's what I did:

if (this->machine->what.b16 & ALLEGRO_SYS) {
            if (this->machine->plugin) {
                if (USER_C(user->it)->object->of->image && ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory) {
                    BP32 dx = (USER_C(user->it)->object->x_route - this->map->x) + USER_C(user->it)->object->x_axis;
                    BP32 dy = (USER_C(user->it)->object->y_route - this->map->y) + USER_C(user->it)->object->y_axis;
                    BP32 cx = USER_C(user->it)->object->x_axis / USER_C(user->it)->object->zw;
                    BP32 cy = USER_C(user->it)->object->y_axis / USER_C(user->it)->object->zh;
                    al_draw_tinted_scaled_rotated_bitmap_region(ALLEGROIMG_C(USER_C(user->it)->object->of->image)->memory, USER_C(user->it)->object->left, USER_C(user->it)->object->top, USER_C(user->it)->object->width, USER_C(user->it)->object->height, al_map_rgba(USER_C(user->it)->object->of->color->r, USER_C(user->it)->object->of->color->g, USER_C(user->it)->object->of->color->b, USER_C(user->it)->object->of->color->a), cx, cy, dx, dy, USER_C(user->it)->object->zw, USER_C(user->it)->object->zh, USER_C(user->it)->object->angle * ((M_PI + M_PI) / 360), USER_C(user->it)->object->flip); // flag = ALLEGRO_FLIP_VERTICAL
                    return (On);
                }
            }
        }

You're fucking great bro. I don't know how happy I am after all this time trying and not finding the bloody problem. Thank you very much. And thanks for the guys that helped my with comments too. Now it looks like we're using the same engine no matter what we chose

saisilcastro commented 5 years ago

I think I see - in matrix terms, SDL does scale ->translate->rotate whereas Allegro does the translate first, which is IMO more intuitive but will indeed produce incorrect results if you assume the SDL behavior.

I find that sdl is more intuitive than allegro at this point, but in SDL you have to call many functions to reach the same result, which is in my opinion a waste of memory, but anyways. I think allegro manages the memory wisely than sdl. It seems more low level functions

saisilcastro commented 5 years ago

I must ask. How do you do in order to put the code into that blue boxe here in github? I use the insert code tag, but it looks ugly to me.

fatcerberus commented 5 years ago

SDL from what I’ve seen tries to be all things to all people, so it can sometimes feel a little bloated. Allegro is a much thinner layer that merely abstracts away the low-level platform differences without layering too many extras on top. In most cases you’re simply talking directly to the graphics backend with a modified API. It doesn’t hold your hand, which is nice.

fatcerberus commented 5 years ago

For code, use a triple-backtick with an optional lowercase name of the programming language: ```c // code here do_stuff(); ```

// code here
do_stuff();
saisilcastro commented 5 years ago

SDL from what I’ve seen tries to be all things to all people, so it can sometimes feel a little bloated. Allegro is a much thinner layer that merely abstracts away the low-level platform differences without layering too many extras on top. In most cases you’re simply talking directly to the graphics backend with a modified API. It doesn’t hold your hand, which is nice.

I think so too dude. I feel capable doing the thinks the way allegro offears me

saisilcastro commented 5 years ago

For code, use a triple-backtick with an optional lowercase name of the programming language:

// code here
do_stuff();
// code here
do_stuff();

And thanks for the tip bro.