654 lines
25 KiB
C++
654 lines
25 KiB
C++
/*
|
|
* Copyright (C) 2022 - 2023 spnda
|
|
* This file is part of fastgltf <https://github.com/spnda/fastgltf>.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <cstring>
|
|
#include <iterator>
|
|
|
|
#include "types.hpp"
|
|
|
|
namespace fastgltf {
|
|
|
|
template <typename>
|
|
struct ComponentTypeConverter;
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<std::int8_t> {
|
|
static constexpr auto type = ComponentType::Byte;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<std::uint8_t> {
|
|
static constexpr auto type = ComponentType::UnsignedByte;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<std::int16_t> {
|
|
static constexpr auto type = ComponentType::Short;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<std::uint16_t> {
|
|
static constexpr auto type = ComponentType::UnsignedShort;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<std::int32_t> {
|
|
static constexpr auto type = ComponentType::Int;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<std::uint32_t> {
|
|
static constexpr auto type = ComponentType::UnsignedInt;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<float> {
|
|
static constexpr auto type = ComponentType::Float;
|
|
};
|
|
|
|
template<>
|
|
struct ComponentTypeConverter<double> {
|
|
static constexpr auto type = ComponentType::Double;
|
|
};
|
|
|
|
template <typename ElementType, AccessorType EnumAccessorType, typename ComponentType = ElementType>
|
|
struct ElementTraitsBase {
|
|
using element_type = ElementType;
|
|
using component_type = ComponentType;
|
|
static constexpr auto type = EnumAccessorType;
|
|
static constexpr auto enum_component_type = ComponentTypeConverter<ComponentType>::type;
|
|
};
|
|
|
|
template <typename>
|
|
struct ElementTraits;
|
|
|
|
template<>
|
|
struct ElementTraits<std::int8_t> : ElementTraitsBase<std::int8_t, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<std::uint8_t> : ElementTraitsBase<std::uint8_t, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<std::int16_t> : ElementTraitsBase<std::int16_t, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<std::uint16_t> : ElementTraitsBase<std::uint16_t, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<std::int32_t> : ElementTraitsBase<std::int32_t, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<std::uint32_t> : ElementTraitsBase<std::uint32_t, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<float> : ElementTraitsBase<float, AccessorType::Scalar> {};
|
|
|
|
template<>
|
|
struct ElementTraits<double> : ElementTraitsBase<double, AccessorType::Scalar> {};
|
|
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
template <typename ElementType>
|
|
concept Element = std::is_arithmetic_v<typename ElementTraits<ElementType>::component_type>
|
|
&& ElementTraits<ElementType>::type != AccessorType::Invalid
|
|
&& ElementTraits<ElementType>::enum_component_type != ComponentType::Invalid
|
|
&& std::is_default_constructible_v<ElementType>
|
|
&& std::is_constructible_v<ElementType>
|
|
&& std::is_move_assignable_v<ElementType>;
|
|
#endif
|
|
|
|
namespace internal {
|
|
|
|
template <typename DestType, typename SourceType>
|
|
constexpr DestType convertComponent(const SourceType& source, bool normalized) {
|
|
if (normalized) {
|
|
if constexpr (std::is_floating_point_v<SourceType> && std::is_integral_v<DestType>) {
|
|
// float -> int conversion
|
|
return static_cast<DestType>(std::round(source * static_cast<SourceType>(std::numeric_limits<DestType>::max())));
|
|
} else if constexpr (std::is_integral_v<SourceType> && std::is_floating_point_v<DestType>) {
|
|
// int -> float conversion
|
|
DestType minValue;
|
|
if constexpr (std::is_signed_v<DestType>) {
|
|
minValue = static_cast<DestType>(-1.0);
|
|
} else {
|
|
minValue = static_cast<DestType>(0.0);
|
|
}
|
|
|
|
// We have to use max here because for uchar -> float we could have -128 but 1.0 should represent 127,
|
|
// which is why -128 and -127 both equate to 1.0.
|
|
return fastgltf::max(static_cast<DestType>(source) / static_cast<DestType>(std::numeric_limits<SourceType>::max()),
|
|
minValue);
|
|
}
|
|
}
|
|
|
|
return static_cast<DestType>(source);
|
|
}
|
|
|
|
template <typename SourceType, typename DestType, std::size_t Index>
|
|
constexpr DestType convertComponent(const std::byte* bytes, bool normalized) {
|
|
return convertComponent<DestType>(reinterpret_cast<const SourceType*>(bytes)[Index], normalized);
|
|
}
|
|
|
|
template <typename ElementType, typename SourceType, std::size_t... I>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
constexpr ElementType convertAccessorElement(const std::byte* bytes, bool normalized, std::index_sequence<I...>) {
|
|
using DestType = typename ElementTraits<ElementType>::component_type;
|
|
static_assert(std::is_arithmetic_v<DestType>, "Accessor traits must provide a valid component type");
|
|
|
|
if constexpr (std::is_aggregate_v<ElementType>) {
|
|
return {convertComponent<SourceType, DestType, I>(bytes, normalized)...};
|
|
} else {
|
|
return ElementType{convertComponent<SourceType, DestType, I>(bytes, normalized)...};
|
|
}
|
|
}
|
|
|
|
template <typename ElementType,
|
|
typename Seq = std::make_index_sequence<getNumComponents(ElementTraits<ElementType>::type)>>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
ElementType getAccessorElementAt(ComponentType componentType, const std::byte* bytes, bool normalized = false) {
|
|
switch (componentType) {
|
|
case ComponentType::Byte:
|
|
return convertAccessorElement<ElementType, std::int8_t>(bytes, normalized, Seq{});
|
|
case ComponentType::UnsignedByte:
|
|
return convertAccessorElement<ElementType, std::uint8_t>(bytes, normalized, Seq{});
|
|
case ComponentType::Short:
|
|
return convertAccessorElement<ElementType, std::int16_t>(bytes, normalized, Seq{});
|
|
case ComponentType::UnsignedShort:
|
|
return convertAccessorElement<ElementType, std::uint16_t>(bytes, normalized, Seq{});
|
|
case ComponentType::Int:
|
|
return convertAccessorElement<ElementType, std::int32_t>(bytes, normalized, Seq{});
|
|
case ComponentType::UnsignedInt:
|
|
return convertAccessorElement<ElementType, std::uint32_t>(bytes, normalized, Seq{});
|
|
case ComponentType::Float:
|
|
return convertAccessorElement<ElementType, float>(bytes, normalized, Seq{});
|
|
case ComponentType::Double:
|
|
return convertAccessorElement<ElementType, double>(bytes, normalized, Seq{});
|
|
case ComponentType::Invalid:
|
|
default:
|
|
return ElementType{};
|
|
}
|
|
}
|
|
|
|
// Performs a binary search for the index into the sparse index list whose value matches the desired index
|
|
template <typename ElementType>
|
|
bool findSparseIndex(const std::byte* bytes, std::size_t indexCount, std::size_t desiredIndex,
|
|
std::size_t& resultIndex) {
|
|
auto* elements = reinterpret_cast<const ElementType*>(bytes);
|
|
auto count = indexCount;
|
|
|
|
resultIndex = 0;
|
|
|
|
while (count > 0) {
|
|
auto step = count / 2;
|
|
auto index = resultIndex + step;
|
|
|
|
if (elements[index] < static_cast<ElementType>(desiredIndex)) {
|
|
resultIndex = index + 1;
|
|
count -= step + 1;
|
|
} else {
|
|
count = step;
|
|
}
|
|
}
|
|
|
|
return resultIndex < indexCount && elements[resultIndex] == static_cast<ElementType>(desiredIndex);
|
|
}
|
|
|
|
// Finds the index of the nearest sparse index to the desired index
|
|
inline bool findSparseIndex(ComponentType componentType, const std::byte* bytes, std::size_t indexCount,
|
|
std::size_t desiredIndex, std::size_t& resultIndex) {
|
|
switch (componentType) {
|
|
case ComponentType::Byte:
|
|
return findSparseIndex<std::int8_t>(bytes, indexCount, desiredIndex, resultIndex);
|
|
case ComponentType::UnsignedByte:
|
|
return findSparseIndex<std::uint8_t>(bytes, indexCount, desiredIndex, resultIndex);
|
|
case ComponentType::Short:
|
|
return findSparseIndex<std::int16_t>(bytes, indexCount, desiredIndex, resultIndex);
|
|
case ComponentType::UnsignedShort:
|
|
return findSparseIndex<std::uint16_t>(bytes, indexCount, desiredIndex, resultIndex);
|
|
case ComponentType::Int:
|
|
return findSparseIndex<std::int32_t>(bytes, indexCount, desiredIndex, resultIndex);
|
|
case ComponentType::UnsignedInt:
|
|
return findSparseIndex<std::uint32_t>(bytes, indexCount, desiredIndex, resultIndex);
|
|
case ComponentType::Float:
|
|
case ComponentType::Double:
|
|
case ComponentType::Invalid:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
struct DefaultBufferDataAdapter {
|
|
const std::byte* operator()(const Buffer& buffer) const {
|
|
return std::visit(visitor {
|
|
[](auto&) -> const std::byte* {
|
|
return nullptr;
|
|
},
|
|
[&](const sources::Vector& vec) {
|
|
return reinterpret_cast<const std::byte*>(vec.bytes.data());
|
|
},
|
|
[&](const sources::ByteView& bv) {
|
|
return bv.bytes.data();
|
|
},
|
|
}, buffer.data);
|
|
}
|
|
};
|
|
|
|
template <typename ElementType, typename BufferDataAdapter>
|
|
class IterableAccessor;
|
|
|
|
template <typename ElementType, typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
class AccessorIterator {
|
|
protected:
|
|
const IterableAccessor<ElementType, BufferDataAdapter>* accessor;
|
|
std::size_t idx;
|
|
std::size_t sparseIdx = 0;
|
|
std::size_t nextSparseIndex = 0;
|
|
|
|
public:
|
|
using value_type = ElementType;
|
|
using reference = ElementType&;
|
|
using pointer = ElementType*;
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
// This iterator isn't truly random access (as per the C++ definition), but we do want to support
|
|
// some things that these come with (e.g. std::distance using operator-).
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
|
|
AccessorIterator(const IterableAccessor<ElementType, BufferDataAdapter>* accessor, std::size_t idx = 0)
|
|
: accessor(accessor), idx(idx) {
|
|
if (accessor->accessor.sparse.has_value()) {
|
|
// Get the first sparse index.
|
|
nextSparseIndex = internal::getAccessorElementAt<std::uint32_t>(accessor->indexComponentType,
|
|
accessor->indicesBytes + accessor->indexStride * sparseIdx);
|
|
}
|
|
}
|
|
|
|
AccessorIterator& operator++() noexcept {
|
|
++idx;
|
|
return *this;
|
|
}
|
|
|
|
AccessorIterator operator++(int) noexcept {
|
|
auto x = *this;
|
|
++(*this);
|
|
return x;
|
|
}
|
|
|
|
[[nodiscard]] difference_type operator-(const AccessorIterator& other) const noexcept {
|
|
return static_cast<difference_type>(idx - other.idx);
|
|
}
|
|
|
|
[[nodiscard]] bool operator==(const AccessorIterator& iterator) const noexcept {
|
|
// We don't compare sparse properties
|
|
return idx == iterator.idx &&
|
|
accessor->bufferBytes == iterator.accessor->bufferBytes &&
|
|
accessor->stride == iterator.accessor->stride &&
|
|
accessor->componentType == iterator.accessor->componentType;
|
|
}
|
|
|
|
[[nodiscard]] bool operator!=(const AccessorIterator& iterator) const noexcept {
|
|
return !(*this == iterator);
|
|
}
|
|
|
|
[[nodiscard]] ElementType operator*() noexcept {
|
|
if (accessor->accessor.sparse.has_value()) {
|
|
if (idx == nextSparseIndex) {
|
|
// Get the sparse value for this index
|
|
auto value = internal::getAccessorElementAt<ElementType>(accessor->componentType,
|
|
accessor->valuesBytes + accessor->valueStride * sparseIdx,
|
|
accessor->accessor.normalized);
|
|
|
|
// Find the next sparse index.
|
|
++sparseIdx;
|
|
if (sparseIdx < accessor->sparseCount) {
|
|
nextSparseIndex = internal::getAccessorElementAt<std::uint32_t>(accessor->indexComponentType,
|
|
accessor->indicesBytes + accessor->indexStride * sparseIdx);
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return internal::getAccessorElementAt<ElementType>(accessor->componentType,
|
|
accessor->bufferBytes + idx * accessor->stride,
|
|
accessor->accessor.normalized);
|
|
}
|
|
};
|
|
|
|
template <typename ElementType, typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
class IterableAccessor {
|
|
friend class AccessorIterator<ElementType, BufferDataAdapter>;
|
|
|
|
const Asset& asset;
|
|
const Accessor& accessor;
|
|
|
|
const std::byte* bufferBytes;
|
|
std::size_t stride;
|
|
fastgltf::ComponentType componentType;
|
|
|
|
// Data needed for sparse accessors
|
|
fastgltf::ComponentType indexComponentType;
|
|
const std::byte* indicesBytes;
|
|
const std::byte* valuesBytes;
|
|
std::size_t indexStride;
|
|
std::size_t valueStride;
|
|
std::size_t sparseCount;
|
|
|
|
public:
|
|
using iterator = AccessorIterator<ElementType, BufferDataAdapter>;
|
|
|
|
explicit IterableAccessor(const Asset& asset, const Accessor& accessor, const BufferDataAdapter& adapter) : asset(asset), accessor(accessor) {
|
|
componentType = accessor.componentType;
|
|
|
|
const auto& view = asset.bufferViews[*accessor.bufferViewIndex];
|
|
stride = view.byteStride ? *view.byteStride : getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
bufferBytes = adapter(asset.buffers[view.bufferIndex]);
|
|
bufferBytes += view.byteOffset + accessor.byteOffset;
|
|
|
|
if (accessor.sparse.has_value()) {
|
|
const auto& indicesView = asset.bufferViews[accessor.sparse->indicesBufferView];
|
|
indicesBytes = adapter(asset.buffers[indicesView.bufferIndex])
|
|
+ indicesView.byteOffset + accessor.sparse->indicesByteOffset;
|
|
|
|
indexStride = getElementByteSize(AccessorType::Scalar, accessor.sparse->indexComponentType);
|
|
|
|
const auto& valuesView = asset.bufferViews[accessor.sparse->valuesBufferView];
|
|
valuesBytes = adapter(asset.buffers[valuesView.bufferIndex])
|
|
+ valuesView.byteOffset + accessor.sparse->valuesByteOffset;
|
|
|
|
// "The index of the bufferView with sparse values. The referenced buffer view MUST NOT
|
|
// have its target or byteStride properties defined."
|
|
valueStride = getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
indexComponentType = accessor.sparse->indexComponentType;
|
|
sparseCount = accessor.sparse->count;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] iterator begin() const noexcept {
|
|
return iterator(this, 0);
|
|
}
|
|
|
|
[[nodiscard]] iterator end() const noexcept {
|
|
return iterator(this, accessor.count);
|
|
}
|
|
};
|
|
|
|
template <typename ElementType, typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
ElementType getAccessorElement(const Asset& asset, const Accessor& accessor, size_t index,
|
|
const BufferDataAdapter& adapter = {}) {
|
|
using Traits = ElementTraits<ElementType>;
|
|
static_assert(Traits::type != AccessorType::Invalid, "Accessor traits must provide a valid Accessor Type");
|
|
static_assert(std::is_default_constructible_v<ElementType>, "Element type must be default constructible");
|
|
static_assert(std::is_constructible_v<ElementType>, "Element type must be constructible");
|
|
static_assert(std::is_move_assignable_v<ElementType>, "Element type must be move-assignable");
|
|
|
|
if (accessor.sparse) {
|
|
const auto& indicesView = asset.bufferViews[accessor.sparse->indicesBufferView];
|
|
auto* indicesBytes = adapter(asset.buffers[indicesView.bufferIndex])
|
|
+ indicesView.byteOffset + accessor.sparse->indicesByteOffset;
|
|
|
|
const auto& valuesView = asset.bufferViews[accessor.sparse->valuesBufferView];
|
|
auto* valuesBytes = adapter(asset.buffers[valuesView.bufferIndex])
|
|
+ valuesView.byteOffset + accessor.sparse->valuesByteOffset;
|
|
// "The index of the bufferView with sparse values. The referenced buffer view MUST NOT
|
|
// have its target or byteStride properties defined."
|
|
auto valueStride = getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
std::size_t sparseIndex{};
|
|
if (internal::findSparseIndex(accessor.sparse->indexComponentType, indicesBytes, accessor.sparse->count,
|
|
index, sparseIndex)) {
|
|
return internal::getAccessorElementAt<ElementType>(accessor.componentType,
|
|
valuesBytes + valueStride * sparseIndex,
|
|
accessor.normalized);
|
|
}
|
|
}
|
|
|
|
// 5.1.1. accessor.bufferView
|
|
// The index of the buffer view. When undefined, the accessor MUST be initialized with zeros; sparse
|
|
// property or extensions MAY override zeros with actual values.
|
|
if (!accessor.bufferViewIndex) {
|
|
if constexpr (std::is_aggregate_v<ElementType>) {
|
|
return {};
|
|
} else {
|
|
return ElementType{};
|
|
}
|
|
}
|
|
|
|
const auto& view = asset.bufferViews[*accessor.bufferViewIndex];
|
|
auto stride = view.byteStride ? *view.byteStride : getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
auto* bytes = adapter(asset.buffers[view.bufferIndex]);
|
|
bytes += view.byteOffset + accessor.byteOffset;
|
|
|
|
return internal::getAccessorElementAt<ElementType>(accessor.componentType, bytes + index * stride, accessor.normalized);
|
|
}
|
|
|
|
template<typename ElementType, typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
IterableAccessor<ElementType, BufferDataAdapter> iterateAccessor(const Asset& asset, const Accessor& accessor, const BufferDataAdapter& adapter = {}) {
|
|
return IterableAccessor<ElementType, BufferDataAdapter>(asset, accessor, adapter);
|
|
}
|
|
|
|
template <typename ElementType, typename Functor, typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
void iterateAccessor(const Asset& asset, const Accessor& accessor, Functor&& func,
|
|
const BufferDataAdapter& adapter = {}) {
|
|
using Traits = ElementTraits<ElementType>;
|
|
static_assert(Traits::type != AccessorType::Invalid, "Accessor traits must provide a valid accessor type");
|
|
static_assert(Traits::enum_component_type != ComponentType::Invalid, "Accessor traits must provide a valid component type");
|
|
static_assert(std::is_default_constructible_v<ElementType>, "Element type must be default constructible");
|
|
static_assert(std::is_constructible_v<ElementType>, "Element type must be constructible");
|
|
static_assert(std::is_move_assignable_v<ElementType>, "Element type must be move-assignable");
|
|
|
|
if (accessor.type != Traits::type) {
|
|
return;
|
|
}
|
|
|
|
if (accessor.sparse && accessor.sparse->count > 0) {
|
|
auto& indicesView = asset.bufferViews[accessor.sparse->indicesBufferView];
|
|
auto* indicesBytes = adapter(asset.buffers[indicesView.bufferIndex])
|
|
+ indicesView.byteOffset + accessor.sparse->indicesByteOffset;
|
|
auto indexStride = getElementByteSize(AccessorType::Scalar, accessor.sparse->indexComponentType);
|
|
|
|
auto& valuesView = asset.bufferViews[accessor.sparse->valuesBufferView];
|
|
auto* valuesBytes = adapter(asset.buffers[valuesView.bufferIndex])
|
|
+ valuesView.byteOffset + accessor.sparse->valuesByteOffset;
|
|
// "The index of the bufferView with sparse values. The referenced buffer view MUST NOT
|
|
// have its target or byteStride properties defined."
|
|
auto valueStride = getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
const std::byte* srcBytes = nullptr;
|
|
std::size_t srcStride = 0;
|
|
|
|
// 5.1.1. accessor.bufferView
|
|
// The index of the buffer view. When undefined, the accessor MUST be initialized with zeros; sparse
|
|
// property or extensions MAY override zeros with actual values.
|
|
if (accessor.bufferViewIndex) {
|
|
auto& view = asset.bufferViews[*accessor.bufferViewIndex];
|
|
srcBytes = adapter(asset.buffers[view.bufferIndex]) + view.byteOffset + accessor.byteOffset;
|
|
srcStride = view.byteStride ? *view.byteStride
|
|
: getElementByteSize(accessor.type, accessor.componentType);
|
|
}
|
|
|
|
auto nextSparseIndex = internal::getAccessorElementAt<std::uint32_t>(
|
|
accessor.sparse->indexComponentType, indicesBytes);
|
|
std::size_t sparseIndexCount = 0;
|
|
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
if (i == nextSparseIndex) {
|
|
func(internal::getAccessorElementAt<ElementType>(accessor.componentType,
|
|
valuesBytes + valueStride * sparseIndexCount,
|
|
accessor.normalized));
|
|
|
|
++sparseIndexCount;
|
|
|
|
if (sparseIndexCount < accessor.sparse->count) {
|
|
nextSparseIndex = internal::getAccessorElementAt<std::uint32_t>(
|
|
accessor.sparse->indexComponentType, indicesBytes + indexStride * sparseIndexCount);
|
|
}
|
|
} else if (accessor.bufferViewIndex) {
|
|
func(internal::getAccessorElementAt<ElementType>(accessor.componentType,
|
|
srcBytes + srcStride * i,
|
|
accessor.normalized));
|
|
} else {
|
|
func(ElementType{});
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// 5.1.1. accessor.bufferView
|
|
// The index of the buffer view. When undefined, the accessor MUST be initialized with zeros; sparse
|
|
// property or extensions MAY override zeros with actual values.
|
|
if (!accessor.bufferViewIndex) {
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
func(ElementType{});
|
|
}
|
|
}
|
|
else {
|
|
auto& view = asset.bufferViews[*accessor.bufferViewIndex];
|
|
auto stride = view.byteStride ? *view.byteStride : getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
auto* bytes = adapter(asset.buffers[view.bufferIndex]);
|
|
bytes += view.byteOffset + accessor.byteOffset;
|
|
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
func(internal::getAccessorElementAt<ElementType>(accessor.componentType, bytes + i * stride, accessor.normalized));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename ElementType, typename Functor, typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
void iterateAccessorWithIndex(const Asset& asset, const Accessor& accessor, Functor&& func,
|
|
const BufferDataAdapter& adapter = {}) {
|
|
std::size_t idx = 0;
|
|
iterateAccessor<ElementType>(asset, accessor, [&](auto&& elementType) {
|
|
func(std::forward<ElementType>(elementType), idx++);
|
|
}, adapter);
|
|
}
|
|
|
|
template <typename ElementType, std::size_t TargetStride = sizeof(ElementType),
|
|
typename BufferDataAdapter = DefaultBufferDataAdapter>
|
|
#if FASTGLTF_HAS_CONCEPTS
|
|
requires Element<ElementType>
|
|
#endif
|
|
void copyFromAccessor(const Asset& asset, const Accessor& accessor, void* dest,
|
|
const BufferDataAdapter& adapter = {}) {
|
|
using Traits = ElementTraits<ElementType>;
|
|
static_assert(Traits::type != AccessorType::Invalid, "Accessor traits must provide a valid accessor type");
|
|
static_assert(Traits::enum_component_type != ComponentType::Invalid, "Accessor traits must provide a valid component type");
|
|
static_assert(std::is_default_constructible_v<ElementType>, "Element type must be default constructible");
|
|
static_assert(std::is_constructible_v<ElementType>, "Element type must be constructible");
|
|
static_assert(std::is_move_assignable_v<ElementType>, "Element type must be move-assignable");
|
|
|
|
if (accessor.type != Traits::type) {
|
|
return;
|
|
}
|
|
|
|
auto* dstBytes = reinterpret_cast<std::byte*>(dest);
|
|
|
|
if (accessor.sparse && accessor.sparse->count > 0) {
|
|
return iterateAccessorWithIndex<ElementType>(asset, accessor, [&](auto&& value, std::size_t index) {
|
|
auto* pDest = reinterpret_cast<ElementType*>(dstBytes + TargetStride * index);
|
|
*pDest = std::forward<ElementType>(value);
|
|
}, adapter);
|
|
}
|
|
|
|
auto elemSize = getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
// 5.1.1. accessor.bufferView
|
|
// The index of the buffer view. When undefined, the accessor MUST be initialized with zeros; sparse
|
|
// property or extensions MAY override zeros with actual values.
|
|
if (!accessor.bufferViewIndex) {
|
|
if constexpr (std::is_trivially_copyable_v<ElementType>) {
|
|
if (TargetStride == elemSize) {
|
|
std::memset(dest, 0, elemSize * accessor.count);
|
|
} else {
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
std::memset(dstBytes + i * TargetStride, 0, elemSize);
|
|
}
|
|
}
|
|
} else {
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
auto* pDest = reinterpret_cast<ElementType*>(dstBytes + TargetStride * i);
|
|
|
|
if constexpr (std::is_aggregate_v<ElementType>) {
|
|
*pDest = {};
|
|
} else {
|
|
*pDest = ElementType{};
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
auto& view = asset.bufferViews[*accessor.bufferViewIndex];
|
|
auto srcStride = view.byteStride ? *view.byteStride
|
|
: getElementByteSize(accessor.type, accessor.componentType);
|
|
|
|
auto* srcBytes = adapter(asset.buffers[view.bufferIndex]) + view.byteOffset + accessor.byteOffset;
|
|
|
|
// We have to perform normalization if the accessor is marked as containing normalized data, which is why
|
|
// we can't just memcpy then.
|
|
if (std::is_trivially_copyable_v<ElementType> && !accessor.normalized && accessor.componentType == Traits::enum_component_type) {
|
|
if (srcStride == elemSize && srcStride == TargetStride) {
|
|
std::memcpy(dest, srcBytes, elemSize * accessor.count);
|
|
} else {
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
std::memcpy(dstBytes + TargetStride * i, srcBytes + srcStride * i, elemSize);
|
|
}
|
|
}
|
|
} else {
|
|
for (std::size_t i = 0; i < accessor.count; ++i) {
|
|
auto* pDest = reinterpret_cast<ElementType*>(dstBytes + TargetStride * i);
|
|
*pDest = internal::getAccessorElementAt<ElementType>(accessor.componentType, srcBytes + srcStride * i);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace fastgltf
|