Add gltf accessor iterator
This commit is contained in:
parent
c3d964fafd
commit
efbcf48d01
3 changed files with 284 additions and 21 deletions
243
libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp
Normal file
243
libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp
Normal 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)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue