From d65653cb7604a88a0566e6622710cfed228c2d28 Mon Sep 17 00:00:00 2001 From: Plamen Dragiyski Date: Tue, 18 Nov 2025 01:38:28 +0200 Subject: [PATCH] feature: vertex and triangle index storage --- src/compile.hpp | 198 +++++++++++++++++++++++++++++++++++++++++++++++- src/main.cpp | 52 ++++++++++++- src/parse.hpp | 28 +++++-- src/repeat.hpp | 16 ++++ 4 files changed, 282 insertions(+), 12 deletions(-) create mode 100644 src/repeat.hpp diff --git a/src/compile.hpp b/src/compile.hpp index 6407e3d..199bd38 100644 --- a/src/compile.hpp +++ b/src/compile.hpp @@ -6,6 +6,7 @@ #include #include "parse.hpp" +#include "repeat.hpp" namespace wavefront { template typename ContainerType, typename ... Args> @@ -26,6 +27,13 @@ namespace wavefront { > &source ) { for (const auto &[line_number, coordinates] : source) { + if constexpr (std::ranges::range>) { + for (const auto &coordinate : coordinates) { + assert(coordinate != 0); + } + } else { + assert(coordinates != 0); + } std::get(target).emplace(coordinates); } } @@ -73,7 +81,14 @@ namespace wavefront { auto &target, const auto &source ) { - target.reserve(source.size()); + target.reserve(source.size() + 1); + if (source.size() > 0) { + if constexpr (std::ranges::range::value_type>) { + target.push_back(repeat_value::value_type>>(0)); + } else { + target.push_back(0); + } + } std::copy( source.begin(), source.end(), @@ -106,6 +121,7 @@ namespace wavefront { auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector); assert(data_vector_iterator != data_storage.end()); auto data_vector_index = std::distance(data_storage.begin(), data_vector_iterator); + assert(data_vector_index != 0); result.position_coordinate_index[line_number] = data_vector_index; } } @@ -115,6 +131,7 @@ namespace wavefront { auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector); assert(data_vector_iterator != data_storage.end()); auto data_vector_index = std::distance(data_storage.begin(), data_vector_iterator); + assert(data_vector_index != 0); result.normal_coordinate_index[line_number] = data_vector_index; } } @@ -126,10 +143,12 @@ namespace wavefront { auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector); assert(data_vector_iterator != data_storage.end()); auto data_vector_index = std::distance(data_storage.begin(), data_vector_iterator); + assert(data_vector_index != 0); result.texcoord_coordinate_index[line_number] = data_vector_index; } } else { for (const auto &[line_number, data_index] : texture_coordinates) { + assert(data_index != 0); result.texcoord_coordinate_index[line_number] = data_index; } } @@ -138,4 +157,181 @@ namespace wavefront { return result; } + + using unwrap_vertex_func_t = std::function; + + namespace { + static const unwrap_vertex_func_t get_position_vertex_index = [] (const triangle_vertex_indices &vertex_indices) -> vertex_index_t { + return vertex_indices.position_index; + }; + + static const unwrap_vertex_func_t get_normal_vertex_index = [] (const triangle_vertex_indices &vertex_indices) -> vertex_index_t { + return vertex_indices.normal_index; + }; + + static const unwrap_vertex_func_t get_texcoord_vertex_index = [] (const triangle_vertex_indices &vertex_indices) -> vertex_index_t { + return vertex_indices.texcoord_index; + }; + + template + struct coordinate_mapper_t { + const unwrap_vertex_func_t &get_vertex_index; + const std::vector &attribute_line_map; + const std::map &line_coordinate_map; + }; + + template + std::vector> compile_vertex_storage_impl( + const scan_result &scan_data, + const wavefront_face_data_result_t &face_data, + const std::array, VertexDimCount> &mapper + ) { + std::set> vertex_storage_set; + + for (const auto &triangle : face_data.triangle_list) { + for (const auto &vertex : triangle) { + std::array coordinate_indices; + for (std::size_t i = 0; i < VertexDimCount; ++i) { + const auto &wavefront_index = mapper[i].get_vertex_index(vertex); + const auto &line_index = mapper[i].attribute_line_map.at(wavefront_index); + const auto &coordinate_index = mapper[i].line_coordinate_map.at(line_index); + assert(coordinate_index != 0); + coordinate_indices[i] = coordinate_index; + } + vertex_storage_set.emplace(coordinate_indices); + } + } + + std::vector> result; + result.reserve(vertex_storage_set.size() + 1); + result.push_back(repeat_value(0)); + std::copy( + vertex_storage_set.begin(), + vertex_storage_set.end(), + std::back_inserter(result) + ); + + return result; + } + + template + std::vector> compile_triangle_storage_impl( + const scan_result &scan_data, + const wavefront_face_data_result_t &face_data, + const std::array, VertexDimCount> &mapper, + const std::vector> &vertex_data + ) { + std::set> triangle_data_set; + + for (const auto &triangle : face_data.triangle_list) { + std::array triangle_data; + std::size_t triangle_vertex_index = 0; + for (const auto &vertex : triangle) { + std::array coordinate_indices; + for (std::size_t i = 0; i < VertexDimCount; ++i) { + const auto &wavefront_index = mapper[i].get_vertex_index(vertex); + const auto &line_index = mapper[i].attribute_line_map.at(wavefront_index); + const auto &coordinate_index = mapper[i].line_coordinate_map.at(line_index); + assert(coordinate_index != 0); + coordinate_indices[i] = coordinate_index; + } + auto iterator = std::lower_bound(vertex_data.begin(), vertex_data.end(), coordinate_indices); + assert(iterator != vertex_data.end()); + auto index = std::distance(vertex_data.begin(), iterator); + assert(index != 0); + assert(triangle_vertex_index < 3); + triangle_data[triangle_vertex_index++] = index; + } + assert(triangle_vertex_index == 3); + triangle_data_set.emplace(triangle_data); + } + + std::vector> result; + result.reserve(triangle_data_set.size() + 1); + result.push_back(repeat_value(0)); + std::copy( + triangle_data_set.begin(), + triangle_data_set.end(), + std::back_inserter(result) + ); + + return result; + } + } + + template typename ContainerType, typename ... Args> + using vertex_storage_t = std::variant< + ContainerType>, + ContainerType>, + ContainerType> + >; + + template typename ContainerType, typename ... Args> + struct compile_triangle_data_result_t { + vertex_storage_t vertices; + ContainerType> triangles; + }; + + template + compile_triangle_data_result_t compile_vertex_storage( + const scan_result &scan_data, + const wavefront_face_data_result_t &face_data, + const coordinate_storage_line_mapping_t &coordinate_storage_index + ) { + std::vector> coordinate_mapper; + if (coordinate_storage_index.position_coordinate_index.size() > 0) [[likely]] { + coordinate_mapper.push_back(coordinate_mapper_t{ + .get_vertex_index = get_position_vertex_index, + .attribute_line_map = scan_data.category_map.at("v"), + .line_coordinate_map = coordinate_storage_index.position_coordinate_index + }); + } else [[unlikely]] { + throw parse_error("Expected at least one position coordinate index"); + } + if (coordinate_storage_index.normal_coordinate_index.size() > 0) { + coordinate_mapper.push_back(coordinate_mapper_t{ + .get_vertex_index = get_normal_vertex_index, + .attribute_line_map = scan_data.category_map.at("vn"), + .line_coordinate_map = coordinate_storage_index.normal_coordinate_index + }); + } + if (coordinate_storage_index.texcoord_coordinate_index.size() > 0) { + coordinate_mapper.push_back(coordinate_mapper_t{ + .get_vertex_index = get_texcoord_vertex_index, + .attribute_line_map = scan_data.category_map.at("vt"), + .line_coordinate_map = coordinate_storage_index.texcoord_coordinate_index + }); + } + std::variant< + std::array, 1>, + std::array, 2>, + std::array, 3> + > coordinate_mapper_data = ([&]() -> std::variant< + std::array, 1>, + std::array, 2>, + std::array, 3> + > { + auto vertex_size = coordinate_mapper.size(); + if (vertex_size == 1) { + return std::array, 1>{coordinate_mapper[0]}; + } else if (vertex_size == 2) { + return std::array, 2>{coordinate_mapper[0], coordinate_mapper[1]}; + } else if (vertex_size == 3) { + return std::array, 3>{coordinate_mapper[0], coordinate_mapper[1], coordinate_mapper[2]}; + } else [[unlikely]] { + throw std::runtime_error(std::format("Invalid vertex size: {}", vertex_size)); + } + })(); + + auto vertex_data = std::visit([&](const auto &coordinate_mapper) -> vertex_storage_t { + return compile_vertex_storage_impl>>(scan_data, face_data, coordinate_mapper); + }, coordinate_mapper_data); + auto triangle_data = std::visit([&](const auto &coordinate_mapper, const auto &vertex_data) -> std::vector> { + if constexpr (std::tuple_size_v> == std::tuple_size_v::value_type>) { + return compile_triangle_storage_impl>>(scan_data, face_data, coordinate_mapper, vertex_data); + } + throw std::runtime_error("Invalid state"); + }, coordinate_mapper_data, vertex_data); + return { vertex_data, triangle_data }; + } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4db7e6c..924cbb7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -151,10 +151,54 @@ int main(int argc, char** argv) { auto coordinate_storage = compile_coordinate_storage(coordinate_index_data); - auto coordinate_storage_index = compile_coordinate_storage_line_mapping(coordinate_index_data, coordinate_storage); + if (std::get<1>(coordinate_storage).size() > 0) { + std::cerr << "Indexed " << 1 << "D coordinates: " << std::get<1>(coordinate_storage).size() << std::endl; + } + + if (std::get<2>(coordinate_storage).size() > 0) { + std::cerr << "Indexed " << 2 << "D coordinates: " << std::get<2>(coordinate_storage).size() << std::endl; + } + + if (std::get<3>(coordinate_storage).size() > 0) { + std::cerr << "Indexed " << 3 << "D coordinates: " << std::get<3>(coordinate_storage).size() << std::endl; + } + + auto coordinate_storage_index = compile_coordinate_storage_line_mapping(coordinate_index_data, coordinate_storage); + + auto index_storage = compile_vertex_storage(scan_data, face_data, coordinate_storage_index); + + std::visit([&](const auto &vertex_storage) { + std::cerr << "Compiled " << vertex_storage.size() << " vertices" << std::endl; + }, index_storage.vertices); + + std::cerr << "Compiled " << index_storage.triangles.size() << " triangles" << std::endl; + + // TODO: We need to output: + // 1. Some kind of singature; + // 2. A version number; + // 3. Flags describing the data: + // 3.1. position: required, always 3 coordinates (no flag needed) + // 3.2. normal: optional, always 3 coordinates (single flag needed) + // 3.3. texcoord: optional, up to 3 coordinates (two flags needed, 0 = none, 1 = 1D, 2 = 2D, 3 = 3D) + // 3.4. type of number data: 0 = float, 1 = double + // 3.5. vertices: 0 = never used, 1 = one attribute per vertex, 2 = two attributes per vertex, 3 = three attributes per vertex + // Flags: (0 = number type, 1 = normals present, 2-3 = texture_coordinate size, 4-7 = number of vertex attributes (up to 16), only 3 supported for now) + // 4. Data order: up to attribute_count attributes, 1-byte each, specifying an index of map in file order. 0 is the number_list and therefore reserved. + // 5. Sizes: + // 5.1. Size of the number list (in items): 1 item = 1 float/double (depending on number_type) + // 5.2. Size of the position list (in items): 1 item = 3 indices into the float_list + // 5.3. Size of the normal list (in items, only if normal flag is set): 1 item = 3 indices into the float_list + // 5.4. Size of the texcoord list (in items, only if texcoord_size is not 0): 1 item = 1-3 indices into the float_list (depending on texcoord_size) + // 5.5. Size of the vertex list (in items): 1 item = 1-attribute_count indices into different maps. + // 5.6. Size of the triangle list (in items): 1 item = 3 triangle vertex indices into the vertex list. + // Special references: number_list, triangle_list, vertex_list, position_list + // All other references are additional attributes. + // In order for quick load into GPU buffer we want: + // - each map start file pos to be known + // - each map size in bytes to be known + // - each map type (float32, float64, int8, int16, int32, int64) + // - number of elements per item (or stride, i.e. number of bytes per item) + // - each map file pos aligned to 16-bytes (assuming file is loaded aligned to 16-bytes). Elements within the map do not need to be aligned. - // 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 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; } diff --git a/src/parse.hpp b/src/parse.hpp index aa1e25b..3ca9caa 100644 --- a/src/parse.hpp +++ b/src/parse.hpp @@ -270,21 +270,30 @@ namespace wavefront { coordinate_data_t create_coordinate_index(const coordinate_data_t &coordinate_data, const std::vector &float_list) { coordinate_data_t coordinate_index_data; + static const auto float_compare = float_is_equal{}; + static const auto float_less = less_with_nan_first_and_nearly_equal{}; + for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) { std::array 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]); + auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less); assert(iterator != float_list.end()); - position_coordinate_indices[dim] = std::distance(float_list.begin(), iterator); + assert(float_compare(*iterator, coordinates[dim])); + auto index = std::distance(float_list.begin(), iterator); + assert(index != 0); + position_coordinate_indices[dim] = index; } coordinate_index_data.position_coordinates[line_number] = position_coordinate_indices; } for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) { std::array 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]); + auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less); assert(iterator != float_list.end()); - normal_coordinate_indices[dim] = std::distance(float_list.begin(), iterator); + assert(float_compare(*iterator, coordinates[dim])); + auto index = std::distance(float_list.begin(), iterator); + assert(index != 0); + normal_coordinate_indices[dim] = index; } coordinate_index_data.normal_coordinates[line_number] = normal_coordinate_indices; } @@ -309,9 +318,12 @@ namespace wavefront { if constexpr (std::ranges::range>) { std::array 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]); + auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less); assert(iterator != float_list.end()); - texcoord_coordinate_indices[dim] = std::distance(float_list.begin(), iterator); + assert(float_compare(*iterator, coordinates[dim])); + auto index = std::distance(float_list.begin(), iterator); + assert(index != 0); + texcoord_coordinate_indices[dim] = index; } std::visit([&](auto &target_data) { if constexpr (std::is_same_v< @@ -328,9 +340,11 @@ namespace wavefront { } }, coordinate_index_data.texture_coordinates); } else { - auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates); + auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates, float_less); assert(iterator != float_list.end()); + assert(float_compare(*iterator, coordinates)); auto coordinate = std::distance(float_list.begin(), iterator); + assert(coordinate != 0); std::visit([&](auto &target_data) { if constexpr (std::is_same_v< std::map, diff --git a/src/repeat.hpp b/src/repeat.hpp new file mode 100644 index 0000000..7ecd72b --- /dev/null +++ b/src/repeat.hpp @@ -0,0 +1,16 @@ +#ifndef __WAVEFRONT_REPEAT_HPP__ +#define __WAVEFRONT_REPEAT_HPP__ + +#include +#include + +namespace wavefront { + template + std::array repeat_value(ValueType value) { + std::array result; + result.fill(value); + return result; + }; +} + +#endif // __WAVEFRONT_REPEAT_HPP__ \ No newline at end of file