#include #include #include #include #include #include #include #include #include #include #include #include #include #include "scan.hpp" #include "parse.hpp" #include "compile.hpp" #include "settings.hpp" #include "output.hpp" static void usage(const char* prog) { std::cerr << "Usage: " << prog << " [options] \n" "Options:\n" " --float64 output double instead of float\n" " --object select an object (can be given multiple times)\n" " --group select a group (can be given multiple times)\n" " --with-normals extract normal vectors\n" " --with-texcoords extract texture coordinates\n" " -- end of options\n"; } #define CATCH_AND_RETURN(variable, exception, return_value, initializer) \ decltype(initializer) variable; \ try { \ variable = initializer; \ } catch (exception __ex__) { \ std::cerr << __ex__.what() << std::endl; \ return return_value; \ } int main(int argc, char** argv) { using namespace wavefront; if (argc < 3) { usage(argv[0]); return 1; } SettingsBuilder settings_builder; int arg_index = 1; bool end_of_options = false; std::vector positional; while (arg_index < argc) { std::string arg = argv[arg_index]; if (!end_of_options && arg == "--") { end_of_options = true; ++arg_index; continue; } if (!end_of_options && arg.size() > 0 && arg[0] == '-' && arg != "-") { if (arg == "--float64") { settings_builder.use_float64(true); ++arg_index; continue; } else if (arg == "--with-normals") { settings_builder.with_normals(true); ++arg_index; continue; } else if (arg == "--with-texcoords") { settings_builder.with_texcoords(true); ++arg_index; continue; } else if (arg == "--object") { if (arg_index + 1 >= argc) { std::cerr << "--object requires an argument\n"; return 2; } settings_builder.selected_objects().emplace_back(argv[arg_index + 1]); arg_index += 2; continue; } else if (arg == "--group") { if (arg_index + 1 >= argc) { std::cerr << "--group requires an argument\n"; return 2; } settings_builder.selected_groups().emplace_back(argv[arg_index + 1]); arg_index += 2; continue; } else { std::cerr << "Unknown option: " << arg << "\n"; usage(argv[0]); return 2; } } else { positional.emplace_back(arg); ++arg_index; } } if (positional.size() != 2) { std::cerr << "Expected exactly two positional arguments: input_file output_file\n"; usage(argv[0]); return 3; } const std::string& input_file = positional[0]; if (input_file != "-") { settings_builder.input(input_file); } // Open output const std::string& output_file = positional[1]; if (output_file != "-") { settings_builder.output(output_file); } Settings settings = settings_builder.build(); using FloatType = float; using IndexType = std::uint32_t; auto scan_data = wavefront::scan( settings.input(), settings.selected_objects(), settings.selected_groups() ); std::cerr << "Scanned " << scan_data.total_lines << " lines" << std::endl; std::cerr << "Found " << scan_data.category_map["v"].size() << " vertices" << std::endl; std::cerr << "Found " << scan_data.category_map["vn"].size() << " normals" << std::endl; std::cerr << "Found " << scan_data.category_map["vt"].size() << " texture coordinates" << std::endl; std::cerr << "Found " << scan_data.category_map["f"].size() << " faces" << std::endl; CATCH_AND_RETURN(face_data, wavefront::parse_error, 1, wavefront::parse_face_data(scan_data)); std::cerr << "Selected " << face_data.triangle_list.size() << " triangles" << std::endl; std::cerr << "Selected " << face_data.index_position_set.size() << " vertex positions" << std::endl; std::cerr << "Selected " << face_data.index_normal_set.size() << " vertex normals" << std::endl; std::cerr << "Selected " << face_data.index_texcoord_set.size() << " vertex texture coordinates" << std::endl; CATCH_AND_RETURN(coordinate_data, wavefront::parse_error, 1, wavefront::parse_coordinate_data(scan_data, face_data)); auto number_list = create_number_list(coordinate_data); std::cerr << "Generated number list with " << number_list.size() << " values" << std::endl; auto coordinate_index_data = create_coordinate_index(coordinate_data, number_list); auto coordinate_storage = compile_coordinate_storage(coordinate_index_data); if (std::get<1>(coordinate_storage).size() > 0) { std::cerr << "Indexed " << 1 << "D coordinates: " << std::get<1>(coordinate_storage).size() << std::endl; } if (std::get<2>(coordinate_storage).size() > 0) { std::cerr << "Indexed " << 2 << "D coordinates: " << std::get<2>(coordinate_storage).size() << std::endl; } if (std::get<3>(coordinate_storage).size() > 0) { std::cerr << "Indexed " << 3 << "D coordinates: " << std::get<3>(coordinate_storage).size() << std::endl; } auto coordinate_storage_index = compile_coordinate_storage_line_mapping(coordinate_index_data, coordinate_storage); auto index_storage = compile_vertex_storage(scan_data, face_data, coordinate_storage_index); std::visit([&](const auto &vertex_storage) { std::cerr << "Compiled " << vertex_storage.size() << " vertices" << std::endl; }, index_storage.vertices); std::cerr << "Compiled " << index_storage.triangles.size() << " triangles" << std::endl; auto &output = settings.output(); // 0:4: Signature settings.output().write("\x7F" "3DG", 4); // 4:8: Version wavefront::write_number(output, 1); std::uint32_t flags = 0; if (coordinate_storage_index.normal_coordinate_index.size() > 0) { flags |= 1 << 1; } std::visit([&](const auto &texture_coordinates) { if constexpr (std::ranges::range>) { using mapped_type = typename std::decay_t::mapped_type; if constexpr (std::ranges::range) { flags |= std::tuple_size_v << 2; } else { flags |= 1 << 2; } } }, coordinate_data.texture_coordinates); // 8:12: flags<0: float_size, 1: has_normals, 2&3: texcoord_ndims (0 = no), wavefront::write_number(output, flags); // 12:16: number_list item count wavefront::write_number(output, number_list.size()); // 16:20: 2D storage item count wavefront::write_number(output, std::get<2>(coordinate_storage).size()); // 20:24: 3D storage item count wavefront::write_number(output, std::get<3>(coordinate_storage).size()); // 24:28: Vertex storage item count std::visit([&](const auto &vertex_data) { using value_t = std::decay_t::value_type; if constexpr (std::tuple_size_v > 1) { wavefront::write_number(output, vertex_data.size()); } else { wavefront::write_number(output, 0); } }, index_storage.vertices); wavefront::write_number(output, index_storage.triangles.size()); output.write(reinterpret_cast(number_list.data()), number_list.size() * sizeof(decltype(number_list)::value_type)); std::array padding; padding.fill(0); std::size_t required_padding; required_padding = (16 - (number_list.size() * sizeof(decltype(number_list)::value_type) % 16)) % 16; output.write(padding.data(), required_padding); if (std::get<2>(coordinate_storage).size() > 0) { const auto &value_list = std::get<2>(coordinate_storage); using value_t = std::decay_t::value_type; output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; if (required_padding > 0) { output.write(padding.data(), required_padding); } } if (std::get<3>(coordinate_storage).size() > 0) [[likely]] { const auto &value_list = std::get<3>(coordinate_storage); using value_t = std::decay_t::value_type; output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; if (required_padding > 0) { output.write(padding.data(), required_padding); } } std::visit([&](const auto &vertex_data) { using value_t = std::decay_t::value_type; if constexpr (std::tuple_size_v > 1) { output.write(reinterpret_cast(vertex_data.data()), vertex_data.size() * sizeof(value_t)); required_padding = (16 - (vertex_data.size() * sizeof(value_t) % 16)) % 16; if (required_padding > 0) { output.write(padding.data(), required_padding); } } }, index_storage.vertices); { const auto &value_list = index_storage.triangles; using value_t = std::decay_t::value_type; output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; if (required_padding > 0) { output.write(padding.data(), required_padding); } } return 0; }