#include "config.h" #include #include #include #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/scene.h" #include "o3d/mesh.h" #include "o3d/obj3d.h" #include "o3d/vertex.h" #include "o3d/vertex_data.h" #include "o3d/tri_vertex.h" #include "o3d/camera.h" #include "math/vector.h" #include "math/mat4.h" #include "renderer.h" #define FPS 60 #define PI 3.1415926535f #define MODE_HELP 0 #define MODE_TERM 1 #define MODE_GRAPHICAL 2 static void print_usage(std::ostream& output_stream) { output_stream << "Usage: ./engine [-htg] [--help] [--term] [--graphical]\n" << " -h, --help show usage (this)\n" << " -t, --term terminal mode\n" << " -g, --graphical graphical mode (default)\n" << std::flush; } [[noreturn]] static void usage_error_exit() { print_usage(std::cerr); std::exit(EXIT_FAILURE); } static void scene_main(engine::Renderer& renderer, engine::math::Matrix4 final_transform_mat, std::function update_frame) { float dist = 4.f; float rad = 5.f; bool cont = true; engine::o3d::Scene scene{ {{0.f, 0.f, rad * dist}, 0.f, 0.f, 0.f}, // camera { // objects engine::o3d::Object3D{ engine::o3d::Mesh::cube(), -rad * engine::math::Vector3(.5f, .5f, .5f), rad, 0.f, 0.f, 0.f }, engine::o3d::Object3D{ engine::o3d::Mesh::cube(), +rad * engine::math::Vector3(.5f, .5f, .5f), rad, 0.f, 0.f, 0.f }, } }; auto scale_mat = engine::math::Matrix4::scale(rad); while (cont) { scene.camera.rot_x += .0050f; scene.camera.rot_y += .0065f; scene.camera.rot_z += .0080f; renderer.clear(); auto transform_mat = engine::math::Matrix4::translate(-scene.camera.loc) * engine::math::Matrix4::rot_x(-scene.camera.rot_x) * engine::math::Matrix4::rot_y(-scene.camera.rot_y) * engine::math::Matrix4::rot_z(-scene.camera.rot_z); std::array mats{{ transform_mat * engine::math::Matrix4::translate(engine::math::Vector3{-.5f * rad, -.5f * rad, -.5f * rad}) * scale_mat, transform_mat * engine::math::Matrix4::translate(engine::math::Vector3{+.5f * rad, +.5f * rad, +.5f * rad}) * scale_mat, }}; auto pre_final_mat = final_transform_mat * engine::math::Matrix4::projection(static_cast(renderer.height()) / static_cast(renderer.width()), 2.f, 50.f); for (int i = 0; i < 2; i++) { auto final_mat = pre_final_mat * mats[i]; const auto& mesh = scene.objs[i].mesh; std::vector pts; for (const auto& vert : mesh.pts) pts.push_back({ final_mat * engine::math::Vector4{vert.point}, vert.data }); for (auto face : mesh.faces) renderer.draw_triangle({pts[face[0]], pts[face[1]], pts[face[2]]}); } 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 static 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); engine::Renderer renderer{ std::make_unique(static_cast(w), static_cast(h))}; scene_main(renderer, engine::math::Matrix4::scale(engine::math::Vector3(2.f, 1.f, 1.f)), [&]() { mvaddnstr(0, 0, static_cast(renderer.fb.get())->chars(), renderer.width() * renderer.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); renderer.resize(static_cast(w), static_cast(h)); return cont; }); // terminate endwin(); return EXIT_SUCCESS; } #endif #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 static int main_graphical() { SDL_Window* window = NULL; SDL_Renderer* renderer = NULL; SDL_Texture* texture = NULL; // init if (SDL_Init(SDL_INIT_VIDEO) < 0) { std::cerr << "Error: SDL_Init error: " << SDL_GetError() << std::endl; return EXIT_FAILURE; } window = SDL_CreateWindow("Engine", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (window == NULL) { SDL_Quit(); std::cerr << "Error: SDL_CreateWindow error: " << SDL_GetError() << std::endl; 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); engine::Renderer engine_renderer{ std::make_unique(SCREEN_WIDTH, SCREEN_HEIGHT)}; SDL_Event e; scene_main(engine_renderer, engine::math::Matrix4::idty(), [&]() { SDL_UpdateTexture(texture, NULL, static_cast(engine_renderer.fb.get())->pixels(), SCREEN_WIDTH * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); SDL_UpdateWindowSurface(window); bool cont = true; if (SDL_WaitEventTimeout(&e, 10)) { do { if (e.type == SDL_QUIT) { cont = false; } } while (SDL_PollEvent(&e)); } return cont; }); // terminate SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_SUCCESS; } static std::vector convert_args(int argc, char *argv[]) { std::vector args(argc); for (int i = 0; i < argc; i++) args[i] = argv[i]; return args; } static void parse_args(const std::vector& args, int& mode) { for (auto args_iter = std::next(args.begin()); args_iter != args.end(); args_iter++) { const auto& arg = *args_iter; if (arg.size() >= 1 && arg[0] == '-') { if (arg.size() >= 2 && arg[1] == '-') { auto long_opt = arg.substr(2); if (long_opt == "help") { mode = MODE_HELP; } else if (long_opt == "term") { mode = MODE_TERM; } else if (long_opt == "graphical") { mode = MODE_GRAPHICAL; } else { std::cerr << "Error: Unexpected option `--" << long_opt << "'." << std::endl; usage_error_exit(); } } else { std::size_t arg_len = arg.size(); if (arg_len == 1) { std::cerr << "Error: Unexpected argument `-'." << std::endl; usage_error_exit(); } for (auto arg_iter = std::next(arg.begin()); arg_iter != arg.end(); arg_iter++) { const auto& opt = *arg_iter; switch (opt) { case 'h': mode = MODE_HELP; break; case 't': mode = MODE_TERM; break; case 'g': mode = MODE_GRAPHICAL; break; default: std::cerr << "Error: Unexpected option `-" << opt << "'." << std::endl; usage_error_exit(); } } } } else { std::cerr << "Error: Unexpected argument `" << arg << "'." << std::endl; usage_error_exit(); } } } int main(int argc, char *argv[]) { int mode = MODE_GRAPHICAL; parse_args(convert_args(argc, argv), mode); 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 }