aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile36
-rw-r--r--chfb.cpp152
-rw-r--r--chfb.h28
-rw-r--r--crop.txt94
-rw-r--r--main.cpp257
-rw-r--r--math_vector.cpp173
-rw-r--r--math_vector.h69
-rw-r--r--obj3d.cpp46
-rw-r--r--obj3d.h42
-rw-r--r--perspective.txt38
-rw-r--r--tri_vertex.cpp314
-rw-r--r--tri_vertex.h29
-rw-r--r--vertex.cpp16
-rw-r--r--vertex.h25
-rw-r--r--vertex_data.cpp12
-rw-r--r--vertex_data.h12
17 files changed, 1348 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d291118
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+# main executable
+main
+
+# object files
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..04d6739
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+EXE=main
+OBJECT_FILES=main.o math_vector.o chfb.o obj3d.o vertex_data.o vertex.o tri_vertex.o
+CC=g++
+LD=g++
+COMMON_ARGS=-Wall -Wextra
+CC_ARGS=$(COMMON_ARGS)
+LD_ARGS=$(COMMON_ARGS) -lncurses
+
+.PHONY: clean
+
+$(EXE): $(OBJECT_FILES)
+ $(LD) -o $@ $^ $(LD_ARGS)
+
+main.o: main.cpp chfb.h obj3d.h math_vector.h vertex_data.h vertex.h tri_vertex.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+math_vector.o: math_vector.cpp math_vector.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+chfb.o: chfb.cpp chfb.h math_vector.h vertex_data.h vertex.h tri_vertex.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+obj3d.o: obj3d.cpp obj3d.h math_vector.h vertex_data.h vertex.h tri_vertex.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+vertex_data.o: vertex_data.cpp vertex_data.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+vertex.o: vertex.cpp vertex.h math_vector.h vertex_data.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+tri_vertex.o: tri_vertex.cpp tri_vertex.h math_vector.h vertex_data.h vertex.h
+ $(CC) -o $@ -c $< $(CC_ARGS)
+
+clean:
+ rm -f $(EXE) $(OBJECT_FILES)
diff --git a/chfb.cpp b/chfb.cpp
new file mode 100644
index 0000000..249fb16
--- /dev/null
+++ b/chfb.cpp
@@ -0,0 +1,152 @@
+#include "chfb.h"
+#include <array>
+#include <cmath>
+#include <vector>
+#include "math_vector.h"
+#include "vertex.h"
+#include "tri_vertex.h"
+#include "vertex_data.h"
+
+CharacterFrameBuffer::CharacterFrameBuffer(unsigned int w, unsigned int h) {
+ resize(w, h);
+}
+
+void CharacterFrameBuffer::resize(unsigned int w, unsigned int h) {
+ this->w = w;
+ this->h = h;
+ chars_vector.resize(w * h);
+ depth_buf.resize(w * h);
+ clear();
+}
+
+unsigned int CharacterFrameBuffer::width() const {
+ return w;
+}
+
+unsigned int CharacterFrameBuffer::height() const {
+ return h;
+}
+
+const char* CharacterFrameBuffer::chars() const {
+ return chars_vector.data();
+}
+
+void CharacterFrameBuffer::clear() {
+ std::fill(chars_vector.begin(), chars_vector.end(), ' ');
+ std::fill(depth_buf.begin(), depth_buf.end(), 1.f);
+ face_ind = -1;
+}
+
+void CharacterFrameBuffer::_draw_cropped_triangle(TriangleVertex3 triangle) {
+ std::array<Vertex3*, 3> sorted_vs = { &triangle.vertex1, &triangle.vertex2, &triangle.vertex3 };
+
+#define SWAP_IF_LT(X,Y) ({\
+ if (sorted_vs[X]->point.y < sorted_vs[Y]->point.y) {\
+ Vertex3* temp = sorted_vs[X];\
+ sorted_vs[X] = sorted_vs[Y];\
+ sorted_vs[Y] = temp;\
+ }\
+ })
+ SWAP_IF_LT(1, 0);
+ SWAP_IF_LT(2, 1);
+ SWAP_IF_LT(1, 0);
+#undef SWAP_IF_LT
+
+ Vertex3 middle_vl = *sorted_vs[1];
+ float fac = (sorted_vs[1]->point.y - sorted_vs[0]->point.y) / (sorted_vs[2]->point.y - sorted_vs[0]->point.y);
+ Vertex3 middle_vr{
+ {
+ sorted_vs[0]->point.x + fac * (sorted_vs[2]->point.x - sorted_vs[0]->point.x),
+ sorted_vs[1]->point.y,
+ sorted_vs[0]->point.z + fac * (sorted_vs[2]->point.z - sorted_vs[0]->point.z)
+ },
+ VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac)
+ };
+ if (middle_vr.point.x < middle_vl.point.x) {
+ Vertex3 temp = middle_vr;
+ middle_vr = middle_vl;
+ middle_vl = temp;
+ }
+
+ // top triangle
+ {
+ if (sorted_vs[0]->point.y != sorted_vs[1]->point.y) {
+ int top_y = static_cast<int>(std::floor(sorted_vs[0]->point.y + 1.f));
+ int bottom_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y - 1.f));
+ for (int y = top_y; y <= bottom_y; y++) {
+ float iy = static_cast<float>(y);
+ float s = (iy - sorted_vs[0]->point.y) / (sorted_vs[1]->point.y - sorted_vs[0]->point.y);
+ float xl = sorted_vs[0]->point.x + s * (middle_vl.point.x - sorted_vs[0]->point.x);
+ float xr = sorted_vs[0]->point.x + s * (middle_vr.point.x - sorted_vs[0]->point.x);
+ int left_x = static_cast<int>(std::ceil(xl));
+ int right_x = static_cast<int>(std::ceil(xr - 1.f));
+ for (int x = left_x; x <= right_x; x++) {
+ float ix = static_cast<float>(x);
+ float t = (ix - xl) / (xr - xl);
+ // depth and vd don't take into account perspective
+ float depth = sorted_vs[0]->point.z + s * (middle_vl.point.z - sorted_vs[0]->point.z + t * (middle_vr.point.z - middle_vl.point.z));
+ // VertexData vd = VertexData::bilerp(sorted_vs[0]->data, middle_vl.data, middle_vr.data, s, t);
+ if (depth < depth_buf[x + y * w]) {
+ depth_buf[x + y * w] = depth;
+ chars_vector[x + y * w] = face_char();
+ }
+ }
+ }
+ }
+ }
+
+ // bottom triangle
+ {
+ if (sorted_vs[1]->point.y != sorted_vs[2]->point.y) {
+ int bottom_y = static_cast<int>(std::floor(sorted_vs[2]->point.y));
+ int top_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y));
+ for (int y = top_y; y <= bottom_y; y++) {
+ float iy = static_cast<float>(y);
+ float s = (iy - sorted_vs[2]->point.y) / (sorted_vs[1]->point.y - sorted_vs[2]->point.y);
+ float xl = sorted_vs[2]->point.x + s * (middle_vl.point.x - sorted_vs[2]->point.x);
+ float xr = sorted_vs[2]->point.x + s * (middle_vr.point.x - sorted_vs[2]->point.x);
+ int left_x = static_cast<int>(std::ceil(xl));
+ int right_x = static_cast<int>(std::ceil(xr - 1.f));
+ for (int x = left_x; x <= right_x; x++) {
+ float ix = static_cast<float>(x);
+ float t = (ix - xl) / (xr - xl);
+ // depth and vd don't take into account perspective
+ float depth = sorted_vs[2]->point.z + s * (middle_vl.point.z - sorted_vs[2]->point.z + t * (middle_vr.point.z - middle_vl.point.z));
+ // VertexData vd = VertexData::bilerp(sorted_vs[2]->data, middle_vl.data, middle_vr.data, s, t);
+ if (depth < depth_buf[x + y * w]) {
+ depth_buf[x + y * w] = depth;
+ chars_vector[x + y * w] = face_char();
+ }
+ }
+ }
+ }
+ }
+}
+
+void CharacterFrameBuffer::draw_triangle(TriangleVertex4 triangle) {
+ face_ind++;
+ for (auto t1 : triangle.crop_z_out(-1.f, 1.f)) {
+ for (auto t2 : t1.div_by_w().crop_xy_out(-1.f, 1.f, -1.f, 1.f)) {
+ MathVector2 pp1 = t2.vertex1.point.xy(),
+ pp2 = t2.vertex2.point.xy(),
+ pp3 = t2.vertex3.point.xy();
+ if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue;
+ t2.vertex1.point = (t2.vertex1.point + MathVector3{1.f, 1.f, 0.f}) / 2.f;
+ t2.vertex2.point = (t2.vertex2.point + MathVector3{1.f, 1.f, 0.f}) / 2.f;
+ t2.vertex3.point = (t2.vertex3.point + MathVector3{1.f, 1.f, 0.f}) / 2.f;
+ float fw = static_cast<float>(w), fh = static_cast<float>(h);
+ t2.vertex1.point.x = t2.vertex1.point.x * fw - .5f;
+ t2.vertex1.point.y = t2.vertex1.point.y * fh - .5f;
+ t2.vertex2.point.x = t2.vertex2.point.x * fw - .5f;
+ t2.vertex2.point.y = t2.vertex2.point.y * fh - .5f;
+ t2.vertex3.point.x = t2.vertex3.point.x * fw - .5f;
+ t2.vertex3.point.y = t2.vertex3.point.y * fh - .5f;
+ _draw_cropped_triangle(t2);
+ }
+ }
+}
+
+char CharacterFrameBuffer::face_char() const {
+ int n = 1 + face_ind / 2;
+ return (n < 10 ? '0' : 'A' - 10) + n;
+}
diff --git a/chfb.h b/chfb.h
new file mode 100644
index 0000000..66fd79c
--- /dev/null
+++ b/chfb.h
@@ -0,0 +1,28 @@
+#ifndef CHFB_H
+#define CHFB_H
+
+#include <vector>
+#include "math_vector.h"
+#include "tri_vertex.h"
+
+class CharacterFrameBuffer {
+ public:
+ CharacterFrameBuffer(unsigned int w, unsigned int h);
+ void resize(unsigned int w, unsigned int h);
+ unsigned int width() const;
+ unsigned int height() const;
+ const char* chars() const;
+ void clear();
+ void draw_triangle(TriangleVertex4 triangle);
+
+ private:
+ unsigned int w, h;
+ std::vector<char> chars_vector;
+ std::vector<float> depth_buf;
+ int face_ind;
+
+ void _draw_cropped_triangle(TriangleVertex3 triangle);
+ char face_char() const;
+};
+
+#endif // CHFB_H
diff --git a/crop.txt b/crop.txt
new file mode 100644
index 0000000..7e78c99
--- /dev/null
+++ b/crop.txt
@@ -0,0 +1,94 @@
+ .
+ |\
+ | \
+ | \ OUT
+ ~~|~~~\~~~~~~~~~
+ | \ IN
+ | \
+ | _-`
+ |_-`
+ `
+
+ p1
+ q1
+ .
+ |\ r3, q2, q3
+ | \ r3, r2, q2
+ r2| \r3 OUT
+ ~~|~~~\~~~~~~~~~
+ | \ IN
+ | \ p3
+ | _-` q3
+ |_-`
+ p2 `
+ q2
+
+ p2
+ q1
+ .
+ |\ r2, q3, q2
+ | \ r2, r3, q3
+ r3| \r2 OUT
+ ~~|~~~\~~~~~~~~~
+ | \ IN
+ | \ p1
+ | _-` q2
+ |_-`
+ p3 `
+ q3
+
+ p3
+ q1
+ .
+ |\ r2, q3, q2
+ | \ r2, r3, q3
+ r3| \r2 OUT
+ ~~|~~~\~~~~~~~~~
+ | \ IN
+ | \ p2
+ | _-` q2
+ |_-`
+ p1 `
+ q3
+
+ p1
+ q1
+ .
+ |\ q1, r2, r3
+ | \
+ r2| \r3 IN
+ ~~|~~~\~~~~~~~~~
+ | \ OUT
+ | \ p3
+ | _-` q3
+ |_-`
+ p2 `
+ q2
+
+ p2
+ q1
+ .
+ |\ q1, r3, r2
+ | \
+ r3| \r2 IN
+ ~~|~~~\~~~~~~~~~
+ | \ OUT
+ | \ p1
+ | _-` q2
+ |_-`
+ p3 `
+ q3
+
+ p3
+ q1
+ .
+ |\ q1, r3, r2
+ | \
+ r3| \r2 IN
+ ~~|~~~\~~~~~~~~~
+ | \ OUT
+ | \ p2
+ | _-` q2
+ |_-`
+ p1 `
+ q3
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..836418c
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,257 @@
+#include <iostream>
+#include <ncurses.h>
+#include <cmath>
+#include "chfb.h"
+#include "obj3d.h"
+#include "math_vector.h"
+#include "vertex.h"
+#include "vertex_data.h"
+#include "tri_vertex.h"
+
+#define MKEY_Z 122
+#define MKEY_Q 113
+#define MKEY_S 115
+#define MKEY_D 100
+
+#define MKEY_ESC 27
+
+#define FPS 60
+
+#define PI 3.1415926535f
+
+int main() {
+ // init
+ setlocale(LC_ALL, "");
+ initscr();
+ cbreak();
+ noecho();
+ intrflush(stdscr, FALSE);
+ keypad(stdscr, TRUE);
+ set_escdelay(0);
+ curs_set(0);
+
+ int w, h;
+ getmaxyx(stdscr, h, w);
+ CharacterFrameBuffer cfb{static_cast<unsigned int>(w), static_cast<unsigned int>(h)};
+
+ MathVector3 a{0.f, 0.f, 0.f};
+ float dist = 4.f;
+ while (1) {
+ //timeout(1000 / FPS);
+ timeout(10);
+ int c = getch();
+
+ if (c == MKEY_ESC) break;
+ switch (c) {
+ case KEY_UP:
+ // a.x += 0.1f;
+ dist += .1f;
+ break;
+ case KEY_DOWN:
+ // a.x -= 0.1f;
+ dist -= .1f;
+ break;
+ case KEY_LEFT:
+ // a.y += 0.1f;
+ break;
+ case KEY_RIGHT:
+ // a.y -= 0.1f;
+ break;
+ case MKEY_Q:
+ // a.z += 0.1f;
+ break;
+ case MKEY_D:
+ // a.z -= 0.1f;
+ break;
+ }
+
+ a.x += .0050f;
+ a.y += .0065f;
+ a.z += .0080f;
+
+ getmaxyx(stdscr, h, w);
+ cfb.resize(static_cast<unsigned int>(w), static_cast<unsigned int>(h));
+
+ float rad = 5.f;
+ MathVector3 ca{std::cos(a.x), std::cos(a.y), std::cos(a.z)};
+ MathVector3 sa{std::sin(a.x), std::sin(a.y), std::sin(a.z)};
+
+ std::array<MathVector3, 3> rot_x = {{
+ { 1.f, 0.f, 0.f },
+ { 0.f, ca.x, sa.x },
+ { 0.f, -sa.x, ca.x },
+ }};
+ std::array<MathVector3, 3> rot_y = {{
+ { ca.y, 0.f, -sa.y },
+ { 0.f, 1.f, 0.f },
+ { sa.y, 0.f, ca.y },
+ }};
+ std::array<MathVector3, 3> rot_z = {{
+ { ca.z, sa.z, 0.f },
+ { -sa.z, ca.z, 0.f },
+ { 0.f, 0.f, 1.f },
+ }};
+
+ auto [e_x, e_y, e_z] = rot_x;
+ e_x = e_x.x * rot_y[0] + e_x.y * rot_y[1] + e_x.z * rot_y[2];
+ e_y = e_y.x * rot_y[0] + e_y.y * rot_y[1] + e_y.z * rot_y[2];
+ e_z = e_z.x * rot_y[0] + e_z.y * rot_y[1] + e_z.z * rot_y[2];
+ e_x = e_x.x * rot_z[0] + e_x.y * rot_z[1] + e_x.z * rot_z[2];
+ e_y = e_y.x * rot_z[0] + e_y.y * rot_z[1] + e_y.z * rot_z[2];
+ e_z = e_z.x * rot_z[0] + e_z.y * rot_z[1] + e_z.z * rot_z[2];
+
+ std::array<Object3D, 2> objs{{
+ {
+ {
+ {
+ rad * (-e_x + -e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + -e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (-e_x + +e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + +e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (-e_x + -e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + -e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (-e_x + +e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + +e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ }
+ },
+ {
+ // face 1
+ { 0, 2, 3 },
+ { 0, 3, 1 },
+
+ // face 2
+ { 0, 4, 6 },
+ { 0, 6, 2 },
+
+ // face 3
+ { 0, 1, 5 },
+ { 0, 5, 4 },
+
+ // face 4
+ { 7, 6, 4 },
+ { 7, 4, 5 },
+
+ // face 5
+ { 7, 3, 2 },
+ { 7, 2, 6 },
+
+ // face 6
+ { 7, 5, 1 },
+ { 7, 1, 3 },
+ }
+ },
+ {
+ {
+ {
+ rad * (-e_x + -e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + -e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (-e_x + +e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + +e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (-e_x + -e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + -e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (-e_x + +e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ },
+ {
+ rad * (+e_x + +e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f},
+ {}
+ }
+ },
+ {
+ // face 1
+ { 0, 2, 3 },
+ { 0, 3, 1 },
+
+ // face 2
+ { 0, 4, 6 },
+ { 0, 6, 2 },
+
+ // face 3
+ { 0, 1, 5 },
+ { 0, 5, 4 },
+
+ // face 4
+ { 7, 6, 4 },
+ { 7, 4, 5 },
+
+ // face 5
+ { 7, 3, 2 },
+ { 7, 2, 6 },
+
+ // face 6
+ { 7, 5, 1 },
+ { 7, 1, 3 },
+ }
+ }
+ }};
+ cfb.clear();
+ float min_z = 2.f, max_z = 50.f;
+ float fac_for_aspect_ratio = 2.f * static_cast<float>(cfb.height()) / static_cast<float>(cfb.width());
+ for (auto obj : objs) {
+ for (auto triangle : obj) {
+ TriangleVertex4 t{triangle};
+
+ // should be multiplied by a matrix, temporary replacement
+ t.vertex1.point.x *= fac_for_aspect_ratio;
+ t.vertex1.point.y = -t.vertex1.point.y;
+ t.vertex1.point.w = -t.vertex1.point.z;
+ t.vertex1.point.z = 2.f * (-t.vertex1.point.z - min_z) / (max_z - min_z) - 1.f;
+ t.vertex2.point.x *= fac_for_aspect_ratio;
+ t.vertex2.point.y = -t.vertex2.point.y;
+ t.vertex2.point.w = -t.vertex2.point.z;
+ t.vertex2.point.z = 2.f * (-t.vertex2.point.z - min_z) / (max_z - min_z) - 1.f;
+ t.vertex3.point.x *= fac_for_aspect_ratio;
+ t.vertex3.point.y = -t.vertex3.point.y;
+ t.vertex3.point.w = -t.vertex3.point.z;
+ t.vertex3.point.z = 2.f * (-t.vertex3.point.z - min_z) / (max_z - min_z) - 1.f;
+
+ cfb.draw_triangle(t);
+ }
+ }
+ mvaddnstr(0, 0, cfb.chars(), cfb.width() * cfb.height());
+ }
+
+ // terminate
+ endwin();
+ return EXIT_SUCCESS;
+}
diff --git a/math_vector.cpp b/math_vector.cpp
new file mode 100644
index 0000000..60081bf
--- /dev/null
+++ b/math_vector.cpp
@@ -0,0 +1,173 @@
+#include "math_vector.h"
+#include <cmath>
+
+MathVector2::MathVector2() {
+}
+
+MathVector2::MathVector2(float x, float y) : x{x}, y{y} {
+}
+
+bool MathVector2::operator==(MathVector2 other) const {
+ return x == other.x && y == other.y;
+}
+
+bool MathVector2::operator!=(MathVector2 other) const {
+ return !(*this == other);
+}
+
+MathVector2 MathVector2::operator+() const {
+ return *this;
+}
+
+MathVector2 MathVector2::operator-() const {
+ return { -x, -y };
+}
+
+MathVector2 MathVector2::operator+(MathVector2 other) const {
+ return { x + other.x, y + other.y };
+}
+
+MathVector2 MathVector2::operator-(MathVector2 other) const {
+ return *this + (-other);
+}
+
+float MathVector2::det(MathVector2 other) const {
+ return this->x * other.y - other.x * this->y;
+}
+
+MathVector2 MathVector2::round() const {
+ return { std::round(x), std::round(y) };
+}
+
+MathVector2 operator*(float n, MathVector2 other) {
+ return { n * other.x, n * other.y };
+}
+
+MathVector2 operator*(MathVector2 other, float n) {
+ return n * other;
+}
+
+MathVector2 operator/(MathVector2 other, float n) {
+ return { other.x / n, other.y / n };
+}
+
+MathVector3::MathVector3() {
+}
+
+MathVector3::MathVector3(float x, float y, float z) : x{x}, y{y}, z{z} {
+}
+
+bool MathVector3::operator==(MathVector3 other) const {
+ return x == other.x && y == other.y && z == other.z;
+}
+
+bool MathVector3::operator!=(MathVector3 other) const {
+ return !(*this == other);
+}
+
+MathVector3 MathVector3::operator+() const {
+ return *this;
+}
+
+MathVector3 MathVector3::operator-() const {
+ return { -x, -y, -z };
+}
+
+MathVector3 MathVector3::operator+(MathVector3 other) const {
+ return { x + other.x, y + other.y, z + other.z };
+}
+
+MathVector3 MathVector3::operator-(MathVector3 other) const {
+ return *this + (-other);
+}
+
+MathVector3 MathVector3::round() const {
+ return { std::round(x), std::round(y), std::round(z) };
+}
+
+MathVector2 MathVector3::xy() const {
+ return { x, y };
+}
+
+MathVector3 MathVector3::cross(MathVector3 other) const {
+ return {
+ y * other.z - z * other.y,
+ z * other.x - x * other.z,
+ x * other.y - y * other.x
+ };
+}
+
+MathVector3 operator*(float n, MathVector3 other) {
+ return { n * other.x, n * other.y, n * other.z };
+}
+
+MathVector3 operator*(MathVector3 other, float n) {
+ return n * other;
+}
+
+MathVector3 operator/(MathVector3 other, float n) {
+ return { other.x / n, other.y / n, other.z / n };
+}
+
+MathVector4::MathVector4() {
+}
+
+MathVector4::MathVector4(float x, float y, float z, float w) : x{x}, y{y}, z{z}, w{w} {
+}
+
+MathVector4::MathVector4(float x, float y, float z) : x{x}, y{y}, z{z}, w{1.f} {
+}
+
+MathVector4::MathVector4(MathVector3 v, float w) : x{v.x}, y{v.y}, z{v.z}, w{w} {
+}
+
+MathVector4::MathVector4(MathVector3 v) : x{v.x}, y{v.y}, z{v.z}, w{1.f} {
+}
+
+bool MathVector4::operator==(MathVector4 other) const {
+ return x == other.x && y == other.y && z == other.z && w == other.w;
+}
+
+bool MathVector4::operator!=(MathVector4 other) const {
+ return !(*this == other);
+}
+
+MathVector4 MathVector4::operator+() const {
+ return *this;
+}
+
+MathVector4 MathVector4::operator-() const {
+ return { -x, -y, -z, -w };
+}
+
+MathVector4 MathVector4::operator+(MathVector4 other) const {
+ return { x + other.x, y + other.y, z + other.z, w + other.w };
+}
+
+MathVector4 MathVector4::operator-(MathVector4 other) const {
+ return *this + (-other);
+}
+
+MathVector4 MathVector4::round() const {
+ return { std::round(x), std::round(y), std::round(z), std::round(w) };
+}
+
+MathVector3 MathVector4::xyz() const {
+ return { x, y, z };
+}
+
+MathVector3 MathVector4::div_by_w() const {
+ return xyz() / w;
+}
+
+MathVector4 operator*(float n, MathVector4 other) {
+ return { n * other.x, n * other.y, n * other.z, n * other.w };
+}
+
+MathVector4 operator*(MathVector4 other, float n) {
+ return n * other;
+}
+
+MathVector4 operator/(MathVector4 other, float n) {
+ return { other.x / n, other.y / n, other.z / n, other.w / n };
+}
diff --git a/math_vector.h b/math_vector.h
new file mode 100644
index 0000000..336c3fe
--- /dev/null
+++ b/math_vector.h
@@ -0,0 +1,69 @@
+#ifndef MATH_VECTOR_H
+#define MATH_VECTOR_H
+
+class MathVector2 {
+ public:
+ float x, y;
+
+ MathVector2();
+ MathVector2(float x, float y);
+ bool operator==(MathVector2 other) const;
+ bool operator!=(MathVector2 other) const;
+ MathVector2 operator+() const;
+ MathVector2 operator-() const;
+ MathVector2 operator+(MathVector2 other) const;
+ MathVector2 operator-(MathVector2 other) const;
+ float det(MathVector2 other) const;
+ MathVector2 round() const;
+};
+
+MathVector2 operator*(float n, MathVector2 other);
+MathVector2 operator*(MathVector2 other, float n);
+MathVector2 operator/(MathVector2 other, float n);
+
+class MathVector3 {
+ public:
+ float x, y, z;
+
+ MathVector3();
+ MathVector3(float x, float y, float z);
+ bool operator==(MathVector3 other) const;
+ bool operator!=(MathVector3 other) const;
+ MathVector3 operator+() const;
+ MathVector3 operator-() const;
+ MathVector3 operator+(MathVector3 other) const;
+ MathVector3 operator-(MathVector3 other) const;
+ MathVector3 round() const;
+ MathVector2 xy() const;
+ MathVector3 cross(MathVector3 other) const;
+};
+
+MathVector3 operator*(float n, MathVector3 other);
+MathVector3 operator*(MathVector3 other, float n);
+MathVector3 operator/(MathVector3 other, float n);
+
+class MathVector4 {
+ public:
+ float x, y, z, w;
+
+ MathVector4();
+ MathVector4(float x, float y, float z, float w);
+ MathVector4(float x, float y, float z);
+ MathVector4(MathVector3 v, float w);
+ MathVector4(MathVector3 v);
+ bool operator==(MathVector4 other) const;
+ bool operator!=(MathVector4 other) const;
+ MathVector4 operator+() const;
+ MathVector4 operator-() const;
+ MathVector4 operator+(MathVector4 other) const;
+ MathVector4 operator-(MathVector4 other) const;
+ MathVector4 round() const;
+ MathVector3 xyz() const;
+ MathVector3 div_by_w() const;
+};
+
+MathVector4 operator*(float n, MathVector4 other);
+MathVector4 operator*(MathVector4 other, float n);
+MathVector4 operator/(MathVector4 other, float n);
+
+#endif // MATH_VECTOR_H
diff --git a/obj3d.cpp b/obj3d.cpp
new file mode 100644
index 0000000..3902d30
--- /dev/null
+++ b/obj3d.cpp
@@ -0,0 +1,46 @@
+#include "obj3d.h"
+#include <vector>
+#include <array>
+#include "vertex.h"
+#include "tri_vertex.h"
+
+Object3D::TriangleVertex3Iterator::TriangleVertex3Iterator(const Object3D* obj, int face_ind) : obj{obj}, face_ind{face_ind} {
+}
+
+Object3D::TriangleVertex3Iterator& Object3D::TriangleVertex3Iterator::operator++() {
+ face_ind++;
+ return *this;
+}
+
+Object3D::TriangleVertex3Iterator Object3D::TriangleVertex3Iterator::operator++(int) {
+ TriangleVertex3Iterator retval = *this;
+ ++(*this);
+ return retval;
+}
+
+bool Object3D::TriangleVertex3Iterator::operator==(TriangleVertex3Iterator other) const {
+ return obj == other.obj && face_ind == other.face_ind;
+}
+
+bool Object3D::TriangleVertex3Iterator::operator!=(TriangleVertex3Iterator other) const {
+ return !(*this == other);
+}
+
+Object3D::TriangleVertex3Iterator::reference Object3D::TriangleVertex3Iterator::operator*() const {
+ return {
+ obj->pts[obj->faces[face_ind][0]],
+ obj->pts[obj->faces[face_ind][1]],
+ obj->pts[obj->faces[face_ind][2]]
+ };
+}
+
+Object3D::Object3D(std::vector<Vertex3> pts, std::vector<std::array<int, 3>> faces) : pts{pts}, faces{faces} {
+}
+
+Object3D::TriangleVertex3Iterator Object3D::begin() {
+ return Object3D::TriangleVertex3Iterator{this};
+}
+
+Object3D::TriangleVertex3Iterator Object3D::end() {
+ return Object3D::TriangleVertex3Iterator{this, static_cast<int>(faces.size())};
+}
diff --git a/obj3d.h b/obj3d.h
new file mode 100644
index 0000000..b76f06d
--- /dev/null
+++ b/obj3d.h
@@ -0,0 +1,42 @@
+#ifndef OBJ3D_H
+#define OBJ3D_H
+
+#include <vector>
+#include <array>
+#include <iterator>
+#include "vertex.h"
+#include "tri_vertex.h"
+
+class Object3D {
+ public:
+ class TriangleVertex3Iterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = TriangleVertex3;
+ using difference_type = TriangleVertex3;
+ using pointer = const TriangleVertex3*;
+ using reference = TriangleVertex3;
+
+ explicit TriangleVertex3Iterator(const Object3D* obj, int face_ind = 0);
+ TriangleVertex3Iterator& operator++();
+ TriangleVertex3Iterator operator++(int);
+ bool operator==(TriangleVertex3Iterator other) const;
+ bool operator!=(TriangleVertex3Iterator other) const;
+ reference operator*() const;
+
+ private:
+ const Object3D* obj;
+ int face_ind;
+
+ };
+
+ Object3D(std::vector<Vertex3> pts, std::vector<std::array<int, 3>> faces);
+ TriangleVertex3Iterator begin();
+ TriangleVertex3Iterator end();
+
+ private:
+ std::vector<Vertex3> pts;
+ std::vector<std::array<int, 3>> faces;
+};
+
+#endif // OBJ3D_H
diff --git a/perspective.txt b/perspective.txt
new file mode 100644
index 0000000..a9bc097
--- /dev/null
+++ b/perspective.txt
@@ -0,0 +1,38 @@
+fx = rx / rw
+fy = ry / rw
+
+fx = f0x + u * (flx - f0x + v * (frx - flx))
+fy = f0y + u * (fly - f0y + v * (fry - fly))
+fz = f0z + u * (flz - f0z + v * (frz - flz))
+
+rx = r0x + s * (rlx - r0x + t * (rrx - rlx))
+ry = r0y + s * (rly - r0y + t * (rry - rly))
+rz = r0z + s * (rlz - r0z + t * (rrz - rlz))
+
+rx = r0x + s * rlx - s * r0x + s * t * rrx - s * t * rlx
+ = (1 - s) * r0x + s * ((1 - t) * rlx + t * rrx)
+<=> s = (rx - r0x) / ((1 - t) * rlx + t * rrx - r0x)
+
+s = (rx - r0x) / ((1 - t) * rlx + t * rrx - r0x)
+s = (ry - r0y) / ((1 - t) * rly + t * rry - r0y)
+s = (rz - r0z) / ((1 - t) * rlz + t * rrz - r0z)
+
+s = (rx - r0x) / (tlx + t * (rrx - rlx) - r0x)
+s = (ry - r0y) / ((1 - t) * tly + t * rry - r0y)
+s = (rz - r0z) / ((1 - t) * tlz + t * rrz - r0z)
+
+# ry = (1 - s) * r0y + s * ((1 - t) * rly + t * rry)
+# <=> (ry - (1 - s) * r0y) / s = (1 - t) * rly + t * rry
+# <=> t = ((ry - (1 - s) * r0y) / s - rly) / (rry - rly)
+# = ((ry - r0y + s * r0y) / s - rly) / (rry - rly)
+# = ((ry - r0y) / s + r0y - rly) / (rry - rly)
+# = (ry - r0y) / (s * (rry - rly)) + (r0y - rly) / (rry - rly)
+# = (ry - r0y) / ((rx - r0x) / ((1 - t) * tlx + t * rrx - r0x) * (rry - rly)) + (r0y - rly) / (rry - rly)
+
+ry = r0y + s * (rly - r0y + t * (rry - rly))
+ = r0y + ((rx - r0x) / ((1 - t) * tlx + t * rrx - r0x)) * (rly - r0y + t * (rry - rly))
+
+ a = (1 - k) * x + k * y
+ = x + k * (y - x)
+<=> a - x = k * (y - x)
+<=> k = (a - x) / (y - x)
diff --git a/tri_vertex.cpp b/tri_vertex.cpp
new file mode 100644
index 0000000..cd07770
--- /dev/null
+++ b/tri_vertex.cpp
@@ -0,0 +1,314 @@
+#include "tri_vertex.h"
+#include <vector>
+#include "math_vector.h"
+#include "vertex_data.h"
+#include "vertex.h"
+#include "tri_vertex.h"
+
+TriangleVertex3::TriangleVertex3(Vertex3 vertex1, Vertex3 vertex2, Vertex3 vertex3) : vertex1{vertex1}, vertex2{vertex2}, vertex3{vertex3} {
+}
+
+#define P1_OUT 1
+#define P2_OUT 2
+#define P3_OUT 4
+static void _crop_x(std::vector<TriangleVertex3>& tris, TriangleVertex3 t, int n, float x) {
+ switch (n) {
+ case 0:
+ tris.push_back(t);
+ break;
+ case P1_OUT:
+ case P2_OUT:
+ case P3_OUT:
+ case P2_OUT | P3_OUT:
+ case P1_OUT | P3_OUT:
+ case P1_OUT | P2_OUT:
+ {
+ Vertex3* q1;
+ Vertex3* q2;
+ Vertex3* q3;
+ switch (n) {
+ case P1_OUT:
+ case P2_OUT | P3_OUT:
+ q1 = &t.vertex1;
+ q2 = &t.vertex2;
+ q3 = &t.vertex3;
+ break;
+ case P2_OUT:
+ case P1_OUT | P3_OUT:
+ q1 = &t.vertex2;
+ q2 = &t.vertex1;
+ q3 = &t.vertex3;
+ break;
+ case P3_OUT:
+ case P1_OUT | P2_OUT:
+ q1 = &t.vertex3;
+ q2 = &t.vertex2;
+ q3 = &t.vertex1;
+ break;
+ }
+ MathVector3 dq2 = q1->point - q2->point;
+ float fac2 = (x - q2->point.x) / dq2.x;
+ Vertex3 r2{
+ {
+ x,
+ q2->point.y + fac2 * dq2.y,
+ q2->point.z + fac2 * dq2.z
+ },
+ VertexData::lerp(q2->data, q1->data, fac2)
+ };
+ MathVector3 dq3 = q1->point - q3->point;
+ float fac3 = (x - q3->point.x) / dq3.x;
+ Vertex3 r3{
+ {
+ x,
+ q3->point.y + fac3 * dq3.y,
+ q3->point.z + fac3 * dq3.z
+ },
+ VertexData::lerp(q3->data, q1->data, fac3)
+ };
+ switch (n) {
+ case P1_OUT:
+ tris.push_back({r3, *q2, *q3});
+ tris.push_back({r3, r2, *q2});
+ break;
+ case P2_OUT:
+ tris.push_back({r2, *q3, *q2});
+ tris.push_back({r2, r3, *q3});
+ break;
+ case P3_OUT:
+ tris.push_back({r2, *q3, *q2});
+ tris.push_back({r2, r3, *q3});
+ break;
+ case P2_OUT | P3_OUT:
+ tris.push_back({*q1, r2, r3});
+ break;
+ case P1_OUT | P3_OUT:
+ tris.push_back({*q1, r3, r2});
+ break;
+ case P1_OUT | P2_OUT:
+ tris.push_back({*q1, r3, r2});
+ break;
+ }
+ }
+ break;
+ case P1_OUT | P2_OUT | P3_OUT:
+ break;
+ }
+}
+
+static void _crop_y(std::vector<TriangleVertex3>& tris, TriangleVertex3 t, int n, float y) {
+ switch (n) {
+ case 0:
+ tris.push_back(t);
+ break;
+ case P1_OUT:
+ case P2_OUT:
+ case P3_OUT:
+ case P2_OUT | P3_OUT:
+ case P1_OUT | P3_OUT:
+ case P1_OUT | P2_OUT:
+ {
+ Vertex3* q1;
+ Vertex3* q2;
+ Vertex3* q3;
+ switch (n) {
+ case P1_OUT:
+ case P2_OUT | P3_OUT:
+ q1 = &t.vertex1;
+ q2 = &t.vertex2;
+ q3 = &t.vertex3;
+ break;
+ case P2_OUT:
+ case P1_OUT | P3_OUT:
+ q1 = &t.vertex2;
+ q2 = &t.vertex1;
+ q3 = &t.vertex3;
+ break;
+ case P3_OUT:
+ case P1_OUT | P2_OUT:
+ q1 = &t.vertex3;
+ q2 = &t.vertex2;
+ q3 = &t.vertex1;
+ break;
+ }
+ MathVector3 dq2 = q1->point - q2->point;
+ float fac2 = (y - q2->point.y) / dq2.y;
+ Vertex3 r2{
+ {
+ q2->point.x + fac2 * dq2.x,
+ y,
+ q2->point.z + fac2 * dq2.z
+ },
+ VertexData::lerp(q2->data, q1->data, fac2)
+ };
+ MathVector3 dq3 = q1->point - q3->point;
+ float fac3 = (y - q3->point.y) / dq3.y;
+ Vertex3 r3{
+ {
+ q3->point.x + fac3 * dq3.x,
+ y,
+ q3->point.z + fac3 * dq3.z
+ },
+ VertexData::lerp(q3->data, q1->data, fac3)
+ };
+ switch (n) {
+ case P1_OUT:
+ tris.push_back({r3, *q2, *q3});
+ tris.push_back({r3, r2, *q2});
+ break;
+ case P2_OUT:
+ tris.push_back({r2, *q3, *q2});
+ tris.push_back({r2, r3, *q3});
+ break;
+ case P3_OUT:
+ tris.push_back({r2, *q3, *q2});
+ tris.push_back({r2, r3, *q3});
+ break;
+ case P2_OUT | P3_OUT:
+ tris.push_back({*q1, r2, r3});
+ break;
+ case P1_OUT | P3_OUT:
+ tris.push_back({*q1, r3, r2});
+ break;
+ case P1_OUT | P2_OUT:
+ tris.push_back({*q1, r3, r2});
+ break;
+ }
+ }
+ break;
+ case P1_OUT | P2_OUT | P3_OUT:
+ break;
+ }
+}
+
+std::vector<TriangleVertex3> TriangleVertex3::crop_xy_out(float x1, float x2, float y1, float y2) const {
+ std::vector<TriangleVertex3> tris_final;
+ std::vector<TriangleVertex3> tris1;
+ _crop_x(tris1, *this, (vertex1.point.x < x1) | ((vertex2.point.x < x1) << 1) | ((vertex3.point.x < x1) << 2), x1);
+ for (auto t1 : tris1) {
+ std::vector<TriangleVertex3> tris2;
+ _crop_x(tris2, t1, (t1.vertex1.point.x > x2) | ((t1.vertex2.point.x > x2) << 1) | ((t1.vertex3.point.x > x2) << 2), x2);
+ for (auto t2 : tris2) {
+ std::vector<TriangleVertex3> tris3;
+ _crop_y(tris3, t2, (t2.vertex1.point.y < y1) | ((t2.vertex2.point.y < y1) << 1) | ((t2.vertex3.point.y < y1) << 2), y1);
+ for (auto t3 : tris3)
+ _crop_y(tris_final, t3, (t3.vertex1.point.y > y2) | ((t3.vertex2.point.y > y2) << 1) | ((t3.vertex3.point.y > y2) << 2), y2);
+ }
+ }
+ return tris_final;
+}
+#undef P1_OUT
+#undef P2_OUT
+#undef P3_OUT
+
+TriangleVertex4::TriangleVertex4(Vertex4 vertex1, Vertex4 vertex2, Vertex4 vertex3) : vertex1{vertex1}, vertex2{vertex2}, vertex3{vertex3} {
+}
+
+TriangleVertex4::TriangleVertex4(TriangleVertex3 triangle) : vertex1{triangle.vertex1}, vertex2{triangle.vertex2}, vertex3{triangle.vertex3} {
+}
+
+#define P1_OUT 1
+#define P2_OUT 2
+#define P3_OUT 4
+static void _crop_z(std::vector<TriangleVertex4>& tris, TriangleVertex4 t, int n, float z) {
+ switch (n) {
+ case 0:
+ tris.push_back(t);
+ break;
+ case P1_OUT:
+ case P2_OUT:
+ case P3_OUT:
+ case P2_OUT | P3_OUT:
+ case P1_OUT | P3_OUT:
+ case P1_OUT | P2_OUT:
+ {
+ Vertex4* q1;
+ Vertex4* q2;
+ Vertex4* q3;
+ switch (n) {
+ case P1_OUT:
+ case P2_OUT | P3_OUT:
+ q1 = &t.vertex1;
+ q2 = &t.vertex2;
+ q3 = &t.vertex3;
+ break;
+ case P2_OUT:
+ case P1_OUT | P3_OUT:
+ q1 = &t.vertex2;
+ q2 = &t.vertex1;
+ q3 = &t.vertex3;
+ break;
+ case P3_OUT:
+ case P1_OUT | P2_OUT:
+ q1 = &t.vertex3;
+ q2 = &t.vertex2;
+ q3 = &t.vertex1;
+ break;
+ }
+ MathVector4 dq2 = q1->point - q2->point;
+ float fac2 = (z - q2->point.z) / dq2.z;
+ Vertex4 r2{
+ {
+ q2->point.x + fac2 * dq2.x,
+ q2->point.y + fac2 * dq2.y,
+ z,
+ q2->point.w + fac2 * dq2.w
+ },
+ VertexData::lerp(q2->data, q1->data, fac2)
+ };
+ MathVector4 dq3 = q1->point - q3->point;
+ float fac3 = (z - q3->point.z) / dq3.z;
+ Vertex4 r3{
+ {
+ q3->point.x + fac3 * dq3.x,
+ q3->point.y + fac3 * dq3.y,
+ z,
+ q3->point.w + fac3 * dq3.w
+ },
+ VertexData::lerp(q3->data, q1->data, fac3)
+ };
+ switch (n) {
+ case P1_OUT:
+ tris.push_back({r3, *q2, *q3});
+ tris.push_back({r3, r2, *q2});
+ break;
+ case P2_OUT:
+ tris.push_back({r2, *q3, *q2});
+ tris.push_back({r2, r3, *q3});
+ break;
+ case P3_OUT:
+ tris.push_back({r2, *q3, *q2});
+ tris.push_back({r2, r3, *q3});
+ break;
+ case P2_OUT | P3_OUT:
+ tris.push_back({*q1, r2, r3});
+ break;
+ case P1_OUT | P3_OUT:
+ tris.push_back({*q1, r3, r2});
+ break;
+ case P1_OUT | P2_OUT:
+ tris.push_back({*q1, r3, r2});
+ break;
+ }
+ }
+ break;
+ case P1_OUT | P2_OUT | P3_OUT:
+ break;
+ }
+}
+
+std::vector<TriangleVertex4> TriangleVertex4::crop_z_out(float z1, float z2) const {
+ std::vector<TriangleVertex4> tris;
+ _crop_z(tris, *this, (vertex1.point.z < z1) | ((vertex2.point.z < z1) << 1) | ((vertex3.point.z < z1) << 2), z1);
+ std::vector<TriangleVertex4> tris2;
+ for (auto t : tris)
+ _crop_z(tris2, t, (t.vertex1.point.z > z2) | ((t.vertex2.point.z > z2) << 1) | ((t.vertex3.point.z > z2) << 2), z2);
+ return tris2;
+}
+#undef P1_OUT
+#undef P2_OUT
+#undef P3_OUT
+
+TriangleVertex3 TriangleVertex4::div_by_w() const {
+ return {vertex1.div_by_w(), vertex2.div_by_w(), vertex3.div_by_w()};
+}
diff --git a/tri_vertex.h b/tri_vertex.h
new file mode 100644
index 0000000..5bbdd40
--- /dev/null
+++ b/tri_vertex.h
@@ -0,0 +1,29 @@
+#ifndef TRI_VERTEX_H
+#define TRI_VERTEX_H
+
+#include "vertex.h"
+#include <vector>
+
+class TriangleVertex3 {
+ public:
+ Vertex3 vertex1;
+ Vertex3 vertex2;
+ Vertex3 vertex3;
+
+ TriangleVertex3(Vertex3 vertex1, Vertex3 vertex2, Vertex3 vertex3);
+ std::vector<TriangleVertex3> crop_xy_out(float x1, float x2, float y1, float y2) const;
+};
+
+class TriangleVertex4 {
+ public:
+ Vertex4 vertex1;
+ Vertex4 vertex2;
+ Vertex4 vertex3;
+
+ TriangleVertex4(Vertex4 vertex1, Vertex4 vertex2, Vertex4 vertex3);
+ TriangleVertex4(TriangleVertex3 triangle);
+ std::vector<TriangleVertex4> crop_z_out(float z1, float z2) const;
+ TriangleVertex3 div_by_w() const;
+};
+
+#endif // TRI_VERTEX_H
diff --git a/vertex.cpp b/vertex.cpp
new file mode 100644
index 0000000..d136754
--- /dev/null
+++ b/vertex.cpp
@@ -0,0 +1,16 @@
+#include "vertex.h"
+#include "math_vector.h"
+#include "vertex_data.h"
+
+Vertex3::Vertex3(MathVector3 point, VertexData data) : point{point}, data{data} {
+}
+
+Vertex4::Vertex4(MathVector4 point, VertexData data) : point{point}, data{data} {
+}
+
+Vertex4::Vertex4(Vertex3 vertex) : point{vertex.point}, data{vertex.data} {
+}
+
+Vertex3 Vertex4::div_by_w() const {
+ return {point.div_by_w(), data};
+}
diff --git a/vertex.h b/vertex.h
new file mode 100644
index 0000000..92b7dcb
--- /dev/null
+++ b/vertex.h
@@ -0,0 +1,25 @@
+#ifndef VERTEX_H
+#define VERTEX_H
+
+#include "math_vector.h"
+#include "vertex_data.h"
+
+class Vertex3 {
+ public:
+ MathVector3 point;
+ VertexData data;
+
+ Vertex3(MathVector3 point, VertexData data);
+};
+
+class Vertex4 {
+ public:
+ MathVector4 point;
+ VertexData data;
+
+ Vertex4(MathVector4 point, VertexData data);
+ Vertex4(Vertex3 vertex);
+ Vertex3 div_by_w() const;
+};
+
+#endif // VERTEX_H
diff --git a/vertex_data.cpp b/vertex_data.cpp
new file mode 100644
index 0000000..23c8b03
--- /dev/null
+++ b/vertex_data.cpp
@@ -0,0 +1,12 @@
+#include "vertex_data.h"
+
+VertexData VertexData::lerp(VertexData& vd1, VertexData& vd2, float s) {
+ return {};
+}
+
+VertexData VertexData::bilerp(VertexData& vd1, VertexData& vd2, VertexData& vd3, float s, float t) {
+ return {};
+}
+
+VertexData::VertexData() {
+}
diff --git a/vertex_data.h b/vertex_data.h
new file mode 100644
index 0000000..e35d65a
--- /dev/null
+++ b/vertex_data.h
@@ -0,0 +1,12 @@
+#ifndef VERTEX_DATA_H
+#define VERTEX_DATA_H
+
+class VertexData {
+ public:
+ static VertexData lerp(VertexData& vd1, VertexData& vd2, float s);
+ static VertexData bilerp(VertexData& vd1, VertexData& vd2, VertexData& vd3, float s, float t);
+
+ VertexData();
+};
+
+#endif // VERTEX_DATA_H