diff --git a/CMakeLists.txt b/CMakeLists.txt index d311dee..5cb757c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,5 +4,8 @@ project(wavefront_parser LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -O3") +set(CMAKE_CXX_FLAGS "-fno-threadsafe-statics") + file(GLOB SRC_FILES src/*.cpp) add_executable(wavefront_parser ${SRC_FILES}) diff --git a/src/file.hpp b/src/file.hpp new file mode 100644 index 0000000..ac0e7d5 --- /dev/null +++ b/src/file.hpp @@ -0,0 +1,273 @@ +#ifndef _WAVEFRONT_PARSER_PARSER_HPP_ +#define _WAVEFRONT_PARSER_PARSER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "numset.hpp" +#include "settings.hpp" + +namespace wavefront::parser { + template + class File { + std::vector> context_position_data_; + std::vector> context_normal_data_; + std::vector< + std::variant< + std::monostate, + FloatType, + std::array, + std::array + > + > context_texcoord_data_; + numset_t output_floatlist_set_; + std::set> output_position_set_; + std::optional>> output_normal_set_; + std::optional< + std::variant< + std::set, + std::set>, + std::set> + > + > output_texcoord_set_; + std::variant< + std::set, + std::set>, + std::set> + > output_vertex_set_; + std::set> output_triangle_set_; + + public: + File(); + + // Stage 1: Read the file contents and fill in context data with traingulized faces. + void parse(const Settings &settings); + + // Stage 2: Create float list (from vectorized set). + void create_floatlist(); + }; + + namespace { + template + std::array repeat_value(ValueType value) { + std::array result; + result.fill(value); + return result; + } + } + + template + File::File() + { + context_position_data_.push_back(repeat_value(std::numeric_limits::quiet_NaN())); + context_normal_data_.push_back(repeat_value(std::numeric_limits::quiet_NaN())); + context_texcoord_data_.push_back(std::monostate{}); + } + + namespace { + inline std::string trim(const std::string &source) { + static const auto if_space = [](auto source_char){ + return std::isspace(source_char); + }; + auto left = std::find_if_not(source.begin(), source.end(), if_space); + auto right = std::find_if_not(source.rbegin(), source.rend(), if_space).base(); + return std::string(left, right); + } + + inline void assign_if_not_empty(std::optional &context, const std::string &name) { + auto trimmed_name = trim(name); + if (trimmed_name.empty()) { + context.reset(); + } else { + context = name; + } + } + } + + template + void File::parse(const Settings &settings) { + // Prepare to filter by object and group by the settings. + using check_filter_func_t = std::function; + using line_processor_func_t = std::function; + using vec3_inserter_factory_func_t = std::function> &)>; + + static const auto return_true = []()->bool { return true; }; + + std::size_t current_line_number = 0; + + std::vector> position_data; + std::vector> normal_data; + std::vector< + std::variant< + std::monostate, + FloatType, + std::array, + std::array + > + > texcoord_data; + + position_data.push_back(repeat_value(std::numeric_limits::quiet_NaN())); + normal_data.push_back(repeat_value(std::numeric_limits::quiet_NaN())); + texcoord_data.push_back(std::monostate{}); + + vec3_inserter_factory_func_t create_vec3_inserter = [&](std::vector> &target_vector) { + return [&](const std::string &type, const std::string &content) { + std::istringstream content_parser(content); + std::array vec3; + auto line_size = content.size(); + std::size_t content_begin_pos = 0; + auto line_begin = content.data(); + auto content_begin = line_begin; + auto line_end = line_begin + line_size; + std::size_t component_count = 0; + while (component_count < 3 && content_begin_pos < line_size) { + decltype(content_begin_pos) content_end_pos = content.find(' ', content_begin_pos); + decltype(content_begin) content_end; + if (content_end_pos == std::string::npos) { + content_end_pos = line_size; + content_end = line_end; + } else { + content_end = line_begin + content_end_pos; + } + auto [number_end, conversion_error] = std::from_chars( + content_begin, + content_end, + vec3[component_count], + std::chars_format::fixed + ); + if (conversion_error != std::errc() || number_end != content_end) { + std::stringstream error_message; + error_message << "Line [" << current_line_number << "]" << ": " << "Invalid float data"; + throw std::runtime_error(error_message.str()); + } + content_begin_pos = content_end_pos + 1; + content_begin = content.data() + content_begin_pos; + ++component_count; + } + if (component_count < 3) { + std::stringstream error_message; + error_message << "Line [" << current_line_number << "]" << ": " << "Insufficient number of components for \"" << type << "\" line"; + throw std::runtime_error(error_message.str()); + } + target_vector.push_back(vec3); + }; + }; + + std::optional current_object, current_group; + check_filter_func_t check_object = return_true; + check_filter_func_t check_group = return_true; + if (settings.selected_objects().size() > 0) { + check_object = [&]()->bool { + auto selected = settings.selected_objects(); + auto begin = selected.begin(); + auto end = selected.end(); + return std::find(begin, end, current_object.value_or("")) == end; + }; + } + if (settings.selected_groups().size() > 0) { + check_group = [&]()->bool { + auto selected = settings.selected_groups(); + auto begin = selected.begin(); + auto end = selected.end(); + return std::find(begin, end, current_group.value_or("")) == end; + }; + } + + static const auto process_vertex_line = create_vec3_inserter(position_data); + static const auto process_normal_line = create_vec3_inserter(normal_data); + static const line_processor_func_t process_texcoord_line = [&](const std::string &type, const std::string &content) { + std::array data_vec3; + + using insert_data_func_t = std::function; + + static const std::array insert_data = { + [&](void) { texcoord_data.push_back(data_vec3[0]); }, + [&](void) { texcoord_data.push_back(std::array{data_vec3[0], data_vec3[1]}); }, + [&](void) { texcoord_data.push_back(data_vec3); }, + }; + + auto line_size = content.size(); + auto line_begin = content.data(); + auto line_end = line_begin + line_size; + auto content_begin = line_begin; + decltype(line_size) content_begin_pos = 0; + size_t component_count = 0; + + while(component_count < 3 && content_begin_pos < line_size) { + auto content_end_pos = content.find(' ', content_begin_pos); + decltype(content_begin) content_end; + if (content_end_pos == std::string::npos) { + content_end_pos = line_size; + content_end = line_end; + } else { + content_end = line_begin + content_end_pos; + } + auto [number_end, conversion_error] = std::from_chars( + content_begin, + content_end, + data_vec3[component_count], + std::chars_format::fixed + ); + if (conversion_error != std::errc() || number_end != content_end) { + throw std::runtime_error("Invalid float data: " + std::string(content_begin, content_end)); + } + + content_begin_pos = content_end_pos + 1; + content_begin = line_begin + content_begin_pos; + ++component_count; + } + + insert_data[component_count - 1](); + }; + + + std::string line; + + static const std::map line_post_processor_map{ + { "o", [&](const std::string &type, const std::string &content) { + assign_if_not_empty(current_object, content); + }}, + { "g", [&](const std::string &type, const std::string &content) { + assign_if_not_empty(current_group, content); + }}, + { "f", [&](const std::string &type, const std::string &content) { + if (!check_object() || !check_group()) { + return; + } + // TODO: Implement face triangulation and context data population. + }}, + { "v", process_vertex_line }, + { "vt", process_texcoord_line }, + { "vn", process_normal_line }, + }; + + while (std::getline(settings.input(), line)) { + ++current_line_number; + + if (line.empty() || line[0] == '#') { + continue; // Skip empty lines and comments + } + + std::istringstream line_parser(line); + std::string line_type; + + std::getline(line_parser, line_type, ' '); + if (line_type.empty() || !line_post_processor_map.contains(line_type)) { + continue; + } + + line_post_processor_map.at(line_type)(line_type, line.substr(line.find(' ') + 1)); + } + } +} + +#endif /* _WAVEFRONT_PARSER_PARSER_HPP_ */ \ No newline at end of file diff --git a/src/helper.hpp b/src/helper.hpp deleted file mode 100644 index 42c4d75..0000000 --- a/src/helper.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef APP_HELPER_H -#define APP_HELPER_H - -#include -#include -#include - -template -constexpr std::array array_fill(const T& value) { - std::array array{}; - array.fill(value); - return array; -} - -template -constexpr void _static_for_impl(F &&f, std::index_sequence) { - (f(std::integral_constant{}), ...); -} - -template -constexpr void static_for(F &&f) { - _static_for_impl(std::forward(f), std::make_index_sequence{}); -} - -#endif // APP_HELPER_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d651e51..12aef5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,129 +1,124 @@ -#include -#include -#include -#include -#include #include -#include -#include #include -#include -#include -#include +#include #include -#include "wavefront.hpp" +#include +#include +#include +#include +#include +#include +#include +#include -namespace fs = std::filesystem; +#include "file.hpp" +#include "settings.hpp" -void printUsage() { - std::cout - << "Usage: parse-wavefront [options] " << std::endl - << "Options:" << std::endl - << " --filter-object / -O Filter objects by name (can be used multiple times)" << std::endl - << " --filter-group / -G Filter groups by name (can be used multiple times)" << std::endl - << " --output-float-set / -f Output float set to file" << std::endl - << " --output-position / -p Output positions to file" << std::endl - << " --output-vertex / -v Output vertices to file" << std::endl - << " --output-mesh / -m Output mesh to file" << std::endl - << " --output-normal-file / -n Output normals to file" << std::endl - << " --output-texcoord-file / -t Output texture coordinates to file" << std::endl - << " -- Stop processing flags" << std::endl - ; - std::cout - << " Input Wavefront OBJ file" << std::endl - << "Examples:" << std::endl - << " parse-wavefront --float64 --output-float-set output.fset --output-position output.pos --filter-object Car,Wheel --filter-group Metal -- model.obj" << std::endl - ; +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"; } -[[noreturn]] -void exitWithError(const std::string& message) { - std::cerr << "Error: " << message << std::endl; - std::exit(1); -} +int main(int argc, char** argv) { + using namespace wavefront::parser; -int main(int argc, char *argv[]) { - std::vector selected_objects, selected_groups; - bool process_flags = true; - bool has_input_file = false; - bool has_output_float_set_file = false; - bool has_output_position_file = false; - bool has_output_mesh_file = false; - bool has_output_vertex_file = false; - std::string input_file_path; - for (decltype(argc) i = 1; i < argc; ++i) { - std::string arg = argv[i]; - if (!process_flags) { - if (has_input_file) { - std::cerr << "Unexpected argument after the input file: " << arg << std::endl; - printUsage(); - return 1; - } - has_input_file = true; - input_file_path = arg; - continue; - } - if (arg == "--filter-object" || arg == "-O") { - if (i + 1 < argc) { - selected_groups.push_back(argv[++i]); - } - } else if (arg == "--filter-group" || arg == "-G") { - if (i + 1 < argc) { - selected_groups.push_back(argv[++i]); - } - } else if (arg == "--") { - process_flags = false; - } else if (arg.starts_with("-")) { - std::cerr << "Unknown option: " << arg << std::endl; - printUsage(); - return 1; - } else { - input_file_path = arg; - has_input_file = true; - } - } - if (!has_input_file) { - std::cerr << "No input file specified." << std::endl; - printUsage(); + if (argc < 3) { + usage(argv[0]); return 1; } - // if (!has_output_float_set_file) { - // std::cerr << "No output float set file specified." << std::endl; - // printUsage(); - // return 1; - // } - // if (!has_output_vertex_file) { - // std::cerr << "No output vertex file specified." << std::endl; - // printUsage(); - // return 1; - // } - // if (!has_output_mesh_file) { - // std::cerr << "No output mesh file specified." << std::endl; - // printUsage(); - // return 1; - // } - // if (!has_output_position_file) { - // std::cerr << "No output position file specified." << std::endl; - // printUsage(); - // return 1; - // } - // WaveFrontFilter wavefront_filter( - // selected_objects.empty() ? std::nullopt : std::make_optional(selected_objects), - // selected_groups.empty() ? std::nullopt : std::make_optional(selected_groups) - // ); - std::ifstream input_file; - std::istream &input_stream = (input_file_path == "") - ? std::cin - : (input_file.open(input_file_path), input_file); + SettingsBuilder settings_builder; + int arg_index = 1; + bool end_of_options = false; + std::vector positional; - if (!input_stream) { - std::cerr << "Failed to open file: " << input_file_path << std::endl; - std::exit(1); + 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; + } } - auto wavefront_lines = parse_wavefront(input_stream); + 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(); + + std::variant, File> file = [&]() -> decltype(file) { + if (settings.use_float64()) { + return File(); + } else { + return File(); + } + }(); + + std::visit([&](auto& file) { + file.parse(settings); + }, file); return 0; } diff --git a/src/numset.hpp b/src/numset.hpp index fe92750..8b13999 100644 --- a/src/numset.hpp +++ b/src/numset.hpp @@ -6,12 +6,12 @@ #include #include -template +template struct float_is_equal { - bool operator()(T a, T b) const { - static const auto epsilon = std::numeric_limits::epsilon(); - static const auto min_value = std::numeric_limits::min(); - static const auto max_value = std::numeric_limits::max(); + bool operator()(FloatType a, FloatType b) const { + static const auto epsilon = std::numeric_limits::epsilon(); + static const auto min_value = std::numeric_limits::min(); + static const auto max_value = std::numeric_limits::max(); // Nothing is equal to NaN (not even NaN) if (std::isnan(a) || std::isnan(b)) { return false; @@ -27,10 +27,10 @@ struct float_is_equal { } }; -template +template struct float_compare_less { - bool operator()(T a, T b) const { - static const auto is_equal_function = float_is_equal(); + bool operator()(FloatType a, FloatType b) const { + static const auto is_equal_function = float_is_equal(); if (!is_equal_function(a, b)) { return a < b; } @@ -38,12 +38,12 @@ struct float_compare_less { } }; -template +template struct less_with_nan_first_and_nearly_equal { - bool operator()(T a, T b) const { - static const auto epsilon = std::numeric_limits::epsilon(); - static const auto min_value = std::numeric_limits::min(); - static const auto max_value = std::numeric_limits::max(); + bool operator()(FloatType a, FloatType b) const { + static const auto epsilon = std::numeric_limits::epsilon(); + static const auto min_value = std::numeric_limits::min(); + static const auto max_value = std::numeric_limits::max(); if (std::isnan(a)) { return !std::isnan(b); @@ -64,7 +64,7 @@ struct less_with_nan_first_and_nearly_equal { } }; -template -using numset_t = std::set>; +template +using numset_t = std::set>; #endif // APP_NUMSET_H diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 0000000..6199a93 --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,141 @@ +#include "settings.hpp" +#include + +namespace wavefront::parser { + std::istream &Settings::input() const { + return *input_; + } + + std::ostream &Settings::output() const { + return *output_; + } + + const std::vector &Settings::selected_objects() const { + return selected_objects_; + } + + const std::vector &Settings::selected_groups() const { + return selected_groups_; + } + + bool Settings::extract_normals() const { + return extract_normals_; + } + + bool Settings::extract_texcoords() const { + return extract_texcoords_; + } + + bool Settings::use_float64() const { + return use_float64_; + } + + Settings::Settings( + std::istream *input, + std::ostream *output, + std::vector selected_objects, + std::vector selected_groups, + bool extract_normals, + bool extract_texcoords, + bool use_float64 + ) : + input_(input), + output_(output), + selected_objects_(std::move(selected_objects)), + selected_groups_(std::move(selected_groups)), + extract_normals_(extract_normals), + extract_texcoords_(extract_texcoords), + use_float64_(use_float64) + {} + + std::istream &SettingsBuilder::input() { + return *input_; + } + + SettingsBuilder &SettingsBuilder::input(std::istream &in) { + input_ = ∈ + input_handle_.reset(); + return *this; + } + + SettingsBuilder &SettingsBuilder::input(const std::string &filename) { + input_handle_ = std::make_unique(filename); + input_ = reinterpret_cast(input_handle_.get()); + return *this; + } + + SettingsBuilder &SettingsBuilder::input(std::ifstream &&file) { + input_handle_ = std::make_unique(std::move(file)); + input_ = reinterpret_cast(input_handle_.get()); + return *this; + } + + std::ostream &SettingsBuilder::output() { + return *output_; + } + + SettingsBuilder &SettingsBuilder::output(std::ostream &out) { + output_ = &out; + output_handle_.reset(); + return *this; + } + + SettingsBuilder &SettingsBuilder::output(const std::string &filename) { + output_handle_ = std::make_unique(filename); + output_ = reinterpret_cast(output_handle_.get()); + return *this; + } + + SettingsBuilder &SettingsBuilder::output(std::ofstream &&file) { + output_handle_ = std::make_unique(std::move(file)); + output_ = reinterpret_cast(output_handle_.get()); + return *this; + } + + bool SettingsBuilder::with_normals() const { + return with_normals_; + } + + SettingsBuilder &SettingsBuilder::with_normals(bool value) { + with_normals_ = value; + return *this; + } + + bool SettingsBuilder::with_texcoords() const { + return with_texcoords_; + } + + SettingsBuilder &SettingsBuilder::with_texcoords(bool value) { + with_texcoords_ = value; + return *this; + } + + bool SettingsBuilder::use_float64() const { + return use_float64_; + } + + SettingsBuilder &SettingsBuilder::use_float64(bool value) { + use_float64_ = value; + return *this; + } + + std::vector &SettingsBuilder::selected_objects() { + return selected_objects_; + } + + std::vector &SettingsBuilder::selected_groups() { + return selected_groups_; + } + + Settings SettingsBuilder::build() const { + return Settings( + input_, + output_, + std::move(selected_objects_), + std::move(selected_groups_), + with_normals_, + with_texcoords_, + use_float64_ + ); + } +} diff --git a/src/settings.hpp b/src/settings.hpp new file mode 100644 index 0000000..93d6d74 --- /dev/null +++ b/src/settings.hpp @@ -0,0 +1,83 @@ +#ifndef _WAVEFRONT_PARSER_SETTINGS_HPP_ +#define _WAVEFRONT_PARSER_SETTINGS_HPP_ + +#include +#include +#include + +namespace wavefront::parser { + class Settings { + friend class SettingsBuilder; + private: + std::istream *input_; + std::ostream *output_; + std::vector selected_objects_; + std::vector selected_groups_; + bool extract_normals_; + bool extract_texcoords_; + bool use_float64_; + + Settings( + std::istream *input, + std::ostream *output, + std::vector selected_objects, + std::vector selected_groups, + bool extract_normals, + bool extract_texcoords, + bool use_float64 + ); + public: + std::istream &input() const; + std::ostream &output() const; + const std::vector &selected_objects() const; + const std::vector &selected_groups() const; + bool extract_normals() const; + bool extract_texcoords() const; + bool use_float64() const; + }; + + class SettingsBuilder { + std::istream *input_; + std::ostream *output_; + + std::vector selected_objects_; + std::vector selected_groups_; + bool with_normals_ = false; + bool with_texcoords_ = false; + + bool use_float64_ = false; + + // Keep ownership of opened file streams so pointers remain valid + std::unique_ptr input_handle_; + std::unique_ptr output_handle_; + + public: + SettingsBuilder() : input_(&std::cin), output_(&std::cout) {} + + std::istream &input(); + SettingsBuilder &input(std::istream &); + SettingsBuilder &input(const std::string &filename); + SettingsBuilder &input(std::ifstream &&file); + + std::ostream &output(); + SettingsBuilder &output(std::ostream &); + SettingsBuilder &output(const std::string &filename); + SettingsBuilder &output(std::ofstream &&file); + + std::vector &selected_objects(); + std::vector &selected_groups(); + + bool with_normals() const; + SettingsBuilder &with_normals(bool value); + + bool with_texcoords() const; + SettingsBuilder &with_texcoords(bool value); + + bool use_float64() const; + SettingsBuilder &use_float64(bool value); + + Settings build() const; + }; +} + +#endif /* _WAVEFRONT_PARSER_SETTINGS_HPP_ */ diff --git a/src/wavefront.cpp b/src/wavefront.cpp deleted file mode 100644 index 054b891..0000000 --- a/src/wavefront.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include - -#include "wavefront.hpp" - -WaveFrontLine::WaveFrontLine(const std::size_t &line_number, const std::string &type, const std::string &content) - : line_number_(line_number), type_(type), content_(content) { -} - -const std::size_t &WaveFrontLine::line_number() { - return line_number_; -} -const std::string &WaveFrontLine::type() { - return type_; -} -const std::string &WaveFrontLine::content() { - return content_; -} - -const WaveFrontFilter WaveFrontFilter::no_filter{std::nullopt, std::nullopt}; - -inline std::string trim(const std::string &source) { - static const auto if_space = [](auto source_char){ - return std::isspace(source_char); - }; - auto left = std::find_if_not(source.begin(), source.end(), if_space); - auto right = std::find_if_not(source.rbegin(), source.rend(), if_space).base(); - return std::string(left, right); -} - -inline void assign_if_not_empty(std::optional &context, const std::string &name) { - auto trimmed_name = trim(name); - if (trimmed_name.empty()) { - context.reset(); - } else { - context = name; - } -} - -std::map> parse_wavefront( - std::istream &input_stream, - const WaveFrontFilter &filter -) { - std::map> line_data; - std::optional current_object, current_group; - std::size_t current_line_number = 0; - bool select_face = true; - using check_filter_func_t = std::function; - static const auto return_true = []()->bool { return true; }; - check_filter_func_t check_object = return_true; - check_filter_func_t check_group = return_true; - if (filter.selected_objects.has_value()) { - check_object = [&]()->bool { - auto selected = filter.selected_objects.value(); - auto begin = selected.begin(); - auto end = selected.end(); - return std::find(begin, end, current_object.value_or("")) == end; - }; - } - if (filter.selected_groups.has_value()) { - check_group = [&]()->bool { - auto selected = filter.selected_objects.value(); - auto begin = selected.begin(); - auto end = selected.end(); - return std::find(begin, end, current_object.value_or("")) == end; - }; - } - using line_processor_func_t = std::function; - line_processor_func_t insert_line = [&](const std::string &type, const std::string &content) { - line_data[type].push_back(WaveFrontLine( - current_line_number, - type, - content - )); - }; - static const std::map line_post_processor_map{ - { "o", [&](const std::string &type, const std::string &content) { - assign_if_not_empty(current_object, content); - }}, - { "g", [&](const std::string &type, const std::string &content) { - assign_if_not_empty(current_group, content); - }}, - { "f", [&](const std::string &type, const std::string &content) { - if (!check_object() || !check_group()) { - return; - } - insert_line(type, content); - }}, - { "v", insert_line }, - { "vt", insert_line }, - { "vn", insert_line }, - }; - - { - std::string line; - while(std::getline(input_stream, line)) { - ++current_line_number; - - if (line.empty() || line[0] == '#') { - continue; // Skip empty lines and comments - } - - std::istringstream line_parser(line); - std::string line_type; - - std::getline(line_parser, line_type, ' '); - if (line_type.empty()) { - continue; - } - - auto preprocessor_iterator = line_post_processor_map.find(line_type); - if (preprocessor_iterator != line_post_processor_map.end()) { - std::ostringstream line_content; - line_content << line_parser.rdbuf(); - (preprocessor_iterator->second)(line_type, line_content.str()); - } - } - } - - return line_data; -} \ No newline at end of file diff --git a/src/wavefront.hpp b/src/wavefront.hpp deleted file mode 100644 index 311199e..0000000 --- a/src/wavefront.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef APP_WAVEFRONT_HPP -#define APP_WAVEFRONT_HPP - -#include -#include -#include -#include -#include -#include - -class WaveFrontLine { - std::size_t line_number_; - std::string type_; - std::string content_; - -public: - WaveFrontLine(const std::size_t &line_number, const std::string &type, const std::string &content); - ~WaveFrontLine() = default; - WaveFrontLine(const WaveFrontLine &) = default; - WaveFrontLine(WaveFrontLine &&) = default; - -public: - const std::size_t &line_number(); - const std::string &type(); - const std::string &content(); -}; - -class WaveFrontFilter { -public: - std::optional> selected_objects; - std::optional> selected_groups; -public: - static const WaveFrontFilter no_filter; -}; - -std::map> parse_wavefront( - std::istream &input_stream, - const WaveFrontFilter &filter = WaveFrontFilter::no_filter -); - -#endif // APP_WAVEFRONT_HPP