Added Sprite Transforms video
This commit is contained in:
parent
e6717aa94e
commit
aba108d77c
257
OneLoneCoder_PGE_SpriteTransforms.cpp
Normal file
257
OneLoneCoder_PGE_SpriteTransforms.cpp
Normal file
@ -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;
|
||||
}
|
||||
BIN
car_top1.png
Normal file
BIN
car_top1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Loading…
x
Reference in New Issue
Block a user