Add gltf accessor iterator

This commit is contained in:
Nikita Lisitsa 2024-01-03 03:25:28 +03:00
parent c3d964fafd
commit efbcf48d01
3 changed files with 284 additions and 21 deletions

View file

@ -0,0 +1,243 @@
#pragma once
#include <psemek/gfx/gltf_parser.hpp>
#include <psemek/util/range.hpp>
#include <type_traits>
namespace psemek::gfx
{
namespace detail
{
template <typename T, bool IsArithmetic>
struct accessor_traits_helper
{
static constexpr bool is_floating_point = std::is_floating_point_v<T>;
static constexpr std::size_t components = 1;
using component_type = T;
static auto pointer(T & value)
{
return &value;
}
};
template <typename T>
struct accessor_traits_helper<T, false>
{};
template <typename T>
struct accessor_traits
: accessor_traits_helper<T, std::is_arithmetic_v<T>>
{
};
template <typename T, std::size_t N>
struct accessor_traits<geom::vector<T, N>>
{
static constexpr bool is_floating_point = accessor_traits<T>::is_floating_point;
static constexpr std::size_t components = N;
using component_type = T;
static auto pointer(geom::vector<T, N> & value)
{
return &value[0];
}
};
template <typename T, std::size_t N>
struct accessor_traits<geom::point<T, N>>
{
static constexpr bool is_floating_point = accessor_traits<T>::is_floating_point;
static constexpr std::size_t components = N;
using component_type = T;
static auto pointer(geom::point<T, N> & value)
{
return &value[0];
}
};
template <typename T>
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<T>)
{
if (normalized)
{
switch (type)
{
case component_type_t::byte:
dst = std::max<std::int8_t>(-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<std::int16_t>(-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<T>)
{
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<T>::min() || value > (std::int64_t)std::numeric_limits<T>::max())
throw util::exception("Value for glTF accessor iterator is out of range");
dst = value;
}
}
}
template <typename T>
T accessor_cast(gltf_asset::accessor const & accessor, char const * ptr)
{
using traits = accessor_traits<T>;
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 <typename T>
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<T>(accessor_, ptr_);
}
accessor_iterator & operator ++()
{
ptr_ += stride_;
return *this;
}
accessor_iterator operator ++(int)
{
auto copy = *this;
operator++();
return copy;
}
friend bool operator == (accessor_iterator<T> const & it1, accessor_iterator<T> const & it2)
{
return it1.ptr_ == it2.ptr_;
}
private:
gltf_asset::accessor accessor_;
std::size_t stride_;
char const * ptr_;
};
template <typename T>
auto accessor_range(gltf_asset const & asset, std::size_t accessor_id)
{
return util::range<accessor_iterator<T>>{
accessor_iterator<T>(asset, accessor_id, 0),
accessor_iterator<T>(asset, accessor_id, asset.accessors[accessor_id].count)
};
}
}

View file

@ -111,7 +111,15 @@ namespace psemek::gfx
struct accessor struct accessor
{ {
std::size_t buffer_view; 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; std::size_t count;
enum type_t enum type_t
@ -188,6 +196,31 @@ namespace psemek::gfx
gltf_asset parse_gltf(io::istream && stream); gltf_asset parse_gltf(io::istream && stream);
gltf_asset parse_glb(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");
}
}
} }

View file

@ -352,7 +352,7 @@ namespace psemek::gfx
auto & target = result.accessors.emplace_back(); auto & target = result.accessors.emplace_back();
target.buffer_view = accessor["bufferView"].GetUint64(); 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.count = accessor["count"].GetUint64();
target.type = parse_accessor_type(accessor["type"].GetString()); target.type = parse_accessor_type(accessor["type"].GetString());
@ -462,10 +462,10 @@ namespace psemek::gfx
stream.read_all((char *)&length, 4); stream.read_all((char *)&length, 4);
if (magic != 0x46546c67u) if (magic != 0x46546c67u)
throw std::runtime_error("Error parsing GLB magic"); throw util::exception("Error parsing GLB magic");
if (version != 2) if (version != 2)
throw std::runtime_error("Unknown GLB file version"); throw util::exception("Unknown GLB file version");
length -= 12; length -= 12;
@ -490,31 +490,18 @@ namespace psemek::gfx
else if (chunk_type == 0x004e4942u) else if (chunk_type == 0x004e4942u)
{ {
if (result.buffers.empty()) 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()) 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); result.buffers[0].data = std::move(data);
} }
else else
throw std::runtime_error("Error parsing GLB: unknown chunk type"); throw util::exception("Error parsing GLB: unknown chunk type");
} }
return result; 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");
}
}
} }