feature: parse coordinates, create the number list
This commit is contained in:
28
src/main.cpp
28
src/main.cpp
@@ -124,13 +124,29 @@ int main(int argc, char** argv) {
|
|||||||
settings.selected_groups())
|
settings.selected_groups())
|
||||||
);
|
);
|
||||||
|
|
||||||
CATCH_AND_RETURN(triangle_data, wavefront::parse_error, 1, wavefront::parse_face_data(scan_data));
|
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;
|
||||||
|
|
||||||
std::cerr << "Scanned " << scan_data.total_lines << " lines\n";
|
CATCH_AND_RETURN(face_data, wavefront::parse_error, 1, wavefront::parse_face_data(scan_data));
|
||||||
std::cerr << "Found " << scan_data.category_map["v"].size() << " vertices\n";
|
|
||||||
std::cerr << "Found " << scan_data.category_map["vn"].size() << " normals\n";
|
|
||||||
std::cerr << "Found " << scan_data.category_map["vt"].size() << " texture coordinates\n";
|
|
||||||
std::cerr << "Found " << scan_data.category_map["f"].size() << " faces\n";
|
|
||||||
|
|
||||||
|
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<float>(scan_data, face_data));
|
||||||
|
|
||||||
|
auto number_list = create_number_list<float>(coordinate_data);
|
||||||
|
|
||||||
|
std::cerr << "Generated number list with " << number_list.size() << " values" << std::endl;
|
||||||
|
|
||||||
|
auto coordinate_index_data = create_coordinate_index<float, std::size_t>(coordinate_data, number_list);
|
||||||
|
|
||||||
|
// Form coordinate_list_data : std::vector variant of coordinate_index_data. Internally, it creates std::set initially, stores everything in the set and then convert to vector.
|
||||||
|
// Then we can form coordinate_line_mapping : std::map<file_line_t, std::size_t> pointing for each entry to entry in coordinate_list_data.
|
||||||
|
// Then using face_data.triangle_list and scan_data.category_map[v/vn/vt], coordinate_line_mapping, we can form vertex_list_data. The vertex here is 1-3 IndexType numbers pointing to coordinate_list_data.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ struct less_with_nan_first_and_nearly_equal {
|
|||||||
static const auto min_value = std::numeric_limits<FloatType>::min();
|
static const auto min_value = std::numeric_limits<FloatType>::min();
|
||||||
static const auto max_value = std::numeric_limits<FloatType>::max();
|
static const auto max_value = std::numeric_limits<FloatType>::max();
|
||||||
|
|
||||||
if (std::isnan(a)) {
|
if (std::isnan(a)) [[unlikely]] {
|
||||||
return !std::isnan(b);
|
return !std::isnan(b);
|
||||||
}
|
}
|
||||||
if (std::isnan(b)) {
|
if (std::isnan(b)) [[unlikely]] {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (a == b) {
|
if (a == b) [[unlikely]] {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto difference = std::abs(a - b);
|
auto difference = std::abs(a - b);
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
#include <cassert>
|
|
||||||
#include <charconv>
|
|
||||||
#include <format>
|
|
||||||
#include <functional>
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "parse.hpp"
|
#include "parse.hpp"
|
||||||
@@ -91,11 +87,11 @@ namespace wavefront {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_face_data_result parse_face_data(const scan_result &scan_data) {
|
wavefront_face_data_result_t parse_face_data(const scan_result &scan_data) {
|
||||||
using register_vertex_func_t = std::function<void(triangle_vertex_indices &)>;
|
using register_vertex_func_t = std::function<void(triangle_vertex_indices &)>;
|
||||||
using register_index_func_t = std::function<void(std::set<vertex_index_t> &, const std::size_t &)>;
|
using register_index_func_t = std::function<void(std::set<vertex_index_t> &, const std::size_t &)>;
|
||||||
|
|
||||||
parse_face_data_result result;
|
wavefront_face_data_result_t result;
|
||||||
std::array<triangle_vertex_indices, 3> current_triangle;
|
std::array<triangle_vertex_indices, 3> current_triangle;
|
||||||
std::size_t current_triangle_index;
|
std::size_t current_triangle_index;
|
||||||
std::size_t triangles_added;
|
std::size_t triangles_added;
|
||||||
@@ -106,11 +102,28 @@ namespace wavefront {
|
|||||||
target.emplace(source);
|
target.emplace(source);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const register_vertex_func_t register_vertex_indices = [&](triangle_vertex_indices &vertex) {
|
||||||
|
if (vertex.position_index > 0) {
|
||||||
|
result.index_position_set.emplace(vertex.position_index);
|
||||||
|
} else {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}:{}]: {}",
|
||||||
|
vertex.face_line_number,
|
||||||
|
current_triangle_index,
|
||||||
|
"Position index must be positive integer"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (vertex.normal_index > 0) {
|
||||||
|
result.index_normal_set.emplace(vertex.normal_index);
|
||||||
|
}
|
||||||
|
if (vertex.texcoord_index > 0) {
|
||||||
|
result.index_texcoord_set.emplace(vertex.texcoord_index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const register_vertex_func_t add_vertex_to_triangle = [&](triangle_vertex_indices &vertex) {
|
const register_vertex_func_t add_vertex_to_triangle = [&](triangle_vertex_indices &vertex) {
|
||||||
register_index[1](result.index_position_set, vertex.position_index);
|
register_vertex_indices(vertex);
|
||||||
register_index[!!vertex.normal_index](result.index_normal_set, vertex.normal_index);
|
|
||||||
register_index[!!vertex.texcoord_index](result.index_texcoord_set, vertex.texcoord_index);
|
|
||||||
current_triangle[current_triangle_index++] = vertex;
|
current_triangle[current_triangle_index++] = vertex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,6 +141,7 @@ namespace wavefront {
|
|||||||
add_triangle();
|
add_triangle();
|
||||||
},
|
},
|
||||||
[&](triangle_vertex_indices &vertex) {
|
[&](triangle_vertex_indices &vertex) {
|
||||||
|
register_vertex_indices(vertex);
|
||||||
current_triangle[1] = current_triangle[2];
|
current_triangle[1] = current_triangle[2];
|
||||||
current_triangle[2] = vertex;
|
current_triangle[2] = vertex;
|
||||||
add_triangle();
|
add_triangle();
|
||||||
|
|||||||
321
src/parse.hpp
321
src/parse.hpp
@@ -2,14 +2,22 @@
|
|||||||
#define __WAVEFRONT_PARSE_HPP__
|
#define __WAVEFRONT_PARSE_HPP__
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <charconv>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "scan.hpp"
|
#include "scan.hpp"
|
||||||
|
#include "numset.hpp"
|
||||||
|
|
||||||
namespace wavefront {
|
namespace wavefront {
|
||||||
class parse_error : public std::runtime_error {
|
class parse_error : public std::runtime_error {
|
||||||
@@ -17,7 +25,7 @@ namespace wavefront {
|
|||||||
parse_error(const std::string &message) : std::runtime_error(message) {}
|
parse_error(const std::string &message) : std::runtime_error(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
using vertex_index_t = std::int32_t;
|
using vertex_index_t = std::int64_t;
|
||||||
using file_line_t = typename decltype(std::declval<scan_result>().line_data)::key_type;
|
using file_line_t = typename decltype(std::declval<scan_result>().line_data)::key_type;
|
||||||
|
|
||||||
struct triangle_vertex_indices {
|
struct triangle_vertex_indices {
|
||||||
@@ -27,12 +35,319 @@ namespace wavefront {
|
|||||||
vertex_index_t normal_index;
|
vertex_index_t normal_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct parse_face_data_result {
|
struct wavefront_face_data_result_t {
|
||||||
std::vector<std::array<triangle_vertex_indices, 3>> triangle_list;
|
std::vector<std::array<triangle_vertex_indices, 3>> triangle_list;
|
||||||
std::set<vertex_index_t> index_position_set, index_normal_set, index_texcoord_set;
|
std::set<vertex_index_t> index_position_set, index_normal_set, index_texcoord_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_face_data_result parse_face_data(const scan_result &scan_data);
|
template<typename CoordinateType>
|
||||||
|
struct coordinate_data_t {
|
||||||
|
std::map<file_line_t, std::array<CoordinateType, 3>> position_coordinates;
|
||||||
|
std::map<file_line_t, std::array<CoordinateType, 3>> normal_coordinates;
|
||||||
|
std::variant<
|
||||||
|
std::monostate,
|
||||||
|
std::map<file_line_t, CoordinateType>,
|
||||||
|
std::map<file_line_t, std::array<CoordinateType, 2>>,
|
||||||
|
std::map<file_line_t, std::array<CoordinateType, 3>>
|
||||||
|
> texture_coordinates;
|
||||||
|
};
|
||||||
|
|
||||||
|
wavefront_face_data_result_t parse_face_data(const scan_result &scan_data);
|
||||||
|
|
||||||
|
template<typename FloatType>
|
||||||
|
coordinate_data_t<FloatType> parse_coordinate_data(const scan_result &scan_data, const wavefront_face_data_result_t &face_data) {
|
||||||
|
using namespace std::string_literals;
|
||||||
|
coordinate_data_t<FloatType> result;
|
||||||
|
|
||||||
|
const auto &position_data = scan_data.category_map.at("v"s);
|
||||||
|
for (const auto &position_index : face_data.index_position_set) {
|
||||||
|
std::array<FloatType, 3> position_coordinates;
|
||||||
|
const auto &position_line_index = position_data.at(position_index);
|
||||||
|
const auto &position_line = scan_data.line_data.at(position_line_index);
|
||||||
|
|
||||||
|
decltype(position_line.find_first_of(' ')) number_begin_pos = 2, number_end_pos;
|
||||||
|
for (std::size_t position_coordinate_index = 0; position_coordinate_index < 3; position_coordinate_index++) {
|
||||||
|
if (number_begin_pos >= position_line.size()) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
position_line_index,
|
||||||
|
std::format("Line \"{}\" must contain at least {} numbers", "v", 3)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number_end_pos = std::min(position_line.find_first_of(' ', number_begin_pos), position_line.size());
|
||||||
|
auto [number_end, conversion_error] = std::from_chars(
|
||||||
|
position_line.data() + number_begin_pos,
|
||||||
|
position_line.data() + number_end_pos,
|
||||||
|
position_coordinates[position_coordinate_index],
|
||||||
|
std::chars_format::fixed
|
||||||
|
);
|
||||||
|
if (conversion_error != std::errc() || position_line.data() + number_end_pos != number_end) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
position_line_index,
|
||||||
|
std::format("Unable to parse \"{}\" floating point value", "v")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number_begin_pos = std::min(position_line.find_first_not_of(' ', number_end_pos), position_line.size());
|
||||||
|
}
|
||||||
|
if (number_begin_pos < position_line.size()) {
|
||||||
|
if (position_line.find_first_not_of("0123456789.-", number_begin_pos)) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
position_line_index,
|
||||||
|
std::format("Additional data in \"{}\" line: expected {} only", "v", "<x> <y> <z> [weight]")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.position_coordinates[position_line_index] = position_coordinates;
|
||||||
|
}
|
||||||
|
if (face_data.index_normal_set.size() > 0 && scan_data.category_map.contains("vn"s)) {
|
||||||
|
const auto &normal_data = scan_data.category_map.at("vn"s);
|
||||||
|
for (const auto &normal_index : face_data.index_normal_set) {
|
||||||
|
std::array<FloatType, 3> normal_coordinates;
|
||||||
|
const auto &normal_line_index = normal_data.at(normal_index);
|
||||||
|
const auto &normal_line = scan_data.line_data.at(normal_line_index);
|
||||||
|
|
||||||
|
decltype(normal_line.find_first_of(' ')) number_begin_pos = 3, number_end_pos;
|
||||||
|
for (std::size_t normal_coordinate_index = 0; normal_coordinate_index < 3; normal_coordinate_index++) {
|
||||||
|
if (number_begin_pos >= normal_line.size()) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
normal_line_index,
|
||||||
|
std::format("Line \"{}\" must contain exactly {} numbers", "vn", 3)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number_end_pos = std::min(normal_line.find_first_of(' ', number_begin_pos), normal_line.size());
|
||||||
|
auto [number_end, conversion_error] = std::from_chars(
|
||||||
|
normal_line.data() + number_begin_pos,
|
||||||
|
normal_line.data() + number_end_pos,
|
||||||
|
normal_coordinates[normal_coordinate_index],
|
||||||
|
std::chars_format::fixed
|
||||||
|
);
|
||||||
|
if (conversion_error != std::errc() || normal_line.data() + number_end_pos != number_end) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
normal_line_index,
|
||||||
|
std::format("Unable to parse \"{}\" floating point value", "vn")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number_begin_pos = std::min(normal_line.find_first_not_of(' ', number_end_pos), normal_line.size());
|
||||||
|
}
|
||||||
|
if (number_end_pos != normal_line.size()) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
normal_line_index,
|
||||||
|
std::format("Additional data in \"{}\" line: expected {} only", "vn", "<x> <y> <z>")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
result.normal_coordinates[normal_line_index] = normal_coordinates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (face_data.index_texcoord_set.size() > 0 && scan_data.category_map.contains("vt"s)) {
|
||||||
|
using insert_texcoord_data_t = std::function<void()>;
|
||||||
|
|
||||||
|
const auto &texcoord_data = scan_data.category_map.at("vt"s);
|
||||||
|
for (const auto &texcoord_index : face_data.index_texcoord_set) {
|
||||||
|
std::array<FloatType, 3> texcoord_coordinates;
|
||||||
|
const auto &texcoord_line_index = texcoord_data.at(texcoord_index);
|
||||||
|
const auto &texcoord_line = scan_data.line_data.at(texcoord_line_index);
|
||||||
|
|
||||||
|
decltype(texcoord_line.find_first_of(' ')) number_begin_pos = 3, number_end_pos;
|
||||||
|
std::size_t texcoord_coordinate_index = 0;
|
||||||
|
while (number_begin_pos < texcoord_line.size()) {
|
||||||
|
if (texcoord_coordinate_index >= 3) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
texcoord_line_index,
|
||||||
|
std::format("Line \"{}\" must contain maximum {} numbers", "vt", 3)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number_end_pos = std::min(texcoord_line.find_first_of(' ', number_begin_pos), texcoord_line.size());
|
||||||
|
auto [number_end, conversion_error] = std::from_chars(
|
||||||
|
texcoord_line.data() + number_begin_pos,
|
||||||
|
texcoord_line.data() + number_end_pos,
|
||||||
|
texcoord_coordinates[texcoord_coordinate_index++],
|
||||||
|
std::chars_format::fixed
|
||||||
|
);
|
||||||
|
if (conversion_error != std::errc() || texcoord_line.data() + number_end_pos != number_end) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
texcoord_line_index,
|
||||||
|
std::format("Unable to parse \"{}\" floating point value", "vt")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number_begin_pos = std::min(texcoord_line.find_first_not_of(' ', number_end_pos), texcoord_line.size());
|
||||||
|
}
|
||||||
|
if (texcoord_coordinate_index == 0) [[unlikely]] {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
texcoord_line_index,
|
||||||
|
std::format("Unable to parse \"{}\" line: expected minimum one number", "vt")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (result.texture_coordinates.valueless_by_exception() || result.texture_coordinates.index() == 0) [[unlikely]] {
|
||||||
|
if (texcoord_coordinate_index == 1) {
|
||||||
|
result.texture_coordinates = std::map<file_line_t, FloatType>{};
|
||||||
|
} else if (texcoord_coordinate_index == 2) {
|
||||||
|
result.texture_coordinates = std::map<file_line_t, std::array<FloatType, 2>>{};
|
||||||
|
} else if (texcoord_coordinate_index == 3) {
|
||||||
|
result.texture_coordinates = std::map<file_line_t, std::array<FloatType, 3>>{};
|
||||||
|
} else {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
texcoord_line_index,
|
||||||
|
std::format("Line \"{}\" must contain maximum {} numbers", "vt", 3)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (texcoord_coordinate_index == 0) [[unlikely]] {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
texcoord_line_index,
|
||||||
|
std::format("Unable to parse \"{}\" line: expected minimum one number", "vt")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (texcoord_coordinate_index == 1) {
|
||||||
|
std::get<1>(result.texture_coordinates)[texcoord_line_index] = texcoord_coordinates[0];
|
||||||
|
} else if (texcoord_coordinate_index == 2) {
|
||||||
|
std::get<2>(result.texture_coordinates)[texcoord_line_index] = std::array<FloatType, 2>{texcoord_coordinates[0], texcoord_coordinates[1]};
|
||||||
|
} else if (texcoord_coordinate_index == 3) {
|
||||||
|
std::get<3>(result.texture_coordinates)[texcoord_line_index] = texcoord_coordinates;
|
||||||
|
}
|
||||||
|
} catch (std::bad_variant_access&) {
|
||||||
|
throw parse_error(std::format(
|
||||||
|
"[{}]: {}",
|
||||||
|
texcoord_line_index,
|
||||||
|
std::format("Line \"{}\": {}D texture coordinates expected, got {}D texture coordinates", "vt", result.texture_coordinates.index(), texcoord_coordinate_index)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FloatType>
|
||||||
|
std::vector<FloatType> create_number_list(const coordinate_data_t<FloatType> &coordinate_data) {
|
||||||
|
numset_t<FloatType> number_set;
|
||||||
|
|
||||||
|
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
|
||||||
|
for (const auto &coordinate : coordinates) {
|
||||||
|
number_set.emplace(coordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
|
||||||
|
for (const auto &coordinate : coordinates) {
|
||||||
|
number_set.emplace(coordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::visit([&](const auto &texture_coordinate_map) {
|
||||||
|
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinate_map)>>) {
|
||||||
|
for (const auto& [line_number, coordinates] : texture_coordinate_map) {
|
||||||
|
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
|
||||||
|
for (const auto &coordinate : coordinates) {
|
||||||
|
number_set.emplace(coordinate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
number_set.emplace(coordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, coordinate_data.texture_coordinates);
|
||||||
|
|
||||||
|
number_set.emplace(std::numeric_limits<FloatType>::quiet_NaN());
|
||||||
|
|
||||||
|
std::vector<FloatType> number_list;
|
||||||
|
number_list.reserve(number_set.size());
|
||||||
|
std::copy(number_set.begin(), number_set.end(), std::back_inserter(number_list));
|
||||||
|
|
||||||
|
return number_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FloatType, typename IndexType>
|
||||||
|
coordinate_data_t<IndexType> create_coordinate_index(const coordinate_data_t<FloatType> &coordinate_data, const std::vector<FloatType> &float_list) {
|
||||||
|
coordinate_data_t<IndexType> coordinate_index_data;
|
||||||
|
|
||||||
|
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
|
||||||
|
std::array<IndexType, coordinates.size()> position_coordinate_indices;
|
||||||
|
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) {
|
||||||
|
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim]);
|
||||||
|
assert(iterator != float_list.end());
|
||||||
|
position_coordinate_indices[dim] = std::distance(float_list.begin(), iterator);
|
||||||
|
}
|
||||||
|
coordinate_index_data.position_coordinates[line_number] = position_coordinate_indices;
|
||||||
|
}
|
||||||
|
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
|
||||||
|
std::array<IndexType, coordinates.size()> normal_coordinate_indices;
|
||||||
|
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) {
|
||||||
|
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim]);
|
||||||
|
assert(iterator != float_list.end());
|
||||||
|
normal_coordinate_indices[dim] = std::distance(float_list.begin(), iterator);
|
||||||
|
}
|
||||||
|
coordinate_index_data.normal_coordinates[line_number] = normal_coordinate_indices;
|
||||||
|
}
|
||||||
|
if (!coordinate_data.texture_coordinates.valueless_by_exception() && coordinate_data.texture_coordinates.index() > 0) {
|
||||||
|
std::visit([&](const auto &texture_coordinate_map) {
|
||||||
|
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinate_map)>>) {
|
||||||
|
if constexpr (std::ranges::range<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>) {
|
||||||
|
coordinate_index_data.texture_coordinates = std::map<
|
||||||
|
typename std::decay_t<decltype(texture_coordinate_map)>::key_type,
|
||||||
|
std::array<
|
||||||
|
IndexType,
|
||||||
|
std::tuple_size_v<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>
|
||||||
|
>
|
||||||
|
>{};
|
||||||
|
} else {
|
||||||
|
coordinate_index_data.texture_coordinates = std::map<
|
||||||
|
typename std::decay_t<decltype(texture_coordinate_map)>::key_type,
|
||||||
|
IndexType
|
||||||
|
>{};
|
||||||
|
}
|
||||||
|
for (const auto& [line_number, coordinates] : texture_coordinate_map) {
|
||||||
|
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
|
||||||
|
std::array<IndexType, coordinates.size()> texcoord_coordinate_indices;
|
||||||
|
for (std::size_t dim = 0; dim < coordinates.size(); ++dim) {
|
||||||
|
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim]);
|
||||||
|
assert(iterator != float_list.end());
|
||||||
|
texcoord_coordinate_indices[dim] = std::distance(float_list.begin(), iterator);
|
||||||
|
}
|
||||||
|
std::visit([&](auto &target_data) {
|
||||||
|
if constexpr (std::is_same_v<
|
||||||
|
std::map<
|
||||||
|
file_line_t,
|
||||||
|
std::array<
|
||||||
|
IndexType,
|
||||||
|
std::tuple_size_v<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>
|
||||||
|
>
|
||||||
|
>,
|
||||||
|
typename std::decay_t<decltype(target_data)>
|
||||||
|
>) {
|
||||||
|
target_data[line_number] = texcoord_coordinate_indices;
|
||||||
|
}
|
||||||
|
}, coordinate_index_data.texture_coordinates);
|
||||||
|
} else {
|
||||||
|
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates);
|
||||||
|
assert(iterator != float_list.end());
|
||||||
|
auto coordinate = std::distance(float_list.begin(), iterator);
|
||||||
|
std::visit([&](auto &target_data) {
|
||||||
|
if constexpr (std::is_same_v<
|
||||||
|
std::map<file_line_t, IndexType>,
|
||||||
|
typename std::decay_t<decltype(target_data)>
|
||||||
|
>) {
|
||||||
|
target_data[line_number] = coordinate;
|
||||||
|
}
|
||||||
|
}, coordinate_index_data.texture_coordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int j = 0;
|
||||||
|
}, coordinate_data.texture_coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return coordinate_index_data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __WAVEFRONT_PARSE_HPP__
|
#endif // __WAVEFRONT_PARSE_HPP__
|
||||||
306
src/parser.hpp
306
src/parser.hpp
@@ -1,306 +0,0 @@
|
|||||||
#ifndef _WAVEFRONT_PARSER_FILE_HPP_
|
|
||||||
#define _WAVEFRONT_PARSER_FILE_HPP_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <charconv>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <functional>
|
|
||||||
#include <limits>
|
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "settings.hpp"
|
|
||||||
|
|
||||||
namespace wavefront::parser {
|
|
||||||
class parse_error : public std::runtime_error {
|
|
||||||
public:
|
|
||||||
parse_error(const std::string &message) : std::runtime_error(message) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename FloatType, typename IndexType>
|
|
||||||
class File {
|
|
||||||
|
|
||||||
public:
|
|
||||||
File() = default;
|
|
||||||
|
|
||||||
void parse(const Settings &settings);
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<typename ValueType, size_t Length>
|
|
||||||
std::array<ValueType, Length> repeat_value(ValueType value) {
|
|
||||||
std::array<ValueType, Length> result;
|
|
||||||
result.fill(value);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string_view trim(const std::string_view &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_view(left, right - left);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FloatType, typename IndexType>
|
|
||||||
void File<FloatType, IndexType>::parse(const Settings &settings) {
|
|
||||||
using line_processor_func_t = std::function<
|
|
||||||
void(const std::string_view &type, const std::string_view &content)
|
|
||||||
>;
|
|
||||||
using store_vec3_factory_func_t = std::function<
|
|
||||||
line_processor_func_t(std::vector<std::array<FloatType, 3>> &target_vector)
|
|
||||||
>;
|
|
||||||
using store_filter_factory_func_t = std::function<
|
|
||||||
line_processor_func_t(std::optional<std::string> &target_filter)
|
|
||||||
>;
|
|
||||||
using check_filter_func_t = std::function<bool()>;
|
|
||||||
|
|
||||||
static const auto return_true = []()->bool { return true; };
|
|
||||||
|
|
||||||
std::size_t current_line_number = 0;
|
|
||||||
|
|
||||||
const auto process_float = [&](FloatType &target_value, const char *data_begin, const char *data_end) {
|
|
||||||
auto [number_end, conversion_error] = std::from_chars(
|
|
||||||
data_begin,
|
|
||||||
data_end,
|
|
||||||
target_value,
|
|
||||||
std::chars_format::fixed
|
|
||||||
);
|
|
||||||
if (conversion_error != std::errc() || number_end != data_end) {
|
|
||||||
std::stringstream error_message;
|
|
||||||
error_message
|
|
||||||
<< "["
|
|
||||||
<< current_line_number
|
|
||||||
<< "]"
|
|
||||||
<< ": "
|
|
||||||
<< "Unable to parse the expected float data";
|
|
||||||
if (conversion_error != std::errc()) {
|
|
||||||
auto error_condition = std::make_error_condition(conversion_error);
|
|
||||||
error_message
|
|
||||||
<< ": "
|
|
||||||
<< error_condition.message();
|
|
||||||
} else {
|
|
||||||
error_message
|
|
||||||
<< ": "
|
|
||||||
<< "Unexpected trailing characters";
|
|
||||||
}
|
|
||||||
throw parse_error(error_message.str());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::array<FloatType, 3>> wavefront_data_position;
|
|
||||||
std::vector<std::array<FloatType, 3>> wavefront_data_normal;
|
|
||||||
std::vector<
|
|
||||||
std::variant<
|
|
||||||
std::monostate,
|
|
||||||
FloatType,
|
|
||||||
std::array<FloatType, 2>,
|
|
||||||
std::array<FloatType, 3>
|
|
||||||
>
|
|
||||||
> wavefront_data_texcoord;
|
|
||||||
std::vector<std::array<std::array<IndexType, 3>, 3>> wavefront_data_triangle;
|
|
||||||
|
|
||||||
wavefront_data_position.push_back(repeat_value<FloatType, 3>(std::numeric_limits<FloatType>::quiet_NaN()));
|
|
||||||
wavefront_data_normal.push_back(repeat_value<FloatType, 3>(std::numeric_limits<FloatType>::quiet_NaN()));
|
|
||||||
wavefront_data_texcoord.push_back(std::monostate{});
|
|
||||||
|
|
||||||
const store_vec3_factory_func_t create_vec3_store_func = [&](std::vector<std::array<FloatType, 3>> &target_vector) {
|
|
||||||
return line_processor_func_t([&](const std::string_view &type, const std::string_view &content) {
|
|
||||||
std::array<FloatType, 3> vec3{};
|
|
||||||
std::size_t component_count = 0;
|
|
||||||
decltype(content.size()) content_processed = 0;
|
|
||||||
|
|
||||||
// 4th component (weight) is ignored if present.
|
|
||||||
while (component_count < 3 && content_processed < content.size()) {
|
|
||||||
auto chunk_end_pos = content.find_first_of(' ', content_processed);
|
|
||||||
if (chunk_end_pos == std::string_view::npos) {
|
|
||||||
chunk_end_pos = content.size();
|
|
||||||
}
|
|
||||||
process_float(
|
|
||||||
vec3[component_count],
|
|
||||||
content.data() + content_processed,
|
|
||||||
content.data() + chunk_end_pos
|
|
||||||
);
|
|
||||||
|
|
||||||
++component_count;
|
|
||||||
content_processed = chunk_end_pos + 1;
|
|
||||||
auto next_non_space_pos = content.find_first_not_of(' ', content_processed);
|
|
||||||
if (next_non_space_pos == std::string_view::npos) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
content_processed = next_non_space_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component_count < 3) {
|
|
||||||
std::stringstream error_message;
|
|
||||||
error_message
|
|
||||||
<< "["
|
|
||||||
<< current_line_number
|
|
||||||
<< "]"
|
|
||||||
<< ": "
|
|
||||||
<< "Insufficient number of components for "
|
|
||||||
<< '"'
|
|
||||||
<< type
|
|
||||||
<< '"'
|
|
||||||
<< " line";
|
|
||||||
throw parse_error(error_message.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
target_vector.push_back(vec3);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const line_processor_func_t store_texcoords = [&](const std::string_view &type, const std::string_view &content) {
|
|
||||||
using insert_data_func_t = std::function<void(void)>;
|
|
||||||
|
|
||||||
std::array<FloatType, 3> data_vec3{};
|
|
||||||
decltype(content.size()) content_processed = 0;
|
|
||||||
std::size_t component_count = 0;
|
|
||||||
|
|
||||||
const std::array<insert_data_func_t, 4> insert_data = {
|
|
||||||
[](){},
|
|
||||||
[&](void) { wavefront_data_texcoord.push_back(data_vec3[0]); },
|
|
||||||
[&](void) { wavefront_data_texcoord.push_back(std::array<FloatType, 2>{data_vec3[0], data_vec3[1]}); },
|
|
||||||
[&](void) { wavefront_data_texcoord.push_back(data_vec3); }
|
|
||||||
};
|
|
||||||
|
|
||||||
while (content_processed < content.size()) {
|
|
||||||
if (component_count >= 3) {
|
|
||||||
std::stringstream error_message;
|
|
||||||
error_message
|
|
||||||
<< "["
|
|
||||||
<< current_line_number
|
|
||||||
<< "]"
|
|
||||||
<< ": "
|
|
||||||
<< "Too many components for "
|
|
||||||
<< '"'
|
|
||||||
<< type
|
|
||||||
<< '"'
|
|
||||||
<< " line";
|
|
||||||
throw parse_error(error_message.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto chunk_end_pos = content.find_first_of(' ', content_processed);
|
|
||||||
if (chunk_end_pos == std::string_view::npos) {
|
|
||||||
chunk_end_pos = content.size();
|
|
||||||
}
|
|
||||||
process_float(
|
|
||||||
data_vec3[component_count],
|
|
||||||
content.data() + content_processed,
|
|
||||||
content.data() + chunk_end_pos
|
|
||||||
);
|
|
||||||
|
|
||||||
++component_count;
|
|
||||||
content_processed = chunk_end_pos + 1;
|
|
||||||
auto next_non_space_pos = content.find_first_not_of(' ', content_processed);
|
|
||||||
if (next_non_space_pos == std::string_view::npos) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
content_processed = next_non_space_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component_count < 1) {
|
|
||||||
std::stringstream error_message;
|
|
||||||
error_message
|
|
||||||
<< "["
|
|
||||||
<< current_line_number
|
|
||||||
<< "]"
|
|
||||||
<< ": "
|
|
||||||
<< "Insufficient number of components for "
|
|
||||||
<< '"'
|
|
||||||
<< type
|
|
||||||
<< '"'
|
|
||||||
<< " line";
|
|
||||||
throw parse_error(error_message.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
insert_data[component_count]();
|
|
||||||
};
|
|
||||||
|
|
||||||
static const store_filter_factory_func_t create_filter_store = [](std::optional<std::string> &target_filter) {
|
|
||||||
return [&](const std::string_view &type, const std::string_view &content) {
|
|
||||||
auto trimmed_name = trim(content);
|
|
||||||
if (trimmed_name.empty()) {
|
|
||||||
target_filter.reset();
|
|
||||||
} else {
|
|
||||||
target_filter = std::string(trimmed_name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::optional<std::string> 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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::map<std::string_view, line_processor_func_t> line_processor_map{
|
|
||||||
{ "v", create_vec3_store_func(wavefront_data_position) },
|
|
||||||
{ "vn", create_vec3_store_func(wavefront_data_normal) },
|
|
||||||
{ "vt", store_texcoords },
|
|
||||||
{ "o", create_filter_store(current_object) },
|
|
||||||
{ "g", create_filter_store(current_group) },
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto max_type_length = [&]() {
|
|
||||||
std::size_t max_length = 0;
|
|
||||||
for (const auto &[type, _] : line_processor_map) {
|
|
||||||
if (type.size() > max_length) {
|
|
||||||
max_length = type.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return max_length;
|
|
||||||
}();
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
while (std::getline(settings.input(), line)) {
|
|
||||||
++current_line_number;
|
|
||||||
|
|
||||||
if (line.empty() || line[0] == '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto first_space_pos = line.find_first_of(' ');
|
|
||||||
|
|
||||||
if (first_space_pos == std::string::npos || first_space_pos > max_type_length || first_space_pos + 1 >= line.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto type = std::string_view(line.data(), first_space_pos);
|
|
||||||
auto content = std::string_view(line.data() + first_space_pos + 1, line.size() - first_space_pos - 1);
|
|
||||||
|
|
||||||
auto processor_func = line_processor_map.find(type);
|
|
||||||
if (processor_func == line_processor_map.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
processor_func->second(type, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _WAVEFRONT_PARSER_FILE_HPP_
|
|
||||||
15
src/scan.cpp
15
src/scan.cpp
@@ -104,20 +104,9 @@ namespace wavefront {
|
|||||||
return max_length;
|
return max_length;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
while (!input.eof()) {
|
std::string line;
|
||||||
|
while (std::getline(input, line)) {
|
||||||
++result.total_lines;
|
++result.total_lines;
|
||||||
input.getline(buffer.data(), buffer.size());
|
|
||||||
auto line_size = input.gcount();
|
|
||||||
|
|
||||||
if (input.fail() && line_size == buffer.size()) [[unlikely]] {
|
|
||||||
throw scan_error(std::format(
|
|
||||||
"[{}]: {}",
|
|
||||||
result.total_lines,
|
|
||||||
"Line too long"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view line(buffer.data(), line_size);
|
|
||||||
|
|
||||||
if (line.empty() || line[0] == '#' || trim(line).empty()) [[unlikely]] {
|
if (line.empty() || line[0] == '#' || trim(line).empty()) [[unlikely]] {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user