1
0

feature: vertex and triangle index storage

This commit is contained in:
2025-11-18 01:38:28 +02:00
parent 7ae430ff4a
commit d65653cb76
4 changed files with 282 additions and 12 deletions

View File

@@ -6,6 +6,7 @@
#include <vector> #include <vector>
#include "parse.hpp" #include "parse.hpp"
#include "repeat.hpp"
namespace wavefront { namespace wavefront {
template<typename IndexType, template<typename, typename...> typename ContainerType, typename ... Args> template<typename IndexType, template<typename, typename...> typename ContainerType, typename ... Args>
@@ -26,6 +27,13 @@ namespace wavefront {
> &source > &source
) { ) {
for (const auto &[line_number, coordinates] : source) { for (const auto &[line_number, coordinates] : source) {
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
for (const auto &coordinate : coordinates) {
assert(coordinate != 0);
}
} else {
assert(coordinates != 0);
}
std::get<NumDims>(target).emplace(coordinates); std::get<NumDims>(target).emplace(coordinates);
} }
} }
@@ -73,7 +81,14 @@ namespace wavefront {
auto &target, auto &target,
const auto &source const auto &source
) { ) {
target.reserve(source.size()); target.reserve(source.size() + 1);
if (source.size() > 0) {
if constexpr (std::ranges::range<typename std::decay_t<decltype(target)>::value_type>) {
target.push_back(repeat_value<IndexType, std::tuple_size_v<typename std::decay_t<decltype(target)>::value_type>>(0));
} else {
target.push_back(0);
}
}
std::copy( std::copy(
source.begin(), source.begin(),
source.end(), source.end(),
@@ -106,6 +121,7 @@ namespace wavefront {
auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector); auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector);
assert(data_vector_iterator != data_storage.end()); assert(data_vector_iterator != data_storage.end());
auto data_vector_index = std::distance(data_storage.begin(), data_vector_iterator); 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; 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); auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector);
assert(data_vector_iterator != data_storage.end()); assert(data_vector_iterator != data_storage.end());
auto data_vector_index = std::distance(data_storage.begin(), data_vector_iterator); 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; 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); auto data_vector_iterator = std::lower_bound(data_storage.begin(), data_storage.end(), data_vector);
assert(data_vector_iterator != data_storage.end()); assert(data_vector_iterator != data_storage.end());
auto data_vector_index = std::distance(data_storage.begin(), data_vector_iterator); 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; result.texcoord_coordinate_index[line_number] = data_vector_index;
} }
} else { } else {
for (const auto &[line_number, data_index] : texture_coordinates) { for (const auto &[line_number, data_index] : texture_coordinates) {
assert(data_index != 0);
result.texcoord_coordinate_index[line_number] = data_index; result.texcoord_coordinate_index[line_number] = data_index;
} }
} }
@@ -138,4 +157,181 @@ namespace wavefront {
return result; return result;
} }
using unwrap_vertex_func_t = std::function<vertex_index_t(const triangle_vertex_indices &)>;
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<typename IndexType>
struct coordinate_mapper_t {
const unwrap_vertex_func_t &get_vertex_index;
const std::vector<std::size_t> &attribute_line_map;
const std::map<file_line_t, IndexType> &line_coordinate_map;
};
template<typename IndexType, std::size_t VertexDimCount>
std::vector<std::array<IndexType, VertexDimCount>> compile_vertex_storage_impl(
const scan_result &scan_data,
const wavefront_face_data_result_t &face_data,
const std::array<coordinate_mapper_t<IndexType>, VertexDimCount> &mapper
) {
std::set<std::array<IndexType, VertexDimCount>> vertex_storage_set;
for (const auto &triangle : face_data.triangle_list) {
for (const auto &vertex : triangle) {
std::array<IndexType, VertexDimCount> 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<std::array<IndexType, VertexDimCount>> result;
result.reserve(vertex_storage_set.size() + 1);
result.push_back(repeat_value<IndexType, VertexDimCount>(0));
std::copy(
vertex_storage_set.begin(),
vertex_storage_set.end(),
std::back_inserter(result)
);
return result;
}
template<typename IndexType, std::size_t VertexDimCount>
std::vector<std::array<IndexType, 3>> compile_triangle_storage_impl(
const scan_result &scan_data,
const wavefront_face_data_result_t &face_data,
const std::array<coordinate_mapper_t<IndexType>, VertexDimCount> &mapper,
const std::vector<std::array<IndexType, VertexDimCount>> &vertex_data
) {
std::set<std::array<IndexType, 3>> triangle_data_set;
for (const auto &triangle : face_data.triangle_list) {
std::array<IndexType, 3> triangle_data;
std::size_t triangle_vertex_index = 0;
for (const auto &vertex : triangle) {
std::array<IndexType, VertexDimCount> 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<std::array<IndexType, 3>> result;
result.reserve(triangle_data_set.size() + 1);
result.push_back(repeat_value<IndexType, 3>(0));
std::copy(
triangle_data_set.begin(),
triangle_data_set.end(),
std::back_inserter(result)
);
return result;
}
}
template<typename IndexType, template<typename, typename...> typename ContainerType, typename ... Args>
using vertex_storage_t = std::variant<
ContainerType<std::array<IndexType, 1>>,
ContainerType<std::array<IndexType, 2>>,
ContainerType<std::array<IndexType, 3>>
>;
template<typename IndexType, template<typename, typename...> typename ContainerType, typename ... Args>
struct compile_triangle_data_result_t {
vertex_storage_t<IndexType, ContainerType, Args...> vertices;
ContainerType<std::array<IndexType, 3>> triangles;
};
template<typename IndexType>
compile_triangle_data_result_t<IndexType, std::vector> compile_vertex_storage(
const scan_result &scan_data,
const wavefront_face_data_result_t &face_data,
const coordinate_storage_line_mapping_t<IndexType> &coordinate_storage_index
) {
std::vector<coordinate_mapper_t<IndexType>> 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<coordinate_mapper_t<IndexType>, 1>,
std::array<coordinate_mapper_t<IndexType>, 2>,
std::array<coordinate_mapper_t<IndexType>, 3>
> coordinate_mapper_data = ([&]() -> std::variant<
std::array<coordinate_mapper_t<IndexType>, 1>,
std::array<coordinate_mapper_t<IndexType>, 2>,
std::array<coordinate_mapper_t<IndexType>, 3>
> {
auto vertex_size = coordinate_mapper.size();
if (vertex_size == 1) {
return std::array<coordinate_mapper_t<IndexType>, 1>{coordinate_mapper[0]};
} else if (vertex_size == 2) {
return std::array<coordinate_mapper_t<IndexType>, 2>{coordinate_mapper[0], coordinate_mapper[1]};
} else if (vertex_size == 3) {
return std::array<coordinate_mapper_t<IndexType>, 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<IndexType, std::vector> {
return compile_vertex_storage_impl<IndexType, std::tuple_size_v<std::decay_t<decltype(coordinate_mapper)>>>(scan_data, face_data, coordinate_mapper);
}, coordinate_mapper_data);
auto triangle_data = std::visit([&](const auto &coordinate_mapper, const auto &vertex_data) -> std::vector<std::array<IndexType, 3>> {
if constexpr (std::tuple_size_v<std::decay_t<decltype(coordinate_mapper)>> == std::tuple_size_v<typename std::decay_t<decltype(vertex_data)>::value_type>) {
return compile_triangle_storage_impl<IndexType, std::tuple_size_v<std::decay_t<decltype(coordinate_mapper)>>>(scan_data, face_data, coordinate_mapper, vertex_data);
}
throw std::runtime_error("Invalid state");
}, coordinate_mapper_data, vertex_data);
return { vertex_data, triangle_data };
}
} }

View File

@@ -151,10 +151,54 @@ int main(int argc, char** argv) {
auto coordinate_storage = compile_coordinate_storage(coordinate_index_data); 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<IndexType>(coordinate_index_data, coordinate_storage);
auto index_storage = compile_vertex_storage<IndexType>(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<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;
} }

View File

@@ -270,21 +270,30 @@ namespace wavefront {
coordinate_data_t<IndexType> create_coordinate_index(const coordinate_data_t<FloatType> &coordinate_data, const std::vector<FloatType> &float_list) { 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; coordinate_data_t<IndexType> coordinate_index_data;
static const auto float_compare = float_is_equal<FloatType>{};
static const auto float_less = less_with_nan_first_and_nearly_equal<FloatType>{};
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) { for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
std::array<IndexType, coordinates.size()> position_coordinate_indices; std::array<IndexType, coordinates.size()> position_coordinate_indices;
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) { 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()); 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; coordinate_index_data.position_coordinates[line_number] = position_coordinate_indices;
} }
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) { for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
std::array<IndexType, coordinates.size()> normal_coordinate_indices; std::array<IndexType, coordinates.size()> normal_coordinate_indices;
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) { 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()); 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; coordinate_index_data.normal_coordinates[line_number] = normal_coordinate_indices;
} }
@@ -309,9 +318,12 @@ namespace wavefront {
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) { if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
std::array<IndexType, coordinates.size()> texcoord_coordinate_indices; std::array<IndexType, coordinates.size()> texcoord_coordinate_indices;
for (std::size_t dim = 0; dim < coordinates.size(); ++dim) { 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()); 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) { std::visit([&](auto &target_data) {
if constexpr (std::is_same_v< if constexpr (std::is_same_v<
@@ -328,9 +340,11 @@ namespace wavefront {
} }
}, coordinate_index_data.texture_coordinates); }, coordinate_index_data.texture_coordinates);
} else { } 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(iterator != float_list.end());
assert(float_compare(*iterator, coordinates));
auto coordinate = std::distance(float_list.begin(), iterator); auto coordinate = std::distance(float_list.begin(), iterator);
assert(coordinate != 0);
std::visit([&](auto &target_data) { std::visit([&](auto &target_data) {
if constexpr (std::is_same_v< if constexpr (std::is_same_v<
std::map<file_line_t, IndexType>, std::map<file_line_t, IndexType>,

16
src/repeat.hpp Normal file
View File

@@ -0,0 +1,16 @@
#ifndef __WAVEFRONT_REPEAT_HPP__
#define __WAVEFRONT_REPEAT_HPP__
#include<array>
#include<cstdint>
namespace wavefront {
template<typename ValueType, std::size_t Length>
std::array<ValueType, Length> repeat_value(ValueType value) {
std::array<ValueType, Length> result;
result.fill(value);
return result;
};
}
#endif // __WAVEFRONT_REPEAT_HPP__