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.
This commit is contained in:
Stephen Seo 2023-08-02 19:59:06 +09:00
parent 1feefc5d9b
commit c99407a474
9 changed files with 765 additions and 10 deletions

View file

@ -13,25 +13,38 @@ SOURCES = \
src/main.cc \ src/main.cc \
src/game.cc \ src/game.cc \
src/screen.cc \ src/screen.cc \
src/screen_test.cc src/screen_test.cc \
src/screen_trunner.cc \
src/3d_helpers.cc
HEADERS = \ HEADERS = \
src/game.h \ src/game.h \
src/screen.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})) 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 all: | format demo_0
demo_0: ${OBJECTS} demo_0: ${OBJECTS}
${CXX} ${CXX_FLAGS} ${LINKER_FLAGS} -o $@ $^ ${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 .PHONY: clean format
clean: clean:
rm -rf ${OBJDIR} rm -rf ${OBJDIR}
rm -f demo_0 rm -f demo_0
rm -f test
format: format:
clang-format -i --style=google ${HEADERS} ${SOURCES} clang-format -i --style=google ${HEADERS} ${SOURCES}

128
src/3d_helpers.cc Normal file
View file

@ -0,0 +1,128 @@
#include "3d_helpers.h"
// standard library includes
#include <cmath>
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,
};
}

17
src/3d_helpers.h Normal file
View file

@ -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 <raylib.h>
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

View file

@ -1,11 +1,13 @@
#include "game.h" #include "game.h"
// local includes // local includes
#include "screen_test.h" #include "screen_trunner.h"
Game::Game() Game::Game()
: screen_stack(ScreenStack::new_instance()), : screen_stack(ScreenStack::new_instance()),
prev_time(std::chrono::steady_clock::now()) {} prev_time(std::chrono::steady_clock::now()) {
screen_stack->push_screen<TRunnerScreen>();
}
void Game::update() { void Game::update() {
auto next_time = std::chrono::steady_clock::now(); auto next_time = std::chrono::steady_clock::now();

View file

@ -39,9 +39,9 @@ void jumpartifact_demo_update(void *ud) {
int main() { int main() {
InitWindow(800, 800, "Demo"); InitWindow(800, 800, "Demo");
#ifdef __EMSCRIPTEN__
Game game{}; Game game{};
#ifdef __EMSCRIPTEN__
SetWindowSize(call_js_get_canvas_width(), call_js_get_canvas_height()); SetWindowSize(call_js_get_canvas_width(), call_js_get_canvas_height());
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, &game, false, 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); emscripten_set_main_loop_arg(jumpartifact_demo_update, &game, 0, 1);
#else #else
SetTargetFPS(60); {
Game game{};
SetTargetFPS(60);
while (!WindowShouldClose()) { while (!WindowShouldClose()) {
jumpartifact_demo_update(&game); jumpartifact_demo_update(&game);
}
} }
CloseAudioDevice(); CloseAudioDevice();

119
src/screen_trunner.cc Normal file
View file

@ -0,0 +1,119 @@
#include "screen_trunner.h"
// standard library includes
#include <raylib.h>
#include <cassert>
#include <cmath>
// local includes
#include "3d_helpers.h"
TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> 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};
}
}

53
src/screen_trunner.h Normal file
View file

@ -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 <array>
#include <bitset>
// third party includes
#include <raylib.h>
constexpr float GRID_SPACING_RATE = 1.0F;
constexpr float GRID_SPACING_OFFSET = 1.5F;
class TRunnerScreen : public Screen {
public:
TRunnerScreen(std::weak_ptr<ScreenStack> 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

416
src/test/test.cc Normal file
View file

@ -0,0 +1,416 @@
// standard library includes
#include <iostream>
// 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;
}

View file

@ -9,13 +9,17 @@ SOURCES = \
../src/ems.cc \ ../src/ems.cc \
../src/game.cc \ ../src/game.cc \
../src/screen.cc \ ../src/screen.cc \
../src/screen_test.cc ../src/screen_test.cc \
../src/screen_trunner.cc \
../src/3d_helpers.cc
HEADERS = \ HEADERS = \
../src/ems.h \ ../src/ems.h \
../src/game.h \ ../src/game.h \
../src/screen.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++ CXX = source ${HOME}/git/emsdk/emsdk_env.sh && em++