diff --git a/src/app.cpp b/src/app.cpp index da68615..dcdf3c2 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5,6 +5,8 @@ #include "tiles/dirt.h" #include "tiles/grass.h" #include "util/vector2.h" +#include "util/vector3.h" +#include "util/matrix3x3.h" #include diff --git a/src/util/matrix3x3.h b/src/util/matrix3x3.h new file mode 100644 index 0000000..4da3f5c --- /dev/null +++ b/src/util/matrix3x3.h @@ -0,0 +1,147 @@ +#pragma once + +#include "vector3.h" + +#include + +template +class Matrix3x3 { + public: + Matrix3x3() = default; + Matrix3x3(T s) : m_rows{ + {s, static_cast(0), static_cast(0)}, + {static_cast(0), s, static_cast(0)}, + {static_cast(0), static_cast(0), s} + } {} + Matrix3x3(Vector3 r1, Vector3 r2, Vector3 r3) : m_rows{r1, r2, r3} {} + Matrix3x3(const Matrix3x3 &other) = default; + + Matrix3x3 operator-() const { return Matrix3x3(-m_rows[0], -m_rows[1], -m_rows[2]); } + + Matrix3x3 &operator=(const Matrix3x3 &other) { + m_rows[0] = other.m_rows[0]; + m_rows[1] = other.m_rows[1]; + m_rows[2] = other.m_rows[2]; + return *this; + } + + Matrix3x3 operator+(const Matrix3x3 &other) const { + return Matrix3x3( + m_rows[0] + other.m_rows[0], + m_rows[1] + other.m_rows[1], + m_rows[2] + other.m_rows[2] + ); + } + Matrix3x3 &operator+=(const Matrix3x3 &other) { + m_rows[0] += other.m_rows[0]; + m_rows[1] += other.m_rows[1]; + m_rows[2] += other.m_rows[2]; + return *this; + } + + Matrix3x3 operator-(const Matrix3x3 &other) const { + return Matrix3x3( + m_rows[0] - other.m_rows[0], + m_rows[1] - other.m_rows[1], + m_rows[2] - other.m_rows[2] + ); + } + Matrix3x3 &operator-=(const Matrix3x3 &other) { + m_rows[0] -= other.m_rows[0]; + m_rows[1] -= other.m_rows[1]; + m_rows[2] -= other.m_rows[2]; + return *this; + } + + Matrix3x3 &operator*=(const Matrix3x3 &other) { + return (*this = *this * other); + } + Matrix3x3 operator*(const T &s) const { + return Matrix3x3( + m_rows[0] * s, + m_rows[1] * s, + m_rows[2] * s + ); + } + Matrix3x3 &operator*=(const T &s) { + return (*this = *this * s); + } + + /* + Matrix3x3 operator/(const Matrix3x3 &other) const { return Matrix3x3(m_x / other.m_x, m_y / other.m_y, m_z / other.m_z); } + Matrix3x3 &operator/=(const Matrix3x3 &other) { m_x /= other.m_x; m_y /= other.m_y; m_z /= other.m_z; return *this; } + Matrix3x3 operator/(const T &s) const { return Matrix3x3(m_x / s, m_y / s, m_z / s); } + Matrix3x3 &operator/=(const T &s) { m_x /= s; m_y /= s; m_z /= s; return *this; } + */ + + Vector3 &operator[](const int idx) { return m_rows[idx]; } + const Vector3 &operator[](const int idx) const { return m_rows[idx]; } + + T &operator()(const int x, const int y) { return m_rows[y][x]; } + const T &operator()(const int x, const int y) const { return m_rows[y][x]; } + + bool operator==(const Matrix3x3 &other) const { + return m_rows[0] == other.m_rows[0] && + m_rows[1] == other.m_rows[1] && + m_rows[2] == other.m_rows[2]; + } + bool operator!=(const Matrix3x3 &other) const { + return m_rows[0] == other.m_rows[0] || + m_rows[1] != other.m_rows[1] || + m_rows[2] != other.m_rows[2]; + } + + friend std::ostream &operator<<(std::ostream &os, const Matrix3x3 &m) { + os << "Matrix3x3(\n\t" << m[0] << "\n\t" << m[1] << "\n\t" << m[2] << "\n)"; + return os; + } + + private: + Vector3 m_rows[3]; +}; + +template +Matrix3x3 operator*(const Matrix3x3 &m1, const Matrix3x3 &m2) { + Matrix3x3 result( + { + m1(0, 0) * m2(0, 0) + m1(1, 0) * m2(0, 1) + m1(2, 0) * m2(0, 2), + m1(0, 0) * m2(1, 0) + m1(1, 0) * m2(1, 1) + m1(2, 0) * m2(1, 2), + m1(0, 0) * m2(2, 0) + m1(1, 0) * m2(2, 1) + m1(2, 0) * m2(2, 2) + + }, + { + m1(0, 1) * m2(0, 0) + m1(1, 1) * m2(0, 1) + m1(2, 1) * m2(0, 2), + m1(0, 1) * m2(1, 0) + m1(1, 1) * m2(1, 1) + m1(2, 1) * m2(1, 2), + m1(0, 1) * m2(2, 0) + m1(1, 1) * m2(2, 1) + m1(2, 1) * m2(2, 2) + }, + { + m1(0, 2) * m2(0, 0) + m1(1, 2) * m2(0, 1) + m1(2, 2) * m2(0, 2), + m1(0, 2) * m2(1, 0) + m1(1, 2) * m2(1, 1) + m1(2, 2) * m2(1, 2), + m1(0, 2) * m2(2, 0) + m1(1, 2) * m2(2, 1) + m1(2, 2) * m2(2, 2) + } + ); + return result; +} + +template +Vector3 operator*(const Matrix3x3 &m, const Vector3 &v) { + Vector3 result({ + m(0, 0) * v[0] + m(1, 0) * v[1] + m(2, 0) * v[2], + m(0, 1) * v[0] + m(1, 1) * v[1] + m(2, 1) * v[2], + m(0, 2) * v[0] + m(1, 2) * v[1] + m(2, 2) * v[2] + }); + + return result; +} + +template +Vector3 operator*(const Vector3 &v, const Matrix3x3 &m) { + Vector3 result({ + m(0, 0) * v[0] + m(0, 1) * v[1] + m(0, 2) * v[2], + m(1, 0) * v[0] + m(1, 1) * v[1] + m(1, 2) * v[2], + m(2, 0) * v[0] + m(2, 1) * v[1] + m(2, 2) * v[2] + }); + + return result; +} + diff --git a/src/util/render.cpp b/src/util/render.cpp index 2375cea..b953954 100644 --- a/src/util/render.cpp +++ b/src/util/render.cpp @@ -1,22 +1,93 @@ #include "util/render.h" void Render::rectangle(Vector2 start, Vector2 end, Color color) { - auto origin = start + m_camera_pos; - m_pge->DrawRect(origin.x(), origin.y(), end.x() - start.x(), end.y() - start.y(), olc::Pixel(color.r(), color.g(), color.b())); + auto screen_start = m_matrix * m_view_matrix * Vector3(start, 1); + auto screen_end = m_matrix * m_view_matrix * Vector3(end, 1); + m_pge->DrawRect( + screen_start.x(), + screen_start.y(), + screen_end.x() - screen_start.x(), + screen_end.y() - screen_start.y(), + olc::Pixel(color.r(), color.g(), color.b()) + ); } void Render::rectangle_sz(Vector2 start, Vector2 size, Color color) { - auto origin = start + m_camera_pos; - m_pge->DrawRect(origin.x(), origin.y(), size.x(), size.y(), olc::Pixel(color.r(), color.g(), color.b())); + auto screen_start = m_matrix * m_view_matrix * Vector3(start, 1); + m_pge->DrawRect( + screen_start.x(), + screen_start.y(), + size.x(), + size.y(), + olc::Pixel(color.r(), color.g(), color.b()) + ); } void Render::fill_rectangle(Vector2 start, Vector2 end, Color color) { - auto origin = start + m_camera_pos; - m_pge->FillRect(origin.x(), origin.y(), end.x() - start.x() + 1, end.y() - start.y() + 1, olc::Pixel(color.r(), color.g(), color.b())); + auto screen_start = m_matrix * m_view_matrix * Vector3(start, 1); + auto screen_end = m_matrix * m_view_matrix * Vector3(end, 1); + m_pge->FillRect( + screen_start.x(), + screen_start.y(), + screen_end.x() - screen_start.x() + 1, + screen_end.y() - screen_start.y() + 1, + olc::Pixel(color.r(), color.g(), color.b()) + ); } void Render::fill_rectangle_sz(Vector2 start, Vector2 size, Color color) { - auto origin = start + m_camera_pos; - m_pge->FillRect(origin.x(), origin.y(), size.x() + 1, size.y() + 1, olc::Pixel(color.r(), color.g(), color.b())); + auto screen_start = m_view_matrix * Vector3(start, 1); + m_pge->FillRect( + screen_start.x(), + screen_start.y(), + size.x() + 1, + size.y() + 1, + olc::Pixel(color.r(), color.g(), color.b()) + ); +} + +void Render::set_camera_pos(Vector2 camera_pos) { + m_camera_pos = camera_pos; + m_view_matrix = translation_matrix(camera_pos); +} + +Vector2 Render::camera_pos() { + return m_camera_pos; +} + +void Render::translate(Vector2 trans) { + m_matrix *= translation_matrix(trans); +} + +void Render::rotate(float theta) { + m_matrix *= rotation_matrix(theta); +} + +void Render::scale(Vector2 scale) { + m_matrix *= scale_matrix(scale); +} + +Matrix3x3 Render::translation_matrix(Vector2 trans) { + return Matrix3x3( + {1., 0., trans[0]}, + {0., 1., trans[1]}, + {0., 0., 1.} + ); +} + +Matrix3x3 Render::rotation_matrix(float theta) { + return Matrix3x3( + {cos(theta), -sin(theta), 0}, + {sin(theta), cos(theta), 0}, + {0., 0., 1.} + ); +} + +Matrix3x3 Render::scale_matrix(Vector2 scale) { + return Matrix3x3( + {scale[0], 0., 0.}, + {0., scale[1], 0.}, + {0., 0., 1.} + ); } diff --git a/src/util/render.h b/src/util/render.h index 74d8ff3..0f79126 100644 --- a/src/util/render.h +++ b/src/util/render.h @@ -1,8 +1,13 @@ #pragma once +#include + #include "olcPixelGameEngine.h" #include "util/color.h" #include "util/vector2.h" +#include "util/vector3.h" +#include "util/matrix3x3.h" + class Render { public: @@ -14,11 +19,23 @@ class Render { void fill_rectangle(Vector2 start, Vector2 end, Color color = {0, 0, 0}); void fill_rectangle_sz(Vector2 start, Vector2 size, Color color = {0, 0, 0}); - void set_camera_pos(Vector2 camera_pos) { m_camera_pos = camera_pos; } - Vector2 camera_pos() { return m_camera_pos; } + void set_camera_pos(Vector2 camera_pos); + Vector2 camera_pos(); + + void translate(Vector2 trans); + void rotate(float theta); + void scale(Vector2 scale); + + private: + Matrix3x3 translation_matrix(Vector2 trans); + Matrix3x3 rotation_matrix(float theta); + Matrix3x3 scale_matrix(Vector2 scale); private: olc::PixelGameEngine *m_pge; Vector2 m_camera_pos; + Matrix3x3 m_view_matrix{1}; + Matrix3x3 m_matrix{1}; + std::stack> m_matrix_stack; }; diff --git a/src/util/vector2.h b/src/util/vector2.h index 52c332e..9d962ce 100644 --- a/src/util/vector2.h +++ b/src/util/vector2.h @@ -6,8 +6,18 @@ template class Vector2 { public: Vector2() = default; + Vector2(T x, T y) : m_x(x), m_y(y) {} + template + Vector2(X x, Y y) : + m_x(static_cast(x)), + m_y(static_cast(y)) {} + Vector2(const Vector2 &other) = default; + template + Vector2(const Vector2 &other) : + m_x(static_cast(other[0])), + m_y(static_cast(other[1])) {}; T x() { return m_x; } T y() { return m_y; } @@ -46,6 +56,25 @@ class Vector2 { bool operator<=(const Vector2 &other) const { return m_x <= other.m_x || (m_x == other.m_x && m_y <= other.m_y); } bool operator>=(const Vector2 &other) const { return m_x >= other.m_x || (m_x == other.m_x && m_y >= other.m_y); } + T &operator[](const int idx) { + switch(idx) { + default: + case 0: + return m_x; + case 1: + return m_y; + } + } + const T &operator[](const int idx) const { + switch(idx) { + default: + case 0: + return m_x; + case 1: + return m_y; + } + } + friend std::ostream &operator<<(std::ostream &os, const Vector2 &vec) { os << "Vector2(" << vec.m_x << ", " << vec.m_y << ")"; return os; diff --git a/src/util/vector3.h b/src/util/vector3.h new file mode 100644 index 0000000..e317a51 --- /dev/null +++ b/src/util/vector3.h @@ -0,0 +1,103 @@ +#pragma once + +#include "vector2.h" + +#include + +template +class Vector3 { + public: + Vector3() = default; + + Vector3(T x, T y, T z) : m_x(x), m_y(y), m_z(z) {} + template + Vector3(X x, Y y, Z z) : + m_x(static_cast(x)), + m_y(static_cast(y)), + m_z(static_cast(z)) {} + + template + Vector3(Vector2 xy, B z) : + m_x(static_cast(xy[0])), + m_y(static_cast(xy[1])), + m_z(static_cast(z)) {} + template + Vector3(A x, Vector2 yz) : + m_x(static_cast(x)), + m_y(static_cast(yz[0])), + m_z(static_cast(yz[1])) {} + + Vector3(const Vector3 &other) = default; + template + Vector3(const Vector3 &other) : + m_x(static_cast(other[0])), + m_y(static_cast(other[1])), + m_z(static_cast(other[2])) {} + + T x() { return m_x; } + T y() { return m_y; } + T z() { return m_z; } + + void set_x(T x) { m_x = x; } + void set_y(T y) { m_y = y; } + void set_z(T z) { m_z = z; } + + void set(T x, T y, T z) { m_x = x; m_y = y; m_z = z; } + + Vector3 operator-() const { return Vector3(-m_x, -m_y, -m_z); } + + Vector3 &operator=(const Vector3 &other) { m_x = other.m_x; m_y = other.m_y; m_z = other.m_z; return *this; } + + Vector3 operator+(const Vector3 &other) const { return Vector3(m_x + other.m_x, m_y + other.m_y, m_z + other.m_z); } + Vector3 &operator+=(const Vector3 &other) { m_x += other.m_x; m_y += other.m_y; m_z += other.m_z; return *this; } + + Vector3 operator-(const Vector3 &other) const { return Vector3(m_x - other.m_x, m_y - other.m_y, m_z - other.m_z); } + Vector3 &operator-=(const Vector3 &other) { m_x -= other.m_x; m_y -= other.m_y; m_z -= other.m_z; return *this; } + + Vector3 operator*(const Vector3 &other) const { return Vector3(m_x * other.m_x, m_y * other.m_y, m_z * other.m_z); } + Vector3 &operator*=(const Vector3 &other) { m_x *= other.m_x; m_y *= other.m_y; m_z *= other.m_z; return *this; } + Vector3 operator*(const T &s) const { return Vector3(m_x * s, m_y * s, m_z * s); } + Vector3 &operator*=(const T &s) { m_x *= s; m_y *= s; m_z *= s; return *this; } + + Vector3 operator/(const Vector3 &other) const { return Vector3(m_x / other.m_x, m_y / other.m_y, m_z / other.m_z); } + Vector3 &operator/=(const Vector3 &other) { m_x /= other.m_x; m_y /= other.m_y; m_z /= other.m_z; return *this; } + Vector3 operator/(const T &s) const { return Vector3(m_x / s, m_y / s, m_z / s); } + Vector3 &operator/=(const T &s) { m_x /= s; m_y /= s; m_z /= s; return *this; } + + bool operator==(const Vector3 &other) const { return m_x == other.m_x && m_y == other.m_y && m_z == other.m_z; } + bool operator!=(const Vector3 &other) const { return m_x == other.m_x || m_y != other.m_y || m_z != other.m_z; } + + T &operator[](const int idx) { + switch(idx) { + default: + case 0: + return m_x; + case 1: + return m_y; + case 2: + return m_z; + } + } + const T &operator[](const int idx) const { + switch(idx) { + default: + case 0: + return m_x; + case 1: + return m_y; + case 2: + return m_z; + } + } + + friend std::ostream &operator<<(std::ostream &os, const Vector3 &vec) { + os << "Vector3(" << vec.m_x << ", " << vec.m_y << ", " << vec.m_z << ")"; + return os; + } + + private: + T m_x = 0; + T m_y = 0; + T m_z = 0; +}; +