Skip to content

Commit

Permalink
gltfio: enable extended implementation (#7776)
Browse files Browse the repository at this point in the history
This change will enable proper flat-shading and MikkTSpace.

Caveats:
 - Only for disk-local glTF resources
 - iOS, Web, Android do not work as of now

Fixes #6358, #7444
  • Loading branch information
poweifeng authored Apr 27, 2024
1 parent 8ab8525 commit 4c75e6e
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 97 deletions.
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;

//! 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;
};

/**
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

0 comments on commit 4c75e6e

Please sign in to comment.