Files
QuaternionEngine/third_party/fastgltf/include/fastgltf/parser.hpp

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