From efbcf48d0103e200601e2743851c8eb24c98fb6b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 3 Jan 2024 03:25:28 +0300 Subject: [PATCH] Add gltf accessor iterator --- .../psemek/gfx/gltf_accessor_iterator.hpp | 243 ++++++++++++++++++ libs/gfx/include/psemek/gfx/gltf_parser.hpp | 37 ++- libs/gfx/source/gltf_parser.cpp | 25 +- 3 files changed, 284 insertions(+), 21 deletions(-) create mode 100644 libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp diff --git a/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp b/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp new file mode 100644 index 00000000..5c200427 --- /dev/null +++ b/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp @@ -0,0 +1,243 @@ +#pragma once + +#include +#include + +#include + +namespace psemek::gfx +{ + + namespace detail + { + + template + struct accessor_traits_helper + { + static constexpr bool is_floating_point = std::is_floating_point_v; + static constexpr std::size_t components = 1; + using component_type = T; + + static auto pointer(T & value) + { + return &value; + } + }; + + template + struct accessor_traits_helper + {}; + + template + struct accessor_traits + : accessor_traits_helper> + { + + }; + + template + struct accessor_traits> + { + static constexpr bool is_floating_point = accessor_traits::is_floating_point; + static constexpr std::size_t components = N; + using component_type = T; + + static auto pointer(geom::vector & value) + { + return &value[0]; + } + }; + + template + struct accessor_traits> + { + static constexpr bool is_floating_point = accessor_traits::is_floating_point; + static constexpr std::size_t components = N; + using component_type = T; + + static auto pointer(geom::point & value) + { + return &value[0]; + } + }; + + template + void safe_cast(gltf_asset::accessor::component_type_t type, bool normalized, T & dst, char const * src) + { + using component_type_t = gltf_asset::accessor::component_type_t; + if (type == gltf_asset::accessor::component_type_t::_float) + { + dst = *(float const *)(src); + } + else + { + if constexpr (std::is_floating_point_v) + { + if (normalized) + { + switch (type) + { + case component_type_t::byte: + dst = std::max(-127, *(std::int8_t const *)(src)) / 127.f; + break; + case component_type_t::unsigned_byte: + dst = *(std::uint8_t const *)(src) / 255.f; + break; + case component_type_t::_short: + dst = std::max(-32767, *(std::int16_t const *)(src)) / 32767.f; + break; + case component_type_t::unsigned_short: + dst = *(std::uint16_t const *)(src) / 65535.f; + break; + case component_type_t::unsigned_int: + dst = *(std::uint32_t const *)(src) / 4294967296.f; + break; + default: + break; + } + } + else + { + switch (type) + { + case component_type_t::byte: + dst = *(std::int8_t const *)(src); + break; + case component_type_t::unsigned_byte: + dst = *(std::uint8_t const *)(src); + break; + case component_type_t::_short: + dst = *(std::int16_t const *)(src); + break; + case component_type_t::unsigned_short: + dst = *(std::uint16_t const *)(src); + break; + case component_type_t::unsigned_int: + dst = *(std::uint32_t const *)(src); + break; + default: + break; + } + } + } + + if constexpr (std::is_integral_v) + { + std::int64_t value; + switch (type) + { + case component_type_t::byte: + value = *(std::int8_t const *)(src); + break; + case component_type_t::unsigned_byte: + value = *(std::uint8_t const *)(src); + break; + case component_type_t::_short: + value = *(std::int16_t const *)(src); + break; + case component_type_t::unsigned_short: + value = *(std::uint16_t const *)(src); + break; + case component_type_t::unsigned_int: + value = *(std::uint32_t const *)(src); + break; + default: + break; + } + + if (value < (std::int64_t)std::numeric_limits::min() || value > (std::int64_t)std::numeric_limits::max()) + throw util::exception("Value for glTF accessor iterator is out of range"); + + dst = value; + } + } + } + + template + T accessor_cast(gltf_asset::accessor const & accessor, char const * ptr) + { + using traits = accessor_traits; + + if (traits::components != attribute_size(accessor.type)) + throw util::exception("glTF accessor component count mismatch"); + + T result; + auto result_ptr = traits::pointer(result); + + if constexpr (!traits::is_floating_point) + { + if (accessor.component_type == gltf_asset::accessor::_float) + throw util::exception("Cannot read floating point accessor as integral type"); + } + + for (std::size_t i = 0; i < traits::components; ++i) + { + safe_cast(accessor.component_type, accessor.normalized, result_ptr[i], ptr); + ptr += component_size(accessor.component_type); + } + + return result; + } + + } + + template + struct accessor_iterator + { + accessor_iterator(gltf_asset const & asset, std::size_t accessor_id, std::size_t element_index) + { + accessor_ = asset.accessors[accessor_id]; + auto const & buffer_view = asset.buffer_views[accessor_.buffer_view]; + auto const & buffer = asset.buffers[buffer_view.buffer]; + + if (buffer_view.stride == 0) + stride_ = component_size(accessor_.component_type) * attribute_size(accessor_.type); + else + stride_ = buffer_view.stride; + + if (!buffer.data) + throw util::exception("Buffer data not loaded for glTF accessor iterator"); + + ptr_ = buffer.data->data() + buffer_view.offset; + ptr_ += stride_ * element_index; + } + + T operator *() const + { + return detail::accessor_cast(accessor_, ptr_); + } + + accessor_iterator & operator ++() + { + ptr_ += stride_; + return *this; + } + + accessor_iterator operator ++(int) + { + auto copy = *this; + operator++(); + return copy; + } + + friend bool operator == (accessor_iterator const & it1, accessor_iterator const & it2) + { + return it1.ptr_ == it2.ptr_; + } + + private: + gltf_asset::accessor accessor_; + std::size_t stride_; + char const * ptr_; + }; + + template + auto accessor_range(gltf_asset const & asset, std::size_t accessor_id) + { + return util::range>{ + accessor_iterator(asset, accessor_id, 0), + accessor_iterator(asset, accessor_id, asset.accessors[accessor_id].count) + }; + } + +} diff --git a/libs/gfx/include/psemek/gfx/gltf_parser.hpp b/libs/gfx/include/psemek/gfx/gltf_parser.hpp index 7c52cdbf..08923379 100644 --- a/libs/gfx/include/psemek/gfx/gltf_parser.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_parser.hpp @@ -111,7 +111,15 @@ namespace psemek::gfx struct accessor { std::size_t buffer_view; - std::size_t component_type; + enum component_type_t + { + byte = 5120, + unsigned_byte = 5121, + _short = 5122, + unsigned_short = 5123, + unsigned_int = 5125, + _float = 5126, + } component_type; std::size_t count; enum type_t @@ -188,6 +196,31 @@ namespace psemek::gfx gltf_asset parse_gltf(io::istream && stream); gltf_asset parse_glb(io::istream && stream); - std::size_t attribute_size(gltf_asset::accessor::type_t type); + inline std::size_t component_size(gltf_asset::accessor::component_type_t type) + { + switch (type) + { + case gltf_asset::accessor::byte: return 1; + case gltf_asset::accessor::unsigned_byte: return 1; + case gltf_asset::accessor::_short: return 2; + case gltf_asset::accessor::unsigned_short: return 2; + case gltf_asset::accessor::unsigned_int: return 4; + case gltf_asset::accessor::_float: return 4; + default: throw util::exception("Unknown glTF component type"); + } + } + + inline std::size_t attribute_size(gltf_asset::accessor::type_t type) + { + using type_t = gltf_asset::accessor::type_t; + switch (type) + { + case type_t::scalar: return 1; + case type_t::vec2: return 2; + case type_t::vec3: return 3; + case type_t::vec4: return 4; + default: throw util::exception("Unsupported attribute type"); + } + } } diff --git a/libs/gfx/source/gltf_parser.cpp b/libs/gfx/source/gltf_parser.cpp index d4083d55..d8620988 100644 --- a/libs/gfx/source/gltf_parser.cpp +++ b/libs/gfx/source/gltf_parser.cpp @@ -352,7 +352,7 @@ namespace psemek::gfx auto & target = result.accessors.emplace_back(); target.buffer_view = accessor["bufferView"].GetUint64(); - target.component_type = accessor["componentType"].GetUint64(); + target.component_type = (gltf_asset::accessor::component_type_t)accessor["componentType"].GetUint64(); target.count = accessor["count"].GetUint64(); target.type = parse_accessor_type(accessor["type"].GetString()); @@ -462,10 +462,10 @@ namespace psemek::gfx stream.read_all((char *)&length, 4); if (magic != 0x46546c67u) - throw std::runtime_error("Error parsing GLB magic"); + throw util::exception("Error parsing GLB magic"); if (version != 2) - throw std::runtime_error("Unknown GLB file version"); + throw util::exception("Unknown GLB file version"); length -= 12; @@ -490,31 +490,18 @@ namespace psemek::gfx else if (chunk_type == 0x004e4942u) { if (result.buffers.empty()) - throw std::runtime_error("Error parsing GLB: glTF chunk has no buffers"); + throw util::exception("Error parsing GLB: glTF chunk has no buffers"); if (!result.buffers[0].uri.empty()) - throw std::runtime_error("Error parsing GLB: first glTF buffer has URI"); + throw util::exception("Error parsing GLB: first glTF buffer has URI"); result.buffers[0].data = std::move(data); } else - throw std::runtime_error("Error parsing GLB: unknown chunk type"); + throw util::exception("Error parsing GLB: unknown chunk type"); } return result; } - std::size_t attribute_size(gltf_asset::accessor::type_t type) - { - using type_t = gltf_asset::accessor::type_t; - switch (type) - { - case type_t::scalar: return 1; - case type_t::vec2: return 2; - case type_t::vec3: return 3; - case type_t::vec4: return 4; - default: throw util::exception("Unsupported attribute type"); - } - } - }