Skip to content

Commit

Permalink
Gltf: experiment promises (unfinished, but promising!)
Browse files Browse the repository at this point in the history
  • Loading branch information
pthom committed Dec 23, 2019
1 parent 35b3514 commit feee188
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 81 deletions.
10 changes: 5 additions & 5 deletions src/Loaders/include/babylon/loading/glTF/2.0/gltf_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ class BABYLON_SHARED_EXPORT GLTFLoader : public IGLTFLoader {
* @returns A promise that resolves with the loaded data when the load is
* complete
*/
ArrayBufferView& loadBufferViewAsync(const std::string& context, IBufferView& bufferView);
MyPromise<ArrayBufferView> loadBufferViewAsync(const std::string& context, IBufferView& bufferView);

/**
* @brief Hidden
Expand Down Expand Up @@ -355,7 +355,7 @@ class BABYLON_SHARED_EXPORT GLTFLoader : public IGLTFLoader {
* @returns A promise that resolves with the loaded data when the load is
* complete
*/
ArrayBufferView loadUriAsync(const std::string& context, const std::string& uri);
MyPromise<ArrayBufferView> loadUriAsync(const std::string& context, const std::string& uri);

/**
* @brief Adds a JSON pointer to the metadata of the Babylon object at
Expand Down Expand Up @@ -463,9 +463,9 @@ class BABYLON_SHARED_EXPORT GLTFLoader : public IGLTFLoader {
void _loadAnimationsAsync();
_IAnimationSamplerData _loadAnimationSamplerAsync(const std::string& context,
IAnimationSampler& sampler);
ArrayBufferView& _loadBufferAsync(const std::string& context, IBuffer& buffer);
MyPromise<ArrayBufferView> _loadBufferAsync(const std::string& context, IBuffer& buffer);
template <typename T>
ArrayBufferView& _loadAccessorAsync(const std::string& context, IAccessor& accessor);
MyPromise<ArrayBufferView> _loadAccessorAsync(const std::string& context, IAccessor& accessor);
Float32Array& _loadFloatAccessorAsync(const std::string& context, IAccessor& accessor);
IndicesArray& _getConverted32bitIndices(IAccessor& accessor);
IndicesArray _castIndicesTo32bit(const IGLTF2::AccessorComponentType& type,
Expand Down Expand Up @@ -526,7 +526,7 @@ class BABYLON_SHARED_EXPORT GLTFLoader : public IGLTFLoader {
const std::function<void(const BaseTexturePtr& babylonTexture)>& assign);
AnimationGroupPtr _extensionsLoadAnimationAsync(const std::string& context,
const IAnimation& animation);
std::optional<ArrayBufferView> _extensionsLoadUriAsync(const std::string& context,
std::optional<MyPromise<ArrayBufferView>> _extensionsLoadUriAsync(const std::string& context,
const std::string& uri);

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <nlohmann/json.hpp>

#include <babylon/core/array_buffer_view.h>
#include <babylon/loading/glTF/igltf_loader.h>
#include <babylon/meshes/vertex_buffer.h>

using json = nlohmann::json;
Expand Down Expand Up @@ -837,7 +838,7 @@ struct IBuffer : public IChildRootProperty {
* The uri of the buffer. Relative paths are relative to the .gltf file.
* Instead of referencing an external file, the uri can also be a data-uri
*/
std::string uri = "";
std::optional<std::string> uri = "";
/**
* The length of the buffer in bytes
*/
Expand Down Expand Up @@ -1373,10 +1374,10 @@ struct IArrayItem {
*/
struct IAccessor : public IGLTF2::IAccessor, IArrayItem {
/** @hidden */
std::optional<ArrayBufferView> _data = std::nullopt;
std::optional<MyPromise<ArrayBufferView>> _data;

/** @hidden */
VertexBufferPtr _babylonVertexBuffer = nullptr;
std::optional<MyPromise<VertexBufferPtr>> _babylonVertexBuffer;

static IAccessor Parse(const json& parsedAccessor);

Expand Down Expand Up @@ -1425,7 +1426,8 @@ struct IAnimation : public IGLTF2::IAnimation, IArrayItem {
*/
struct IBuffer : public IGLTF2::IBuffer, IArrayItem {
/** @hidden */
ArrayBufferView _data;
//ArrayBufferView _data;
std::optional<MyPromise<ArrayBufferView>> _data;

static IBuffer Parse(const json& parsedBuffer);

Expand All @@ -1436,10 +1438,10 @@ struct IBuffer : public IGLTF2::IBuffer, IArrayItem {
*/
struct IBufferView : public IGLTF2::IBufferView, IArrayItem {
/** @hidden */
ArrayBufferView _data;
std::optional<MyPromise<ArrayBufferView>> _data;

/** @hidden */
BufferPtr _babylonBuffer = nullptr;
std::optional<MyPromise<BufferPtr>> _babylonBuffer;

static IBufferView Parse(const json& parsedBufferView);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ class BABYLON_SHARED_EXPORT GLTFFileLoader : public IDisposable,
/**
* Function called before loading a url referenced by the asset.
*/
std::function<std::string(const std::string& url)> preprocessUrlAsync;
MyPromise<std::string> preprocessUrlAsync(const std::string& url) { MyPromise<std::string> r; r.resolve(url); return r; }

/**
* Observable raised when the loader creates a mesh after parsing the glTF
Expand Down
9 changes: 9 additions & 0 deletions src/Loaders/include/babylon/loading/glTF/igltf_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
#include <babylon/core/array_buffer_view.h>
#include <babylon/interfaces/idisposable.h>

#include "promise.hpp"
template<typename T> using MyPromise = promise::Defer;
using MyPromise_Any = promise::Defer;
template<typename T> auto MyPromise_AlreadyFullfilled(T t) {
auto r = promise::newPromise();
r->resolve(t);
return r;
}

using json = nlohmann::json;

namespace BABYLON {
Expand Down
121 changes: 59 additions & 62 deletions src/Loaders/src/loading/glTF/2.0/gltf_loader.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <babylon/loading/glTF/2.0/gltf_loader.h>

#include <babylon/asio/asio.h>
#include <babylon/animations/animation_group.h>
#include <babylon/animations/ianimatable.h>
#include <babylon/animations/ianimation_key.h>
Expand Down Expand Up @@ -244,7 +245,7 @@ void GLTFLoader::_loadData(const IGLTFLoaderData& data)

if (data.bin.has_value()) {
const auto& buffers = _gltf->buffers;
if (!buffers.empty() && !buffers[0].uri.empty()) {
if (!buffers.empty() && !buffers[0].uri.has_value()) {
const auto& binaryBuffer = buffers[0];
if (binaryBuffer.byteLength < data.bin->byteLength() - 3
|| binaryBuffer.byteLength > data.bin->byteLength()) {
Expand Down Expand Up @@ -1367,47 +1368,37 @@ _IAnimationSamplerData GLTFLoader::_loadAnimationSamplerAsync(const std::string&
return sampler._data.value();
}

ArrayBufferView& GLTFLoader::_loadBufferAsync(const std::string& context, IBuffer& buffer)
MyPromise<ArrayBufferView> GLTFLoader::_loadBufferAsync(const std::string& context, IBuffer& buffer)
{
if (buffer._data) {
return buffer._data;
return buffer._data.value();
}

if (buffer.uri.empty()) {
if (!buffer.uri) {
throw std::runtime_error(String::printf("%s/uri: Value is missing", context.c_str()));
}

buffer._data = loadUriAsync(String::printf("%s/uri", context.c_str()), buffer.uri);
buffer._data = loadUriAsync(String::printf("%s/uri", context.c_str()), buffer.uri.value());

return buffer._data;
return buffer._data.value();
}

ArrayBufferView& GLTFLoader::loadBufferViewAsync(const std::string& context,
MyPromise<ArrayBufferView> GLTFLoader::loadBufferViewAsync(const std::string& context,
IBufferView& bufferView)
{
if (bufferView._data) {
return bufferView._data;
return bufferView._data.value();
}

auto& buffer = ArrayItem::Get(String::printf("%s/buffer", context.c_str()), _gltf->buffers,
bufferView.buffer);
const auto data = _loadBufferAsync(String::printf("/buffers/%ld", buffer.index), buffer);
bufferView._data = _loadBufferAsync(String::printf("/buffers/%ld", buffer.index), buffer);

// ASYNC_FIXME: We cannot treat the data right now, it will be otained later!
try {
bufferView._data = stl_util::to_array<uint8_t>(
data.uint8Array, data.byteOffset + (bufferView.byteOffset.value_or(0)),
bufferView.byteLength);
}
catch (const std::exception& e) {
throw std::runtime_error(String::printf("%s: %s", context.c_str(), e.what()));
}

return bufferView._data;
return bufferView._data.value();
}

template <typename T>
ArrayBufferView& GLTFLoader::_loadAccessorAsync(const std::string& context, IAccessor& accessor)
MyPromise<ArrayBufferView> GLTFLoader::_loadAccessorAsync(const std::string& context, IAccessor& accessor)
{
if (accessor._data.has_value()) {
return *accessor._data;
Expand All @@ -1420,29 +1411,30 @@ ArrayBufferView& GLTFLoader::_loadAccessorAsync(const std::string& context, IAcc
const auto length = numComponents * accessor.count;

if (!accessor.bufferView.has_value()) {
accessor._data = std::vector<T>(length);
accessor._data = MyPromise_AlreadyFullfilled(std::vector<T>(length));
}
else {
auto& bufferView = ArrayItem::Get(String::printf("%s/bufferView", context.c_str()),
_gltf->bufferViews, *accessor.bufferView);
auto data
= loadBufferViewAsync(String::printf("/bufferViews/%ld", bufferView.index), bufferView);
if (accessor.componentType == IGLTF2::AccessorComponentType::FLOAT && !accessor.normalized) {
data = GLTFLoader::_GetTypedArray(context, accessor.componentType, data, accessor.byteOffset,
length);
}
else {
auto typedArray = Float32Array(length);
VertexBuffer::ForEach(
data.float32Array, accessor.byteOffset || 0, bufferView.byteStride || byteStride,
numComponents, static_cast<unsigned>(accessor.componentType), typedArray.size(),
accessor.normalized || false,
[&typedArray](float value, size_t index) -> void { typedArray[index] = value; });
data = typedArray;
}
accessor._data
= loadBufferViewAsync(String::printf("/bufferViews/%ld", bufferView.index), bufferView).then([=](const auto& data) {
if (accessor.componentType == IGLTF2::AccessorComponentType::FLOAT && !accessor.normalized) {
return GLTFLoader::_GetTypedArray(context, accessor.componentType, data,
accessor.byteOffset, length);
}
else {
auto typedArray = Float32Array(length);
VertexBuffer::ForEach(
data.float32Array, accessor.byteOffset || 0, bufferView.byteStride || byteStride,
numComponents, static_cast<unsigned>(accessor.componentType), typedArray.size(),
accessor.normalized || false,
[&typedArray](float value, size_t index) -> void { typedArray[index] = value; });
return typedArray;
}

accessor._data = GLTFLoader::_GetTypedArray(context, accessor.componentType, data,
accessor.byteOffset, length);
accessor._data = GLTFLoader::_GetTypedArray(context, accessor.componentType, data,
accessor.byteOffset, length);
});
}

if (accessor.sparse) {
Expand Down Expand Up @@ -2023,7 +2015,7 @@ ArrayBufferView& GLTFLoader::loadImageAsync(const std::string& context, IImage&
}

// ASYNC_FIXME this should return a promise, not a value !!! As in the js code.
ArrayBufferView GLTFLoader::loadUriAsync(const std::string& context, const std::string& uri)
MyPromise<ArrayBufferView> GLTFLoader::loadUriAsync(const std::string& context, const std::string& uri)
{
const auto extensionPromise = _extensionsLoadUriAsync(context, uri);
if (extensionPromise.has_value()) {
Expand All @@ -2037,33 +2029,38 @@ ArrayBufferView GLTFLoader::loadUriAsync(const std::string& context, const std::
if (Tools::IsBase64(uri)) {
const auto data = Tools::DecodeBase64(uri);
log(String::printf("Decoded %s... (%ld bytes)", uri.substr(0, 64).c_str(), data.size()));
return data;

MyPromise<ArrayBufferView> r;
r.resolve(data);
return r;
}

log(String::printf("Loading %s", uri.c_str()));

ArrayBuffer data;
auto url = _parent.preprocessUrlAsync(_rootUrl + uri);
if (!_disposed) {
FileTools::LoadFile(
url,
[this, &data, &uri](const std::variant<std::string, ArrayBuffer>& fileData,
const std::string & /*responseURL*/) -> void {
if (!_disposed) {
if (std::holds_alternative<ArrayBuffer>(fileData)) {
data = std::get<ArrayBuffer>(fileData);
log(String::printf("Loaded %s (%ld bytes)", uri.c_str(), data.size()));
//ArrayBuffer data;
return _parent.preprocessUrlAsync(this->_rootUrl + uri).then([this](const std::string& url) {
return promise::newPromise( [=](MyPromise_Any &d) {
if (!this->_disposed) {

auto onSuccessFunction = [=](const ArrayBuffer &data) {
if (!this->_disposed)
{
log(String::printf("Loaded %s %i bytes", url.c_str(), data.size());
d.resolve(data);
}
}
},
nullptr, true,
[this, &context, &uri](const std::string& message, const std::string& exception) -> void {
log(String::printf("%s: Failed to load (%s %s)", context.c_str(), uri.c_str(),
message.c_str(), exception.c_str()));
});
}
};
auto onProgressFunction = [=](bool lengthComputable, size_t loaded, size_t total) {
if (!this->_disposed)
this->_onProgress();
};
auto onErrorFunction = [=](const std::string & errorMessage) {
this->log(String::printf("loadUriAsync : Error %s", errorMessage.c_str()));
};

return data;
asio::LoadUrlAsync_Binary(url, onSuccessFunction, onErrorFunction, onProgressFunction);
}
});
});
}

void GLTFLoader::_onProgress()
Expand Down Expand Up @@ -2368,7 +2365,7 @@ AnimationGroupPtr GLTFLoader::_extensionsLoadAnimationAsync(const std::string& /
return nullptr;
}

std::optional<ArrayBufferView> GLTFLoader::_extensionsLoadUriAsync(const std::string& /*context*/,
std::optional<MyPromise<ArrayBufferView>> GLTFLoader::_extensionsLoadUriAsync(const std::string& /*context*/,
const std::string& /*uri*/)
{
return std::nullopt;
Expand Down
9 changes: 2 additions & 7 deletions src/Loaders/src/loading/glTF/gltf_file_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ GLTFFileLoader::GLTFFileLoader()
, useClipPlane{false}
, compileShadowGenerators{false}
, transparencyAsCoverage{false}
, preprocessUrlAsync{nullptr}
, onMeshLoaded{this, &GLTFFileLoader::set_onMeshLoaded}
, onTextureLoaded{this, &GLTFFileLoader::set_onTextureLoaded}
, onMaterialLoaded{this, &GLTFFileLoader::set_onMaterialLoaded}
Expand Down Expand Up @@ -83,8 +82,6 @@ GLTFFileLoader::GLTFFileLoader()
return String::contains(data, "scene") && String::contains(data, "node");
};

preprocessUrlAsync = [](const std::string& url) -> std::string { return url; };

_log = [this](const std::string& message) { _logDisabled(message); };

_startPerformanceCounter
Expand Down Expand Up @@ -246,8 +243,6 @@ void GLTFFileLoader::dispose(bool /*doNotRecurse*/, bool /*disposeMaterialAndTex

void GLTFFileLoader::_clear()
{
preprocessUrlAsync = nullptr;

onMeshLoadedObservable.clear();
onTextureLoadedObservable.clear();
onMaterialLoadedObservable.clear();
Expand All @@ -263,15 +258,15 @@ ImportedMeshes GLTFFileLoader::importMeshAsync(
const std::string& fileName)
{
// ASYNC_FIXME: GLTF loading is decidely incompatible with async loading
BABYLON::asio::set_HACK_DISABLE_ASYNC(true);
//BABYLON::asio::set_HACK_DISABLE_ASYNC(true);

auto loaderData = _parseAsync(scene, data, rootUrl, fileName);
_log(String::printf("Loading %s", fileName.c_str()));
_loader = _getLoader(loaderData);
return _loader->importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fileName);

// ASYNC_FIXME: GLTF loading is decidely incompatible with async loading
BABYLON::asio::set_HACK_DISABLE_ASYNC(false);
//BABYLON::asio::set_HACK_DISABLE_ASYNC(false);
}

void GLTFFileLoader::loadAsync(
Expand Down

0 comments on commit feee188

Please sign in to comment.