Accessor tools
fastgltf provides a utility header for working with accessors. The header contains various functions and utilities for reading, copying, and converting accessor data. All of these tools also directly support sparse accessors to help add support for these without having to understand how they work. This header was written by forenoonwatch with the help of Eearslya and me.
All related functions are templated and take T
as an argument.
This type has to be have a ElementTraits
specialization, which provides information about the
vector properties and data properties.
Using this information, fastgltf can convert the accessor data into your preferred format.
For example, glm::vec3
would be a vector of 3 floats, which would be defined like this:
template <>
struct fastgltf::ElementTraits<glm::vec3> : fastgltf::ElementTraitsBase<glm::vec3, AccessorType::Vec3, float> {};
Note
Note that, for glm types, there is a header with all pre-defined types shipped with fastgltf: fastgltf/glm_element_traits.hpp
.
This header includes the ElementTraits definition for all relevant glm types.
Warning
Note that, by default, these functions will only be able to load from buffers where the source is either a sources::ByteView
, a sources::Array`, or a ``sources::Vector
.
For other data sources, you’ll need to provide a functor similar to the already provided DefaultBufferDataAdapter
to the last parameter of each function.
For more detailed documentation about this see this section.
getAccessorElement
This function can be used to retrieve a single element from an accessor using an index. It handles sparse accessors and can properly convert the type.
-
template<typename ElementType, typename BufferDataAdapter = DefaultBufferDataAdapter>
ElementType fastgltf::getAccessorElement(const Asset &asset, const Accessor &accessor, size_t index, const BufferDataAdapter &adapter = {})
iterateAccessor
Using iterateAccessor
you can iterate over the data of an accessor using a lambda, similarly to std::for_each
.
-
template<typename ElementType, typename Functor, typename BufferDataAdapter = DefaultBufferDataAdapter>
void fastgltf::iterateAccessor(const Asset &asset, const Accessor &accessor, Functor &&func, const BufferDataAdapter &adapter = {})
fastgltf::Primitive& primitive = ...;
std::vector<std::uint32_t> indices;
if (primitive.indicesAccessor.has_value()) {
auto& accessor = asset->accessors[primitive.indicesAccessor.value()];
indices.resize(accessor.count);
std::size_t idx = 0;
fastgltf::iterateAccessor<std::uint32_t>(asset.get(), accessor, [&](std::uint32_t index) {
indices[idx++] = index;
});
}
iterateAccessorWithIndex
Functionally identical to iterateAccessor
, but provides you with the current index as the second parameter to the lambda.
-
template<typename ElementType, typename Functor, typename BufferDataAdapter = DefaultBufferDataAdapter>
void fastgltf::iterateAccessorWithIndex(const Asset &asset, const Accessor &accessor, Functor &&func, const BufferDataAdapter &adapter = {})
copyFromAccessor
This function essentially does a memcpy
on the contents of the accessor data.
In cases where the ElementType is default-constructible, and the accessor type allows direct copying, this performs a direct memcpy
.
Otherwise, this function properly respects normalization and sparse accessors while copying and converting the data.
Warning
doxygenfunction: Unable to resolve function “copyFromAccessor” with arguments (const Asset&, const Accessor&, void*, const BufferDataAdapter&) in doxygen xml output for project “fastgltf” from directory: /home/docs/checkouts/readthedocs.org/user_builds/fastgltf/checkouts/v0.7.x/build/docs/doxygen/xml. Potential matches:
- template<typename ElementType, std::size_t TargetStride = sizeof(ElementType), typename BufferDataAdapter = DefaultBufferDataAdapter> void copyFromAccessor(const Asset &asset, const Accessor &accessor, void *dest, const BufferDataAdapter &adapter = {})
Accessor iterators
fastgltf also provides C++ iterators over accessor data to support the syntactic sugar of C++11’s range-based for-loops.
These iterators can be obtained using iterateAccessor
, and can be used like so:
-
template<typename ElementType, typename BufferDataAdapter = DefaultBufferDataAdapter>
IterableAccessor<ElementType, BufferDataAdapter> fastgltf::iterateAccessor(const Asset &asset, const Accessor &accessor, const BufferDataAdapter &adapter = {})
std::size_t idx = 0;
for (auto element : fastgltf::iterateAccessor(asset.get(), accessor)) {
array[idx++] = element;
}
BufferDataAdapter interface
The accessor tools acquire the binary data through this functional interface.
By default, fastgltf provides a DefaultBufferDataAdapter
struct.
The accessor functions also default to using this class,
however it is important to note that this default interface only works with buffers or images that have a sources::ByteView
, a sources::Array`, or a ``sources::Vector
in the DataSource
member.
-
struct DefaultBufferDataAdapter
If you do not provide Options::LoadExternalBuffers to the Parser while loading the glTF,
external buffers will be available as sources::URI
and will not work with the DefaultBufferDataAdapter
.
Therefore, you’ll either have to set that option or provide a custom functional interface that properly returns a pointer to the memory.
As this is a functional interface it is possible to also use lambdas for this:
std::vector<std::byte> fileBytes;
std::vector<std::uint8_t> accessorData(accessor.count);
fastgltf::copyFromAccessor(asset.get(), accessor, accessorData.data(), [&](const fastgltf::Buffer& buffer) const {
return fileBytes.data();
});
Example: Loading primitive positions
The following snippet illustrates how one could potentially load vertex positions for a primitive into a OpenGL buffer using the accessor tools.
fastgltf::Primitive* primitive = ...;
// findAttribute returns a iterator into the underlying vector of primitive attributes.
// Note that the glTF spec requires every primitive to have a POSITION,
// so it's perfectly valid to assert that positionIt is never nullptr.
auto* positionIt = primitive->findAttribute("POSITION");
auto& positionAccessor = asset.accessors[positionIt->second];
if (!positionAccessor.bufferViewIndex.has_value())
continue;
// Create the vertex buffer for this primitive,
// and use the accessor tools to copy directly into the mapped buffer.
glCreateBuffers(1, &primitive.vertexBuffer);
glNamedBufferData(primitive.vertexBuffer,
positionAccessor.count * sizeof(Vertex), nullptr, GL_STATIC_DRAW);
auto* vertices = static_cast<Vertex*>(glMapNamedBuffer(primitive.vertexBuffer, GL_WRITE_ONLY));
// Iterates over the accessor (potentially handling any sparse accessors),
// and gives each vertex UV a default value, which need to be loaded separately.
fastgltf::iterateAccessorWithIndex<glm::vec3>(
asset, positionAccessor, [&](glm::vec3 pos, std::size_t idx) {
vertices[idx].position = pos;
vertices[idx].uv = glm::vec2();
});