From dbfd4eb19d51f3c17bfc744fe8d6e65e9df3ed9d Mon Sep 17 00:00:00 2001 From: Plamen Dragiyski Date: Wed, 19 Nov 2025 00:52:39 +0200 Subject: [PATCH] feature: write indexed data to file. --- src/main.cpp | 142 +++++++++++++++++++++++++++++++++++++++---------- src/output.hpp | 9 ++++ 2 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 src/output.hpp diff --git a/src/main.cpp b/src/main.cpp index 924cbb7..e690bda 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include "parse.hpp" #include "compile.hpp" #include "settings.hpp" +#include "output.hpp" static void usage(const char* prog) { std::cerr << "Usage: " << prog @@ -120,7 +122,7 @@ int main(int argc, char** argv) { Settings settings = settings_builder.build(); using FloatType = float; - using IndexType = std::size_t; + using IndexType = std::uint32_t; CATCH_AND_RETURN(scan_data, wavefront::scan_error, 1, wavefront::scan( settings.input(), @@ -173,32 +175,118 @@ int main(int argc, char** argv) { 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. + auto &output = settings.output(); + + // 0:8 + settings.output().write("\x7F" "3DG", 4); + wavefront::write_number(output, 0); + + std::uint32_t flags = 0; + + if (coordinate_storage_index.normal_coordinate_index.size() > 0) { + flags |= 1 << 1; + } + + std::visit([&](const auto &texture_coordinates) { + if constexpr (std::ranges::range>) { + using mapped_type = typename std::decay_t::mapped_type; + if constexpr (std::ranges::range) { + flags |= std::tuple_size_v << 2; + } else { + flags |= 1 << 2; + } + } + }, coordinate_data.texture_coordinates); + + std::visit([&](const auto &vertex_data) { + if constexpr (std::ranges::range>) { + using mapped_type = typename std::decay_t::value_type; + if constexpr (std::ranges::range) { + flags |= std::tuple_size_v << 4; + } else { + flags |= 1 << 4; + } + } + }, index_storage.vertices); + + // 8:12 + wavefront::write_number(output, flags); + + // 12:16 + wavefront::write_number(output, number_list.size()); + + // 16:28 + wavefront::write_number(output, std::get<1>(coordinate_storage).size()); + wavefront::write_number(output, std::get<2>(coordinate_storage).size()); + wavefront::write_number(output, std::get<3>(coordinate_storage).size()); + + // 28:36 + std::visit([&](const auto &vertex_data){ + wavefront::write_number(output, vertex_data.size()); + }, index_storage.vertices); + wavefront::write_number(output, index_storage.triangles.size()); + + // 36:48 + wavefront::write_number(output, 0); + wavefront::write_number(output, 0); + + output.write(reinterpret_cast(number_list.data()), number_list.size() * sizeof(decltype(number_list)::value_type)); + + std::array padding; + padding.fill(0); + std::size_t required_padding; + + required_padding = (16 - (number_list.size() * sizeof(decltype(number_list)::value_type) % 16)) % 16; + output.write(padding.data(), required_padding); + + if (std::get<1>(coordinate_storage).size() > 0) { + const auto &value_list = std::get<1>(coordinate_storage); + using value_t = std::decay_t::value_type; + output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); + required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; + if (required_padding > 0) { + output.write(padding.data(), required_padding); + } + } + + if (std::get<2>(coordinate_storage).size() > 0) { + const auto &value_list = std::get<2>(coordinate_storage); + using value_t = std::decay_t::value_type; + output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); + required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; + if (required_padding > 0) { + output.write(padding.data(), required_padding); + } + } + + if (std::get<3>(coordinate_storage).size() > 0) [[likely]] { + const auto &value_list = std::get<3>(coordinate_storage); + using value_t = std::decay_t::value_type; + output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); + required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; + if (required_padding > 0) { + output.write(padding.data(), required_padding); + } + } + + std::visit([&](const auto &vertex_data) { + using value_t = std::decay_t::value_type; + output.write(reinterpret_cast(vertex_data.data()), vertex_data.size() * sizeof(value_t)); + required_padding = (16 - (vertex_data.size() * sizeof(value_t) % 16)) % 16; + if (required_padding > 0) { + output.write(padding.data(), required_padding); + } + }, index_storage.vertices); + + { + const auto &value_list = index_storage.triangles; + using value_t = std::decay_t::value_type; + output.write(reinterpret_cast(value_list.data()), value_list.size() * sizeof(value_t)); + required_padding = (16 - (value_list.size() * sizeof(value_t) % 16)) % 16; + if (required_padding > 0) { + output.write(padding.data(), required_padding); + } + } return 0; } diff --git a/src/output.hpp b/src/output.hpp new file mode 100644 index 0000000..d98c34d --- /dev/null +++ b/src/output.hpp @@ -0,0 +1,9 @@ +#include +#include + +namespace wavefront { + template + void write_number(std::ostream &output, NumericType value) { + output.write(reinterpret_cast(&value), sizeof(NumericType)); + } +}