756 lines
32 KiB
C++
756 lines
32 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 <memory>
|
|
#include <tuple>
|
|
|
|
#include "types.hpp"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 5030) // attribute 'x' is not recognized
|
|
#pragma warning(disable : 4514) // unreferenced inline function has been removed
|
|
#endif
|
|
|
|
// fwd
|
|
#if defined(__ANDROID__)
|
|
struct AAssetManager;
|
|
#endif
|
|
|
|
namespace simdjson::dom {
|
|
class array;
|
|
class object;
|
|
class parser;
|
|
} // namespace simdjson::dom
|
|
|
|
namespace fastgltf {
|
|
enum class Error : std::uint64_t;
|
|
|
|
template <typename T>
|
|
class Expected;
|
|
} // namespace fastgltf
|
|
|
|
namespace std {
|
|
template <typename T>
|
|
struct tuple_size<fastgltf::Expected<T>> : std::integral_constant<std::size_t, 2> {};
|
|
|
|
template <typename T>
|
|
struct tuple_element<0, fastgltf::Expected<T>> { using type = fastgltf::Error; };
|
|
template <typename T>
|
|
struct tuple_element<1, fastgltf::Expected<T>> { using type = T; };
|
|
} // namespace std
|
|
|
|
namespace fastgltf {
|
|
struct BinaryGltfChunk;
|
|
class GltfDataBuffer;
|
|
|
|
enum class Error : std::uint64_t {
|
|
None = 0,
|
|
InvalidPath = 1, ///< The glTF directory passed to load*GLTF is invalid.
|
|
MissingExtensions = 2, ///< One or more extensions are required by the glTF but not enabled in the Parser.
|
|
UnknownRequiredExtension = 3, ///< An extension required by the glTF is not supported by fastgltf.
|
|
InvalidJson = 4, ///< An error occurred while parsing the JSON.
|
|
InvalidGltf = 5, ///< The glTF is either missing something or has invalid data.
|
|
InvalidOrMissingAssetField = 6, ///< The glTF asset object is missing or invalid.
|
|
InvalidGLB = 7, ///< The GLB container is invalid.
|
|
/**
|
|
* A field is missing in the JSON.
|
|
* @note This is only used internally.
|
|
*/
|
|
MissingField = 8,
|
|
MissingExternalBuffer = 9, ///< With Options::LoadExternalBuffers, an external buffer was not found.
|
|
UnsupportedVersion = 10, ///< The glTF version is not supported by fastgltf.
|
|
InvalidURI = 11, ///< A URI from a buffer or image failed to be parsed.
|
|
};
|
|
|
|
inline std::string_view getErrorName(Error error) {
|
|
switch (error) {
|
|
case Error::None: return "None";
|
|
case Error::InvalidPath: return "InvalidPath";
|
|
case Error::MissingExtensions: return "MissingExtensions";
|
|
case Error::UnknownRequiredExtension: return "UnknownRequiredExtension";
|
|
case Error::InvalidJson: return "InvalidJson";
|
|
case Error::InvalidGltf: return "InvalidGltf";
|
|
case Error::InvalidOrMissingAssetField: return "InvalidOrMissingAssetField";
|
|
case Error::InvalidGLB: return "InvalidGLB";
|
|
case Error::MissingField: return "MissingField";
|
|
case Error::MissingExternalBuffer: return "MissingExternalBuffer";
|
|
case Error::UnsupportedVersion: return "UnsupportedVersion";
|
|
case Error::InvalidURI: return "InvalidURI";
|
|
default: FASTGLTF_UNREACHABLE
|
|
}
|
|
}
|
|
|
|
inline std::string_view getErrorMessage(Error error) {
|
|
switch (error) {
|
|
case Error::None: return "";
|
|
case Error::InvalidPath: return "The glTF directory passed to load*GLTF is invalid";
|
|
case Error::MissingExtensions: return "One or more extensions are required by the glTF but not enabled in the Parser.";
|
|
case Error::UnknownRequiredExtension: return "An extension required by the glTF is not supported by fastgltf.";
|
|
case Error::InvalidJson: return "An error occurred while parsing the JSON.";
|
|
case Error::InvalidGltf: return "The glTF is either missing something or has invalid data.";
|
|
case Error::InvalidOrMissingAssetField: return "The glTF asset object is missing or invalid.";
|
|
case Error::InvalidGLB: return "The GLB container is invalid.";
|
|
case Error::MissingField: return "";
|
|
case Error::MissingExternalBuffer: return "An external buffer was not found.";
|
|
case Error::UnsupportedVersion: return "The glTF version is not supported by fastgltf.";
|
|
case Error::InvalidURI: return "A URI from a buffer or image failed to be parsed.";
|
|
default: FASTGLTF_UNREACHABLE
|
|
}
|
|
}
|
|
|
|
// clang-format off
|
|
enum class Extensions : std::uint64_t {
|
|
None = 0,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_transform/README.md
|
|
KHR_texture_transform = 1 << 1,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_basisu/README.md
|
|
KHR_texture_basisu = 1 << 2,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/MSFT_texture_dds/README.md
|
|
MSFT_texture_dds = 1 << 3,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_mesh_quantization/README.md
|
|
KHR_mesh_quantization = 1 << 4,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_meshopt_compression/README.md
|
|
EXT_meshopt_compression = 1 << 5,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual/README.md
|
|
KHR_lights_punctual = 1 << 6,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_texture_webp/README.md
|
|
EXT_texture_webp = 1 << 8,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular/README.md
|
|
KHR_materials_specular = 1 << 9,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior/README.md
|
|
KHR_materials_ior = 1 << 10,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_iridescence/README.md
|
|
KHR_materials_iridescence = 1 << 11,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md
|
|
KHR_materials_volume = 1 << 12,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_transmission/README.md
|
|
KHR_materials_transmission = 1 << 13,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md
|
|
KHR_materials_clearcoat = 1 << 14,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md
|
|
KHR_materials_emissive_strength = 1 << 15,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_sheen/README.md
|
|
KHR_materials_sheen = 1 << 16,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_unlit/README.md
|
|
KHR_materials_unlit = 1 << 17,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md
|
|
KHR_materials_anisotropy = 1 << 18,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_mesh_gpu_instancing/README.md
|
|
EXT_mesh_gpu_instancing = 1 << 19,
|
|
|
|
#if FASTGLTF_ENABLE_DEPRECATED_EXT
|
|
// See https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness/README.md
|
|
KHR_materials_pbrSpecularGlossiness = 1 << 20,
|
|
#endif
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/MSFT_packing_normalRoughnessMetallic/README.md
|
|
MSFT_packing_normalRoughnessMetallic = 1 << 21,
|
|
|
|
// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic/README.md
|
|
MSFT_packing_occlusionRoughnessMetallic = 1 << 22,
|
|
};
|
|
// clang-format on
|
|
|
|
FASTGLTF_ARITHMETIC_OP_TEMPLATE_MACRO(Extensions, Extensions, |)
|
|
FASTGLTF_ARITHMETIC_OP_TEMPLATE_MACRO(Extensions, Extensions, &)
|
|
FASTGLTF_ASSIGNMENT_OP_TEMPLATE_MACRO(Extensions, Extensions, |)
|
|
FASTGLTF_ASSIGNMENT_OP_TEMPLATE_MACRO(Extensions, Extensions, &)
|
|
FASTGLTF_UNARY_OP_TEMPLATE_MACRO(Extensions, ~)
|
|
|
|
// clang-format off
|
|
enum class Options : std::uint64_t {
|
|
None = 0,
|
|
/**
|
|
* This allows 5130 as an accessor component type. 5130 is the OpenGL constant GL_DOUBLE,
|
|
* which is by default not listed as an allowed component type in the glTF spec.
|
|
*
|
|
* The glTF normally only allows these component types:
|
|
* https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#accessor-data-types
|
|
*/
|
|
AllowDouble = 1 << 0,
|
|
|
|
/**
|
|
* This skips validating the asset field, as it is usually there and not used anyway.
|
|
*/
|
|
DontRequireValidAssetMember = 1 << 1,
|
|
|
|
/**
|
|
* Loads all the GLB buffers into CPU memory. If disabled, fastgltf will only provide
|
|
* a byte offset and length into the GLB file, which can be useful when using APIs like
|
|
* DirectStorage or Metal IO.
|
|
*/
|
|
LoadGLBBuffers = 1 << 3,
|
|
|
|
/**
|
|
* Loads all external buffers into CPU memory. If disabled, fastgltf will only provide
|
|
* a full file path to the file holding the buffer, which can be useful when using APIs
|
|
* like DirectStorage or Metal IO. For images, LoadExternalImages has to be explicitly
|
|
* specified, too, if required.
|
|
*/
|
|
LoadExternalBuffers = 1 << 4,
|
|
|
|
/**
|
|
* This option makes fastgltf automatically decompose the transformation matrices of nodes
|
|
* into the translation, rotation, and scale components. This might be useful to have only
|
|
* TRS components, instead of matrices or TRS, which should simplify working with nodes,
|
|
* especially with animations.
|
|
*/
|
|
DecomposeNodeMatrices = 1 << 5,
|
|
|
|
/**
|
|
* This option makes fastgltf minimise the JSON file before parsing. In most cases,
|
|
* minimising it beforehand actually reduces the time spent. However, there are plenty
|
|
* of cases where this option slows down parsing drastically, which from my testing seem
|
|
* to all be glTFs which contain embedded buffers and/or are already minimised. Note that
|
|
* fastgltf only minimises the string if the data was loaded using GltfDataBuffer::loadFromFile
|
|
* or GltfDataBuffer::copyBytes, and that the bytes will also be overwritten.
|
|
*/
|
|
MinimiseJsonBeforeParsing = 1 << 6,
|
|
|
|
/**
|
|
* Loads all external images into CPU memory. It does not decode any texture data. Complementary
|
|
* to LoadExternalBuffers.
|
|
*/
|
|
LoadExternalImages = 1 << 7,
|
|
|
|
/**
|
|
* Lets fastgltf generate indices for all mesh primitives without indices. This currently
|
|
* does not de-duplicate the vertices. This is entirely for compatibility and simplifying the
|
|
* loading process.
|
|
*/
|
|
GenerateMeshIndices = 1 << 8,
|
|
};
|
|
// clang-format on
|
|
|
|
FASTGLTF_ARITHMETIC_OP_TEMPLATE_MACRO(Options, Options, |)
|
|
FASTGLTF_ARITHMETIC_OP_TEMPLATE_MACRO(Options, Options, &)
|
|
FASTGLTF_ASSIGNMENT_OP_TEMPLATE_MACRO(Options, Options, |)
|
|
FASTGLTF_ASSIGNMENT_OP_TEMPLATE_MACRO(Options, Options, &)
|
|
FASTGLTF_UNARY_OP_TEMPLATE_MACRO(Options, ~)
|
|
|
|
// String representations of glTF 2.0 extension identifiers.
|
|
namespace extensions {
|
|
constexpr std::string_view EXT_mesh_gpu_instancing = "EXT_mesh_gpu_instancing";
|
|
constexpr std::string_view EXT_meshopt_compression = "EXT_meshopt_compression";
|
|
constexpr std::string_view EXT_texture_webp = "EXT_texture_webp";
|
|
constexpr std::string_view KHR_lights_punctual = "KHR_lights_punctual";
|
|
constexpr std::string_view KHR_materials_anisotropy = "KHR_materials_anisotropy";
|
|
constexpr std::string_view KHR_materials_clearcoat = "KHR_materials_clearcoat";
|
|
constexpr std::string_view KHR_materials_emissive_strength = "KHR_materials_emissive_strength";
|
|
constexpr std::string_view KHR_materials_ior = "KHR_materials_ior";
|
|
constexpr std::string_view KHR_materials_iridescence = "KHR_materials_iridescence";
|
|
constexpr std::string_view KHR_materials_sheen = "KHR_materials_sheen";
|
|
constexpr std::string_view KHR_materials_specular = "KHR_materials_specular";
|
|
constexpr std::string_view KHR_materials_transmission = "KHR_materials_transmission";
|
|
constexpr std::string_view KHR_materials_unlit = "KHR_materials_unlit";
|
|
constexpr std::string_view KHR_materials_volume = "KHR_materials_volume";
|
|
constexpr std::string_view KHR_mesh_quantization = "KHR_mesh_quantization";
|
|
constexpr std::string_view KHR_texture_basisu = "KHR_texture_basisu";
|
|
constexpr std::string_view KHR_texture_transform = "KHR_texture_transform";
|
|
constexpr std::string_view MSFT_packing_normalRoughnessMetallic = "MSFT_packing_normalRoughnessMetallic";
|
|
constexpr std::string_view MSFT_packing_occlusionRoughnessMetallic = "MSFT_packing_occlusionRoughnessMetallic";
|
|
constexpr std::string_view MSFT_texture_dds = "MSFT_texture_dds";
|
|
|
|
#if FASTGLTF_ENABLE_DEPRECATED_EXT
|
|
constexpr std::string_view KHR_materials_pbrSpecularGlossiness = "KHR_materials_pbrSpecularGlossiness";
|
|
#endif
|
|
} // namespace extensions
|
|
|
|
// clang-format off
|
|
// An array of pairs of string representations of extension identifiers and their respective enum
|
|
// value used for enabling/disabling the loading of it. This also represents all extensions that
|
|
// fastgltf supports and understands.
|
|
#if FASTGLTF_ENABLE_DEPRECATED_EXT
|
|
static constexpr size_t SUPPORTED_EXTENSION_COUNT = 21;
|
|
#else
|
|
static constexpr size_t SUPPORTED_EXTENSION_COUNT = 20;
|
|
#endif
|
|
static constexpr std::array<std::pair<std::string_view, Extensions>, SUPPORTED_EXTENSION_COUNT> extensionStrings = {{
|
|
{ extensions::EXT_mesh_gpu_instancing, Extensions::EXT_mesh_gpu_instancing },
|
|
{ extensions::EXT_meshopt_compression, Extensions::EXT_meshopt_compression },
|
|
{ extensions::EXT_texture_webp, Extensions::EXT_texture_webp },
|
|
{ extensions::KHR_lights_punctual, Extensions::KHR_lights_punctual },
|
|
{ extensions::KHR_materials_anisotropy, Extensions::KHR_materials_anisotropy },
|
|
{ extensions::KHR_materials_clearcoat, Extensions::KHR_materials_clearcoat },
|
|
{ extensions::KHR_materials_emissive_strength, Extensions::KHR_materials_emissive_strength },
|
|
{ extensions::KHR_materials_ior, Extensions::KHR_materials_ior },
|
|
{ extensions::KHR_materials_iridescence, Extensions::KHR_materials_iridescence },
|
|
{ extensions::KHR_materials_sheen, Extensions::KHR_materials_sheen },
|
|
{ extensions::KHR_materials_specular, Extensions::KHR_materials_specular },
|
|
{ extensions::KHR_materials_transmission, Extensions::KHR_materials_transmission },
|
|
{ extensions::KHR_materials_unlit, Extensions::KHR_materials_unlit },
|
|
{ extensions::KHR_materials_volume, Extensions::KHR_materials_volume },
|
|
{ extensions::KHR_mesh_quantization, Extensions::KHR_mesh_quantization },
|
|
{ extensions::KHR_texture_basisu, Extensions::KHR_texture_basisu },
|
|
{ extensions::KHR_texture_transform, Extensions::KHR_texture_transform },
|
|
{ extensions::MSFT_packing_normalRoughnessMetallic, Extensions::MSFT_packing_normalRoughnessMetallic },
|
|
{ extensions::MSFT_packing_occlusionRoughnessMetallic, Extensions::MSFT_packing_occlusionRoughnessMetallic },
|
|
{ extensions::MSFT_texture_dds, Extensions::MSFT_texture_dds },
|
|
|
|
#if FASTGLTF_ENABLE_DEPRECATED_EXT
|
|
{ extensions::KHR_materials_pbrSpecularGlossiness,Extensions::KHR_materials_pbrSpecularGlossiness },
|
|
#endif
|
|
}};
|
|
// clang-format on
|
|
|
|
/**
|
|
* Returns the name of the passed glTF extension.
|
|
*
|
|
* @note If \p extensions has more than one bit set (multiple extensions), this
|
|
* will return the name of the first set bit.
|
|
*/
|
|
#if FASTGLTF_CPP_20
|
|
constexpr
|
|
#else
|
|
inline
|
|
#endif
|
|
std::string_view stringifyExtension(Extensions extensions) {
|
|
// Find the first set bit and mask the value to that
|
|
std::uint8_t position = 0;
|
|
while (position < std::numeric_limits<std::underlying_type_t<Extensions>>::digits) {
|
|
if (((to_underlying(extensions) >> position) & 1) != 0) {
|
|
extensions &= static_cast<Extensions>(1 << position);
|
|
break;
|
|
}
|
|
++position;
|
|
}
|
|
|
|
for (const auto& extensionString : extensionStrings)
|
|
if (extensionString.second == extensions)
|
|
return extensionString.first;
|
|
return "";
|
|
}
|
|
|
|
#if !FASTGLTF_DISABLE_CUSTOM_MEMORY_POOL
|
|
class ChunkMemoryResource : public std::pmr::memory_resource {
|
|
/**
|
|
* The default size of the individual blocks we allocate.
|
|
*/
|
|
constexpr static std::size_t blockSize = 2048;
|
|
|
|
struct Block {
|
|
std::unique_ptr<std::byte[]> data;
|
|
std::size_t size;
|
|
|
|
std::byte* dataPointer;
|
|
};
|
|
SmallVector<Block, 4> blocks;
|
|
std::size_t blockIdx = 0;
|
|
|
|
public:
|
|
explicit ChunkMemoryResource() {
|
|
allocateNewBlock();
|
|
}
|
|
|
|
void allocateNewBlock() {
|
|
auto& block = blocks.emplace_back();
|
|
block.data = std::unique_ptr<std::byte[]>(new std::byte[blockSize]);
|
|
block.dataPointer = block.data.get();
|
|
block.size = blockSize;
|
|
}
|
|
|
|
[[nodiscard]] void* do_allocate(std::size_t bytes, std::size_t alignment) override {
|
|
auto& block = blocks[blockIdx];
|
|
auto availableSize = static_cast<std::size_t>(block.dataPointer - block.data.get());
|
|
if ((availableSize + bytes) > block.size) {
|
|
// The block can't fit the new allocation. We'll just create a new block and use that.
|
|
allocateNewBlock();
|
|
++blockIdx;
|
|
return do_allocate(bytes, alignment);
|
|
}
|
|
|
|
void* alloc = block.dataPointer;
|
|
std::size_t space = availableSize;
|
|
if (std::align(alignment, availableSize, alloc, space) == nullptr) {
|
|
// Not enough space after alignment
|
|
allocateNewBlock();
|
|
++blockIdx;
|
|
return do_allocate(bytes, alignment);
|
|
}
|
|
|
|
// Get the number of bytes used for padding, and calculate the new offset using that
|
|
block.dataPointer = block.dataPointer + (availableSize - space) + bytes;
|
|
return alloc;
|
|
}
|
|
|
|
void do_deallocate([[maybe_unused]] void* p, [[maybe_unused]] std::size_t bytes, [[maybe_unused]] std::size_t alignment) override {
|
|
// We currently do nothing, as we don't keep track of what portions of the blocks are still used.
|
|
// Therefore, we keep all blocks alive until the destruction of this resource (parser).
|
|
}
|
|
|
|
[[nodiscard]] bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
|
|
return this == std::addressof(other);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* A type that stores an error together with an expected value.
|
|
* To use this type, first call error() to inspect if any errors have occurred.
|
|
* If error() is not fastgltf::Error::None,
|
|
* calling get(), operator->(), and operator*() is undefined behaviour.
|
|
*/
|
|
template <typename T>
|
|
class Expected {
|
|
static_assert(std::is_default_constructible_v<T>);
|
|
static_assert(!std::is_same_v<Error, T>);
|
|
|
|
Error err;
|
|
T value;
|
|
|
|
public:
|
|
explicit Expected(Error error) : err(error) {}
|
|
explicit Expected(T&& value) : err(Error::None), value(std::move(value)) {}
|
|
|
|
Expected(const Expected<T>& other) = delete;
|
|
Expected(Expected<T>&& other) noexcept : err(other.err), value(std::move(other.value)) {}
|
|
|
|
Expected<T>& operator=(const Expected<T>& other) = delete;
|
|
Expected<T>& operator=(Expected<T>&& other) noexcept {
|
|
err = other.err;
|
|
value = std::move(other.value);
|
|
return *this;
|
|
}
|
|
|
|
[[nodiscard]] Error error() const noexcept {
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* Returns a reference to the value of T.
|
|
* When error() returns anything but Error::None, the returned value is undefined.
|
|
*/
|
|
[[nodiscard]] T& get() noexcept {
|
|
assert(err == Error::None);
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Returns the address of the value of T, or nullptr if error() returns anything but Error::None.
|
|
*/
|
|
[[nodiscard]] T* get_if() noexcept {
|
|
if (err != Error::None)
|
|
return nullptr;
|
|
return std::addressof(value);
|
|
}
|
|
|
|
template <std::size_t I>
|
|
auto& get() noexcept {
|
|
if constexpr (I == 0) return err;
|
|
else if constexpr (I == 1) return value;
|
|
}
|
|
|
|
template <std::size_t I>
|
|
const auto& get() const noexcept {
|
|
if constexpr (I == 0) return err;
|
|
else if constexpr (I == 1) return value;
|
|
}
|
|
|
|
/**
|
|
* Returns the address of the value of T.
|
|
* When error() returns anything but Error::None, the returned value is undefined.
|
|
*/
|
|
T* operator->() noexcept {
|
|
assert(err == Error::None);
|
|
return std::addressof(value);
|
|
}
|
|
|
|
/**
|
|
* Returns the address of the const value of T.
|
|
* When error() returns anything but Error::None, the returned value is undefined.
|
|
*/
|
|
const T* operator->() const noexcept {
|
|
assert(err == Error::None);
|
|
return std::addressof(value);
|
|
}
|
|
|
|
T&& operator*() && noexcept {
|
|
assert(err == Error::None);
|
|
return std::move(value);
|
|
}
|
|
|
|
operator bool() const noexcept {
|
|
return err == Error::None;
|
|
}
|
|
};
|
|
|
|
struct BufferInfo {
|
|
void* mappedMemory;
|
|
CustomBufferId customId;
|
|
};
|
|
|
|
using BufferMapCallback = BufferInfo(std::uint64_t bufferSize, void* userPointer);
|
|
using BufferUnmapCallback = void(BufferInfo* bufferInfo, void* userPointer);
|
|
using Base64DecodeCallback = void(std::string_view base64, std::uint8_t* dataOutput, std::size_t padding, std::size_t dataOutputSize, void* userPointer);
|
|
|
|
/**
|
|
* Enum to represent the type of a glTF file. glTFs can either be the standard JSON file with
|
|
* paths to buffers or with a base64 embedded buffers, or they can be in a so called GLB
|
|
* container format which has two or more chunks of binary data, where one represents buffers
|
|
* and the other contains the JSON string.
|
|
*/
|
|
enum class GltfType {
|
|
glTF,
|
|
GLB,
|
|
Invalid,
|
|
};
|
|
|
|
/**
|
|
* This function starts reading into the buffer and tries to determine what type of glTF container it is.
|
|
* This should be used to know whether to call Parser::loadGLTF or Parser::loadBinaryGLTF.
|
|
*
|
|
* @return The type of the glTF file, either glTF, GLB, or Invalid if it was not determinable. If this function
|
|
* returns Invalid it is highly likely that the buffer does not actually represent a valid glTF file.
|
|
*/
|
|
GltfType determineGltfFileType(GltfDataBuffer* buffer);
|
|
|
|
/**
|
|
* Gets the amount of byte padding required on the GltfDataBuffer, as simdjson requires to be
|
|
* able to overflow as it uses SIMD to load N bytes at a time.
|
|
*/
|
|
std::size_t getGltfBufferPadding() noexcept;
|
|
|
|
/**
|
|
* This class holds a chunk of data that makes up a JSON string that the glTF parser will use
|
|
* and read from.
|
|
*/
|
|
class GltfDataBuffer {
|
|
friend class Parser;
|
|
friend GltfType determineGltfFileType(GltfDataBuffer* buffer);
|
|
|
|
protected:
|
|
std::size_t allocatedSize = 0;
|
|
std::size_t dataSize = 0;
|
|
std::byte* bufferPointer = nullptr;
|
|
|
|
std::unique_ptr<std::byte[]> buffer;
|
|
|
|
std::filesystem::path filePath = {};
|
|
|
|
public:
|
|
explicit GltfDataBuffer() noexcept;
|
|
|
|
/**
|
|
* Constructs a new GltfDataBuffer from a span object, copying its data as there
|
|
* is no guarantee for the allocation size to have the adequate padding.
|
|
*/
|
|
explicit GltfDataBuffer(span<std::byte> data) noexcept;
|
|
|
|
virtual ~GltfDataBuffer() noexcept;
|
|
|
|
/**
|
|
* Saves the pointer including its range. Does not copy any data. This requires the
|
|
* original allocation to outlive the parsing of the glTF, so after the last relevant
|
|
* call to fastgltf::Parser::loadGLTF. However, this function asks for a capacity size, as
|
|
* the JSON parsing requires some padding. See fastgltf::getGltfBufferPadding for more information.
|
|
* If the capacity does not have enough padding, the function will instead copy the bytes
|
|
* with the copyBytes method. Also, it will set the padding bytes all to 0, so be sure to
|
|
* not use that for any other data.
|
|
*/
|
|
bool fromByteView(std::uint8_t* bytes, std::size_t byteCount, std::size_t capacity) noexcept;
|
|
|
|
/**
|
|
* This will create a copy of the passed bytes and allocate an adequately sized buffer.
|
|
*/
|
|
bool copyBytes(const std::uint8_t* bytes, std::size_t byteCount) noexcept;
|
|
|
|
/**
|
|
* Loads the file with a optional byte offset into a memory buffer.
|
|
*/
|
|
bool loadFromFile(const std::filesystem::path& path, std::uint64_t byteOffset = 0) noexcept;
|
|
|
|
/**
|
|
* Returns the size, in bytes,
|
|
* @return
|
|
*/
|
|
[[nodiscard]] inline std::size_t getBufferSize() const noexcept {
|
|
return dataSize;
|
|
}
|
|
|
|
[[nodiscard]] explicit operator span<std::byte>() {
|
|
return span<std::byte>(bufferPointer, dataSize);
|
|
}
|
|
};
|
|
|
|
#if defined(__ANDROID__)
|
|
class AndroidGltfDataBuffer : public GltfDataBuffer {
|
|
AAssetManager* assetManager;
|
|
|
|
public:
|
|
explicit AndroidGltfDataBuffer(AAssetManager* assetManager) noexcept;
|
|
~AndroidGltfDataBuffer() noexcept = default;
|
|
|
|
/**
|
|
* Loads a file from within an Android APK
|
|
*/
|
|
bool loadFromAndroidAsset(const std::filesystem::path& path, std::uint64_t byteOffset = 0) noexcept;
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* This function further validates all the input more strictly that is parsed from the glTF.
|
|
* Realistically, this should not be necessary in Release applications, but could be helpful
|
|
* when debugging an asset related issue.
|
|
*/
|
|
[[nodiscard]] Error validate(const Asset& asset);
|
|
|
|
/**
|
|
* Some internals the parser passes on to each glTF instance.
|
|
*/
|
|
struct ParserInternalConfig {
|
|
BufferMapCallback* mapCallback = nullptr;
|
|
BufferUnmapCallback* unmapCallback = nullptr;
|
|
Base64DecodeCallback* decodeCallback = nullptr;
|
|
|
|
void* userPointer = nullptr;
|
|
Extensions extensions = Extensions::None;
|
|
};
|
|
|
|
/**
|
|
* A parser for one or more glTF files. It uses a SIMD based JSON parser to maximize efficiency
|
|
* and performance at runtime.
|
|
*
|
|
* @note This class is not thread-safe.
|
|
*/
|
|
class Parser {
|
|
// The simdjson parser object. We want to share it between runs, so it does not need to
|
|
// reallocate over and over again. We're hiding it here to not leak the simdjson header.
|
|
std::unique_ptr<simdjson::dom::parser> jsonParser;
|
|
|
|
ParserInternalConfig config = {};
|
|
DataSource glbBuffer;
|
|
#if !FASTGLTF_DISABLE_CUSTOM_MEMORY_POOL
|
|
std::shared_ptr<ChunkMemoryResource> resourceAllocator;
|
|
#endif
|
|
std::filesystem::path directory;
|
|
Options options;
|
|
|
|
static auto getMimeTypeFromString(std::string_view mime) -> MimeType;
|
|
static void fillCategories(Category& inputCategories) noexcept;
|
|
|
|
[[nodiscard]] auto decodeDataUri(URIView& uri) const noexcept -> Expected<DataSource>;
|
|
[[nodiscard]] auto loadFileFromUri(URIView& uri) const noexcept -> Expected<DataSource>;
|
|
|
|
Error generateMeshIndices(Asset& asset) const;
|
|
|
|
Error parseAccessors(simdjson::dom::array& array, Asset& asset);
|
|
Error parseAnimations(simdjson::dom::array& array, Asset& asset);
|
|
Error parseBuffers(simdjson::dom::array& array, Asset& asset);
|
|
Error parseBufferViews(simdjson::dom::array& array, Asset& asset);
|
|
Error parseCameras(simdjson::dom::array& array, Asset& asset);
|
|
Error parseExtensions(simdjson::dom::object& extensionsObject, Asset& asset);
|
|
Error parseImages(simdjson::dom::array& array, Asset& asset);
|
|
Error parseLights(simdjson::dom::array& array, Asset& asset);
|
|
Error parseMaterials(simdjson::dom::array& array, Asset& asset);
|
|
Error parseMeshes(simdjson::dom::array& array, Asset& asset);
|
|
Error parseNodes(simdjson::dom::array& array, Asset& asset);
|
|
Error parseSamplers(simdjson::dom::array& array, Asset& asset);
|
|
Error parseScenes(simdjson::dom::array& array, Asset& asset);
|
|
Error parseSkins(simdjson::dom::array& array, Asset& asset);
|
|
Error parseTextures(simdjson::dom::array& array, Asset& asset);
|
|
Expected<Asset> parse(simdjson::dom::object root, Category categories);
|
|
|
|
public:
|
|
explicit Parser(Extensions extensionsToLoad = Extensions::None) noexcept;
|
|
explicit Parser(const Parser& parser) = delete;
|
|
Parser(Parser&& parser) noexcept;
|
|
Parser& operator=(const Parser& parser) = delete;
|
|
Parser& operator=(Parser&& other) noexcept;
|
|
|
|
~Parser();
|
|
|
|
/**
|
|
* Loads a glTF file from pre-loaded bytes representing a JSON file.
|
|
*
|
|
* @return An Asset wrapped in an Expected type, which may contain an error if one occurred.
|
|
*/
|
|
[[nodiscard]] Expected<Asset> loadGLTF(GltfDataBuffer* buffer, std::filesystem::path directory, Options options = Options::None, Category categories = Category::All);
|
|
|
|
/**
|
|
* Loads a glTF file embedded within a GLB container, which may contain the first buffer of the glTF asset.
|
|
*
|
|
* @return An Asset wrapped in an Expected type, which may contain an error if one occurred.
|
|
*/
|
|
[[nodiscard]] Expected<Asset> loadBinaryGLTF(GltfDataBuffer* buffer, std::filesystem::path directory, Options options = Options::None, Category categories = Category::All);
|
|
|
|
/**
|
|
* This function can be used to set callbacks so that you can control memory allocation for
|
|
* large buffers and images that are loaded from a glTF file. For example, one could use
|
|
* the callbacks to map a GPU buffer through Vulkan or DirectX so that fastgltf can write
|
|
* the buffer directly to the GPU to avoid a copy into RAM first. To remove the callbacks
|
|
* for a specific load, call this method with both parameters as nullptr before load*GLTF.
|
|
* Using Parser::setUserPointer you can also set a user pointer to access your
|
|
* own class or other data you may need.
|
|
*
|
|
* @param mapCallback function called when the parser requires a buffer to write data
|
|
* embedded in a GLB file or decoded from a base64 URI, cannot be nullptr.
|
|
* @param unmapCallback function called when the parser is done with writing into a
|
|
* buffer, can be nullptr.
|
|
* @note This is likely only useful for advanced users who know what they're doing.
|
|
*/
|
|
void setBufferAllocationCallback(BufferMapCallback* mapCallback, BufferUnmapCallback* unmapCallback = nullptr) noexcept;
|
|
|
|
/**
|
|
* Allows setting callbacks for base64 decoding.
|
|
* This can be useful if you have another base64 decoder optimised for a certain platform or architecture,
|
|
* or want to use your own scheduler to schedule multiple threads for working on decoding individual chunks of the data.
|
|
* Using Parser::setUserPointer you can also set a user pointer to access your own class or other data you may need.
|
|
*
|
|
* It is still recommended to use fastgltf's base64 decoding features as they're highly optimised
|
|
* for SSE4, AVX2, and ARM Neon.
|
|
*
|
|
* @param decodeCallback function called when the parser tries to decode a base64 buffer
|
|
*/
|
|
void setBase64DecodeCallback(Base64DecodeCallback* decodeCallback) noexcept;
|
|
void setUserPointer(void* pointer) noexcept;
|
|
};
|
|
} // namespace fastgltf
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|