parent
e6717aa94e
commit
aba108d77c
@ -0,0 +1,257 @@ |
|||||||
|
/*
|
||||||
|
OneLoneCoder.com - 2D Sprite Affine Transformations |
||||||
|
"No more 90 degree movements" - @Javidx9 |
||||||
|
|
||||||
|
|
||||||
|
Background |
||||||
|
~~~~~~~~~~ |
||||||
|
The sophistication of 2D engines is enhanced when the programmer is |
||||||
|
able to rotate and scale sprites in a convenient manner. This program |
||||||
|
shows the basics of how affine transformations accomplish this. |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 OneLoneCoder.com |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above |
||||||
|
copyright notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce |
||||||
|
the above copyright notice. This list of conditions and the following |
||||||
|
disclaimer must be reproduced in the documentation and/or other |
||||||
|
materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
Links |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Patreon: https://www.patreon.com/javidx9
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Relevant Videos |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
https://youtu.be/zxwLN2blwbQ
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2018 |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#define OLC_PGE_APPLICATION |
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#undef min |
||||||
|
#undef max |
||||||
|
|
||||||
|
|
||||||
|
class SpriteTransforms : public olc::PixelGameEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
SpriteTransforms() |
||||||
|
{ |
||||||
|
sAppName = "Sprite Transforms"; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
olc::Sprite *sprCar; |
||||||
|
|
||||||
|
struct matrix3x3 |
||||||
|
{ |
||||||
|
float m[3][3]; |
||||||
|
}; |
||||||
|
|
||||||
|
void Identity(matrix3x3 &mat) |
||||||
|
{ |
||||||
|
mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f; |
||||||
|
mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f; |
||||||
|
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void Translate(matrix3x3 &mat, float ox, float oy) |
||||||
|
{ |
||||||
|
mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = ox; |
||||||
|
mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = oy; |
||||||
|
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void Rotate(matrix3x3 &mat, float fTheta) |
||||||
|
{ |
||||||
|
mat.m[0][0] = cosf(fTheta); mat.m[1][0] = sinf(fTheta); mat.m[2][0] = 0.0f; |
||||||
|
mat.m[0][1] = -sinf(fTheta); mat.m[1][1] = cosf(fTheta); mat.m[2][1] = 0.0f; |
||||||
|
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void Scale(matrix3x3 &mat, float sx, float sy) |
||||||
|
{ |
||||||
|
mat.m[0][0] = sx; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f; |
||||||
|
mat.m[0][1] = 0.0f; mat.m[1][1] = sy; mat.m[2][1] = 0.0f; |
||||||
|
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void Shear(matrix3x3 &mat, float sx, float sy) |
||||||
|
{
|
||||||
|
mat.m[0][0] = 1.0f; mat.m[1][0] = sx; mat.m[2][0] = 0.0f; |
||||||
|
mat.m[0][1] = sy; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f; |
||||||
|
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void MatrixMultiply(matrix3x3 &matResult, matrix3x3 &matA, matrix3x3 &matB) |
||||||
|
{ |
||||||
|
for (int c = 0; c < 3; c++) |
||||||
|
{ |
||||||
|
for (int r = 0; r < 3; r++) |
||||||
|
{ |
||||||
|
matResult.m[c][r] = matA.m[0][r] * matB.m[c][0] + |
||||||
|
matA.m[1][r] * matB.m[c][1] + |
||||||
|
matA.m[2][r] * matB.m[c][2]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Forward(matrix3x3 &mat, float in_x, float in_y, float &out_x, float &out_y) |
||||||
|
{ |
||||||
|
out_x = in_x * mat.m[0][0] + in_y * mat.m[1][0] + mat.m[2][0]; |
||||||
|
out_y = in_x * mat.m[0][1] + in_y * mat.m[1][1] + mat.m[2][1]; |
||||||
|
} |
||||||
|
|
||||||
|
void Invert(matrix3x3 &matIn, matrix3x3 &matOut) |
||||||
|
{ |
||||||
|
float det = matIn.m[0][0] * (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) - |
||||||
|
matIn.m[1][0] * (matIn.m[0][1] * matIn.m[2][2] - matIn.m[2][1] * matIn.m[0][2]) + |
||||||
|
matIn.m[2][0] * (matIn.m[0][1] * matIn.m[1][2] - matIn.m[1][1] * matIn.m[0][2]); |
||||||
|
|
||||||
|
float idet = 1.0f / det; |
||||||
|
matOut.m[0][0] = (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) * idet; |
||||||
|
matOut.m[1][0] = (matIn.m[2][0] * matIn.m[1][2] - matIn.m[1][0] * matIn.m[2][2]) * idet; |
||||||
|
matOut.m[2][0] = (matIn.m[1][0] * matIn.m[2][1] - matIn.m[2][0] * matIn.m[1][1]) * idet; |
||||||
|
matOut.m[0][1] = (matIn.m[2][1] * matIn.m[0][2] - matIn.m[0][1] * matIn.m[2][2]) * idet; |
||||||
|
matOut.m[1][1] = (matIn.m[0][0] * matIn.m[2][2] - matIn.m[2][0] * matIn.m[0][2]) * idet; |
||||||
|
matOut.m[2][1] = (matIn.m[0][1] * matIn.m[2][0] - matIn.m[0][0] * matIn.m[2][1]) * idet; |
||||||
|
matOut.m[0][2] = (matIn.m[0][1] * matIn.m[1][2] - matIn.m[0][2] * matIn.m[1][1]) * idet; |
||||||
|
matOut.m[1][2] = (matIn.m[0][2] * matIn.m[1][0] - matIn.m[0][0] * matIn.m[1][2]) * idet; |
||||||
|
matOut.m[2][2] = (matIn.m[0][0] * matIn.m[1][1] - matIn.m[0][1] * matIn.m[1][0]) * idet; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
float fRotate = 0.0f; |
||||||
|
|
||||||
|
public: |
||||||
|
bool OnUserCreate() override |
||||||
|
{ |
||||||
|
sprCar = new olc::Sprite("car_top1.png"); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override |
||||||
|
{ |
||||||
|
|
||||||
|
if (GetKey(olc::Key::Z).bHeld) fRotate -= 2.0f * fElapsedTime; |
||||||
|
if (GetKey(olc::Key::X).bHeld) fRotate += 2.0f * fElapsedTime; |
||||||
|
|
||||||
|
|
||||||
|
Clear(olc::DARK_CYAN); |
||||||
|
|
||||||
|
SetPixelMode(olc::Pixel::ALPHA); |
||||||
|
//DrawSprite(0, 0, sprCar, 3);
|
||||||
|
|
||||||
|
|
||||||
|
matrix3x3 matFinal, matA, matB, matC, matFinalInv; |
||||||
|
Translate(matA, -100, -50); |
||||||
|
Rotate(matB, fRotate); |
||||||
|
MatrixMultiply(matC, matB, matA); |
||||||
|
|
||||||
|
Translate(matA, (float)ScreenWidth()/2, (float)ScreenHeight()/2); |
||||||
|
MatrixMultiply(matFinal, matA, matC); |
||||||
|
|
||||||
|
Invert(matFinal, matFinalInv); |
||||||
|
|
||||||
|
// Draws the dumb way, but leaves gaps
|
||||||
|
/*for (int x = 0; x < sprCar->width; x++)
|
||||||
|
{ |
||||||
|
for (int y = 0; y < sprCar->height; y++) |
||||||
|
{ |
||||||
|
olc::Pixel p = sprCar->GetPixel(x, y); |
||||||
|
|
||||||
|
float nx, ny; |
||||||
|
Forward(matFinal, (float)x, (float)y, nx, ny); |
||||||
|
Draw(nx, ny, p); |
||||||
|
} |
||||||
|
}*/ |
||||||
|
|
||||||
|
// Work out bounding box of sprite post-transformation
|
||||||
|
// by passing through sprite corner locations into
|
||||||
|
// transformation matrix
|
||||||
|
float ex, ey; |
||||||
|
float sx, sy; |
||||||
|
float px, py; |
||||||
|
|
||||||
|
Forward(matFinal, 0.0f, 0.0f, px, py); |
||||||
|
sx = px; sy = py; |
||||||
|
ex = px; ey = py; |
||||||
|
|
||||||
|
Forward(matFinal, (float)sprCar->width, (float)sprCar->height, px, py); |
||||||
|
sx = std::min(sx, px); sy = std::min(sy, py); |
||||||
|
ex = std::max(ex, px); ey = std::max(ey, py); |
||||||
|
|
||||||
|
Forward(matFinal, 0.0f, (float)sprCar->height, px, py); |
||||||
|
sx = std::min(sx, px); sy = std::min(sy, py); |
||||||
|
ex = std::max(ex, px); ey = std::max(ey, py); |
||||||
|
|
||||||
|
Forward(matFinal, (float)sprCar->width, 0.0f, px, py); |
||||||
|
sx = std::min(sx, px); sy = std::min(sy, py); |
||||||
|
ex = std::max(ex, px); ey = std::max(ey, py); |
||||||
|
|
||||||
|
// Use transformed corner locations in screen space to establish
|
||||||
|
// region of pixels to fill, using inverse transform to sample
|
||||||
|
// sprite at suitable locations.
|
||||||
|
for (int x = sx; x < ex; x++) |
||||||
|
{ |
||||||
|
for (int y = sy; y < ey; y++) |
||||||
|
{ |
||||||
|
float nx, ny; |
||||||
|
Forward(matFinalInv, (float)x, (float)y, nx, ny); |
||||||
|
olc::Pixel p = sprCar->GetPixel((int32_t)(nx + 0.5f), (int32_t)(ny + 0.5f)); |
||||||
|
Draw(x, y, p); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
SetPixelMode(olc::Pixel::NORMAL); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
SpriteTransforms demo; |
||||||
|
if (demo.Construct(256, 240, 4, 4)) |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
After Width: | Height: | Size: 16 KiB |
Loading…
Reference in new issue