From c99407a474d774bb71602d3d8e38496aae9207c4 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 2 Aug 2023 19:59:06 +0900 Subject: [PATCH] Impl 3d_helpers, unit tests, basic 3D demo 3d_helpers facilitate usage of transformation matrices. Unit tests ensure the 3d_helpers are correct. Currently, the program rotates a Cube mesh in x, y, and z axes. --- Makefile | 17 +- src/3d_helpers.cc | 128 +++++++++++++ src/3d_helpers.h | 17 ++ src/game.cc | 6 +- src/main.cc | 11 +- src/screen_trunner.cc | 119 ++++++++++++ src/screen_trunner.h | 53 ++++++ src/test/test.cc | 416 ++++++++++++++++++++++++++++++++++++++++++ wasm_build/Makefile | 8 +- 9 files changed, 765 insertions(+), 10 deletions(-) create mode 100644 src/3d_helpers.cc create mode 100644 src/3d_helpers.h create mode 100644 src/screen_trunner.cc create mode 100644 src/screen_trunner.h create mode 100644 src/test/test.cc diff --git a/Makefile b/Makefile index 16658a1..7d95aca 100644 --- a/Makefile +++ b/Makefile @@ -13,25 +13,38 @@ SOURCES = \ src/main.cc \ src/game.cc \ src/screen.cc \ - src/screen_test.cc + src/screen_test.cc \ + src/screen_trunner.cc \ + src/3d_helpers.cc HEADERS = \ src/game.h \ src/screen.h \ - src/screen_test.h + src/screen_test.h \ + src/screen_trunner.h \ + src/3d_helpers.h OBJECTS = $(addprefix ${OBJDIR}/,$(subst .cc,.cc.o,${SOURCES})) +TEST_SOURCES = \ + src/test/test.cc + +TEST_OBJECTS = $(addprefix ${OBJDIR}/,$(subst .cc,.cc.o,${TEST_SOURCES})) + all: | format demo_0 demo_0: ${OBJECTS} ${CXX} ${CXX_FLAGS} ${LINKER_FLAGS} -o $@ $^ +test: $(filter-out ${OBJDIR}/src/main.cc.o,${OBJECTS}) ${TEST_OBJECTS} + ${CXX} ${CXX_FLAGS} ${LINKER_FLAGS} -o $@ $^ + .PHONY: clean format clean: rm -rf ${OBJDIR} rm -f demo_0 + rm -f test format: clang-format -i --style=google ${HEADERS} ${SOURCES} diff --git a/src/3d_helpers.cc b/src/3d_helpers.cc new file mode 100644 index 0000000..ffed809 --- /dev/null +++ b/src/3d_helpers.cc @@ -0,0 +1,128 @@ +#include "3d_helpers.h" + +// standard library includes +#include + +Matrix get_identity_matrix() { + return Matrix{1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, + 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F}; +} + +// Rotation is +// cos, -sin, +// sin, cos +// probably? +// +// About z-axis: +// cos, -sin, 0, 0, +// sin, cos, 0, 0, +// 0, 0, 1, 0, +// 0, 0, 0, 1 + +Matrix get_rotation_matrix_about_z(float radians) { + // OpenGL is column-major. + return Matrix{std::cos(radians), + std::sin(radians), + 0.0F, + 0.0F, + -std::sin(radians), + std::cos(radians), + 0.0F, + 0.0F, + 0.0F, + 0.0F, + 1.0F, + 0.0F, + 0.0F, + 0.0F, + 0.0F, + 1.0F}; +} + +// About y-axis +// cos, 0, sin, 0, +// 0, 1, 0, 0, +// -sin, 0, cos, 0, +// 0, 0, 0, 1 + +Matrix get_rotation_matrix_about_y(float radians) { + // OpenGL is column-major. + return Matrix{std::cos(radians), + 0.0F, + -std::sin(radians), + 0.0F, + 0.0F, + 1.0F, + 0.0F, + 0.0F, + std::sin(radians), + 0.0F, + std::cos(radians), + 0.0F, + 0.0F, + 0.0F, + 0.0F, + 1.0F}; +} + +// About x-axis +// 1, 0, 0, 0 +// 0, cos, -sin, 0 +// 0, sin, cos, 0 +// 0, 0, 0, 1 + +Matrix get_rotation_matrix_about_x(float radians) { + // OpenGL is column-major. + return Matrix{1.0F, + 0.0F, + 0.0F, + 0.0F, + 0.0F, + std::cos(radians), + std::sin(radians), + 0.0F, + 0.0F, + -std::sin(radians), + std::cos(radians), + 0.0F, + 0.0F, + 0.0F, + 0.0F, + 1.0F}; +} + +Matrix translate_matrix_z(float distance) { + return Matrix{1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, + 0.0F, 0.0F, 1.0F, distance, 0.0F, 0.0F, 0.0F, 1.0F}; +} + +Matrix operator*(const Matrix &a, const Matrix &b) { + return Matrix{a.m0 * b.m0 + a.m1 * b.m4 + a.m2 * b.m8 + a.m3 * b.m12, + a.m4 * b.m0 + a.m5 * b.m4 + a.m6 * b.m8 + a.m7 * b.m12, + a.m8 * b.m0 + a.m9 * b.m4 + a.m10 * b.m8 + a.m11 * b.m12, + a.m12 * b.m0 + a.m13 * b.m4 + a.m14 * b.m8 + a.m15 * b.m12, + + a.m0 * b.m1 + a.m1 * b.m5 + a.m2 * b.m9 + a.m3 * b.m13, + a.m4 * b.m1 + a.m5 * b.m5 + a.m6 * b.m9 + a.m7 * b.m13, + a.m8 * b.m1 + a.m9 * b.m5 + a.m10 * b.m9 + a.m11 * b.m13, + a.m12 * b.m1 + a.m13 * b.m5 + a.m14 * b.m9 + a.m15 * b.m13, + + a.m0 * b.m2 + a.m1 * b.m6 + a.m2 * b.m10 + a.m3 * b.m14, + a.m4 * b.m2 + a.m5 * b.m6 + a.m6 * b.m10 + a.m7 * b.m14, + a.m8 * b.m2 + a.m9 * b.m6 + a.m10 * b.m10 + a.m11 * b.m14, + a.m12 * b.m2 + a.m13 * b.m6 + a.m14 * b.m10 + a.m15 * b.m14, + + a.m0 * b.m3 + a.m1 * b.m7 + a.m2 * b.m11 + a.m3 * b.m15, + a.m4 * b.m3 + a.m5 * b.m7 + a.m6 * b.m11 + a.m7 * b.m15, + a.m8 * b.m3 + a.m9 * b.m7 + a.m10 * b.m11 + a.m11 * b.m15, + a.m12 * b.m3 + a.m13 * b.m7 + a.m14 * b.m11 + a.m15 * b.m15}; +} + +Vector4 operator*(const Matrix &m, const Vector4 &v) { + return Vector4{ + m.m0 * v.x + m.m1 * v.y + m.m2 * v.z + m.m3 * v.w, + m.m4 * v.x + m.m5 * v.y + m.m6 * v.z + m.m7 * v.w, + m.m8 * v.x + m.m9 * v.y + m.m10 * v.z + m.m11 * v.w, + m.m12 * v.x + m.m13 * v.y + m.m14 * v.z + m.m15 * v.w, + }; +} diff --git a/src/3d_helpers.h b/src/3d_helpers.h new file mode 100644 index 0000000..b77de73 --- /dev/null +++ b/src/3d_helpers.h @@ -0,0 +1,17 @@ +#ifndef JUMPARTIFACT_DOT_COM_DEMO_0_3D_HELPERS_H_ +#define JUMPARTIFACT_DOT_COM_DEMO_0_3D_HELPERS_H_ + +// third party includes +#include + +extern Matrix get_identity_matrix(); +extern Matrix get_rotation_matrix_about_z(float radians); +extern Matrix get_rotation_matrix_about_y(float radians); +extern Matrix get_rotation_matrix_about_x(float radians); + +extern Matrix translate_matrix_z(float distance); + +extern Matrix operator*(const Matrix &a, const Matrix &b); +extern Vector4 operator*(const Matrix &m, const Vector4 &v); + +#endif diff --git a/src/game.cc b/src/game.cc index a1fdab1..3482486 100644 --- a/src/game.cc +++ b/src/game.cc @@ -1,11 +1,13 @@ #include "game.h" // local includes -#include "screen_test.h" +#include "screen_trunner.h" Game::Game() : screen_stack(ScreenStack::new_instance()), - prev_time(std::chrono::steady_clock::now()) {} + prev_time(std::chrono::steady_clock::now()) { + screen_stack->push_screen(); +} void Game::update() { auto next_time = std::chrono::steady_clock::now(); diff --git a/src/main.cc b/src/main.cc index 36e8142..b2c1862 100644 --- a/src/main.cc +++ b/src/main.cc @@ -39,9 +39,9 @@ void jumpartifact_demo_update(void *ud) { int main() { InitWindow(800, 800, "Demo"); +#ifdef __EMSCRIPTEN__ Game game{}; -#ifdef __EMSCRIPTEN__ SetWindowSize(call_js_get_canvas_width(), call_js_get_canvas_height()); emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, &game, false, @@ -49,10 +49,13 @@ int main() { emscripten_set_main_loop_arg(jumpartifact_demo_update, &game, 0, 1); #else - SetTargetFPS(60); + { + Game game{}; + SetTargetFPS(60); - while (!WindowShouldClose()) { - jumpartifact_demo_update(&game); + while (!WindowShouldClose()) { + jumpartifact_demo_update(&game); + } } CloseAudioDevice(); diff --git a/src/screen_trunner.cc b/src/screen_trunner.cc new file mode 100644 index 0000000..5fb1ccb --- /dev/null +++ b/src/screen_trunner.cc @@ -0,0 +1,119 @@ +#include "screen_trunner.h" + +// standard library includes +#include + +#include +#include + +// local includes +#include "3d_helpers.h" + +TRunnerScreen::TRunnerScreen(std::weak_ptr stack) + : Screen(stack), + camera{Vector3{0.0F, 0.0F, 0.0F}, Vector3{0.0F, 0.0F, -1.0F}, + Vector3{0.0F, 1.0F, 0.0F}, 80.0F, CAMERA_PERSPECTIVE}, + flags(), + TEMP_cube(GenMeshCube(2.0F, 2.0F, 2.0F)), + TEMP_default_material(LoadMaterialDefault()), + TEMP_matrix(get_identity_matrix()), + TEMP_offset_matrix(translate_matrix_z(-4.0F)), + grid_spacing(1.0F), + grid_spacing_modifier(0.0F) {} + +TRunnerScreen::~TRunnerScreen() { + UnloadMesh(TEMP_cube); + UnloadMaterial(TEMP_default_material); +} + +bool TRunnerScreen::update(float dt) { + // grid_spacing_modifier += dt * GRID_SPACING_RATE; + // if (grid_spacing_modifier > 2.0F * PI) { + // grid_spacing_modifier -= 2.0F * PI; + // } + // grid_spacing = std::sin(grid_spacing_modifier) + GRID_SPACING_OFFSET; + TEMP_matrix = get_rotation_matrix_about_z(dt) * + get_rotation_matrix_about_y(dt * 1.2F) * + get_rotation_matrix_about_x(dt * 1.4F) * TEMP_matrix; + return false; +} + +bool TRunnerScreen::draw() { + BeginDrawing(); + ClearBackground(PixelToColor(Pixel::PIXEL_SKY)); + BeginMode3D(camera); + + DrawMesh(TEMP_cube, TEMP_default_material, TEMP_matrix * TEMP_offset_matrix); + + // for (unsigned int i = 11; i-- > 1;) { + // // upper + // DrawRay(Ray{Vector3{0.0F, 2.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{1.0F, 0.0F, 0.0F}}, + // BLACK); + // DrawRay(Ray{Vector3{0.0F, 2.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{-1.0F, 0.0F, 0.0F}}, + // BLACK); + // + // // lower + // DrawRay(Ray{Vector3{0.0F, -2.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{1.0F, 0.0F, 0.0F}}, + // BLACK); + // DrawRay(Ray{Vector3{0.0F, -2.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{-1.0F, 0.0F, 0.0F}}, + // BLACK); + // + // // left + // DrawRay(Ray{Vector3{-2.0F, 0.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{0.0F, 1.0F, 0.0F}}, + // BLACK); + // DrawRay(Ray{Vector3{-2.0F, 0.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{0.0F, -1.0F, 0.0F}}, + // BLACK); + // + // // right + // DrawRay(Ray{Vector3{2.0F, 0.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{0.0F, 1.0F, 0.0F}}, + // BLACK); + // DrawRay(Ray{Vector3{2.0F, 0.0F, -(0.5F + grid_spacing * (float)i)}, + // Vector3{0.0F, -1.0F, 0.0F}}, + // BLACK); + // } + EndMode3D(); + EndDrawing(); + + return false; +} + +Color TRunnerScreen::PixelToColor(Pixel p) { + switch (p) { + case PIXEL_BLANK: + return Color{0, 0, 0, 0}; + case PIXEL_BLACK: + return Color{0, 0, 0, 255}; + case PIXEL_RED: + return Color{255, 50, 50, 255}; + case PIXEL_GREEN: + return Color{50, 255, 50, 255}; + case PIXEL_BLUE: + return Color{50, 50, 255, 255}; + case PIXEL_YELLOW: + return Color{255, 255, 0, 255}; + case PIXEL_CYAN: + return Color{0, 255, 255, 255}; + case PIXEL_MAGENTA: + return Color{255, 0, 255, 255}; + case PIXEL_ORANGE: + return Color{255, 200, 0, 255}; + case PIXEL_BROWN: + return Color{180, 130, 0, 255}; + case PIXEL_TURQUOISE: + return Color{0, 255, 200, 255}; + case PIXEL_SKY: + return Color{168, 178, 255, 255}; + case PIXEL_WHITE: + return Color{255, 255, 255, 255}; + default: + assert(!"unreachable"); + return Color{0, 0, 0, 255}; + } +} diff --git a/src/screen_trunner.h b/src/screen_trunner.h new file mode 100644 index 0000000..ea62f15 --- /dev/null +++ b/src/screen_trunner.h @@ -0,0 +1,53 @@ +#ifndef JUMPARTIFACT_DOT_COM_DEMO_0_TANK_RUNNER_H_ +#define JUMPARTIFACT_DOT_COM_DEMO_0_TANK_RUNNER_H_ + +#include "screen.h" + +// standard library includes +#include +#include + +// third party includes +#include + +constexpr float GRID_SPACING_RATE = 1.0F; +constexpr float GRID_SPACING_OFFSET = 1.5F; + +class TRunnerScreen : public Screen { + public: + TRunnerScreen(std::weak_ptr stack); + ~TRunnerScreen() override; + + bool update(float dt) override; + bool draw() override; + + private: + enum Pixel : unsigned char { + PIXEL_BLANK, + PIXEL_BLACK, + PIXEL_RED, + PIXEL_GREEN, + PIXEL_BLUE, + PIXEL_YELLOW, + PIXEL_CYAN, + PIXEL_MAGENTA, + PIXEL_ORANGE, + PIXEL_BROWN, + PIXEL_TURQUOISE, + PIXEL_SKY, + PIXEL_WHITE + }; + + static Color PixelToColor(Pixel p); + + const Camera3D camera; + std::bitset<64> flags; + Mesh TEMP_cube; + Material TEMP_default_material; + Matrix TEMP_matrix; + Matrix TEMP_offset_matrix; + float grid_spacing; + float grid_spacing_modifier; +}; + +#endif diff --git a/src/test/test.cc b/src/test/test.cc new file mode 100644 index 0000000..7193f9c --- /dev/null +++ b/src/test/test.cc @@ -0,0 +1,416 @@ +// standard library includes +#include + +// local includes +#include "../3d_helpers.h" + +#define ASSERT_TRUE(v) \ + if (!(v)) { \ + std::cerr << "False in ASSERT_TRUE at line " << __LINE__ << "!\n"; \ + } + +#define ASSERT_FALSE(v) \ + if (v) { \ + std::cerr << "True in ASSERT_FALSE at line " << __LINE__ << "!\n"; \ + } + +#define ASSERT_FLOAT_EQUALS(f, v) \ + if ((f) < (v) - 0.1F || (f) > (v) + 0.1F) { \ + std::cerr << "ASSERT_FLOAT_EQUALS: " << (f) << " is not (roughly) equal to " << (v) << " at line " << __LINE__ << "!\n"; \ + } + + +int main() { + std::cout << "Testing 3d_helpers...\n"; + { + auto identity = get_identity_matrix(); + ASSERT_TRUE(identity.m0 == 1.0F); + ASSERT_TRUE(identity.m1 == 0.0F); + ASSERT_TRUE(identity.m2 == 0.0F); + ASSERT_TRUE(identity.m3 == 0.0F); + + ASSERT_TRUE(identity.m4 == 0.0F); + ASSERT_TRUE(identity.m5 == 1.0F); + ASSERT_TRUE(identity.m6 == 0.0F); + ASSERT_TRUE(identity.m7 == 0.0F); + + ASSERT_TRUE(identity.m8 == 0.0F); + ASSERT_TRUE(identity.m9 == 0.0F); + ASSERT_TRUE(identity.m10 == 1.0F); + ASSERT_TRUE(identity.m11 == 0.0F); + + ASSERT_TRUE(identity.m12 == 0.0F); + ASSERT_TRUE(identity.m13 == 0.0F); + ASSERT_TRUE(identity.m14 == 0.0F); + ASSERT_TRUE(identity.m15 == 1.0F); + + identity = identity * get_identity_matrix(); + ASSERT_FLOAT_EQUALS(identity.m0, 1.0F); + ASSERT_FLOAT_EQUALS(identity.m1, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m2, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(identity.m4, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m5, 1.0F); + ASSERT_FLOAT_EQUALS(identity.m6, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(identity.m8, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m9, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m10, 1.0F); + ASSERT_FLOAT_EQUALS(identity.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(identity.m12, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m13, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m14, 0.0F); + ASSERT_FLOAT_EQUALS(identity.m15, 1.0F); + + { + auto v = Vector4{1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = identity * v; + ASSERT_FLOAT_EQUALS(v_result.x, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 1.0F, 0.0F, 1.0F}; + auto v_result = identity * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, 1.0F, 1.0F}; + auto v_result = identity * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + } + + { + // 90 degree rotation and identity matrix. + auto m = get_rotation_matrix_about_z(PI / 2.0F) * get_identity_matrix(); + ASSERT_FLOAT_EQUALS(m.m0, 0.0F); + ASSERT_FLOAT_EQUALS(m.m1, -1.0F); + ASSERT_FLOAT_EQUALS(m.m2, 0.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 1.0F); + ASSERT_FLOAT_EQUALS(m.m5, 0.0F); + ASSERT_FLOAT_EQUALS(m.m6, 0.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, 0.0F); + ASSERT_FLOAT_EQUALS(m.m9, 0.0F); + ASSERT_FLOAT_EQUALS(m.m10, 1.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + + { + auto v = Vector4{1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{-1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, -1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + } + { + // Double 90 degree rotation about z. + auto m = get_rotation_matrix_about_z(PI/2.0F) * get_rotation_matrix_about_z(PI/2.0F); + ASSERT_FLOAT_EQUALS(m.m0, -1.0F); + ASSERT_FLOAT_EQUALS(m.m1, 0.0F); + ASSERT_FLOAT_EQUALS(m.m2, 0.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 0.0F); + ASSERT_FLOAT_EQUALS(m.m5, -1.0F); + ASSERT_FLOAT_EQUALS(m.m6, 0.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, 0.0F); + ASSERT_FLOAT_EQUALS(m.m9, 0.0F); + ASSERT_FLOAT_EQUALS(m.m10, 1.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + { + auto v = Vector4{1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{-1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, -1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + // Rotate back 90 degrees. + m = get_rotation_matrix_about_z(-PI/2.0F) * m; + ASSERT_FLOAT_EQUALS(m.m0, 0.0F); + ASSERT_FLOAT_EQUALS(m.m1, -1.0F); + ASSERT_FLOAT_EQUALS(m.m2, 0.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 1.0F); + ASSERT_FLOAT_EQUALS(m.m5, 0.0F); + ASSERT_FLOAT_EQUALS(m.m6, 0.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, 0.0F); + ASSERT_FLOAT_EQUALS(m.m9, 0.0F); + ASSERT_FLOAT_EQUALS(m.m10, 1.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + } + { + // Rotate about y-axis 90 degrees. + auto m = get_rotation_matrix_about_y(PI/2.0F); + ASSERT_FLOAT_EQUALS(m.m0, 0.0F); + ASSERT_FLOAT_EQUALS(m.m1, 0.0F); + ASSERT_FLOAT_EQUALS(m.m2, 1.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 0.0F); + ASSERT_FLOAT_EQUALS(m.m5, 1.0F); + ASSERT_FLOAT_EQUALS(m.m6, 0.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, -1.0F); + ASSERT_FLOAT_EQUALS(m.m9, 0.0F); + ASSERT_FLOAT_EQUALS(m.m10, 0.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + + { + auto v = Vector4{1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, -1.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{-1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, 1.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + + // additional 90 degrees. + m = get_rotation_matrix_about_y(PI/2.0F) * m; + ASSERT_FLOAT_EQUALS(m.m0, -1.0F); + ASSERT_FLOAT_EQUALS(m.m1, 0.0F); + ASSERT_FLOAT_EQUALS(m.m2, 0.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 0.0F); + ASSERT_FLOAT_EQUALS(m.m5, 1.0F); + ASSERT_FLOAT_EQUALS(m.m6, 0.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, 0.0F); + ASSERT_FLOAT_EQUALS(m.m9, 0.0F); + ASSERT_FLOAT_EQUALS(m.m10, -1.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + { + auto v = Vector4{1.0F, 0.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, -1.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + } + + { + // About x-axis 90 degrees. + auto m = get_rotation_matrix_about_x(PI/2.0F); + ASSERT_FLOAT_EQUALS(m.m0, 1.0F); + ASSERT_FLOAT_EQUALS(m.m1, 0.0F); + ASSERT_FLOAT_EQUALS(m.m2, 0.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 0.0F); + ASSERT_FLOAT_EQUALS(m.m5, 0.0F); + ASSERT_FLOAT_EQUALS(m.m6, -1.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, 0.0F); + ASSERT_FLOAT_EQUALS(m.m9, 1.0F); + ASSERT_FLOAT_EQUALS(m.m10, 0.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + { + auto v = Vector4{0.0F, 1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, 1.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, -1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, -1.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + // Additional 90 degrees. + m = get_rotation_matrix_about_x(PI/2.0F) * m; + ASSERT_FLOAT_EQUALS(m.m0, 1.0F); + ASSERT_FLOAT_EQUALS(m.m1, 0.0F); + ASSERT_FLOAT_EQUALS(m.m2, 0.0F); + ASSERT_FLOAT_EQUALS(m.m3, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m4, 0.0F); + ASSERT_FLOAT_EQUALS(m.m5, -1.0F); + ASSERT_FLOAT_EQUALS(m.m6, 0.0F); + ASSERT_FLOAT_EQUALS(m.m7, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m8, 0.0F); + ASSERT_FLOAT_EQUALS(m.m9, 0.0F); + ASSERT_FLOAT_EQUALS(m.m10, -1.0F); + ASSERT_FLOAT_EQUALS(m.m11, 0.0F); + + ASSERT_FLOAT_EQUALS(m.m12, 0.0F); + ASSERT_FLOAT_EQUALS(m.m13, 0.0F); + ASSERT_FLOAT_EQUALS(m.m14, 0.0F); + ASSERT_FLOAT_EQUALS(m.m15, 1.0F); + { + auto v = Vector4{0.0F, 1.0F, 0.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.z, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + { + auto v = Vector4{0.0F, 0.0F, 1.0F, 1.0F}; + auto v_result = m * v; + ASSERT_FLOAT_EQUALS(v_result.x, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.y, 0.0F); + ASSERT_FLOAT_EQUALS(v_result.z, -1.0F); + ASSERT_FLOAT_EQUALS(v_result.w, 1.0F); + } + } + + std::cout << "Finished tests.\n"; + return 0; +} diff --git a/wasm_build/Makefile b/wasm_build/Makefile index f4ffefe..203ef6d 100644 --- a/wasm_build/Makefile +++ b/wasm_build/Makefile @@ -9,13 +9,17 @@ SOURCES = \ ../src/ems.cc \ ../src/game.cc \ ../src/screen.cc \ - ../src/screen_test.cc + ../src/screen_test.cc \ + ../src/screen_trunner.cc \ + ../src/3d_helpers.cc HEADERS = \ ../src/ems.h \ ../src/game.h \ ../src/screen.h \ - ../src/screen_test.h + ../src/screen_test.h \ + ../src/screen_trunner.h \ + ../src/3d_helpers.h CXX = source ${HOME}/git/emsdk/emsdk_env.sh && em++