Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gltfio: enable extended implementation #7776

Merged
merged 2 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions libs/gltfio/include/gltfio/AssetLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ namespace filament::gltfio {

class NodeManager;

// Use this struct to enable mikktspace-based tangent-space computation.
/**
* \struct AssetConfigurationExtended AssetLoader.h gltfio/AssetLoader.h
* \brief extends struct AssetConfiguration
* Useful if client needs mikktspace tangent space computation.
* NOTE: Android, iOS, Web are not supported. And only disk-local glTF resources are supported.
*/
struct AssetConfigurationExtended {
//! Optional The same parameter as provided to \struct ResourceConfiguration ResourceLoader.h
//! gltfio/ResourceLoader.h
char const* gltfPath;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this is realted to mikkt space? Why would setting a glTF path in a separate struct change the tangent space computations? This doesn't seem like a user-friendly API (and does it mean it only works with local files?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, deally, we'd just add support for mikktspace without changing the existing API, but that's not possible with how AssetLoader/ResourceLoader is structured. I opted to introduce minimal changes to existing API to address the opened bugs. So that any use case with the right criteria (desktop and only disk resources) can make use of it. But, the real, long-term solution should be a new loader API (separate from AssetLoader/ResourceLoader) that would better fit the assumption of running a remeshing algo like mikktspace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments in the header and in the commit to be more explicit about this approach's limitation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah.. we should probably have a bug open then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, I filed #7782 for tracking.


//! Client can use this method to check if the extended implementation is supported on their
//! platform or not.
static bool isSupported();
};

/**
* \struct AssetConfiguration AssetLoader.h gltfio/AssetLoader.h
* \brief Construction parameters for AssetLoader.
Expand All @@ -62,6 +79,10 @@ struct AssetConfiguration {

//! Optional default node name for anonymous nodes
char* defaultNodeName = nullptr;

//! Optional to enable mikktspace tangents. Lifetime of struct only needs to be maintained for
// the duration of the constructor of AssetLoader.
AssetConfigurationExtended* ext = nullptr;
poweifeng marked this conversation as resolved.
Show resolved Hide resolved
};

/**
Expand Down
79 changes: 66 additions & 13 deletions libs/gltfio/src/AssetLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "FTrsTransformManager.h"
#include "GltfEnums.h"
#include "Utility.h"
#include "extended/AssetLoaderExtended.h"

#include <filament/Box.h>
#include <filament/BufferObject.h>
Expand Down Expand Up @@ -57,6 +58,8 @@

#include "downcast.h"

#include <memory>

using namespace filament;
using namespace filament::math;
using namespace utils;
Expand Down Expand Up @@ -202,14 +205,21 @@ class MaterialInstanceCache {
};

struct FAssetLoader : public AssetLoader {
FAssetLoader(const AssetConfiguration& config) :
FAssetLoader(AssetConfiguration const& config) :
mEntityManager(config.entities ? *config.entities : EntityManager::get()),
mRenderableManager(config.engine->getRenderableManager()),
mNameManager(config.names),
mTransformManager(config.engine->getTransformManager()),
mMaterials(*config.materials),
mEngine(*config.engine),
mDefaultNodeName(config.defaultNodeName) {}
mDefaultNodeName(config.defaultNodeName) {
if (config.ext) {
ASSERT_PRECONDITION(AssetConfigurationExtended::isSupported(),
"Extend asset loading is not supported on this platform");
mLoaderExtended = std::make_unique<AssetLoaderExtended>(
*config.ext, config.engine, mMaterials);
}
}

FFilamentAsset* createAsset(const uint8_t* bytes, uint32_t nbytes);
FFilamentAsset* createInstancedAsset(const uint8_t* bytes, uint32_t numBytes,
Expand Down Expand Up @@ -292,6 +302,9 @@ struct FAssetLoader : public AssetLoader {

// Weak reference to the largest dummy buffer so far in the current loading phase.
BufferObject* mDummyBufferObject = nullptr;

public:
std::unique_ptr<AssetLoaderExtended> mLoaderExtended;
};

FILAMENT_DOWNCAST(AssetLoader)
Expand Down Expand Up @@ -422,7 +435,7 @@ FFilamentAsset* FAssetLoader::createRootAsset(const cgltf_data* srcAsset) {

mDummyBufferObject = nullptr;
FFilamentAsset* fAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager,
&mNodeManager, &mTrsTransformManager, srcAsset);
&mNodeManager, &mTrsTransformManager, srcAsset, (bool) mLoaderExtended);

// It is not an error for a glTF file to have zero scenes.
fAsset->mScenes.clear();
Expand Down Expand Up @@ -620,12 +633,41 @@ void FAssetLoader::createPrimitives(const cgltf_node* node, const char* name,

for (cgltf_size index = 0, n = mesh->primitives_count; index < n; ++index) {
Primitive& outputPrim = prims[index];
const cgltf_primitive& inputPrim = mesh->primitives[index];

// Create a Filament VertexBuffer and IndexBuffer for this prim if we haven't already.
if (!outputPrim.vertices && !createPrimitive(inputPrim, name, &outputPrim, fAsset)) {
mError = true;
return;
cgltf_primitive& inputPrim = mesh->primitives[index];

if (!outputPrim.vertices) {
if (mLoaderExtended) {
auto& resourceInfo = std::get<FFilamentAsset::ResourceInfoExtended>(fAsset->mResourceInfo);
resourceInfo.uriDataCache = mLoaderExtended->getUriDataCache();
AssetLoaderExtended::Input input{
.gltf = gltf,
.prim = &inputPrim,
.name = name,
.dracoCache = &fAsset->mSourceAsset->dracoCache,
.material = getMaterial(gltf, inputPrim.material, &outputPrim.uvmap,
utility::primitiveHasVertexColor(&inputPrim)),
};

mError = !mLoaderExtended->createPrimitive(&input, &outputPrim, resourceInfo.slots);
if (!mError) {
if (outputPrim.vertices) {
fAsset->mVertexBuffers.push_back(outputPrim.vertices);
}
if (outputPrim.indices) {
fAsset->mIndexBuffers.push_back(outputPrim.indices);
}
if (outputPrim.targets) {
fAsset->mMorphTargetBuffers.push_back(outputPrim.targets);
}
}
} else {
// Create a Filament VertexBuffer and IndexBuffer for this prim if we haven't
// already.
mError = !createPrimitive(inputPrim, name, &outputPrim, fAsset);
}
if (mError) {
return;
}
}

// Expand the object-space bounding box.
Expand Down Expand Up @@ -777,6 +819,8 @@ void FAssetLoader::createMaterialVariants(const cgltf_mesh* mesh, Entity entity,
bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* name,
Primitive* outPrim, FFilamentAsset* fAsset) {

using BufferSlot = FFilamentAsset::ResourceInfo::BufferSlot;

Material* material = getMaterial(fAsset->mSourceAsset->hierarchy,
inPrim.material, &outPrim->uvmap, primitiveHasVertexColor(inPrim));
AttributeBitset requiredAttributes = material->getRequiredAttributes();
Expand All @@ -787,8 +831,8 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na
// request from Google.

// Create a little lambda that appends to the asset's vertex buffer slots.
auto slots = &fAsset->mBufferSlots;
auto addBufferSlot = [slots](BufferSlot entry) {
auto slots = &std::get<FFilamentAsset::ResourceInfo>(fAsset->mResourceInfo).mBufferSlots;
auto addBufferSlot = [slots](FFilamentAsset::ResourceInfo::BufferSlot entry) {
slots->push_back(entry);
};

Expand All @@ -807,7 +851,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na
.bufferType(indexType)
.build(mEngine);

BufferSlot slot = { accessor };
FFilamentAsset::ResourceInfo::BufferSlot slot = { accessor };
slot.indexBuffer = indices;
addBufferSlot(slot);
} else if (inPrim.attributes_count > 0) {
Expand Down Expand Up @@ -1049,7 +1093,8 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na

outPrim->indices = indices;
outPrim->vertices = vertices;
fAsset->mPrimitives.push_back({&inPrim, vertices});
auto& primitives = std::get<FFilamentAsset::ResourceInfo>(fAsset->mResourceInfo).mPrimitives;
primitives.push_back({&inPrim, vertices});
fAsset->mVertexBuffers.push_back(vertices);

for (size_t i = firstSlot; i < slots->size(); ++i) {
Expand Down Expand Up @@ -1549,6 +1594,14 @@ void FAssetLoader::importSkins(FFilamentInstance* instance, const cgltf_data* gl
}
}

bool AssetConfigurationExtended::isSupported() {
#if defined(__ANDROID__) || defined(IOS) || defined(__EMSCRIPTEN__)
return false;
#else
return true;
#endif
}

AssetLoader* AssetLoader::create(const AssetConfiguration& config) {
return new FAssetLoader(config);
}
Expand Down
75 changes: 61 additions & 14 deletions libs/gltfio/src/FFilamentAsset.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

#include <tsl/htrie_map.h>

#include <variant>
#include <vector>

#ifdef NDEBUG
Expand All @@ -74,16 +75,6 @@ namespace filament::gltfio {

struct Wireframe;

// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer().
struct BufferSlot {
const cgltf_accessor* accessor;
cgltf_attribute_type attribute;
int bufferIndex; // for vertex buffer and morph target buffer only
VertexBuffer* vertexBuffer;
IndexBuffer* indexBuffer;
MorphTargetBuffer* morphTargetBuffer;
};

// Stores a connection between Texture and MaterialInstance; consumed by resource loader so that it
// can call "setParameter" on the given MaterialInstance after the Texture has been created.
// Since material instances are not typically shared between FilamentInstance, the slots are a
Expand All @@ -110,14 +101,24 @@ struct Primitive {
using MeshCache = utils::FixedCapacityVector<utils::FixedCapacityVector<Primitive>>;

struct FFilamentAsset : public FilamentAsset {
struct ResourceInfo;
struct ResourceInfoExtended;

FFilamentAsset(Engine* engine, utils::NameComponentManager* names,
utils::EntityManager* entityManager, NodeManager* nodeManager,
TrsTransformManager* trsTransformManager, const cgltf_data* srcAsset) :
TrsTransformManager* trsTransformManager, const cgltf_data* srcAsset,
bool useExtendedAlgo) :
mEngine(engine), mNameManager(names), mEntityManager(entityManager),
mNodeManager(nodeManager), mTrsTransformManager(trsTransformManager),
mSourceAsset(new SourceAsset {(cgltf_data*)srcAsset}),
mTextures(srcAsset->textures_count),
mMeshCache(srcAsset->meshes_count) {}
mMeshCache(srcAsset->meshes_count) {
if (!useExtendedAlgo) {
mResourceInfo = ResourceInfo{};
} else {
mResourceInfo = ResourceInfoExtended{};
}
}

~FFilamentAsset();

Expand Down Expand Up @@ -228,6 +229,10 @@ struct FFilamentAsset : public FilamentAsset {
mDetachedFilamentComponents = true;
}

bool isUsingExtendedAlgorithm() {
return std::holds_alternative<ResourceInfoExtended>(mResourceInfo);
}

// end public API

// If a Filament Texture for the given args already exists, calls setParameter() and returns
Expand Down Expand Up @@ -315,8 +320,50 @@ struct FFilamentAsset : public FilamentAsset {
MeshCache mMeshCache;

// Asset information that is produced by AssetLoader and consumed by ResourceLoader:
std::vector<BufferSlot> mBufferSlots;
std::vector<std::pair<const cgltf_primitive*, VertexBuffer*> > mPrimitives;
struct ResourceInfo {
// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer().
struct BufferSlot {
const cgltf_accessor* accessor;
cgltf_attribute_type attribute;
int bufferIndex;// for vertex buffer and morph target buffer only
VertexBuffer* vertexBuffer;
IndexBuffer* indexBuffer;
MorphTargetBuffer* morphTargetBuffer;
};

std::vector<BufferSlot> mBufferSlots;
std::vector<std::pair<const cgltf_primitive*, VertexBuffer*>> mPrimitives;
};
struct ResourceInfoExtended {
// Used to denote a generated buffer. Set as `index in `CgltfAttribute`.
static constexpr int const GENERATED_0_INDEX = -1;
static constexpr int const GENERATED_1_INDEX = -2;

struct BufferSlot {
VertexBuffer* vertices = nullptr;
IndexBuffer* indices = nullptr;
MorphTargetBuffer* target = nullptr;
int slot = -1;
size_t sizeInBytes = 0;

void* data = nullptr;

// MorphTarget-only data;
struct {
short4* tbn = nullptr;
float3* positions = nullptr;
} targetData;
};

std::vector<BufferSlot> slots;

// This is to workaround the fact that the original ResourceLoader owns the UriDataCache. In
// the extended implementation, we create it in AssetLoader. We pass it along to
// ResourceLoader here.
UriDataCacheHandle uriDataCache;
};

std::variant<ResourceInfo, ResourceInfoExtended> mResourceInfo;
};

FILAMENT_DOWNCAST(FilamentAsset)
Expand Down
Loading
Loading