Skip to content

Commit

Permalink
Engine: backport sprite rotation code update from ags4
Browse files Browse the repository at this point in the history
* Fix Math to use double math when converting between radians and degrees
* Implement RotateSize() helper function, and use it in DynamicSprite_Rotate(), instead of a in-place code.
* Move angle to fixed-point value conversion into the Bitmap::RotateBlt(), for convenience of use.
  • Loading branch information
ivan-mogilko committed Jan 12, 2025
1 parent b754510 commit bb5b3f3
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 27 deletions.
6 changes: 5 additions & 1 deletion Common/gfx/allegrobitmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,12 +379,16 @@ void Bitmap::FlipBlt(Bitmap *src, int dst_x, int dst_y, GraphicFlip flip)

void Bitmap::RotateBlt(Bitmap *src, int dst_x, int dst_y, fixed_t angle)
{
// convert to allegro angle
fixed_t al_angle = itofix((angle * 256) / 360);
BITMAP *al_src_bmp = src->_alBitmap;
rotate_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, angle);
}

void Bitmap::RotateBlt(Bitmap *src, int dst_x, int dst_y, int pivot_x, int pivot_y, fixed_t angle)
{
{
// convert to allegro angle
fixed_t al_angle = itofix((angle * 256) / 360);
BITMAP *al_src_bmp = src->_alBitmap;
pivot_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, pivot_x, pivot_y, angle);
}
Expand Down
4 changes: 2 additions & 2 deletions Common/gfx/allegrobitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ class Bitmap
void LitBlendBlt(Bitmap *src, int dst_x, int dst_y, int light_amount);
// TODO: generic "draw transformed" function? What about mask option?
void FlipBlt(Bitmap *src, int dst_x, int dst_y, GraphicFlip flip);
void RotateBlt(Bitmap *src, int dst_x, int dst_y, fixed_t angle);
void RotateBlt(Bitmap *src, int dst_x, int dst_y, int pivot_x, int pivot_y, fixed_t angle);
void RotateBlt(Bitmap *src, int dst_x, int dst_y, int angle);
void RotateBlt(Bitmap *src, int dst_x, int dst_y, int pivot_x, int pivot_y, int angle);

//=========================================================================
// Pixel operations
Expand Down
16 changes: 16 additions & 0 deletions Common/util/geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,21 @@ Rect IntersectRects(const Rect &r1, const Rect &r2)
std::min(r1.Right, r2.Right), std::min(r1.Bottom, r2.Bottom));
}

Size RotateSize(Size sz, int degrees)
{
// 1 degree = 181 degrees in terms of x/y size, so % 180
int fixangle = degrees % 180;
// and 0..90 is the same as 180..90
if (fixangle > 90)
fixangle = 180 - fixangle;
// useAngle is now between 0 and 90 (otherwise the sin/cos stuff doesn't work)
const double rads = AGSMath::DegreesToRadians(fixangle);
const double sinv = sin(rads);
const double cosv = cos(rads);
const int width = (int)(cosv * (double)sz.Width + sinv * (double)sz.Height);
const int height = (int)(sinv * (double)sz.Width + cosv * (double)sz.Height);
return Size(width, height);
}

//} // namespace Common
//} // namespace AGS
5 changes: 5 additions & 0 deletions Common/util/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,11 @@ Rect PlaceInRect(const Rect &place, const Rect &item, const RectPlacement &place
Rect SumRects(const Rect &r1, const Rect &r2);
// Intersect two rectangles, the resolt is the rectangle bounding their intersection
Rect IntersectRects(const Rect &r1, const Rect &r2);

// Calculates the size of a rectangle necessary to accomodate the rect of original size
// if it were rotated by the given angle (in degrees)
Size RotateSize(Size sz, int degrees);

//} // namespace Common
//} // namespace AGS

Expand Down
16 changes: 12 additions & 4 deletions Common/util/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,22 @@ namespace Math
static_cast<T>(val) : def;
}

inline float RadiansToDegrees(float rads)
inline double RadiansToDegrees(const double rads)
{
return rads * (float)(180.0 / M_PI);
return rads * (180.0 / M_PI);
}

inline float DegreesToRadians(float deg)
inline double DegreesToRadians(const double deg)
{
return deg * (float)(M_PI / 180.0);
return deg * (M_PI / 180.0);
}

// Wraps the angle in degrees into [0;360) range
inline double ClampAngle360(const double degrees)
{
if (degrees >= 0.0)
return std::fmod(degrees, 360.0);
return std::fmod(360.0 + degrees, 360.0);
}
} // namespace Math

Expand Down
23 changes: 6 additions & 17 deletions Engine/ac/dynamicsprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,36 +209,25 @@ void DynamicSprite_Rotate(ScriptDynamicSprite *sds, int angle, int width, int he
if (sds->slot == 0)
quit("!DynamicSprite.Rotate: sprite has been deleted");

const int src_width = game.SpriteInfos[sds->slot].Width;
const int src_height = game.SpriteInfos[sds->slot].Height;
if ((width == SCR_NO_VALUE) || (height == SCR_NO_VALUE)) {
// calculate the new image size automatically
// 1 degree = 181 degrees in terms of x/y size, so % 180
int useAngle = angle % 180;
// and 0..90 is the same as 180..90
if (useAngle > 90)
useAngle = 180 - useAngle;
// useAngle is now between 0 and 90 (otherwise the sin/cos stuff doesn't work)
double angleInRadians = (double)useAngle * (M_PI / 180.0);
double sinVal = sin(angleInRadians);
double cosVal = cos(angleInRadians);

width = (cosVal * (double)game.SpriteInfos[sds->slot].Width + sinVal * (double)game.SpriteInfos[sds->slot].Height);
height = (sinVal * (double)game.SpriteInfos[sds->slot].Width + cosVal * (double)game.SpriteInfos[sds->slot].Height);
Size rot_sz = RotateSize(Size(src_width, src_height), angle);
width = rot_sz.Width;
height = rot_sz.Height;
}
else {
data_to_game_coords(&width, &height);
}

// convert to allegro angle
angle = (angle * 256) / 360;

// resize the sprite to the requested size
Bitmap *sprite = spriteset[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateTransparentBitmap(width, height, sprite->GetColorDepth()));

// rotate the sprite about its centre
// (+ width%2 fixes one pixel offset problem)
new_pic->RotateBlt(sprite, width / 2 + width % 2, height / 2,
sprite->GetWidth() / 2, sprite->GetHeight() / 2, itofix(angle));
src_width / 2, src_height / 2, angle);

// replace the bitmap in the sprite set
add_dynamic_sprite(sds->slot, std::move(new_pic), (game.SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
Expand Down
7 changes: 4 additions & 3 deletions Engine/ac/math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
// https://opensource.org/license/artistic-2-0/
//
//=============================================================================

#include "ac/math.h"
#include <cmath>
#include "ac/common.h" // quit
#include "util/math.h"

using namespace AGS::Common;

int FloatToInt(float value, int roundDirection)
{
switch (roundDirection)
Expand Down Expand Up @@ -115,12 +116,12 @@ float Math_RaiseToPower(float base, float exp)

float Math_DegreesToRadians(float value)
{
return static_cast<float>(value * (M_PI / 180.0));
return static_cast<float>(Math::DegreesToRadians(value));
}

float Math_RadiansToDegrees(float value)
{
return static_cast<float>(value * (180.0 / M_PI));
return static_cast<float>(Math::RadiansToDegrees(value));
}

float Math_GetPi()
Expand Down

0 comments on commit bb5b3f3

Please sign in to comment.