initial parser and compiler
This commit is contained in:
15
src/helper.hpp
Normal file
15
src/helper.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef APP_HELPER_H
|
||||
#define APP_HELPER_H
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
|
||||
template<std::size_t N, typename T>
|
||||
constexpr std::array<T, N> array_fill(const T& value) {
|
||||
std::array<T, N> array{};
|
||||
array.fill(value);
|
||||
return array;
|
||||
}
|
||||
|
||||
#endif // APP_HELPER_H
|
||||
139
src/main.cpp
Normal file
139
src/main.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "wavefront.hpp"
|
||||
#include "output.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void printUsage() {
|
||||
std::cout
|
||||
<< "Usage: parse-wavefront [options] <input.obj>" << std::endl
|
||||
<< "Options:" << std::endl
|
||||
<< " --filter-object / -O <name> Filter objects by name (can be used multiple times)" << std::endl
|
||||
<< " --filter-group / -G <name> Filter groups by name (can be used multiple times)" << std::endl
|
||||
<< " --output-float-set / -f <file> Output float set to file" << std::endl
|
||||
<< " --output-position / -p <file> Output positions to file" << std::endl
|
||||
<< " --output-vertex / -v <file> Output vertices to file" << std::endl
|
||||
<< " --output-mesh / -m <file> Output mesh to file" << std::endl
|
||||
<< " --output-normal-file / -n <file> Output normals to file" << std::endl
|
||||
<< " --output-texcoord-file / -t <file> Output texture coordinates to file" << std::endl
|
||||
<< " -- Stop processing flags" << std::endl
|
||||
;
|
||||
std::cout
|
||||
<< " <input.obj> 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
|
||||
;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void exitWithError(const std::string& message) {
|
||||
std::cerr << "Error: " << message << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::vector<std::string> 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();
|
||||
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;
|
||||
// }
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Output output;
|
||||
|
||||
output.compile(wavefront_parser);
|
||||
|
||||
return 0;
|
||||
}
|
||||
70
src/numset.hpp
Normal file
70
src/numset.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef APP_NUMSET_H
|
||||
#define APP_NUMSET_H
|
||||
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
|
||||
template <typename T>
|
||||
struct float_is_equal {
|
||||
bool operator()(T a, T b) const {
|
||||
static const auto epsilon = std::numeric_limits<T>::epsilon();
|
||||
static const auto min_value = std::numeric_limits<T>::min();
|
||||
static const auto max_value = std::numeric_limits<T>::max();
|
||||
// Nothing is equal to NaN (not even NaN)
|
||||
if (std::isnan(a) || std::isnan(b)) {
|
||||
return false;
|
||||
}
|
||||
// If both are exactly equal, they are equal, no further checks.
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
auto difference = std::abs(a - b);
|
||||
auto finite_amplitude = std::min(std::abs(a) + std::abs(b), max_value);
|
||||
auto min_difference = std::max(min_value, finite_amplitude * epsilon);
|
||||
return difference < min_difference;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct float_compare_less {
|
||||
bool operator()(T a, T b) const {
|
||||
static const auto is_equal_function = float_is_equal<T>();
|
||||
if (!is_equal_function(a, b)) {
|
||||
return a < b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct less_with_nan_first_and_nearly_equal {
|
||||
bool operator()(T a, T b) const {
|
||||
static const auto epsilon = std::numeric_limits<T>::epsilon();
|
||||
static const auto min_value = std::numeric_limits<T>::min();
|
||||
static const auto max_value = std::numeric_limits<T>::max();
|
||||
|
||||
if (std::isnan(a)) {
|
||||
return !std::isnan(b);
|
||||
}
|
||||
if (std::isnan(b)) {
|
||||
return false;
|
||||
}
|
||||
if (a == b) {
|
||||
return false;
|
||||
}
|
||||
auto difference = std::abs(a - b);
|
||||
auto finite_amplitude = std::min(std::abs(a) + std::abs(b), max_value);
|
||||
auto min_difference = std::max(min_value, finite_amplitude * epsilon);
|
||||
if (difference < min_difference) {
|
||||
return false;
|
||||
}
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using numset_t = std::set<T, less_with_nan_first_and_nearly_equal<T>>;
|
||||
|
||||
#endif // APP_NUMSET_H
|
||||
93
src/output.cpp
Normal file
93
src/output.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include "output.hpp"
|
||||
#include "helper.hpp"
|
||||
#include "numset.hpp"
|
||||
|
||||
Output::Output() {
|
||||
float_data_.push_back(std::numeric_limits<float>::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<std::string, 3> attribute_name = {
|
||||
"position",
|
||||
"texture",
|
||||
"normal"
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
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<const std::vector<std::array<float, 3>> &()>;
|
||||
static const std::array<get_data_reference_func_t, 3> get_data_reference{{
|
||||
[&]() -> const std::vector<std::array<float, 3>> & {
|
||||
return input.obj_v();
|
||||
},
|
||||
[&]() -> const std::vector<std::array<float, 3>> & {
|
||||
return input.obj_vt();
|
||||
},
|
||||
[&]() -> const std::vector<std::array<float, 3>> & {
|
||||
return input.obj_vn();
|
||||
},
|
||||
}};
|
||||
std::set<float, less_with_nan_first_and_nearly_equal<float>> float_set;
|
||||
std::size_t texture_size = 0;
|
||||
|
||||
using check_attribute_dimensions_func_t = std::function<void(std::uint32_t, std::uint32_t)>;
|
||||
std::array<check_attribute_dimensions_func_t, 3> 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;
|
||||
}
|
||||
32
src/output.hpp
Normal file
32
src/output.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef APP_MODEL_HPP
|
||||
#define APP_MODEL_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "wavefront.hpp"
|
||||
|
||||
class Output {
|
||||
std::vector<float> float_data_;
|
||||
std::vector<std::array<std::uint32_t, 2>> vector_2d_data_;
|
||||
std::vector<std::array<std::uint32_t, 3>> vector_3d_data_;
|
||||
std::variant<
|
||||
uint32_t,
|
||||
std::array<uint32_t, 2>,
|
||||
std::array<uint32_t, 3>
|
||||
> vertex_data_;
|
||||
std::vector<std::array<uint32_t, 3>> triangle_data_;
|
||||
|
||||
public:
|
||||
Output();
|
||||
~Output() = default;
|
||||
|
||||
private:
|
||||
void compile_float_data(const WaveFront &input);
|
||||
|
||||
public:
|
||||
void compile(const WaveFront &input);
|
||||
};
|
||||
|
||||
#endif // APP_MODEL_HPP
|
||||
278
src/wavefront.cpp
Normal file
278
src/wavefront.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "wavefront.hpp"
|
||||
#include "helper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
WaveFront::Settings::Settings(
|
||||
std::optional<std::vector<std::string>> selected_objects,
|
||||
std::optional<std::vector<std::string>> selected_groups
|
||||
) :
|
||||
selected_objects_(selected_objects),
|
||||
selected_groups_(selected_groups)
|
||||
{
|
||||
}
|
||||
|
||||
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 {
|
||||
return line_number_;
|
||||
}
|
||||
|
||||
const std::map<std::string, WaveFront::_parse_line_t> 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<float>::quiet_NaN()));
|
||||
obj_vt_.push_back(array_fill<3>(std::numeric_limits<float>::quiet_NaN()));
|
||||
obj_vn_.push_back(array_fill<3>(std::numeric_limits<float>::quiet_NaN()));
|
||||
obj_f_.push_back(array_fill<3>(array_fill<3>(uint32_t(0))));
|
||||
}
|
||||
|
||||
void WaveFront::parse(std::istream &input_stream) {
|
||||
std::string line;
|
||||
|
||||
while(std::getline(input_stream, line)) {
|
||||
++current_line_;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
} else {
|
||||
current_object_ = object_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<float, 3> data = array_fill<3>(std::numeric_limits<float>::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<float, 3> data = array_fill<3>(std::numeric_limits<float>::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<float, 3> data = array_fill<3>(std::numeric_limits<float>::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.");
|
||||
};
|
||||
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.");
|
||||
}
|
||||
|
||||
obj_vt_.push_back(data);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t vertex_count = 0;
|
||||
std::array<std::array<uint32_t, 3>, 3> triangle;
|
||||
std::string token;
|
||||
|
||||
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<uint32_t, 3> WaveFront::parse_vertex(std::string &token) {
|
||||
std::istringstream token_parser(token);
|
||||
std::array<uint32_t, 3> vertex_result{0, 0, 0};
|
||||
std::string vertex_token;
|
||||
std::size_t attribute_count = 0;
|
||||
|
||||
static const std::array<std::vector<std::array<float, 3>>, 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());
|
||||
}
|
||||
}
|
||||
|
||||
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<int64_t>(std::numeric_limits<uint32_t>::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<std::array<float, 3>> &WaveFront::obj_v() const noexcept {
|
||||
return obj_v_;
|
||||
}
|
||||
|
||||
const std::vector<std::array<float, 3>> &WaveFront::obj_vt() const noexcept {
|
||||
return obj_vt_;
|
||||
}
|
||||
|
||||
const std::vector<std::array<float, 3>> &WaveFront::obj_vn() const noexcept {
|
||||
return obj_vn_;
|
||||
}
|
||||
|
||||
const std::vector<std::array<std::array<std::uint32_t, 3>, 3>> &WaveFront::obj_f() const noexcept {
|
||||
return obj_f_;
|
||||
}
|
||||
80
src/wavefront.hpp
Normal file
80
src/wavefront.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef APP_WAVEFRONT_HPP
|
||||
#define APP_WAVEFRONT_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::vector<std::string>> selected_objects_;
|
||||
std::optional<std::vector<std::string>> selected_groups_;
|
||||
|
||||
public:
|
||||
Settings() = default;
|
||||
Settings(
|
||||
std::optional<std::vector<std::string>> selected_objects,
|
||||
std::optional<std::vector<std::string>> 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<std::array<float, 3>> obj_v_, obj_vt_, obj_vn_;
|
||||
std::vector<std::array<std::array<std::uint32_t, 3>, 3>> obj_f_;
|
||||
std::size_t num_attributes_, num_texture_coordinates_, current_line_;
|
||||
std::optional<std::string> current_object_, current_group_;
|
||||
|
||||
static const std::map<std::string, _parse_line_t> _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<uint32_t, 3> parse_vertex(std::string &line_parser);
|
||||
|
||||
public:
|
||||
const std::vector<std::array<float, 3>> &obj_v() const noexcept;
|
||||
const std::vector<std::array<float, 3>> &obj_vt() const noexcept;
|
||||
const std::vector<std::array<float, 3>> &obj_vn() const noexcept;
|
||||
const std::vector<std::array<std::array<std::uint32_t, 3>, 3>> &obj_f() const noexcept;
|
||||
|
||||
public:
|
||||
explicit WaveFront(const Settings &settings = Settings(std::nullopt, std::nullopt));
|
||||
~WaveFront() = default;
|
||||
|
||||
void parse(std::istream &input_string);
|
||||
};
|
||||
|
||||
#endif // APP_WAVEFRONT_HPP
|
||||
Reference in New Issue
Block a user