#include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_NCURSES #include #endif #include "fb/chfb.h" #include "fb/pixfb.h" #include "o3d/obj3d.h" #include "o3d/vertex.h" #include "o3d/vertex_data.h" #include "o3d/tri_vertex.h" #include "math/math_vector.h" #include "math/mat4.h" #define FPS 60 #define PI 3.1415926535f #define MODE_HELP 0 #define MODE_TERM 1 #define MODE_GRAPHICAL 2 void print_usage(std::ostream& output_stream) { output_stream << "Usage: ./engine [-tg] [--term] [--graphical]\n" << " -h, --help show usage (this)\n" << " -t, --term terminal mode\n" << " -g, --graphical graphical mode (default)\n" << std::flush; } [[noreturn]] void usage_error_exit() { print_usage(std::cerr); std::exit(EXIT_FAILURE); } template void scene_main(FB& fb, std::function update_frame) { MathVector3 a{0.f, 0.f, 0.f}; float dist = 4.f; bool cont = true; while (cont) { a.x += .0050f; a.y += .0065f; a.z += .0080f; float rad = 5.f; auto [e4_x, e4_y, e4_z, e4_w] = (Mat4::rot_x(a.x) * Mat4::rot_y(a.y) * Mat4::rot_z(a.z)).to_vecs(); auto e_x = e4_x.xyz(); auto e_y = e4_y.xyz(); auto e_z = e4_z.xyz(); std::array 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 }, } } }}; fb.clear(); float min_z = 2.f, max_z = 50.f; float fac_for_aspect_ratio = static_cast(fb.height()) / static_cast(fb.width()); Mat4 mat{{ fac_for_aspect_ratio, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, -2.f / (max_z - min_z), -(max_z + min_z) / (max_z - min_z), 0.f, 0.f, -1.f, 0.f, }}; for (auto obj : objs) { for (auto triangle : obj) { TriangleVertex4 t{triangle}; t.vertex1.point = mat * t.vertex1.point; t.vertex2.point = mat * t.vertex2.point; t.vertex3.point = mat * t.vertex3.point; fb.draw_triangle(t); } } cont = update_frame(); } } #ifdef ENABLE_NCURSES #define MKEY_Z 122 #define MKEY_Q 113 #define MKEY_S 115 #define MKEY_D 100 #define MKEY_ESC 27 int main_term() { // 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(w), static_cast(h)}; scene_main(cfb, [&]() { mvaddnstr(0, 0, cfb.chars(), cfb.width() * cfb.height()); bool cont = true; //timeout(1000 / FPS); timeout(10); int c = getch(); if (c == MKEY_ESC) return false; 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; } getmaxyx(stdscr, h, w); cfb.resize(static_cast(w), static_cast(h)); return cont; }); // terminate endwin(); return EXIT_SUCCESS; } #endif #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 int main_graphical() { SDL_Window* window = NULL; SDL_Renderer* renderer = NULL; SDL_Texture* texture = NULL; if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Error: SDL_Init error: %s\n", SDL_GetError()); return EXIT_FAILURE; } window = SDL_CreateWindow("Engine", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (window == NULL) { fprintf(stderr, "Error: SDL_CreateWindow error: %s\n", SDL_GetError()); return EXIT_FAILURE; } renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); PixelFrameBuffer pfb{SCREEN_WIDTH, SCREEN_HEIGHT}; SDL_Event e; scene_main(pfb, [&]() { SDL_UpdateTexture(texture, NULL, pfb.pixels(), SCREEN_WIDTH * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); SDL_UpdateWindowSurface(window); bool cont = true; while (SDL_WaitEventTimeout(&e, 10)) { if (e.type == SDL_QUIT) { cont = false; } } return cont; }); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_SUCCESS; } int main(int argc, char *argv[]) { int mode = MODE_GRAPHICAL; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (argv[i][1] == '-') { if (strcmp(&argv[i][2], "help") == 0) { mode = MODE_HELP; } else if (strcmp(&argv[i][2], "term") == 0) { mode = MODE_TERM; } else if (strcmp(&argv[i][2], "graphical") == 0) { mode = MODE_GRAPHICAL; } else { std::cerr << "Error: Unexpected option `--" << &argv[i][2] << "'." << std::endl; usage_error_exit(); } } else { if (!argv[i][1]) { std::cerr << "Error: Unexpected argument `-'." << std::endl; usage_error_exit(); } std::size_t arg_len = strlen(&argv[i][1]); for (int j = 0; j < arg_len; j++) { switch (argv[i][1 + j]) { case 'h': mode = MODE_HELP; break; case 't': mode = MODE_TERM; break; case 'g': mode = MODE_GRAPHICAL; break; default: std::cerr << "Error: Unexpected option `-" << argv[i][1] << "'." << std::endl; usage_error_exit(); } } } } else { std::cerr << "Error: Unexpected argument `" << argv[i] << "'." << std::endl; usage_error_exit(); } } switch (mode) { case MODE_HELP: print_usage(std::cout); return EXIT_SUCCESS; case MODE_TERM: #ifdef ENABLE_NCURSES return main_term(); #else std::cerr << "Error: ncurses was not enabled during compilation." << std::endl; return EXIT_FAILURE; #endif case MODE_GRAPHICAL: return main_graphical(); default: ; // unreachable } return EXIT_SUCCESS; // unreachable }