#include "config.h" #include #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_data.h" #include "o3d/tri.h" #include "o3d/camera.h" #include "math/vector.h" #include "math/mat4.h" #include "math/quat.h" #include "math/tform.h" #include "renderer.h" #include "obj_parser.h" using engine::Renderer, engine::fb::CharacterFrameBuffer, engine::fb::PixelFrameBuffer, engine::o3d::Scene, engine::o3d::Mesh, engine::o3d::Triangle, engine::math::Vector3, engine::math::Vector4, engine::math::Matrix4, engine::math::Quaternion; #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); } extern Quaternion camera_quat; Quaternion camera_quat = Quaternion::one(); template static void scene_main(Renderer& renderer, const Matrix4& final_transform_mat, UpdateFrameFn update_frame) { float dist = 1.5f; float rad = 5.f; bool cont = true; Scene scene{ {{{0.f, 0.f, rad * dist}, {1.f, 0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}}, { // { // Mesh::plane(), // { // Vector3(0.f, 0.f, 0.f), // {1.f, 0.f, 0.f, 0.f}, // Vector3(rad, rad, rad), // } // }, { engine::parse_object("../assets/suzanne.obj"), { Vector3(0.f, 0.f, 0.f), {1.f, 0.f, 0.f, 0.f}, Vector3(rad, rad, rad), } }, } }; float mul_angle = 0.f; while (cont) { camera_quat = Quaternion::euler_zyx(mul_angle * .0050f, mul_angle * .0065f, mul_angle * .0080f); mul_angle += 1.f; scene.camera.transform.rot = camera_quat; renderer.clear(); auto pre_final_mat = final_transform_mat * Matrix4::projection(static_cast(renderer.height()) / static_cast(renderer.width()), 2.f, 50.f) * scene.camera.transform.opposite().to_mat4(); for (const auto& obj : scene.objs) { auto final_mat = pre_final_mat * obj.transform.to_mat4(); const auto& mesh = obj.mesh; std::vector vertices; for (const auto& vertex : mesh.vertices) vertices.push_back(final_mat * vertex); for (const auto& triangle_indices : mesh.indices) { [&](std::integer_sequence) { renderer.draw_triangle({{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], mesh.vertices_data[triangle_indices[j][2]]}...}); }(std::make_integer_sequence()); } } 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); Renderer renderer{CharacterFrameBuffer{static_cast(w), static_cast(h)}}; scene_main(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), [&]() { mvaddnstr(0, 0, renderer.fb.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 = nullptr; SDL_Renderer* renderer = nullptr; SDL_Texture* texture = nullptr; // 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 == nullptr) { 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); Renderer engine_renderer{PixelFrameBuffer{SCREEN_WIDTH, SCREEN_HEIGHT}}; SDL_Event e; scene_main(engine_renderer, Matrix4::idty(), [&]() { SDL_UpdateTexture(texture, nullptr, engine_renderer.fb.pixels(), SCREEN_WIDTH * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, nullptr, nullptr); 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: std::unreachable(); } }