diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..10c1f5f --- /dev/null +++ b/build.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Determine absolute project root (script’s parent directory) +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Helper to configure & build +# $1 = build directory name under PROJECT_ROOT/build +# $2 = CMake build type (arbitrary string; flags are driven by -DCMAKE_CXX_FLAGS) +# $3 = CXX flags +cmake_build() { + local name=$1 + local type=$2 + local flags=$3 + local build_dir="${PROJECT_ROOT}/build/${name}" + + echo "=== Configuring ${name} (${flags}) ===" + rm -rf "${build_dir}" + mkdir -p "${build_dir}" + pushd "${build_dir}" >/dev/null + + cmake \ + -DCMAKE_BUILD_TYPE="${type}" \ + -DCMAKE_CXX_FLAGS="${flags}" \ + "${PROJECT_ROOT}" + make -j$(nproc) + + popd >/dev/null +} + +# 1) Debug: -g +cmake_build "Debug" "Debug" "-g" + +# 2) Release: -O3 +cmake_build "Release" "Release" "-O3" + +# 3) RelWithDebugInfo: -g -O3 +cmake_build "RelWithDebugInfo" "RelWithDebInfo" "-g -O3" + +# 4) Generate verbose assembly (.S) for all translation units in RelWithDebugInfo +echo "=== Generating .S assembly in build/RelWithDebugInfo ===" +ASM_OUT_DIR="${PROJECT_ROOT}/build/RelWithDebugInfo/asm" +mkdir -p "${ASM_OUT_DIR}" + +# adjust the source‐file patterns as needed (e.g. .cc, .cxx) +find "${PROJECT_ROOT}" -type f \( -name '*.cpp' -o -name '*.cc' \) | while read -r src; do + fname="$(basename "${src}")" + base="${fname%.*}" + g++ -g -O3 -masm=intel -fno-verbose-asm -std=c++20 -S "${src}" \ + -o "${ASM_OUT_DIR}/${base}.S" +done + +echo "All builds complete." diff --git a/src/helper.hpp b/src/helper.hpp index dd34816..42c4d75 100644 --- a/src/helper.hpp +++ b/src/helper.hpp @@ -12,4 +12,14 @@ constexpr std::array array_fill(const T& 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 6638a23..d651e51 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,6 @@ #include #include "wavefront.hpp" -#include "output.hpp" namespace fs = std::filesystem; @@ -109,31 +108,22 @@ int main(int argc, char *argv[]) { // printUsage(); // return 1; // } - WaveFront::Settings wavefront_settings( - selected_objects.empty() ? std::nullopt : std::make_optional(selected_objects), - selected_groups.empty() ? std::nullopt : std::make_optional(selected_groups) - ); - WaveFront wavefront_parser(wavefront_settings); + // WaveFrontFilter wavefront_filter( + // selected_objects.empty() ? std::nullopt : std::make_optional(selected_objects), + // selected_groups.empty() ? std::nullopt : std::make_optional(selected_groups) + // ); - try { - if (input_file_path == "-") { - wavefront_parser.parse(std::cin); - } else { - std::ifstream input_file(input_file_path); - if (!input_file) { - std::cerr << "Failed to open input file: " << input_file_path << std::endl; - return 1; - } - wavefront_parser.parse(input_file); - } - } catch (const WaveFront::parse_error& e) { - std::cerr << input_file_path << ":" << e.line_number() << ": " << e.what() << std::endl; - return 1; + std::ifstream input_file; + std::istream &input_stream = (input_file_path == "") + ? std::cin + : (input_file.open(input_file_path), input_file); + + if (!input_stream) { + std::cerr << "Failed to open file: " << input_file_path << std::endl; + std::exit(1); } - Output output; - - output.compile(wavefront_parser); + auto wavefront_lines = parse_wavefront(input_stream); return 0; } diff --git a/src/output.cpp b/src/output.cpp deleted file mode 100644 index 6f2688d..0000000 --- a/src/output.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include -#include -#include -#include "output.hpp" -#include "helper.hpp" -#include "numset.hpp" - -Output::Output() { - float_data_.push_back(std::numeric_limits::quiet_NaN()); - vector_2d_data_.push_back(array_fill<2, std::uint32_t>(0)); - vector_3d_data_.push_back(array_fill<3, std::uint32_t>(0)); - triangle_data_.push_back(array_fill<3, uint32_t>(0)); -} - -void Output::compile(const WaveFront &input) { - compile_float_data(input); -} - -static const std::array attribute_name = { - "position", - "texture", - "normal" -}; - -template -void ensure_dims(std::size_t num_dimensions, std::uint32_t attribute_index) { - if (num_dimensions != 3) { - std::stringstream message; - message << "Unexpected unaligned " << attribute_name[attribute_index] << " size: expected exactly " << N << " dimensions, got " << num_dimensions; - throw std::runtime_error(message.str()); - } -} - -void Output::compile_float_data(const WaveFront &input) { - using get_data_reference_func_t = std::function> &()>; - static const std::array get_data_reference{{ - [&]() -> const std::vector> & { - return input.obj_v(); - }, - [&]() -> const std::vector> & { - return input.obj_vt(); - }, - [&]() -> const std::vector> & { - return input.obj_vn(); - }, - }}; - std::set> float_set; - std::size_t texture_size = 0; - - using check_attribute_dimensions_func_t = std::function; - std::array check_attribute_dimensions{{ - ensure_dims<3>, - [&](std::uint32_t num_dimensions, std::uint32_t attribute_index) { - texture_size = num_dimensions; - check_attribute_dimensions[1] = [&](std::uint32_t num_dimensions, std::uint32_t attribute_index) { - if (texture_size != num_dimensions) { - std::stringstream message; - message << "Unexpected unaligned texture size: expected exactly " << texture_size << " dimensions, got " << num_dimensions; - throw std::runtime_error(message.str()); - } - }; - }, - ensure_dims<3>, - }}; - for (const auto &triangle : input.obj_f()) { - for (const auto &vertex : triangle) { - for (std::uint32_t attribute_index = 0; attribute_index < 3; ++attribute_index) { - const auto &attribute_reference = vertex[attribute_index]; - if (attribute_reference == 0) { - continue; - } - const auto &container = get_data_reference[attribute_index](); - assert(attribute_reference < container.size()); - std::uint32_t dim_count = 0; - for (const auto &value : container.at(attribute_reference)) { - if (std::isnan(value)) { - break; - } - ++dim_count; - float_set.insert(value); - } - assert(dim_count > 0); - check_attribute_dimensions[attribute_index](dim_count, attribute_index); - } - } - } - - float_data_.insert(float_data_.end(), float_set.begin(), float_set.end()); - - int j = 0; -} diff --git a/src/output.hpp b/src/output.hpp deleted file mode 100644 index 5091807..0000000 --- a/src/output.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef APP_MODEL_HPP -#define APP_MODEL_HPP - -#include -#include -#include - -#include "wavefront.hpp" - -class Output { - std::vector float_data_; - std::vector> vector_2d_data_; - std::vector> vector_3d_data_; - std::variant< - uint32_t, - std::array, - std::array - > vertex_data_; - std::vector> triangle_data_; - -public: - Output(); - ~Output() = default; - -private: - void compile_float_data(const WaveFront &input); - -public: - void compile(const WaveFront &input); -}; - -#endif // APP_MODEL_HPP diff --git a/src/wavefront.cpp b/src/wavefront.cpp index 275dc09..054b891 100644 --- a/src/wavefront.cpp +++ b/src/wavefront.cpp @@ -1,278 +1,120 @@ -#include "wavefront.hpp" -#include "helper.hpp" - #include -#include -WaveFront::Settings::Settings( - std::optional> selected_objects, - std::optional> selected_groups -) : -selected_objects_(selected_objects), -selected_groups_(selected_groups) -{ +#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) { } -WaveFront::parse_error::parse_error(std::size_t line_number, const std::string& message) : -std::runtime_error(message), -line_number_(line_number) -{ -} - -std::size_t WaveFront::parse_error::line_number() const noexcept { +const std::size_t &WaveFrontLine::line_number() { return line_number_; } - -const std::map WaveFront::_parse_line_{ - { "v", &WaveFront::parse_v }, - { "vt", &WaveFront::parse_vt }, - { "vn", &WaveFront::parse_vn }, - { "f", &WaveFront::parse_f }, - { "o", &WaveFront::parse_o }, - { "g", &WaveFront::parse_g }, -}; - -WaveFront::WaveFront( - const Settings& settings -) : -settings_(settings), -num_attributes_(0), -num_texture_coordinates_(0), -current_line_(0) -{ - obj_v_.push_back(array_fill<3>(std::numeric_limits::quiet_NaN())); - obj_vt_.push_back(array_fill<3>(std::numeric_limits::quiet_NaN())); - obj_vn_.push_back(array_fill<3>(std::numeric_limits::quiet_NaN())); - obj_f_.push_back(array_fill<3>(array_fill<3>(uint32_t(0)))); +const std::string &WaveFrontLine::type() { + return type_; +} +const std::string &WaveFrontLine::content() { + return content_; } -void WaveFront::parse(std::istream &input_stream) { - std::string line; - - while(std::getline(input_stream, line)) { - ++current_line_; +const WaveFrontFilter WaveFrontFilter::no_filter{std::nullopt, std::nullopt}; - if (line.empty() || line[0] == '#') { - continue; // Skip empty lines and comments - } - - std::istringstream line_parser(line); - std::string token; - - std::getline(line_parser, token, ' '); - if (token.empty()) { - continue; - } - - auto token_parser_iterator = _parse_line_.find(token); - if (token_parser_iterator != _parse_line_.end()) { - (this->*(token_parser_iterator->second))(line_parser); - } - } +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); } -void WaveFront::parse_o(std::istringstream &line_parser) { - std::string object_name; - std::getline(line_parser, object_name); - if (object_name.empty()) { - current_object_.reset(); +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 { - current_object_ = object_name; + context = name; } } -void WaveFront::parse_g(std::istringstream &line_parser) { - std::string group_name; - std::getline(line_parser, group_name); - if (group_name.empty()) { - current_group_.reset(); - } else { - current_group_ = group_name; - } -} - -void WaveFront::parse_v(std::istringstream &line_parser) { - std::array data = array_fill<3>(std::numeric_limits::quiet_NaN()); - std::string element; - std::size_t element_count = 0; - - while (std::getline(line_parser, element, ' ')) { - if (element_count >= 3) { - throw parse_error(current_line_, "Type \"v\" with more than 3 components is not supported."); - } - if (element.empty()) { - break; - } - try { - data[element_count++] = std::stof(element); - } catch (const std::invalid_argument&) { - throw parse_error(current_line_, "Invalid float value in vertex definition."); - } catch (const std::out_of_range&) { - throw parse_error(current_line_, "Float value out of range in vertex definition."); - } - } - if (element_count < 3) { - throw parse_error(current_line_, "Type \"v\" must have exactly 3 components."); - } - obj_v_.push_back(data); -} - -void WaveFront::parse_vn(std::istringstream &line_parser) { - std::array data = array_fill<3>(std::numeric_limits::quiet_NaN()); - std::string element; - std::size_t element_count = 0; - - while (std::getline(line_parser, element, ' ')) { - if (element_count >= 3) { - throw parse_error(current_line_, "Type \"vn\" with more than 3 components is not supported."); - } - if (element.empty()) { - break; - } - try { - data[element_count++] = std::stof(element); - } catch (const std::invalid_argument&) { - throw parse_error(current_line_, "Invalid float value in vertex normal definition."); - } catch (const std::out_of_range&) { - throw parse_error(current_line_, "Float value out of range in vertex normal definition."); - } - } - if (element_count < 3) { - throw parse_error(current_line_, "Type \"vn\" with have exactly 3 components."); - } - obj_vn_.push_back(data); -} - -void WaveFront::parse_vt(std::istringstream &line_parser) { - std::array data = array_fill<3>(std::numeric_limits::quiet_NaN()); - std::string element; - std::size_t element_count = 0; - - while (std::getline(line_parser, element, ' ')) { - if (element_count >= 3) { - throw parse_error(current_line_, "Type \"vt\" with more than 3 components is not supported."); +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 (element.empty()) { - break; - } - try { - data[element_count++] = std::stof(element); - } catch (const std::invalid_argument&) { - throw parse_error(current_line_, "Invalid float value in texture coordinate definition."); - } catch (const std::out_of_range&) { - throw parse_error(current_line_, "Float value out of range in texture coordinate definition."); - } } - - if (element_count < 1) { - throw parse_error(current_line_, "Type \"vt\" must have at least 1 component."); + 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 }, + }; - obj_vt_.push_back(data); -} + { + std::string line; + while(std::getline(input_stream, line)) { + ++current_line_number; -void WaveFront::parse_f(std::istringstream &line_parser) { - if (settings_.selected_objects_.has_value()) { - if (!current_object_.has_value()) { - return; - } - auto &selected_objects = settings_.selected_objects_.value(); - if (std::find(selected_objects.begin(), selected_objects.end(), current_object_.value()) == selected_objects.end()) { - return; - } - } - if (settings_.selected_groups_) { - if (!current_group_.has_value()) { - return; - } - auto selected_begin = settings_.selected_groups_->begin(); - auto selected_end = settings_.selected_groups_->end(); - auto current_value = current_group_.value(); - if (std::find(selected_begin, selected_end, current_value) == selected_end) { - return; - } - } + if (line.empty() || line[0] == '#') { + continue; // Skip empty lines and comments + } - std::size_t vertex_count = 0; - std::array, 3> triangle; - std::string token; + std::istringstream line_parser(line); + std::string line_type; + + std::getline(line_parser, line_type, ' '); + if (line_type.empty()) { + continue; + } - while (std::getline(line_parser, token, ' ')) { - if (token.empty()) { - break; - } - auto vertex_indices = parse_vertex(token); - if (vertex_count < 3) { - triangle[vertex_count] = vertex_indices; - } else { - obj_f_.push_back(triangle); - triangle[0] = triangle[1]; - triangle[1] = triangle[2]; - triangle[2] = vertex_indices; - } - ++vertex_count; - } -} - -std::array WaveFront::parse_vertex(std::string &token) { - std::istringstream token_parser(token); - std::array vertex_result{0, 0, 0}; - std::string vertex_token; - std::size_t attribute_count = 0; - - static const std::array>, 3> data_reference = {obj_v_, obj_vt_, obj_vn_}; - - while (std::getline(token_parser, vertex_token, '/')) { - if (attribute_count >= 3) { - throw parse_error(current_line_, "Face vertex with more than 3 components is not supported."); - } - int64_t attribute_value; - if (vertex_token.empty()) { - attribute_value = 0; - } else { - try { - attribute_value = std::stoll(vertex_token); - } catch (const std::invalid_argument&) { - std::stringstream message; - message << "Invalid face vertex index: " << vertex_token; - throw parse_error(current_line_, message.str()); - } catch (const std::out_of_range&) { - std::stringstream message; - message << "Face vertex index out of range: " << vertex_token; - throw parse_error(current_line_, message.str()); + 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()); } } - - if (attribute_value < 0) { - attribute_value = data_reference[attribute_count].size() + attribute_value; - if (attribute_value < 1 || attribute_value > data_reference[attribute_count].size()) { - std::stringstream message; - message << "Face vertex index out of range: " << vertex_token; - throw parse_error(current_line_, message.str()); - } - } else if (attribute_value >= static_cast(std::numeric_limits::max())) { - std::stringstream message; - message << "Face vertex index out of range: " << vertex_token; - throw parse_error(current_line_, message.str()); - } - vertex_result[attribute_count] = attribute_value; - ++attribute_count; } - return vertex_result; -} -const std::vector> &WaveFront::obj_v() const noexcept { - return obj_v_; -} - -const std::vector> &WaveFront::obj_vt() const noexcept { - return obj_vt_; -} - -const std::vector> &WaveFront::obj_vn() const noexcept { - return obj_vn_; -} - -const std::vector, 3>> &WaveFront::obj_f() const noexcept { - return obj_f_; -} + return line_data; +} \ No newline at end of file diff --git a/src/wavefront.hpp b/src/wavefront.hpp index a899001..311199e 100644 --- a/src/wavefront.hpp +++ b/src/wavefront.hpp @@ -1,80 +1,41 @@ #ifndef APP_WAVEFRONT_HPP #define APP_WAVEFRONT_HPP -#include #include +#include #include #include #include -#include #include -#include -class WaveFront; - -class WaveFront { -public: - enum class AttributeIndex : uint32_t { - POSITION = 0, - TEXTURE = 1, - NORMAL = 2 - }; -public: - class Settings { - friend class WaveFront; - private: - std::optional> selected_objects_; - std::optional> selected_groups_; - - public: - Settings() = default; - Settings( - std::optional> selected_objects, - std::optional> selected_groups - ); - Settings(const Settings&) = default; - Settings(Settings&&) = default; - ~Settings() = default; - }; - - class parse_error : public std::runtime_error { - public: - parse_error(std::size_t line_number, const std::string& message); - std::size_t line_number() const noexcept; - - private: - std::size_t line_number_; - }; -private: - using _parse_line_t = void (WaveFront::*)(std::istringstream&); - Settings settings_; - std::vector> obj_v_, obj_vt_, obj_vn_; - std::vector, 3>> obj_f_; - std::size_t num_attributes_, num_texture_coordinates_, current_line_; - std::optional current_object_, current_group_; - - static const std::map _parse_line_; - -private: - void parse_o(std::istringstream &line_parser); - void parse_g(std::istringstream &line_parser); - void parse_v(std::istringstream &line_parser); - void parse_vn(std::istringstream &line_parser); - void parse_vt(std::istringstream &line_parser); - void parse_f(std::istringstream &line_parser); - std::array parse_vertex(std::string &line_parser); +class WaveFrontLine { + std::size_t line_number_; + std::string type_; + std::string content_; public: - const std::vector> &obj_v() const noexcept; - const std::vector> &obj_vt() const noexcept; - const std::vector> &obj_vn() const noexcept; - const std::vector, 3>> &obj_f() const noexcept; + 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: - explicit WaveFront(const Settings &settings = Settings(std::nullopt, std::nullopt)); - ~WaveFront() = default; - - void parse(std::istream &input_string); + 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