libsdl-org / SDL

Simple Directmedia Layer
https://libsdl.org
zlib License
9.98k stars 1.84k forks source link

SDL_RenderTextureRotated should accept angle in different notations #11279

Open brainstream opened 3 weeks ago

brainstream commented 3 weeks ago

Currently this function only accepts angle in degrees, however most applications already have angles in radians. Also, box2d 3.0 returns rotation as a pair of sine and cosine. So SDL3 could improve performance if SDL_RenderTextureRotated also accepted a pair of sine and cosine. For this purpose, two helper functions should be added that will build such a structure from degrees and radians.

struct SDL_Rotation {
    float sine;
    float cosine;
};

SDL_Rotation SDL_GetRotationFromRadians(double radians);
SDL_Rotation SDL_GetRotationFromDegrees(double degrees);

In case of box2d 3.0 we can use pre-calculated value.

icculus commented 3 weeks ago

Want to say no here, unless @slouken feels strongly about it.

Getting degrees from radians is just:

degrees = radians * (180.0f / SDL_PI_F);
slouken commented 3 weeks ago

I think we'll pass on this.

brainstream commented 3 weeks ago

Want to say no here, unless @slouken feels strongly about it.

Getting degrees from radians is just:

degrees = radians * (180.0f / SDL_PI_F);

When I use box2d, I have a cosine and a sine. So, I need to call atan2 to get radians, and then I need to convert them to degrees. But SDL will perform the reverse operations and calculate radians from degrees, and then sine and cosine. It looks like an overhead.

AntTheAlchemist commented 3 weeks ago

@brainstream Trying to warp SDL to bend around box2d's limitations doesn't seem like a good solution. I ended up making my own angle system, which ends up passing an angle to SDL as: angle / ANGLE_DIVISOR. Angle overhead is something you can solve outside of SDL.

slouken commented 3 weeks ago

That's an interesting optimization. We do convert the angle to sin/cos in every path that handles rotation. We're not going to change the API for the 3.2.0 release, but we can consider this in the future.

expikr commented 3 weeks ago

For use cases like this where you already have the rotation basis vectors, it make more sense to just let the user specify the affine parallelogram directly.

This allows them to encompass all of flipping, mirroring, stretching, rotating and shearing.

expikr commented 2 weeks ago

Hhere's how you could use the proposed SDL_RenderTextureAffine with raw cosine and sines, adapted from examples/renderer/08-rotating-textures.c:

SDL_AppResult SDL_AppIterate(void *appstate) {
    const float winspan = SDL_min(WINDOW_WIDTH, WINDOW_HEIGHT);
    const float texspan = SDL_sqrt((float)(texture_width*texture_width + texture_height*texture_height));
    const float scale = winspan / texspan;

    SDL_FRect dst_rect;
    dst_rect.x = 0.5f * WINDOW_WIDTH;
    dst_rect.y = 0.5f * WINDOW_HEIGHT;
    dst_rect.w = 0.5f * texture_width * scale;
    dst_rect.h = 0.5f * texture_height * scale;

    const Uint64 now = SDL_GetTicks();
    const float rad = (((float) ((int) (now % 4000))) / 4000.0f) * SDL_PI_F * 2;
    const float cos = SDL_cos(rad);
    const float sin = SDL_sin(rad);
    const float xx =  cos * dst_rect.w;
    const float xy =  sin * dst_rect.w;
    const float yx = -sin * dst_rect.h;
    const float yy =  cos * dst_rect.h;

    SDL_FPoint origin, right, down;
    origin.x = dst_rect.x - xx - yx;
    origin.y = dst_rect.y - xy - yy;
    right.x  = dst_rect.x + xx - yx;
    right.y  = dst_rect.y + xy - yy;
    down.x   = dst_rect.x - xx + yx;
    down.y   = dst_rect.y - xy + yy;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);

    SDL_RenderTextureAffine(renderer, texture, NULL, &origin, &right, &down);
    SDL_RenderPresent(renderer);

    return SDL_APP_CONTINUE;
}
brainstream commented 2 weeks ago

@expikr It looks great!