From b56f7a217175fb160dce49507c30eee8898a2c0e Mon Sep 17 00:00:00 2001 From: sean Date: Sat, 2 Mar 2024 18:51:13 +0100 Subject: [PATCH] Add: GltfFileStream and rewritten GltfDataBuffer --- include/fastgltf/core.hpp | 132 +++++++++++----------- src/fastgltf.cpp | 228 +++++++++++++++++++------------------- tests/accessor_tests.cpp | 14 ++- tests/base64_tests.cpp | 13 ++- tests/basic_test.cpp | 169 ++++++++++++++-------------- tests/benchmarks.cpp | 32 +++--- tests/extension_tests.cpp | 70 ++++++------ tests/glb_tests.cpp | 17 ++- tests/uri_tests.cpp | 13 ++- tests/write_tests.cpp | 72 +++++++----- 10 files changed, 392 insertions(+), 368 deletions(-) diff --git a/include/fastgltf/core.hpp b/include/fastgltf/core.hpp index a8deba488..060d16338 100644 --- a/include/fastgltf/core.hpp +++ b/include/fastgltf/core.hpp @@ -26,6 +26,7 @@ #pragma once +#include #include #include @@ -67,7 +68,7 @@ namespace std { namespace fastgltf { struct BinaryGltfChunk; - class GltfDataBuffer; + class GltfDataGetter; enum class Error : std::uint64_t { None = 0, @@ -259,15 +260,8 @@ namespace fastgltf { */ 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, + // TODO: Remove or keep for any compatibility? + MinimiseJsonBeforeParsing [[deprecated]] = 1 << 6, /** * Loads all external images into CPU memory. It does not decode any texture data. Complementary @@ -606,72 +600,80 @@ namespace fastgltf { * @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); + GltfType determineGltfFileType(GltfDataGetter& data); - /** - * 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 interface defines how the parser can read the bytes making up a glTF or GLB file. + */ + class GltfDataGetter { + public: + virtual void read(void* ptr, std::size_t count) = 0; + [[nodiscard]] virtual span read(std::size_t count, std::size_t padding) = 0; - /** - * 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); + /** + * Reset is used to put the offset index back to the start of the buffer/file. + * This is only used with determineGltfFileType, as it needs to peek into the beginning of the file. + */ + virtual void reset() = 0; - protected: - std::size_t allocatedSize = 0; - std::size_t dataSize = 0; - std::byte* bufferPointer = nullptr; + [[nodiscard]] virtual std::size_t bytesRead() = 0; + [[nodiscard]] virtual std::size_t totalSize() = 0; + }; - std::unique_ptr buffer; + class GltfDataBuffer : public GltfDataGetter { + std::unique_ptr buffer; - std::filesystem::path filePath = {}; + std::size_t allocatedSize = 0; + std::size_t dataSize = 0; - public: - explicit GltfDataBuffer() noexcept; + std::size_t idx = 0; - /** - * 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 data) noexcept; + void allocateAndCopy(const std::byte* bytes); - virtual ~GltfDataBuffer() noexcept; + public: + GltfDataBuffer(const std::filesystem::path& path); + GltfDataBuffer(const std::byte* bytes, std::size_t count); - /** - * Saves the given pointer including the given range. - * If the capacity of the allocation minus the used size is smaller than fastgltf::getGltfBufferPadding, - * this function will re-allocate and copy the bytes. - * 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; +#if FASTGLTF_CPP_20 + GltfDataBuffer(std::span span); +#endif - /** - * 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; + void read(void* ptr, std::size_t count) override; - /** - * 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; + span read(std::size_t count, std::size_t padding) override; - /** - * Returns the size, in bytes, - * @return - */ - [[nodiscard]] std::size_t getBufferSize() const noexcept { - return dataSize; + void reset() override; + + std::size_t bytesRead() override; + + std::size_t totalSize() override; + + [[nodiscard]] explicit operator span() { + return span(buffer.get(), dataSize); } + }; - [[nodiscard]] explicit operator span() { - return span(bufferPointer, dataSize); - } - }; + class GltfFileStream : public GltfDataGetter { + std::ifstream fileStream; + std::vector buf; + + std::size_t fileSize; + + public: + GltfFileStream(const std::filesystem::path& path); + + bool isOpen() const; + + void read(void* ptr, std::size_t count) override; + + span read(std::size_t count, std::size_t padding) override; + + void reset() override; + + std::size_t bytesRead() override; + + std::size_t totalSize() override; + }; #if defined(__ANDROID__) class AndroidGltfDataBuffer : public GltfDataBuffer { @@ -769,21 +771,21 @@ namespace fastgltf { * * @return An Asset wrapped in an Expected type, which may contain an error if one occurred. */ - [[nodiscard]] Expected loadGltf(GltfDataBuffer* buffer, std::filesystem::path directory, Options options = Options::None, Category categories = Category::All); + [[nodiscard]] Expected loadGltf(GltfDataGetter& buffer, std::filesystem::path directory, Options options = Options::None, Category categories = Category::All); /** * 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 loadGltfJson(GltfDataBuffer* buffer, std::filesystem::path directory, Options options = Options::None, Category categories = Category::All); + [[nodiscard]] Expected loadGltfJson(GltfDataGetter& 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 loadGltfBinary(GltfDataBuffer* buffer, std::filesystem::path directory, Options options = Options::None, Category categories = Category::All); + [[nodiscard]] Expected loadGltfBinary(GltfDataGetter& 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 diff --git a/src/fastgltf.cpp b/src/fastgltf.cpp index 5feb92ae1..2b876612c 100644 --- a/src/fastgltf.cpp +++ b/src/fastgltf.cpp @@ -3687,86 +3687,106 @@ fg::Error fg::Parser::parseTextures(simdjson::dom::array& textures, Asset& asset #pragma endregion #pragma region GltfDataBuffer -std::size_t fg::getGltfBufferPadding() noexcept { - return simdjson::SIMDJSON_PADDING; +fg::GltfDataBuffer::GltfDataBuffer(const std::filesystem::path& path) { + std::error_code ec; + dataSize = static_cast(std::filesystem::file_size(path, ec)); + if (ec) { + // TODO: Fail? + } + + // Open the file and determine the size. + std::ifstream file(path, std::ios::binary); + if (!file.is_open() || file.bad()) + return; + + allocatedSize = dataSize + simdjson::SIMDJSON_PADDING; + buffer = decltype(buffer)(new std::byte[allocatedSize]); // To mimic std::make_unique_for_overwrite (C++20) + if (!buffer) + return; + + // Copy the data and fill the padding region with zeros. + file.read(reinterpret_cast(buffer.get()), static_cast(dataSize)); + std::memset(buffer.get() + dataSize, 0, allocatedSize - dataSize); } -fg::GltfDataBuffer::GltfDataBuffer() noexcept = default; -fg::GltfDataBuffer::~GltfDataBuffer() noexcept = default; +fg::GltfDataBuffer::GltfDataBuffer(const std::byte *bytes, std::size_t count) { + dataSize = count; + allocateAndCopy(bytes); +} -fg::GltfDataBuffer::GltfDataBuffer(span data) noexcept { - dataSize = data.size(); +#if FASTGLTF_CPP_20 +fg::GltfDataBuffer::GltfDataBuffer(std::span span) { + dataSize = span.size_bytes(); + allocateAndCopy(span.data()); +} +#endif - allocatedSize = data.size() + getGltfBufferPadding(); +void fg::GltfDataBuffer::allocateAndCopy(const std::byte *bytes) { + allocatedSize = dataSize + simdjson::SIMDJSON_PADDING; buffer = decltype(buffer)(new std::byte[allocatedSize]); - auto* ptr = buffer.get(); - std::memcpy(ptr, data.data(), dataSize); - std::memset(ptr + dataSize, 0, allocatedSize - dataSize); + std::memcpy(buffer.get(), bytes, dataSize); + std::memset(buffer.get() + dataSize, 0, allocatedSize - dataSize); +} - bufferPointer = ptr; +void fg::GltfDataBuffer::read(void *ptr, std::size_t count) { + std::memcpy(ptr, buffer.get() + idx, count); + idx += count; } -bool fg::GltfDataBuffer::fromByteView(std::uint8_t* bytes, std::size_t byteCount, std::size_t capacity) noexcept { - using namespace simdjson; - if (bytes == nullptr || byteCount == 0 || capacity == 0) - return false; +fg::span fg::GltfDataBuffer::read(std::size_t count, std::size_t padding) { + span sub(buffer.get() + idx, count); + idx += count; + return sub; +} - if (capacity - byteCount < getGltfBufferPadding()) - return copyBytes(bytes, byteCount); +void fg::GltfDataBuffer::reset() { + idx = 0; +} - dataSize = byteCount; - bufferPointer = reinterpret_cast(bytes); - allocatedSize = capacity; - std::memset(bufferPointer + dataSize, 0, getGltfBufferPadding()); - return true; +std::size_t fg::GltfDataBuffer::bytesRead() { + return idx; } -bool fg::GltfDataBuffer::copyBytes(const std::uint8_t* bytes, std::size_t byteCount) noexcept { - using namespace simdjson; - if (bytes == nullptr || byteCount == 0) - return false; +std::size_t fg::GltfDataBuffer::totalSize() { + return dataSize; +} - // Allocate a byte array with a bit of padding. - dataSize = byteCount; - allocatedSize = byteCount + getGltfBufferPadding(); - buffer = decltype(buffer)(new std::byte[allocatedSize]); // To mimic std::make_unique_for_overwrite (C++20) - bufferPointer = buffer.get(); +fg::GltfFileStream::GltfFileStream(const std::filesystem::path &path) : fileStream(path, std::ios::binary) { + fileSize = std::filesystem::file_size(path); +} - // Copy the data and fill the padding region with zeros. - std::memcpy(bufferPointer, bytes, dataSize); - std::memset(bufferPointer + dataSize, 0, allocatedSize - dataSize); - return true; +bool fg::GltfFileStream::isOpen() const { + return fileStream.is_open(); } -bool fg::GltfDataBuffer::loadFromFile(const fs::path& path, std::uint64_t byteOffset) noexcept { - using namespace simdjson; - std::error_code ec; - auto length = static_cast(std::filesystem::file_size(path, ec)); - if (ec) { - return false; - } +void fg::GltfFileStream::read(void *ptr, std::size_t count) { + fileStream.read( + reinterpret_cast(ptr), + static_cast(count)); +} - // Open the file and determine the size. - std::ifstream file(path, std::ios::binary); - if (!file.is_open() || file.bad()) - return false; +fg::span fg::GltfFileStream::read(std::size_t count, std::size_t padding) { + static_assert(sizeof(decltype(buf)::value_type) == sizeof(std::byte)); - filePath = path; + buf.resize(count + padding); + fileStream.read( + reinterpret_cast(buf.data()), + static_cast(count)); - file.seekg(static_cast(byteOffset), std::ifstream::beg); + return span(reinterpret_cast(buf.data()), buf.size()); +} - dataSize = static_cast(length) - byteOffset; - allocatedSize = dataSize + getGltfBufferPadding(); - buffer = decltype(buffer)(new std::byte[allocatedSize]); // To mimic std::make_unique_for_overwrite (C++20) - if (!buffer) - return false; - bufferPointer = buffer.get(); +void fg::GltfFileStream::reset() { + fileStream.seekg(0, std::ifstream::beg); +} - // Copy the data and fill the padding region with zeros. - file.read(reinterpret_cast(bufferPointer), static_cast(dataSize)); - std::memset(bufferPointer + dataSize, 0, allocatedSize - dataSize); - return true; +std::size_t fg::GltfFileStream::bytesRead() { + return fileStream.tellg(); +} + +std::size_t fg::GltfFileStream::totalSize() { + return fileSize; } #pragma endregion @@ -3819,27 +3839,22 @@ bool fg::AndroidGltfDataBuffer::loadFromAndroidAsset(const fs::path& path, std:: #pragma endregion #pragma region Parser -fastgltf::GltfType fg::determineGltfFileType(GltfDataBuffer* buffer) { - if (buffer->bufferPointer == nullptr) - return GltfType::Invalid; - - if (buffer->dataSize > sizeof(BinaryGltfHeader)) { - // We'll try and read a BinaryGltfHeader from the buffer to see if the magic is correct. - BinaryGltfHeader header = {}; - std::memcpy(&header, buffer->bufferPointer, sizeof header); - if (header.magic == binaryGltfHeaderMagic) { - return GltfType::GLB; - } +fastgltf::GltfType fg::determineGltfFileType(GltfDataGetter& data) { + // We'll try and read a BinaryGltfHeader from the buffer to see if the magic is correct. + BinaryGltfHeader header = {}; + data.read(&header, sizeof header); + if (header.magic == binaryGltfHeaderMagic) { + return GltfType::GLB; } - if (buffer->dataSize > sizeof(std::uint8_t) * 4) { - // First, check if any of the first four characters is a '{'. - std::array begin = {}; - std::memcpy(begin.data(), buffer->bufferPointer, sizeof begin); - for (const auto& i : begin) { - if ((char)i == '{') - return GltfType::glTF; - } + // First, check if any of the first four characters is a '{'. + std::array begin = {}; + data.read(begin.data(), begin.size()); + for (const auto& i : begin) { + if ((char)i == ' ') + continue; + if ((char)i == '{') + return GltfType::glTF; } return GltfType::Invalid; @@ -3861,21 +3876,21 @@ fg::Parser& fg::Parser::operator=(Parser&& other) noexcept { fg::Parser::~Parser() = default; -fg::Expected fg::Parser::loadGltf(GltfDataBuffer* buffer, fs::path directory, Options options, Category categories) { - auto type = fastgltf::determineGltfFileType(buffer); +fg::Expected fg::Parser::loadGltf(GltfDataGetter& data, fs::path directory, Options options, Category categories) { + auto type = fastgltf::determineGltfFileType(data); if (type == fastgltf::GltfType::glTF) { - return loadGltfJson(buffer, std::move(directory), options, categories); + return loadGltfJson(data, std::move(directory), options, categories); } if (type == fastgltf::GltfType::GLB) { - return loadGltfBinary(buffer, std::move(directory), options, categories); + return loadGltfBinary(data, std::move(directory), options, categories); } return Expected { Error::InvalidFileData }; } -fg::Expected fg::Parser::loadGltfJson(GltfDataBuffer* buffer, fs::path directory, Options options, Category categories) { +fg::Expected fg::Parser::loadGltfJson(GltfDataGetter& data, fs::path directory, Options options, Category categories) { using namespace simdjson; // If we never have to load the files ourselves, we're fine with the directory being invalid/blank. @@ -3886,20 +3901,10 @@ fg::Expected fg::Parser::loadGltfJson(GltfDataBuffer* buffer, fs::pat this->options = options; this->directory = std::move(directory); - // If we own the allocation of the JSON data, we'll try to minify the JSON, which, in most cases, - // will speed up the parsing by a small amount. - std::size_t jsonLength = buffer->getBufferSize(); - if (buffer->buffer != nullptr && hasBit(options, Options::MinimiseJsonBeforeParsing)) { - std::size_t newLength = 0; - auto result = simdjson::minify(reinterpret_cast(buffer->bufferPointer), buffer->getBufferSize(), - reinterpret_cast(buffer->bufferPointer), newLength); - if (result != SUCCESS || newLength == 0) { - return Expected(Error::InvalidJson); - } - buffer->dataSize = jsonLength = newLength; - } - - auto view = padded_string_view(reinterpret_cast(buffer->bufferPointer), jsonLength, buffer->allocatedSize); + auto jsonSpan = data.read(data.totalSize(), SIMDJSON_PADDING); + simdjson::padded_string_view view(reinterpret_cast(jsonSpan.data()), + data.totalSize(), + data.totalSize() + SIMDJSON_PADDING); simdjson::dom::object root; if (auto error = jsonParser->parse(view).get(root); error != SUCCESS) FASTGLTF_UNLIKELY { return Expected(Error::InvalidJson); @@ -3908,7 +3913,7 @@ fg::Expected fg::Parser::loadGltfJson(GltfDataBuffer* buffer, fs::pat return parse(root, categories); } -fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::path directory, Options options, Category categories) { +fg::Expected fg::Parser::loadGltfBinary(GltfDataGetter& data, fs::path directory, Options options, Category categories) { using namespace simdjson; // If we never have to load the files ourselves, we're fine with the directory being invalid/blank. @@ -3919,21 +3924,15 @@ fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::p this->options = options; this->directory = std::move(directory); - std::size_t offset = 0UL; - auto read = [&buffer, &offset](void* dst, std::size_t size) mutable { - std::memcpy(dst, buffer->bufferPointer + offset, size); - offset += size; - }; - BinaryGltfHeader header = {}; - read(&header, sizeof header); + data.read(&header, sizeof header); if (header.magic != binaryGltfHeaderMagic) { return Expected(Error::InvalidGLB); } if (header.version != 2) { return Expected(Error::UnsupportedVersion); } - if (header.length >= buffer->allocatedSize) { + if (header.length > data.totalSize()) { return Expected(Error::InvalidGLB); } @@ -3941,17 +3940,17 @@ fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::p // 1. JSON chunk // 2. BIN chunk (optional) BinaryGltfChunk jsonChunk = {}; - read(&jsonChunk, sizeof jsonChunk); + data.read(&jsonChunk, sizeof jsonChunk); if (jsonChunk.chunkType != binaryGltfJsonChunkMagic) { return Expected(Error::InvalidGLB); } // Create a string view of the JSON chunk in the GLB data buffer. The documentation of parse() // says the padding can be initialised to anything, apparently. Therefore, this should work. - simdjson::padded_string_view jsonChunkView(reinterpret_cast(buffer->bufferPointer) + offset, + auto jsonSpan = data.read(jsonChunk.chunkLength, SIMDJSON_PADDING); + simdjson::padded_string_view jsonChunkView(reinterpret_cast(jsonSpan.data()), jsonChunk.chunkLength, jsonChunk.chunkLength + SIMDJSON_PADDING); - offset += jsonChunk.chunkLength; simdjson::dom::object root; if (jsonParser->parse(jsonChunkView).get(root) != SUCCESS) FASTGLTF_UNLIKELY { @@ -3959,9 +3958,9 @@ fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::p } // Is there enough room for another chunk header? - if (header.length > (offset + sizeof(BinaryGltfChunk))) { + if (header.length > (data.bytesRead() + sizeof(BinaryGltfChunk))) { BinaryGltfChunk binaryChunk = {}; - read(&binaryChunk, sizeof binaryChunk); + data.read(&binaryChunk, sizeof binaryChunk); if (binaryChunk.chunkType != binaryGltfDataChunkMagic) { return Expected(Error::InvalidGLB); @@ -3971,7 +3970,7 @@ fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::p if (config.mapCallback != nullptr) { auto info = config.mapCallback(binaryChunk.chunkLength, config.userPointer); if (info.mappedMemory != nullptr) { - read(info.mappedMemory, binaryChunk.chunkLength); + data.read(info.mappedMemory, binaryChunk.chunkLength); if (config.unmapCallback != nullptr) { config.unmapCallback(&info, config.userPointer); } @@ -3979,7 +3978,7 @@ fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::p } } else { StaticVector binaryData(binaryChunk.chunkLength); - read(binaryData.data(), binaryChunk.chunkLength); + data.read(binaryData.data(), binaryChunk.chunkLength); sources::Array vectorData = { std::move(binaryData), @@ -3988,9 +3987,10 @@ fg::Expected fg::Parser::loadGltfBinary(GltfDataBuffer* buffer, fs::p glbBuffer = std::move(vectorData); } } else { - const span glbBytes(reinterpret_cast(buffer->bufferPointer + offset), binaryChunk.chunkLength); + // TODO: What to do with this? + //const span glbBytes(reinterpret_cast(buffer->bufferPointer + offset), binaryChunk.chunkLength); sources::ByteView glbByteView = {}; - glbByteView.bytes = glbBytes; + //glbByteView.bytes = glbBytes; glbByteView.mimeType = MimeType::GltfBuffer; glbBuffer = glbByteView; } diff --git a/tests/accessor_tests.cpp b/tests/accessor_tests.cpp index b631c9112..49e5d5bb4 100644 --- a/tests/accessor_tests.cpp +++ b/tests/accessor_tests.cpp @@ -113,11 +113,12 @@ TEST_CASE("Test matrix data padding", "[gltf-tools]") { TEST_CASE("Test accessor", "[gltf-tools]") { auto lightsLamp = sampleModels / "2.0" / "LightsPunctualLamp" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(lightsLamp / "LightsPunctualLamp.gltf")); + + fastgltf::GltfFileStream jsonData(lightsLamp / "LightsPunctualLamp.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_lights_punctual); - auto asset = parser.loadGltfJson(&jsonData, lightsLamp, fastgltf::Options::LoadExternalBuffers, + auto asset = parser.loadGltfJson(jsonData, lightsLamp, fastgltf::Options::LoadExternalBuffers, fastgltf::Category::Buffers | fastgltf::Category::BufferViews | fastgltf::Category::Accessors); REQUIRE(asset.error() == fastgltf::Error::None); @@ -189,11 +190,12 @@ TEST_CASE("Test accessor", "[gltf-tools]") { TEST_CASE("Test sparse accessor", "[gltf-tools]") { auto simpleSparseAccessor = sampleModels / "2.0" / "SimpleSparseAccessor" / "glTF"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(simpleSparseAccessor / "SimpleSparseAccessor.gltf")); + + fastgltf::GltfFileStream jsonData(simpleSparseAccessor / "SimpleSparseAccessor.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(jsonData.get(), simpleSparseAccessor, fastgltf::Options::LoadExternalBuffers, + auto asset = parser.loadGltfJson(jsonData, simpleSparseAccessor, fastgltf::Options::LoadExternalBuffers, fastgltf::Category::Buffers | fastgltf::Category::BufferViews | fastgltf::Category::Accessors); REQUIRE(asset.error() == fastgltf::Error::None); diff --git a/tests/base64_tests.cpp b/tests/base64_tests.cpp index e6e4e1998..aa54ba5a3 100644 --- a/tests/base64_tests.cpp +++ b/tests/base64_tests.cpp @@ -72,13 +72,14 @@ TEST_CASE("Test base64 buffer decoding", "[base64]") { auto cylinderEngine = sampleModels / "2.0" / "2CylinderEngine" / "glTF-Embedded"; auto boxTextured = sampleModels / "2.0" / "BoxTextured" / "glTF-Embedded"; - auto tceJsonData = std::make_unique(); - REQUIRE(tceJsonData->loadFromFile(cylinderEngine / "2CylinderEngine.gltf")); - auto btJsonData = std::make_unique(); - REQUIRE(btJsonData->loadFromFile(boxTextured / "BoxTextured.gltf")); + + fastgltf::GltfFileStream tceJsonData(cylinderEngine / "2CylinderEngine.gltf"); + REQUIRE(tceJsonData.isOpen()); + fastgltf::GltfFileStream btJsonData(boxTextured / "BoxTextured.gltf"); + REQUIRE(btJsonData.isOpen()); SECTION("Validate large buffer load from glTF") { - auto asset = parser.loadGltfJson(tceJsonData.get(), cylinderEngine, fastgltf::Options::None, fastgltf::Category::Buffers); + auto asset = parser.loadGltfJson(tceJsonData, cylinderEngine, fastgltf::Options::None, fastgltf::Category::Buffers); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(asset->buffers.size() == 1); @@ -93,7 +94,7 @@ TEST_CASE("Test base64 buffer decoding", "[base64]") { } SECTION("Validate base64 buffer and image load from glTF") { - auto asset = parser.loadGltfJson(btJsonData.get(), boxTextured, fastgltf::Options::None, fastgltf::Category::Images | fastgltf::Category::Buffers); + auto asset = parser.loadGltfJson(btJsonData, boxTextured, fastgltf::Options::None, fastgltf::Category::Images | fastgltf::Category::Buffers); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(asset->buffers.size() == 1); diff --git a/tests/basic_test.cpp b/tests/basic_test.cpp index 137aee90d..e82c2bd47 100644 --- a/tests/basic_test.cpp +++ b/tests/basic_test.cpp @@ -100,11 +100,12 @@ TEST_CASE("Test if glTF type detection works", "[gltf-loader]") { SECTION("glTF") { auto gltfPath = sampleModels / "2.0" / "ABeautifulGame" / "glTF"; REQUIRE(std::filesystem::exists(gltfPath)); - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(gltfPath / "ABeautifulGame.gltf")); - REQUIRE(fastgltf::determineGltfFileType(&jsonData) == fastgltf::GltfType::glTF); + fastgltf::GltfFileStream jsonData(gltfPath / "ABeautifulGame.gltf"); + REQUIRE(jsonData.isOpen()); - auto model = parser.loadGltfJson(&jsonData, gltfPath); + REQUIRE(fastgltf::determineGltfFileType(jsonData) == fastgltf::GltfType::glTF); + + auto model = parser.loadGltfJson(jsonData, gltfPath); REQUIRE(model.error() == fastgltf::Error::None); REQUIRE(model.get_if() != nullptr); REQUIRE(fastgltf::validate(model.get()) == fastgltf::Error::None); @@ -113,48 +114,51 @@ TEST_CASE("Test if glTF type detection works", "[gltf-loader]") { SECTION("GLB") { auto glbPath = sampleModels / "2.0" / "BoomBox" / "glTF-Binary"; REQUIRE(std::filesystem::exists(glbPath)); - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(glbPath / "BoomBox.glb")); - REQUIRE(fastgltf::determineGltfFileType(&jsonData) == fastgltf::GltfType::GLB); + fastgltf::GltfFileStream jsonData(glbPath / "BoomBox.gltf"); + REQUIRE(jsonData.isOpen()); + + REQUIRE(fastgltf::determineGltfFileType(jsonData) == fastgltf::GltfType::GLB); - auto model = parser.loadGltfBinary(&jsonData, glbPath); + auto model = parser.loadGltfBinary(jsonData, glbPath); REQUIRE(model.error() == fastgltf::Error::None); REQUIRE(model.get_if() != nullptr); } SECTION("Invalid") { - auto gltfPath = path / "base64.txt"; // Random file in the test directory that's not a glTF file. - REQUIRE(std::filesystem::exists(gltfPath)); - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(gltfPath)); - REQUIRE(fastgltf::determineGltfFileType(&jsonData) == fastgltf::GltfType::Invalid); + auto fakePath = path / "base64.txt"; // Random file in the test directory that's not a glTF file. + REQUIRE(std::filesystem::exists(fakePath)); + fastgltf::GltfFileStream jsonData(fakePath); + REQUIRE(jsonData.isOpen()); + + REQUIRE(fastgltf::determineGltfFileType(jsonData) == fastgltf::GltfType::Invalid); } } TEST_CASE("Loading some basic glTF", "[gltf-loader]") { fastgltf::Parser parser; SECTION("Loading basic invalid glTF files") { - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(path / "empty_json.gltf")); - auto emptyGltf = parser.loadGltfJson(jsonData.get(), path); + fastgltf::GltfFileStream jsonData(path / "empty_json.gltf"); + REQUIRE(jsonData.isOpen()); + + auto emptyGltf = parser.loadGltfJson(jsonData, path); REQUIRE(emptyGltf.error() == fastgltf::Error::InvalidOrMissingAssetField); } SECTION("Load basic glTF file") { - auto basicJsonData = std::make_unique(); - REQUIRE(basicJsonData->loadFromFile(path / "basic_gltf.gltf")); + fastgltf::GltfFileStream jsonData(path / "basic_gltf.gltf"); + REQUIRE(jsonData.isOpen()); - auto basicGltf = parser.loadGltfJson(basicJsonData.get(), path); + auto basicGltf = parser.loadGltfJson(jsonData, path); REQUIRE(basicGltf.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(basicGltf.get()) == fastgltf::Error::None); } SECTION("Loading basic Cube.gltf") { auto cubePath = sampleModels / "2.0" / "Cube" / "glTF"; - auto cubeJsonData = std::make_unique(); - REQUIRE(cubeJsonData->loadFromFile(cubePath / "Cube.gltf")); + fastgltf::GltfFileStream jsonData(cubePath / "Cube.gltf"); + REQUIRE(jsonData.isOpen()); - auto cube = parser.loadGltfJson(cubeJsonData.get(), cubePath, noOptions, fastgltf::Category::OnlyRenderable); + auto cube = parser.loadGltfJson(jsonData, cubePath, noOptions, fastgltf::Category::OnlyRenderable); REQUIRE(cube.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(cube.get()) == fastgltf::Error::None); @@ -189,10 +193,10 @@ TEST_CASE("Loading some basic glTF", "[gltf-loader]") { SECTION("Loading basic Box.gltf") { auto boxPath = sampleModels / "2.0" / "Box" / "glTF"; - auto boxJsonData = std::make_unique(); - REQUIRE(boxJsonData->loadFromFile(boxPath / "Box.gltf")); + fastgltf::GltfFileStream jsonData(boxPath / "Box.gltf"); + REQUIRE(jsonData.isOpen()); - auto box = parser.loadGltfJson(boxJsonData.get(), boxPath, noOptions, fastgltf::Category::OnlyRenderable); + auto box = parser.loadGltfJson(jsonData, boxPath, noOptions, fastgltf::Category::OnlyRenderable); REQUIRE(box.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(box.get()) == fastgltf::Error::None); @@ -216,12 +220,11 @@ TEST_CASE("Loading some basic glTF", "[gltf-loader]") { TEST_CASE("Loading glTF animation", "[gltf-loader]") { auto animatedCube = sampleModels / "2.0" / "AnimatedCube" / "glTF"; - - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(animatedCube / "AnimatedCube.gltf")); + fastgltf::GltfFileStream jsonData(animatedCube / "AnimatedCube.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(jsonData.get(), animatedCube, noOptions, fastgltf::Category::OnlyAnimations); + auto asset = parser.loadGltfJson(jsonData, animatedCube, noOptions, fastgltf::Category::OnlyAnimations); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -243,12 +246,11 @@ TEST_CASE("Loading glTF animation", "[gltf-loader]") { TEST_CASE("Loading glTF skins", "[gltf-loader]") { auto simpleSkin = sampleModels / "2.0" / "SimpleSkin" / "glTF"; - - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(simpleSkin / "SimpleSkin.gltf")); + fastgltf::GltfFileStream jsonData(simpleSkin / "SimpleSkin.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(jsonData.get(), simpleSkin, noOptions, fastgltf::Category::Skins | fastgltf::Category::Nodes); + auto asset = parser.loadGltfJson(jsonData, simpleSkin, noOptions, fastgltf::Category::Skins | fastgltf::Category::Nodes); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -270,11 +272,11 @@ TEST_CASE("Loading glTF skins", "[gltf-loader]") { TEST_CASE("Loading glTF cameras", "[gltf-loader]") { auto cameras = sampleModels / "2.0" / "Cameras" / "glTF"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(cameras / "Cameras.gltf")); + fastgltf::GltfFileStream jsonData(cameras / "Cameras.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(jsonData.get(), cameras, noOptions, fastgltf::Category::Cameras); + auto asset = parser.loadGltfJson(jsonData, cameras, noOptions, fastgltf::Category::Cameras); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -298,29 +300,34 @@ TEST_CASE("Loading glTF cameras", "[gltf-loader]") { REQUIRE(pOrthographic->znear == 0.01f); } -TEST_CASE("Validate whole glTF", "[gltf-loader]") { - auto sponza = sampleModels / "2.0" / "Sponza" / "glTF"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(sponza / "Sponza.gltf")); +TEST_CASE("Validate models with re-used parser", "[gltf-loader]") { + auto sponza = sampleModels / "2.0" / "Sponza" / "glTF"; + fastgltf::Parser parser; - fastgltf::Parser parser; - auto model = parser.loadGltfJson(jsonData.get(), sponza); - REQUIRE(model.error() == fastgltf::Error::None); - REQUIRE(fastgltf::validate(model.get()) == fastgltf::Error::None); + SECTION("Validate Sponza.gltf") { + fastgltf::GltfFileStream jsonData(sponza / "Sponza.gltf"); + REQUIRE(jsonData.isOpen()); + + auto model = parser.loadGltfJson(jsonData, sponza); + REQUIRE(model.error() == fastgltf::Error::None); + REQUIRE(fastgltf::validate(model.get()) == fastgltf::Error::None); + } - auto brainStem = sampleModels / "2.0" / "BrainStem" / "glTF"; - jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(brainStem / "BrainStem.gltf")); + SECTION("Validate BrainStem.gltf") { + auto brainStem = sampleModels / "2.0" / "BrainStem" / "glTF"; + fastgltf::GltfFileStream jsonData(brainStem / "BrainStem.gltf"); + REQUIRE(jsonData.isOpen()); - auto model2 = parser.loadGltfJson(jsonData.get(), brainStem); - REQUIRE(model2.error() == fastgltf::Error::None); - REQUIRE(fastgltf::validate(model2.get()) == fastgltf::Error::None); + auto model = parser.loadGltfJson(jsonData, brainStem); + REQUIRE(model.error() == fastgltf::Error::None); + REQUIRE(fastgltf::validate(model.get()) == fastgltf::Error::None); + } } TEST_CASE("Test allocation callbacks for embedded buffers", "[gltf-loader]") { auto boxPath = sampleModels / "2.0" / "Box" / "glTF-Embedded"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(boxPath / "Box.gltf")); + fastgltf::GltfFileStream jsonData(boxPath / "Box.gltf"); + REQUIRE(jsonData.isOpen()); std::vector allocations; @@ -337,7 +344,7 @@ TEST_CASE("Test allocation callbacks for embedded buffers", "[gltf-loader]") { fastgltf::Parser parser; parser.setUserPointer(&allocations); parser.setBufferAllocationCallback(mapCallback, nullptr); - auto asset = parser.loadGltfJson(jsonData.get(), boxPath, noOptions, fastgltf::Category::Buffers); + auto asset = parser.loadGltfJson(jsonData, boxPath, noOptions, fastgltf::Category::Buffers); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -357,8 +364,8 @@ TEST_CASE("Test allocation callbacks for embedded buffers", "[gltf-loader]") { TEST_CASE("Test base64 decoding callbacks", "[gltf-loader]") { auto boxPath = sampleModels / "2.0" / "Box" / "glTF-Embedded"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(boxPath / "Box.gltf")); + fastgltf::GltfFileStream jsonData(boxPath / "Box.gltf"); + REQUIRE(jsonData.isOpen()); size_t decodeCounter = 0; auto decodeCallback = [](std::string_view encodedData, uint8_t* outputData, size_t padding, size_t outputSize, void* userPointer) { @@ -369,7 +376,7 @@ TEST_CASE("Test base64 decoding callbacks", "[gltf-loader]") { fastgltf::Parser parser; parser.setUserPointer(&decodeCounter); parser.setBase64DecodeCallback(decodeCallback); - auto model = parser.loadGltfJson(jsonData.get(), boxPath, noOptions, fastgltf::Category::Buffers); + auto model = parser.loadGltfJson(jsonData, boxPath, noOptions, fastgltf::Category::Buffers); REQUIRE(model.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(model.get()) == fastgltf::Error::None); REQUIRE(decodeCounter != 0); @@ -377,16 +384,16 @@ TEST_CASE("Test base64 decoding callbacks", "[gltf-loader]") { TEST_CASE("Test TRS parsing and optional decomposition", "[gltf-loader]") { SECTION("Test decomposition on glTF asset") { - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(path / "transform_matrices.gltf")); + fastgltf::GltfFileStream jsonData(path / "transform_matrices.gltf"); + REQUIRE(jsonData.isOpen()); // Parse once without decomposing, once with decomposing the matrix. fastgltf::Parser parser; - auto assetWithMatrix = parser.loadGltfJson(jsonData.get(), path, noOptions, fastgltf::Category::Nodes | fastgltf::Category::Cameras); + auto assetWithMatrix = parser.loadGltfJson(jsonData, path, noOptions, fastgltf::Category::Nodes | fastgltf::Category::Cameras); REQUIRE(assetWithMatrix.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(assetWithMatrix.get()) == fastgltf::Error::None); - auto assetDecomposed = parser.loadGltfJson(jsonData.get(), path, fastgltf::Options::DecomposeNodeMatrices, fastgltf::Category::Nodes | fastgltf::Category::Cameras); + auto assetDecomposed = parser.loadGltfJson(jsonData, path, fastgltf::Options::DecomposeNodeMatrices, fastgltf::Category::Nodes | fastgltf::Category::Cameras); REQUIRE(assetDecomposed.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(assetDecomposed.get()) == fastgltf::Error::None); @@ -461,11 +468,11 @@ TEST_CASE("Test TRS parsing and optional decomposition", "[gltf-loader]") { TEST_CASE("Validate sparse accessor parsing", "[gltf-loader]") { auto simpleSparseAccessor = sampleModels / "2.0" / "SimpleSparseAccessor" / "glTF"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(simpleSparseAccessor / "SimpleSparseAccessor.gltf")); + fastgltf::GltfFileStream jsonData(simpleSparseAccessor / "SimpleSparseAccessor.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(jsonData.get(), simpleSparseAccessor, noOptions, fastgltf::Category::Accessors); + auto asset = parser.loadGltfJson(jsonData, simpleSparseAccessor, noOptions, fastgltf::Category::Accessors); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -483,11 +490,11 @@ TEST_CASE("Validate sparse accessor parsing", "[gltf-loader]") { TEST_CASE("Validate morph target parsing", "[gltf-loader]") { auto simpleMorph = sampleModels / "2.0" / "SimpleMorph" / "glTF"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(simpleMorph / "SimpleMorph.gltf")); + fastgltf::GltfFileStream jsonData(simpleMorph / "SimpleMorph.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(jsonData.get(), simpleMorph, noOptions, fastgltf::Category::Meshes); + auto asset = parser.loadGltfJson(jsonData, simpleMorph, noOptions, fastgltf::Category::Meshes); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -514,11 +521,11 @@ TEST_CASE("Validate morph target parsing", "[gltf-loader]") { TEST_CASE("Test accessors min/max", "[gltf-loader]") { auto lightsLamp = sampleModels / "2.0" / "LightsPunctualLamp" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(lightsLamp / "LightsPunctualLamp.gltf")); + fastgltf::GltfFileStream jsonData(lightsLamp / "LightsPunctualLamp.gltf"); + REQUIRE(jsonData.isOpen()); - fastgltf::Parser parser(fastgltf::Extensions::KHR_lights_punctual); - auto asset = parser.loadGltfJson(&jsonData, lightsLamp, noOptions, fastgltf::Category::Accessors); + fastgltf::Parser parser(fastgltf::Extensions::KHR_lights_punctual); + auto asset = parser.loadGltfJson(jsonData, lightsLamp, noOptions, fastgltf::Category::Accessors); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -577,11 +584,11 @@ TEST_CASE("Test accessors min/max", "[gltf-loader]") { TEST_CASE("Test unicode characters", "[gltf-loader]") { auto lightsLamp = sampleModels / "2.0" / std::filesystem::u8path(u8"Unicode❤♻Test") / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(lightsLamp / std::filesystem::u8path(u8"Unicode❤♻Test.gltf"))); + fastgltf::GltfFileStream jsonData(lightsLamp / std::filesystem::u8path(u8"Unicode❤♻Test.gltf")); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(&jsonData, lightsLamp); + auto asset = parser.loadGltfJson(jsonData, lightsLamp); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -595,13 +602,10 @@ TEST_CASE("Test unicode characters", "[gltf-loader]") { TEST_CASE("Test extras callback", "[gltf-loader]") { auto materialVariants = sampleModels / "2.0" / "MaterialsVariantsShoe" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(materialVariants / "MaterialsVariantsShoe.gltf")); - std::vector nodeNames; // lambda callback to parse the glTF JSON from the jsonData buffer - auto parseJson = [&]() { + auto parseJson = [&](fastgltf::GltfDataGetter& data) { auto extrasCallback = [](simdjson::dom::object *extras, std::size_t objectIndex, fastgltf::Category category, void *userPointer) { if (category != fastgltf::Category::Nodes) @@ -618,11 +622,13 @@ TEST_CASE("Test extras callback", "[gltf-loader]") { fastgltf::Parser parser; parser.setExtrasParseCallback(extrasCallback); parser.setUserPointer(&nodeNames); - return parser.loadGltfJson(&jsonData, materialVariants); + return parser.loadGltfJson(data, materialVariants); }; // The asset has to be reused for exporting in the next section. - auto asset = parseJson(); + fastgltf::GltfFileStream jsonData(materialVariants / "MaterialsVariantsShoe.gltf"); + REQUIRE(jsonData.isOpen()); + auto asset = parseJson(jsonData); SECTION("Validate node names from extras") { REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -654,10 +660,11 @@ TEST_CASE("Test extras callback", "[gltf-loader]") { // Update the data buffer auto& string = json.get().output; - jsonData.fromByteView(reinterpret_cast(string.data()), string.size(), string.capacity()); + fastgltf::GltfDataBuffer reexportedJson(reinterpret_cast(string.data()), + string.size()); nodeNames.clear(); - auto reparsed = parseJson(); + auto reparsed = parseJson(reexportedJson); REQUIRE(reparsed.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(reparsed.get()) == fastgltf::Error::None); diff --git a/tests/benchmarks.cpp b/tests/benchmarks.cpp index 1859622cd..712e5e46d 100644 --- a/tests/benchmarks.cpp +++ b/tests/benchmarks.cpp @@ -82,7 +82,7 @@ std::vector readFileAsBytes(const std::filesystem::path& path) { throw std::runtime_error(std::string { "Failed to open file: " } + path.string()); auto fileSize = file.tellg(); - std::vector bytes(static_cast(fileSize) + fastgltf::getGltfBufferPadding()); + std::vector bytes(static_cast(fileSize)); file.seekg(0, std::ifstream::beg); file.read(reinterpret_cast(bytes.data()), fileSize); file.close(); @@ -103,11 +103,10 @@ TEST_CASE("Benchmark loading of NewSponza", "[gltf-benchmark]") { #endif auto bytes = readFileAsBytes(intelSponza / "NewSponza_Main_glTF_002.gltf"); - auto jsonData = std::make_unique(); - REQUIRE(jsonData->fromByteView(bytes.data(), bytes.size() - fastgltf::getGltfBufferPadding(), bytes.size())); + fastgltf::GltfDataBuffer jsonData(reinterpret_cast(bytes.data()), bytes.size()); BENCHMARK("Parse NewSponza") { - return parser.loadGltfJson(jsonData.get(), intelSponza, benchmarkOptions); + return parser.loadGltfJson(jsonData, intelSponza, benchmarkOptions); }; #ifdef HAS_TINYGLTF @@ -153,11 +152,10 @@ TEST_CASE("Benchmark base64 decoding from glTF file", "[gltf-benchmark]") { auto cylinderEngine = sampleModels / "2.0" / "2CylinderEngine" / "glTF-Embedded"; auto bytes = readFileAsBytes(cylinderEngine / "2CylinderEngine.gltf"); - auto jsonData = std::make_unique(); - REQUIRE(jsonData->fromByteView(bytes.data(), bytes.size() - fastgltf::getGltfBufferPadding(), bytes.size())); + fastgltf::GltfDataBuffer jsonData(reinterpret_cast(bytes.data()), bytes.size()); BENCHMARK("Parse 2CylinderEngine and decode base64") { - return parser.loadGltfJson(jsonData.get(), cylinderEngine, benchmarkOptions); + return parser.loadGltfJson(jsonData, cylinderEngine, benchmarkOptions); }; #ifdef HAS_TINYGLTF @@ -207,11 +205,10 @@ TEST_CASE("Benchmark raw JSON parsing", "[gltf-benchmark]") { auto buggyPath = sampleModels / "2.0" / "Buggy" / "glTF"; auto bytes = readFileAsBytes(buggyPath / "Buggy.gltf"); - auto jsonData = std::make_unique(); - REQUIRE(jsonData->fromByteView(bytes.data(), bytes.size() - fastgltf::getGltfBufferPadding(), bytes.size())); + fastgltf::GltfDataBuffer jsonData(reinterpret_cast(bytes.data()), bytes.size()); BENCHMARK("Parse Buggy.gltf") { - return parser.loadGltfJson(jsonData.get(), buggyPath, benchmarkOptions); + return parser.loadGltfJson(jsonData, buggyPath, benchmarkOptions); }; #ifdef HAS_TINYGLTF @@ -262,11 +259,10 @@ TEST_CASE("Benchmark massive gltf file", "[gltf-benchmark]") { #endif auto bytes = readFileAsBytes(bistroPath / "bistro.gltf"); - auto jsonData = std::make_unique(); - REQUIRE(jsonData->fromByteView(bytes.data(), bytes.size() - fastgltf::getGltfBufferPadding(), bytes.size())); + fastgltf::GltfDataBuffer jsonData(reinterpret_cast(bytes.data()), bytes.size()); BENCHMARK("Parse Bistro") { - return parser.loadGltfJson(jsonData.get(), bistroPath, benchmarkOptions); + return parser.loadGltfJson(jsonData, bistroPath, benchmarkOptions); }; #ifdef HAS_TINYGLTF @@ -306,8 +302,7 @@ TEST_CASE("Benchmark massive gltf file", "[gltf-benchmark]") { TEST_CASE("Compare parsing performance with minified documents", "[gltf-benchmark]") { auto buggyPath = sampleModels / "2.0" / "Buggy" / "glTF"; auto bytes = readFileAsBytes(buggyPath / "Buggy.gltf"); - auto jsonData = std::make_unique(); - REQUIRE(jsonData->fromByteView(bytes.data(), bytes.size() - fastgltf::getGltfBufferPadding(), bytes.size())); + fastgltf::GltfDataBuffer jsonData(reinterpret_cast(bytes.data()), bytes.size()); // Create a minified JSON string std::vector minified(bytes.size()); @@ -325,16 +320,15 @@ TEST_CASE("Compare parsing performance with minified documents", "[gltf-benchmar return result; }; - auto minifiedJsonData = std::make_unique(); - REQUIRE(minifiedJsonData->fromByteView(minified.data(), minified.size() - fastgltf::getGltfBufferPadding(), minified.size())); + fastgltf::GltfDataBuffer minifiedJsonData(reinterpret_cast(minified.data()), minified.size()); fastgltf::Parser parser; BENCHMARK("Parse Buggy.gltf with normal JSON") { - return parser.loadGltfJson(jsonData.get(), buggyPath, benchmarkOptions); + return parser.loadGltfJson(jsonData, buggyPath, benchmarkOptions); }; BENCHMARK("Parse Buggy.gltf with minified JSON") { - return parser.loadGltfJson(minifiedJsonData.get(), buggyPath, benchmarkOptions); + return parser.loadGltfJson(minifiedJsonData, buggyPath, benchmarkOptions); }; } diff --git a/tests/extension_tests.cpp b/tests/extension_tests.cpp index 3c29a6052..964edd1da 100644 --- a/tests/extension_tests.cpp +++ b/tests/extension_tests.cpp @@ -11,12 +11,12 @@ TEST_CASE("Loading KHR_texture_basisu glTF files", "[gltf-loader]") { auto stainedLamp = sampleModels / "2.0" / "StainedGlassLamp" / "glTF-KTX-BasisU"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(stainedLamp / "StainedGlassLamp.gltf")); + fastgltf::GltfFileStream jsonData(stainedLamp / "StainedGlassLamp.gltf"); + REQUIRE(jsonData.isOpen()); SECTION("Loading KHR_texture_basisu") { fastgltf::Parser parser(fastgltf::Extensions::KHR_texture_basisu); - auto asset = parser.loadGltfJson(jsonData.get(), path, fastgltf::Options::DontRequireValidAssetMember, + auto asset = parser.loadGltfJson(jsonData, path, fastgltf::Options::DontRequireValidAssetMember, fastgltf::Category::Textures | fastgltf::Category::Images); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -41,7 +41,7 @@ TEST_CASE("Loading KHR_texture_basisu glTF files", "[gltf-loader]") { SECTION("Testing requiredExtensions") { // We specify no extensions, yet the StainedGlassLamp requires KHR_texture_basisu. fastgltf::Parser parser(fastgltf::Extensions::None); - auto stainedGlassLamp = parser.loadGltfJson(jsonData.get(), path, fastgltf::Options::DontRequireValidAssetMember); + auto stainedGlassLamp = parser.loadGltfJson(jsonData, path, fastgltf::Options::DontRequireValidAssetMember); REQUIRE(stainedGlassLamp.error() == fastgltf::Error::MissingExtensions); } } @@ -49,11 +49,11 @@ TEST_CASE("Loading KHR_texture_basisu glTF files", "[gltf-loader]") { TEST_CASE("Loading KHR_texture_transform glTF files", "[gltf-loader]") { auto transformTest = sampleModels / "2.0" / "TextureTransformMultiTest" / "glTF"; - auto jsonData = std::make_unique(); - REQUIRE(jsonData->loadFromFile(transformTest / "TextureTransformMultiTest.gltf")); + fastgltf::GltfFileStream jsonData(transformTest / "TextureTransformMultiTest.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_texture_transform); - auto asset = parser.loadGltfJson(jsonData.get(), transformTest, fastgltf::Options::DontRequireValidAssetMember, fastgltf::Category::Materials); + auto asset = parser.loadGltfJson(jsonData, transformTest, fastgltf::Options::DontRequireValidAssetMember, fastgltf::Category::Materials); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -68,11 +68,12 @@ TEST_CASE("Loading KHR_texture_transform glTF files", "[gltf-loader]") { TEST_CASE("Test KHR_lights_punctual", "[gltf-loader]") { auto lightsLamp = sampleModels / "2.0" / "LightsPunctualLamp" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(lightsLamp / "LightsPunctualLamp.gltf")); + + fastgltf::GltfFileStream jsonData(lightsLamp / "LightsPunctualLamp.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_lights_punctual); - auto asset = parser.loadGltfJson(&jsonData, lightsLamp, fastgltf::Options::None, fastgltf::Category::Nodes); + auto asset = parser.loadGltfJson(jsonData, lightsLamp, fastgltf::Options::None, fastgltf::Category::Nodes); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -94,11 +95,12 @@ TEST_CASE("Test KHR_lights_punctual", "[gltf-loader]") { TEST_CASE("Test KHR_materials_specular", "[gltf-loader]") { auto specularTest = sampleModels / "2.0" / "SpecularTest" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(specularTest / "SpecularTest.gltf")); + + fastgltf::GltfFileStream jsonData(specularTest / "SpecularTest.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_materials_specular); - auto asset = parser.loadGltfJson(&jsonData, specularTest, fastgltf::Options::None, fastgltf::Category::Materials); + auto asset = parser.loadGltfJson(jsonData, specularTest, fastgltf::Options::None, fastgltf::Category::Materials); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -122,12 +124,13 @@ TEST_CASE("Test KHR_materials_specular", "[gltf-loader]") { } TEST_CASE("Test KHR_materials_ior and KHR_materials_iridescence", "[gltf-loader]") { - auto specularTest = sampleModels / "2.0" / "IridescenceDielectricSpheres" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(specularTest / "IridescenceDielectricSpheres.gltf")); + auto spheres = sampleModels / "2.0" / "IridescenceDielectricSpheres" / "glTF"; + + fastgltf::GltfFileStream jsonData(spheres / "IridescenceDielectricSpheres.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_materials_iridescence | fastgltf::Extensions::KHR_materials_ior); - auto asset = parser.loadGltfJson(&jsonData, specularTest, fastgltf::Options::None, fastgltf::Category::Materials); + auto asset = parser.loadGltfJson(jsonData, spheres, fastgltf::Options::None, fastgltf::Category::Materials); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -151,11 +154,12 @@ TEST_CASE("Test KHR_materials_ior and KHR_materials_iridescence", "[gltf-loader] TEST_CASE("Test KHR_materials_volume and KHR_materials_transmission", "[gltf-loader]") { auto beautifulGame = sampleModels / "2.0" / "ABeautifulGame" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(beautifulGame / "ABeautifulGame.gltf")); + + fastgltf::GltfFileStream jsonData(beautifulGame / "ABeautifulGame.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_materials_volume | fastgltf::Extensions::KHR_materials_transmission); - auto asset = parser.loadGltfJson(&jsonData, beautifulGame, fastgltf::Options::None, fastgltf::Category::Materials); + auto asset = parser.loadGltfJson(jsonData, beautifulGame, fastgltf::Options::None, fastgltf::Category::Materials); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -174,11 +178,12 @@ TEST_CASE("Test KHR_materials_volume and KHR_materials_transmission", "[gltf-loa TEST_CASE("Test KHR_materials_clearcoat", "[gltf-loader]") { auto clearcoatTest = sampleModels / "2.0" / "ClearCoatTest" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(clearcoatTest / "ClearCoatTest.gltf")); + + fastgltf::GltfFileStream jsonData(clearcoatTest / "ClearCoatTest.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_materials_clearcoat); - auto asset = parser.loadGltfJson(&jsonData, clearcoatTest, fastgltf::Options::None, fastgltf::Category::Materials); + auto asset = parser.loadGltfJson(jsonData, clearcoatTest, fastgltf::Options::None, fastgltf::Category::Materials); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -199,11 +204,12 @@ TEST_CASE("Test KHR_materials_clearcoat", "[gltf-loader]") { TEST_CASE("Test EXT_mesh_gpu_instancing", "[gltf-loader]") { auto simpleInstancingTest = sampleModels / "2.0" / "SimpleInstancing" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(simpleInstancingTest / "SimpleInstancing.gltf")); + + fastgltf::GltfFileStream jsonData(simpleInstancingTest / "SimpleInstancing.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::EXT_mesh_gpu_instancing); - auto asset = parser.loadGltfJson(&jsonData, simpleInstancingTest, fastgltf::Options::None, fastgltf::Category::Accessors | fastgltf::Category::Nodes); + auto asset = parser.loadGltfJson(jsonData, simpleInstancingTest, fastgltf::Options::None, fastgltf::Category::Accessors | fastgltf::Category::Nodes); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -269,11 +275,10 @@ TEST_CASE("Test KHR_materials_dispersion", "[gltf-loader]") { } } ]})"; - fastgltf::GltfDataBuffer jsonData; - jsonData.copyBytes(reinterpret_cast(json.data()), json.size()); + fastgltf::GltfDataBuffer jsonData(reinterpret_cast(json.data()), json.size()); fastgltf::Parser parser(fastgltf::Extensions::KHR_materials_dispersion); - auto asset = parser.loadGltfJson(&jsonData, {}, fastgltf::Options::DontRequireValidAssetMember); + auto asset = parser.loadGltfJson(jsonData, {}, fastgltf::Options::DontRequireValidAssetMember); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -283,11 +288,12 @@ TEST_CASE("Test KHR_materials_dispersion", "[gltf-loader]") { TEST_CASE("Test KHR_materials_variant", "[gltf-loader]") { auto velvetSofa = sampleModels / "2.0" / "GlamVelvetSofa" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(velvetSofa / "GlamVelvetSofa.gltf")); + + fastgltf::GltfFileStream jsonData(velvetSofa / "GlamVelvetSofa.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser(fastgltf::Extensions::KHR_materials_variants | fastgltf::Extensions::KHR_texture_transform); - auto asset = parser.loadGltfJson(&jsonData, velvetSofa, fastgltf::Options::None); + auto asset = parser.loadGltfJson(jsonData, velvetSofa, fastgltf::Options::None); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); diff --git a/tests/glb_tests.cpp b/tests/glb_tests.cpp index 38f3b1cd1..0c1e877a4 100644 --- a/tests/glb_tests.cpp +++ b/tests/glb_tests.cpp @@ -7,13 +7,13 @@ #include "gltf_path.hpp" TEST_CASE("Load basic GLB file", "[gltf-loader]") { - fastgltf::Parser parser; auto folder = sampleModels / "2.0" / "Box" / "glTF-Binary"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(folder / "Box.glb")); + fastgltf::GltfDataBuffer jsonData(folder / "Box.glb"); + // REQUIRE(jsonData.isOpen()); + fastgltf::Parser parser; SECTION("Load basic Box.glb") { - auto asset = parser.loadGltfBinary(&jsonData, folder, fastgltf::Options::None, fastgltf::Category::Buffers); + auto asset = parser.loadGltfBinary(jsonData, folder, fastgltf::Options::None, fastgltf::Category::Buffers); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -28,7 +28,7 @@ TEST_CASE("Load basic GLB file", "[gltf-loader]") { } SECTION("Load basic Box.glb and load buffers") { - auto asset = parser.loadGltfBinary(&jsonData, folder, fastgltf::Options::LoadGLBBuffers, fastgltf::Category::Buffers); + auto asset = parser.loadGltfBinary(jsonData, folder, fastgltf::Options::LoadGLBBuffers, fastgltf::Category::Buffers); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); @@ -45,13 +45,12 @@ TEST_CASE("Load basic GLB file", "[gltf-loader]") { std::ifstream file(folder / "Box.glb", std::ios::binary | std::ios::ate); auto length = static_cast(file.tellg()); file.seekg(0, std::ifstream::beg); - std::vector bytes(length + fastgltf::getGltfBufferPadding()); + std::vector bytes(length); file.read(reinterpret_cast(bytes.data()), static_cast(length)); - fastgltf::GltfDataBuffer byteBuffer; - REQUIRE(byteBuffer.fromByteView(bytes.data(), length, length + fastgltf::getGltfBufferPadding())); + fastgltf::GltfDataBuffer byteBuffer(reinterpret_cast(bytes.data()), length); - auto asset = parser.loadGltfBinary(&byteBuffer, folder, fastgltf::Options::LoadGLBBuffers, fastgltf::Category::Buffers); + auto asset = parser.loadGltfBinary(byteBuffer, folder, fastgltf::Options::LoadGLBBuffers, fastgltf::Category::Buffers); REQUIRE(asset.error() == fastgltf::Error::None); } } diff --git a/tests/uri_tests.cpp b/tests/uri_tests.cpp index 755993b8f..cca841238 100644 --- a/tests/uri_tests.cpp +++ b/tests/uri_tests.cpp @@ -107,11 +107,11 @@ TEST_CASE("Validate URI copying/moving", "[uri-tests]") { TEST_CASE("Validate escaped/percent-encoded URI", "[uri-tests]") { const std::string_view gltfString = R"({"images": [{"uri": "grande_sph\u00E8re.png"}]})"; - fastgltf::GltfDataBuffer dataBuffer; - dataBuffer.copyBytes(reinterpret_cast(gltfString.data()), gltfString.size()); + fastgltf::GltfDataBuffer dataBuffer(reinterpret_cast(gltfString.data()), + gltfString.size()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(&dataBuffer, "", fastgltf::Options::DontRequireValidAssetMember); + auto asset = parser.loadGltfJson(dataBuffer, "", fastgltf::Options::DontRequireValidAssetMember); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(asset->images.size() == 1); @@ -126,11 +126,12 @@ TEST_CASE("Validate escaped/percent-encoded URI", "[uri-tests]") { TEST_CASE("Test percent-encoded URIs in glTF", "[uri-tests]") { auto boxWithSpaces = sampleModels / "2.0" / "Box With Spaces" / "glTF"; - fastgltf::GltfDataBuffer jsonData; - REQUIRE(jsonData.loadFromFile(boxWithSpaces / "Box With Spaces.gltf")); + + fastgltf::GltfFileStream jsonData(boxWithSpaces / "Box With Spaces.gltf"); + REQUIRE(jsonData.isOpen()); fastgltf::Parser parser; - auto asset = parser.loadGltfJson(&jsonData, boxWithSpaces); + auto asset = parser.loadGltfJson(jsonData, boxWithSpaces); REQUIRE(asset.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); diff --git a/tests/write_tests.cpp b/tests/write_tests.cpp index b04d05eea..f18150c1d 100644 --- a/tests/write_tests.cpp +++ b/tests/write_tests.cpp @@ -23,11 +23,11 @@ TEST_CASE("Test simple glTF composition", "[write-tests]") { TEST_CASE("Read glTF, write it, and then read it again and validate", "[write-tests]") { auto cubePath = sampleModels / "2.0" / "Cube" / "glTF"; - auto cubeJsonData = std::make_unique(); - REQUIRE(cubeJsonData->loadFromFile(cubePath / "Cube.gltf")); + fastgltf::GltfFileStream cubeJsonData(cubePath / "Cube.gltf"); + REQUIRE(cubeJsonData.isOpen()); fastgltf::Parser parser; - auto cube = parser.loadGltfJson(cubeJsonData.get(), cubePath); + auto cube = parser.loadGltfJson(cubeJsonData, cubePath); REQUIRE(cube.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(cube.get()) == fastgltf::Error::None); @@ -35,18 +35,17 @@ TEST_CASE("Read glTF, write it, and then read it again and validate", "[write-te auto expected = exporter.writeGltfJson(cube.get()); REQUIRE(expected.error() == fastgltf::Error::None); - fastgltf::GltfDataBuffer cube2JsonData; - cube2JsonData.copyBytes(reinterpret_cast(expected.get().output.data()), - expected.get().output.size()); - auto cube2 = parser.loadGltfJson(&cube2JsonData, cubePath); + fastgltf::GltfDataBuffer exportedJsonData(reinterpret_cast(expected.get().output.data()), + expected.get().output.size()); + auto cube2 = parser.loadGltfJson(exportedJsonData, cubePath); REQUIRE(cube2.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(cube2.get()) == fastgltf::Error::None); } TEST_CASE("Rewrite read glTF with multiple material extensions", "[write-tests]") { auto dishPath = sampleModels / "2.0" / "IridescentDishWithOlives" / "glTF"; - fastgltf::GltfDataBuffer dishJsonData; - REQUIRE(dishJsonData.loadFromFile(dishPath / "IridescentDishWithOlives.gltf")); + fastgltf::GltfFileStream dishJsonData(dishPath / "IridescentDishWithOlives.gltf"); + REQUIRE(dishJsonData.isOpen()); static constexpr auto requiredExtensions = fastgltf::Extensions::KHR_materials_ior | fastgltf::Extensions::KHR_materials_iridescence | @@ -54,7 +53,7 @@ TEST_CASE("Rewrite read glTF with multiple material extensions", "[write-tests]" fastgltf::Extensions::KHR_materials_volume; fastgltf::Parser parser(requiredExtensions); - auto dish = parser.loadGltfJson(&dishJsonData, dishPath); + auto dish = parser.loadGltfJson(dishJsonData, dishPath); REQUIRE(dish.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(dish.get()) == fastgltf::Error::None); @@ -62,10 +61,9 @@ TEST_CASE("Rewrite read glTF with multiple material extensions", "[write-tests]" auto expected = exporter.writeGltfJson(dish.get()); REQUIRE(expected.error() == fastgltf::Error::None); - fastgltf::GltfDataBuffer exportedDishJsonData; - exportedDishJsonData.copyBytes(reinterpret_cast(expected.get().output.data()), - expected.get().output.size()); - auto exportedDish = parser.loadGltfJson(&exportedDishJsonData, dishPath); + fastgltf::GltfDataBuffer exportedDishJsonData(reinterpret_cast(expected.get().output.data()), + expected.get().output.size()); + auto exportedDish = parser.loadGltfJson(exportedDishJsonData, dishPath); REQUIRE(exportedDish.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(exportedDish.get()) == fastgltf::Error::None); } @@ -73,36 +71,51 @@ TEST_CASE("Rewrite read glTF with multiple material extensions", "[write-tests]" TEST_CASE("Try writing a glTF with all buffers and images", "[write-tests]") { auto cubePath = sampleModels / "2.0" / "Cube" / "glTF"; - fastgltf::GltfDataBuffer gltfDataBuffer; - gltfDataBuffer.loadFromFile(cubePath / "Cube.gltf"); + fastgltf::GltfFileStream cubeJson(cubePath / "Cube.gltf"); + REQUIRE(cubeJson.isOpen()); fastgltf::Parser parser; auto options = fastgltf::Options::LoadExternalBuffers | fastgltf::Options::LoadExternalImages; - auto cube = parser.loadGltfJson(&gltfDataBuffer, cubePath, options); + auto cube = parser.loadGltfJson(cubeJson, cubePath, options); REQUIRE(cube.error() == fastgltf::Error::None); - std::filesystem::create_directory(path / "export"); + auto exportFolder = path / "export"; + if (std::filesystem::is_directory(exportFolder)) { + std::filesystem::remove_all(exportFolder); + } + + std::filesystem::create_directory(exportFolder); fastgltf::FileExporter exporter; - auto error = exporter.writeGltfJson(cube.get(), path / "export" / "cube.gltf", + auto error = exporter.writeGltfJson(cube.get(), exportFolder / "cube.gltf", fastgltf::ExportOptions::PrettyPrintJson); REQUIRE(error == fastgltf::Error::None); + REQUIRE(std::filesystem::exists(exportFolder / "buffer0.bin")); + REQUIRE(std::filesystem::exists(exportFolder / "image0.bin")); + REQUIRE(std::filesystem::exists(exportFolder / "image1.bin")); } TEST_CASE("Try writing a GLB with all buffers and images", "[write-tests]") { auto cubePath = sampleModels / "2.0" / "Cube" / "glTF"; - fastgltf::GltfDataBuffer gltfDataBuffer; - gltfDataBuffer.loadFromFile(cubePath / "Cube.gltf"); + fastgltf::GltfFileStream cubeJson(cubePath / "Cube.gltf"); + REQUIRE(cubeJson.isOpen()); fastgltf::Parser parser; auto options = fastgltf::Options::LoadExternalBuffers | fastgltf::Options::LoadExternalImages; - auto cube = parser.loadGltfJson(&gltfDataBuffer, cubePath, options); + auto cube = parser.loadGltfJson(cubeJson, cubePath, options); REQUIRE(cube.error() == fastgltf::Error::None); - std::filesystem::create_directory(path / "export_glb"); + auto exportFolder = path / "export_glb"; + if (std::filesystem::is_directory(exportFolder)) { + std::filesystem::remove_all(exportFolder); + } + + std::filesystem::create_directory(exportFolder); fastgltf::FileExporter exporter; - auto error = exporter.writeGltfBinary(cube.get(), path / "export_glb" / "cube.glb"); + auto error = exporter.writeGltfBinary(cube.get(), exportFolder / "cube.glb"); REQUIRE(error == fastgltf::Error::None); + REQUIRE(std::filesystem::exists(exportFolder / "image0.bin")); + REQUIRE(std::filesystem::exists(exportFolder / "image1.bin")); } TEST_CASE("Test string escape", "[write-tests]") { @@ -138,9 +151,8 @@ TEST_CASE("Test all local models and re-export them", "[write-tests]") { continue; // Parse the glTF - fastgltf::GltfDataBuffer gltfDataBuffer; - gltfDataBuffer.loadFromFile(epath); - auto model = parser.loadGltf(&gltfDataBuffer, epath.parent_path()); + fastgltf::GltfFileStream gltfData(epath); + auto model = parser.loadGltf(gltfData, epath.parent_path()); if (model.error() == fastgltf::Error::UnsupportedVersion || model.error() == fastgltf::Error::UnknownRequiredExtension) continue; // Skip any glTF 1.0 or 0.x files or glTFs with unsupported extensions. @@ -154,9 +166,9 @@ TEST_CASE("Test all local models and re-export them", "[write-tests]") { // Parse the re-generated glTF and validate auto& exportedJson = exported.get().output; - fastgltf::GltfDataBuffer regeneratedJson; - regeneratedJson.copyBytes(reinterpret_cast(exportedJson.data()), exportedJson.size()); - auto regeneratedModel = parser.loadGltf(®eneratedJson, epath.parent_path()); + fastgltf::GltfDataBuffer regeneratedJson(reinterpret_cast(exportedJson.data()), + exportedJson.size()); + auto regeneratedModel = parser.loadGltf(regeneratedJson, epath.parent_path()); REQUIRE(regeneratedModel.error() == fastgltf::Error::None); REQUIRE(fastgltf::validate(regeneratedModel.get()) == fastgltf::Error::None);