Skip to content

Commit

Permalink
Merge pull request #610 from timoore/load-gltf-uri
Browse files Browse the repository at this point in the history
Add a function to load a glTF file
  • Loading branch information
kring authored Dec 19, 2023
2 parents 41f0186 + 260c0f3 commit 8bfdcb3
Show file tree
Hide file tree
Showing 6 changed files with 729 additions and 0 deletions.
18 changes: 18 additions & 0 deletions CesiumGltfReader/include/CesiumGltfReader/GltfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,24 @@ class CESIUMGLTFREADER_API GltfReader {
const gsl::span<const std::byte>& data,
const GltfReaderOptions& options = GltfReaderOptions()) const;

/**
* @brief Reads a glTF or binary glTF file from a URL and resolves external
* buffers and images.
*
* @param asyncSystem The async system to use for resolving external data.
* @param url The url for reading the file.
* @param headers http headers needed to make the request.
* @param pAssetAccessor The asset accessor to use to make the necessary
* requests.
* @param options Options for how to read the glTF.
*/
CesiumAsync::Future<GltfReaderResult> loadGltf(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::string& url,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const GltfReaderOptions& options = GltfReaderOptions()) const;

/**
* @brief Accepts the result of {@link readGltf} and resolves any remaining
* external buffers and images.
Expand Down
55 changes: 55 additions & 0 deletions CesiumGltfReader/src/GltfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,61 @@ GltfReaderResult GltfReader::readGltf(
return result;
}

CesiumAsync::Future<GltfReaderResult> GltfReader::loadGltf(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::string& uri,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const GltfReaderOptions& options) const {
return pAssetAccessor->get(asyncSystem, uri, headers)
.thenInWorkerThread(
[this, options, asyncSystem, pAssetAccessor, uri](
std::shared_ptr<CesiumAsync::IAssetRequest>&& pRequest) {
const CesiumAsync::IAssetResponse* pResponse = pRequest->response();

if (!pResponse) {
return asyncSystem.createResolvedFuture(GltfReaderResult{
std::nullopt,
{fmt::format("Request for {} failed.", uri)},
{}});
}

uint16_t statusCode = pResponse->statusCode();
if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) {
return asyncSystem.createResolvedFuture(GltfReaderResult{
std::nullopt,
{fmt::format(
"Request for {} failed with code {}",
uri,
pResponse->statusCode())},
{}});
}

const CesiumJsonReader::JsonReaderOptions& context =
this->getExtensions();
GltfReaderResult result =
isBinaryGltf(pResponse->data())
? readBinaryGltf(context, pResponse->data())
: readJsonGltf(context, pResponse->data());

if (!result.model) {
return asyncSystem.createResolvedFuture(std::move(result));
}

return resolveExternalData(
asyncSystem,
uri,
pRequest->headers(),
pAssetAccessor,
options,
std::move(result));
})
.thenInWorkerThread([options, this](GltfReaderResult&& result) {
postprocess(*this, result, options);
return std::move(result);
});
}

/*static*/
Future<GltfReaderResult> GltfReader::resolveExternalData(
AsyncSystem asyncSystem,
Expand Down
64 changes: 64 additions & 0 deletions CesiumGltfReader/test/TestGltfReader.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#include "CesiumGltfReader/GltfReader.h"

#include <CesiumAsync/AsyncSystem.h>
#include <CesiumGltf/AccessorView.h>
#include <CesiumGltf/ExtensionCesiumRTC.h>
#include <CesiumGltf/ExtensionKhrDracoMeshCompression.h>
#include <CesiumNativeTests/SimpleAssetAccessor.h>
#include <CesiumNativeTests/SimpleTaskProcessor.h>
#include <CesiumNativeTests/waitForFuture.h>
#include <CesiumUtility/Math.h>

#include <catch2/catch.hpp>
Expand All @@ -15,9 +19,11 @@
#include <limits>
#include <string>

using namespace CesiumAsync;
using namespace CesiumGltf;
using namespace CesiumGltfReader;
using namespace CesiumUtility;
using namespace CesiumNativeTests;

namespace {
std::vector<std::byte> readFile(const std::filesystem::path& fileName) {
Expand Down Expand Up @@ -681,3 +687,61 @@ TEST_CASE("Decodes images with data uris") {
CHECK(image.height == 256);
CHECK(!image.pixelData.empty());
}

TEST_CASE("GltfReader::loadGltf") {
auto pMockTaskProcessor = std::make_shared<SimpleTaskProcessor>();
CesiumAsync::AsyncSystem asyncSystem{pMockTaskProcessor};

std::filesystem::path dataDir(CesiumGltfReader_TEST_DATA_DIR);

std::map<std::string, std::shared_ptr<SimpleAssetRequest>> mapUrlToRequest;

for (const auto& entry : std::filesystem::recursive_directory_iterator(
dataDir / "DracoCompressed")) {
if (!entry.is_regular_file())
continue;
auto pResponse = std::make_unique<SimpleAssetResponse>(
uint16_t(200),
"application/binary",
CesiumAsync::HttpHeaders{},
readFile(entry.path()));
std::string url = "file:///" + entry.path().generic_u8string();
auto pRequest = std::make_unique<SimpleAssetRequest>(
"GET",
url,
CesiumAsync::HttpHeaders{},
std::move(pResponse));
mapUrlToRequest[url] = std::move(pRequest);
}

auto pMockAssetAccessor =
std::make_shared<SimpleAssetAccessor>(std::move(mapUrlToRequest));

GltfReader reader{};
Future<GltfReaderResult> future = reader.loadGltf(
asyncSystem,
"file:///" + std::filesystem::directory_entry(
dataDir / "DracoCompressed" / "CesiumMilkTruck.gltf")
.path()
.generic_u8string(),
{},
pMockAssetAccessor);
GltfReaderResult result = waitForFuture(asyncSystem, std::move(future));
REQUIRE(result.model);
CHECK(result.errors.empty());
// There will be warnings, because this model has accessors that don't match
// the Draco-decoded size. It seems to be ambiguous whether this is
// technically allowed or not. See:
// https://github.com/KhronosGroup/glTF/issues/1342

REQUIRE(result.model->images.size() == 1);
const CesiumGltf::Image& image = result.model->images[0];
CHECK(image.cesium.width == 2048);
CHECK(image.cesium.height == 2048);
CHECK(image.cesium.pixelData.size() == 2048 * 2048 * 4);

CHECK(!result.model->buffers.empty());
for (const CesiumGltf::Buffer& buffer : result.model->buffers) {
CHECK(!buffer.cesium.data.empty());
}
}
Binary file added CesiumGltfReader/test/data/DracoCompressed/0.bin
Binary file not shown.
Loading

0 comments on commit 8bfdcb3

Please sign in to comment.