Rendering with matricies

Started to use matricies to handle transformations and the camera

Added Vector3 and Matrix3x3 classes and improved Vector2
This commit is contained in:
Thomas Muller 2021-08-01 21:06:48 -04:00
parent 30e4f41138
commit 0913c7b05e
Signed by: thomas
GPG key ID: AF006EB730564952
6 changed files with 379 additions and 10 deletions

View file

@ -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 <iostream>

147
src/util/matrix3x3.h Normal file
View file

@ -0,0 +1,147 @@
#pragma once
#include "vector3.h"
#include <ostream>
template <typename T>
class Matrix3x3 {
public:
Matrix3x3() = default;
Matrix3x3(T s) : m_rows{
{s, static_cast<T>(0), static_cast<T>(0)},
{static_cast<T>(0), s, static_cast<T>(0)},
{static_cast<T>(0), static_cast<T>(0), s}
} {}
Matrix3x3(Vector3<T> r1, Vector3<T> r2, Vector3<T> r3) : m_rows{r1, r2, r3} {}
Matrix3x3(const Matrix3x3<T> &other) = default;
Matrix3x3<T> operator-() const { return Matrix3x3<T>(-m_rows[0], -m_rows[1], -m_rows[2]); }
Matrix3x3<T> &operator=(const Matrix3x3<T> &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<T> operator+(const Matrix3x3<T> &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<T> &operator+=(const Matrix3x3<T> &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<T> operator-(const Matrix3x3<T> &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<T> &operator-=(const Matrix3x3<T> &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<T> &operator*=(const Matrix3x3<T> &other) {
return (*this = *this * other);
}
Matrix3x3<T> operator*(const T &s) const {
return Matrix3x3(
m_rows[0] * s,
m_rows[1] * s,
m_rows[2] * s
);
}
Matrix3x3<T> &operator*=(const T &s) {
return (*this = *this * s);
}
/*
Matrix3x3<T> operator/(const Matrix3x3<T> &other) const { return Matrix3x3(m_x / other.m_x, m_y / other.m_y, m_z / other.m_z); }
Matrix3x3<T> &operator/=(const Matrix3x3<T> &other) { m_x /= other.m_x; m_y /= other.m_y; m_z /= other.m_z; return *this; }
Matrix3x3<T> operator/(const T &s) const { return Matrix3x3(m_x / s, m_y / s, m_z / s); }
Matrix3x3<T> &operator/=(const T &s) { m_x /= s; m_y /= s; m_z /= s; return *this; }
*/
Vector3<T> &operator[](const int idx) { return m_rows[idx]; }
const Vector3<T> &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<T> &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<T> &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<T> &m) {
os << "Matrix3x3(\n\t" << m[0] << "\n\t" << m[1] << "\n\t" << m[2] << "\n)";
return os;
}
private:
Vector3<T> m_rows[3];
};
template <typename T>
Matrix3x3<T> operator*(const Matrix3x3<T> &m1, const Matrix3x3<T> &m2) {
Matrix3x3<T> 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 <typename T>
Vector3<T> operator*(const Matrix3x3<T> &m, const Vector3<T> &v) {
Vector3<T> 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 <typename T>
Vector3<T> operator*(const Vector3<T> &v, const Matrix3x3<T> &m) {
Vector3<T> 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;
}

View file

@ -1,22 +1,93 @@
#include "util/render.h"
void Render::rectangle(Vector2<int> start, Vector2<int> 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<float>(start, 1);
auto screen_end = m_matrix * m_view_matrix * Vector3<float>(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<int> start, Vector2<int> 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<float>(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<int> start, Vector2<int> 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<float>(start, 1);
auto screen_end = m_matrix * m_view_matrix * Vector3<float>(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<int> start, Vector2<int> 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<float>(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<int> camera_pos) {
m_camera_pos = camera_pos;
m_view_matrix = translation_matrix(camera_pos);
}
Vector2<int> Render::camera_pos() {
return m_camera_pos;
}
void Render::translate(Vector2<float> trans) {
m_matrix *= translation_matrix(trans);
}
void Render::rotate(float theta) {
m_matrix *= rotation_matrix(theta);
}
void Render::scale(Vector2<float> scale) {
m_matrix *= scale_matrix(scale);
}
Matrix3x3<float> Render::translation_matrix(Vector2<float> trans) {
return Matrix3x3<float>(
{1., 0., trans[0]},
{0., 1., trans[1]},
{0., 0., 1.}
);
}
Matrix3x3<float> Render::rotation_matrix(float theta) {
return Matrix3x3<float>(
{cos(theta), -sin(theta), 0},
{sin(theta), cos(theta), 0},
{0., 0., 1.}
);
}
Matrix3x3<float> Render::scale_matrix(Vector2<float> scale) {
return Matrix3x3<float>(
{scale[0], 0., 0.},
{0., scale[1], 0.},
{0., 0., 1.}
);
}

View file

@ -1,8 +1,13 @@
#pragma once
#include <stack>
#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<int> start, Vector2<int> end, Color color = {0, 0, 0});
void fill_rectangle_sz(Vector2<int> start, Vector2<int> size, Color color = {0, 0, 0});
void set_camera_pos(Vector2<int> camera_pos) { m_camera_pos = camera_pos; }
Vector2<int> camera_pos() { return m_camera_pos; }
void set_camera_pos(Vector2<int> camera_pos);
Vector2<int> camera_pos();
void translate(Vector2<float> trans);
void rotate(float theta);
void scale(Vector2<float> scale);
private:
Matrix3x3<float> translation_matrix(Vector2<float> trans);
Matrix3x3<float> rotation_matrix(float theta);
Matrix3x3<float> scale_matrix(Vector2<float> scale);
private:
olc::PixelGameEngine *m_pge;
Vector2<int> m_camera_pos;
Matrix3x3<float> m_view_matrix{1};
Matrix3x3<float> m_matrix{1};
std::stack<Matrix3x3<float>> m_matrix_stack;
};

View file

@ -6,8 +6,18 @@ template <typename T>
class Vector2 {
public:
Vector2() = default;
Vector2(T x, T y) : m_x(x), m_y(y) {}
template <typename X, typename Y>
Vector2(X x, Y y) :
m_x(static_cast<T>(x)),
m_y(static_cast<T>(y)) {}
Vector2(const Vector2<T> &other) = default;
template <typename A>
Vector2(const Vector2<A> &other) :
m_x(static_cast<T>(other[0])),
m_y(static_cast<T>(other[1])) {};
T x() { return m_x; }
T y() { return m_y; }
@ -46,6 +56,25 @@ class Vector2 {
bool operator<=(const Vector2<T> &other) const { return m_x <= other.m_x || (m_x == other.m_x && m_y <= other.m_y); }
bool operator>=(const Vector2<T> &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<T> &vec) {
os << "Vector2(" << vec.m_x << ", " << vec.m_y << ")";
return os;

103
src/util/vector3.h Normal file
View file

@ -0,0 +1,103 @@
#pragma once
#include "vector2.h"
#include <ostream>
template <typename T>
class Vector3 {
public:
Vector3() = default;
Vector3(T x, T y, T z) : m_x(x), m_y(y), m_z(z) {}
template <typename X, typename Y, typename Z>
Vector3(X x, Y y, Z z) :
m_x(static_cast<T>(x)),
m_y(static_cast<T>(y)),
m_z(static_cast<T>(z)) {}
template <typename A, typename B>
Vector3(Vector2<A> xy, B z) :
m_x(static_cast<T>(xy[0])),
m_y(static_cast<T>(xy[1])),
m_z(static_cast<T>(z)) {}
template <typename A, typename B>
Vector3(A x, Vector2<B> yz) :
m_x(static_cast<T>(x)),
m_y(static_cast<T>(yz[0])),
m_z(static_cast<T>(yz[1])) {}
Vector3(const Vector3<T> &other) = default;
template <typename A>
Vector3(const Vector3<A> &other) :
m_x(static_cast<T>(other[0])),
m_y(static_cast<T>(other[1])),
m_z(static_cast<T>(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<T> operator-() const { return Vector3<T>(-m_x, -m_y, -m_z); }
Vector3<T> &operator=(const Vector3<T> &other) { m_x = other.m_x; m_y = other.m_y; m_z = other.m_z; return *this; }
Vector3<T> operator+(const Vector3<T> &other) const { return Vector3(m_x + other.m_x, m_y + other.m_y, m_z + other.m_z); }
Vector3<T> &operator+=(const Vector3<T> &other) { m_x += other.m_x; m_y += other.m_y; m_z += other.m_z; return *this; }
Vector3<T> operator-(const Vector3<T> &other) const { return Vector3(m_x - other.m_x, m_y - other.m_y, m_z - other.m_z); }
Vector3<T> &operator-=(const Vector3<T> &other) { m_x -= other.m_x; m_y -= other.m_y; m_z -= other.m_z; return *this; }
Vector3<T> operator*(const Vector3<T> &other) const { return Vector3(m_x * other.m_x, m_y * other.m_y, m_z * other.m_z); }
Vector3<T> &operator*=(const Vector3<T> &other) { m_x *= other.m_x; m_y *= other.m_y; m_z *= other.m_z; return *this; }
Vector3<T> operator*(const T &s) const { return Vector3(m_x * s, m_y * s, m_z * s); }
Vector3<T> &operator*=(const T &s) { m_x *= s; m_y *= s; m_z *= s; return *this; }
Vector3<T> operator/(const Vector3<T> &other) const { return Vector3(m_x / other.m_x, m_y / other.m_y, m_z / other.m_z); }
Vector3<T> &operator/=(const Vector3<T> &other) { m_x /= other.m_x; m_y /= other.m_y; m_z /= other.m_z; return *this; }
Vector3<T> operator/(const T &s) const { return Vector3(m_x / s, m_y / s, m_z / s); }
Vector3<T> &operator/=(const T &s) { m_x /= s; m_y /= s; m_z /= s; return *this; }
bool operator==(const Vector3<T> &other) const { return m_x == other.m_x && m_y == other.m_y && m_z == other.m_z; }
bool operator!=(const Vector3<T> &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<T> &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;
};