From a82b3bedb2a6ade2ba1b808e2956708bb7113edd Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Fri, 25 Mar 2022 14:02:10 -0700 Subject: [PATCH] gltfio: material variants can now be applied to FilamentInstance. --- .../src/main/cpp/FilamentInstance.cpp | 7 +++ .../filament/gltfio/FilamentAsset.java | 3 +- .../filament/gltfio/FilamentInstance.java | 11 ++++ libs/gltfio/include/gltfio/FilamentAsset.h | 5 +- libs/gltfio/include/gltfio/FilamentInstance.h | 7 +++ libs/gltfio/src/AssetLoader.cpp | 58 +++++++++++++------ libs/gltfio/src/FFilamentAsset.h | 11 ---- libs/gltfio/src/FFilamentInstance.h | 22 ++++++- libs/gltfio/src/FilamentInstance.cpp | 16 +++++ libs/gltfio/src/ResourceLoader.cpp | 2 +- samples/gltf_instances.cpp | 7 +++ web/filament-js/jsbindings.cpp | 1 + 12 files changed, 115 insertions(+), 35 deletions(-) diff --git a/android/gltfio-android/src/main/cpp/FilamentInstance.cpp b/android/gltfio-android/src/main/cpp/FilamentInstance.cpp index 62a61331fc..45ea5ec7cb 100644 --- a/android/gltfio-android/src/main/cpp/FilamentInstance.cpp +++ b/android/gltfio-android/src/main/cpp/FilamentInstance.cpp @@ -54,3 +54,10 @@ Java_com_google_android_filament_gltfio_FilamentInstance_nGetAnimator(JNIEnv* , FilamentInstance* instance = (FilamentInstance*) nativeInstance; return (jlong) instance->getAnimator(); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nApplyMaterialVariant(JNIEnv* env, jclass, + jlong nativeInstance, jint variantIndex) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + instance->applyMaterialVariant(variantIndex); +} diff --git a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java index a0fb11f49e..1b705f8a6d 100644 --- a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java +++ b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java @@ -243,7 +243,7 @@ public class FilamentAsset { return names; } - /* + /** * Applies the given material variant to all primitives that it affects. * * This is efficient because it merely swaps around persistent MaterialInstances. If you change @@ -251,6 +251,7 @@ public class FilamentAsset { * after you re-apply that variant. * * If the asset is instanced, this affects all instances in the same way. + * To set the variant on an individual instance, use FilamentInstance#applyMaterialVariant. * * Ignored if variantIndex is out of bounds. */ diff --git a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentInstance.java b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentInstance.java index accc49790d..be00ed8e05 100644 --- a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentInstance.java +++ b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentInstance.java @@ -16,6 +16,7 @@ package com.google.android.filament.gltfio; +import androidx.annotation.IntRange; import androidx.annotation.NonNull; import com.google.android.filament.Entity; @@ -84,8 +85,18 @@ public class FilamentInstance { return mAnimator; } + /** + * Applies the given material variant to all primitives in this instance. + * + * Ignored if variantIndex is out of bounds. + */ + void applyMaterialVariant(@IntRange(from = 0) int variantIndex) { + nApplyMaterialVariant(mNativeObject, variantIndex); + } + private static native int nGetRoot(long nativeAsset); private static native int nGetEntityCount(long nativeAsset); private static native void nGetEntities(long nativeAsset, int[] result); private static native long nGetAnimator(long nativeAsset); + private static native void nApplyMaterialVariant(long nativeAsset, int variantIndex); } diff --git a/libs/gltfio/include/gltfio/FilamentAsset.h b/libs/gltfio/include/gltfio/FilamentAsset.h index 8f73e5acbb..6078aa6cff 100644 --- a/libs/gltfio/include/gltfio/FilamentAsset.h +++ b/libs/gltfio/include/gltfio/FilamentAsset.h @@ -200,8 +200,6 @@ public: * Must be called after loadResources or asyncBeginLoad, otherwise returns null. * If the asset is instanced, this returns a "primary" animator that controls all instances. * To animate each instance individually, use \see FilamentInstance. - * - * TODO(prideout): this should return a ref not a ptr */ Animator* getAnimator() const noexcept; @@ -245,7 +243,7 @@ public: */ const char* getMaterialVariantName(size_t variantIndex) const noexcept; - /* + /** * Applies the given material variant to all primitives that it affects. * * This is efficient because it merely swaps around persistent MaterialInstances. If you change @@ -253,6 +251,7 @@ public: * after you re-apply that variant. * * If the asset is instanced, this affects all instances in the same way. + * To set the variant on an individual instance, use FilamentInstance::applyMaterialVariant. * * Ignored if variantIndex is out of bounds. */ diff --git a/libs/gltfio/include/gltfio/FilamentInstance.h b/libs/gltfio/include/gltfio/FilamentInstance.h index 8d462eab56..1f1509e38b 100644 --- a/libs/gltfio/include/gltfio/FilamentInstance.h +++ b/libs/gltfio/include/gltfio/FilamentInstance.h @@ -56,6 +56,13 @@ public: /** Gets the transform root for the instance, which has no matching glTF node. */ utils::Entity getRoot() const noexcept; + /** + * Applies the given material variant to all primitives in this instance. + * + * Ignored if variantIndex is out of bounds. + */ + void applyMaterialVariant(size_t variantIndex) noexcept; + /** * Returns the animation engine for the instance. * diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 93db7cbe52..34103d646a 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -140,6 +140,8 @@ struct FAssetLoader : public AssetLoader { void addTextureBinding(MaterialInstance* materialInstance, const char* parameterName, const cgltf_texture* srcTexture, bool srgb); bool primitiveHasVertexColor(const cgltf_primitive* inPrim) const; + void createMaterialVariants(const cgltf_data* srcAsset, const cgltf_mesh* mesh, Entity entity, + FFilamentInstance* instance); static LightManager::Type getLightType(const cgltf_light_type type); @@ -355,6 +357,12 @@ FFilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary, instance->owner = primary; primary->mInstances.push_back(instance); + // Check if the asset has variants. + instance->variants.reserve(srcAsset->variants_count); + for (cgltf_size i = 0, len = srcAsset->variants_count; i < len; ++i) { + instance->variants.push_back({CString(srcAsset->variants[i].name)}); + } + // For each scene root, recursively create all entities. for (cgltf_size i = 0, len = scene->nodes_count; i < len; ++i) { cgltf_node** nodes = scene->nodes; @@ -414,6 +422,9 @@ void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* no // If the node has a mesh, then create a renderable component. if (node->mesh) { createRenderable(srcAsset, node, entity, name); + if (srcAsset->variants_count > 0) { + createMaterialVariants(srcAsset, node->mesh, entity, instance); + } } if (node->light && enableLight) { @@ -489,23 +500,6 @@ void FAssetLoader::createRenderable(const cgltf_data* srcAsset, const cgltf_node continue; } - // Add variants if they exist. - for (size_t i = 0, n = inputPrim->mappings_count; i < n; i++) { - const size_t variantIndex = inputPrim->mappings[i].variant; - const cgltf_material* material = inputPrim->mappings[i].material; - if (variantIndex >= mResult->mVariants.size()) { - mError = true; - break; - } - MaterialInstance* mi = createMaterialInstance(srcAsset, material, &uvmap, hasVertexColor); - if (!mi) { - mError = true; - break; - } - mResult->mDependencyGraph.addEdge(entity, mi); - mResult->mVariants[variantIndex].mappings.push_back({entity, index, mi}); - } - // Expand the object-space bounding box. aabb.min = min(outputPrim->aabb.min, aabb.min); aabb.max = max(outputPrim->aabb.max, aabb.max); @@ -572,6 +566,36 @@ void FAssetLoader::createRenderable(const cgltf_data* srcAsset, const cgltf_node } } +void FAssetLoader::createMaterialVariants(const cgltf_data* srcAsset, const cgltf_mesh* mesh, + Entity entity, FFilamentInstance* instance) { + UvMap uvmap {}; + for (cgltf_size prim = 0, n = mesh->primitives_count; prim < n; ++prim) { + const cgltf_primitive* srcPrim = &mesh->primitives[prim]; + for (size_t i = 0, m = srcPrim->mappings_count; i < m; i++) { + const size_t variantIndex = srcPrim->mappings[i].variant; + const cgltf_material* material = srcPrim->mappings[i].material; + assert_invariant(variantIndex < mResult->mVariants.size()); + if (variantIndex >= mResult->mVariants.size()) { + mError = true; + break; + } + bool hasVertexColor = primitiveHasVertexColor(srcPrim); + MaterialInstance* mi = createMaterialInstance(srcAsset, material, &uvmap, hasVertexColor); + assert_invariant(mi); + if (!mi) { + mError = true; + break; + } + mResult->mDependencyGraph.addEdge(entity, mi); + if (instance) { + instance->variants[variantIndex].mappings.push_back({entity, prim, mi}); + } else { + mResult->mVariants[variantIndex].mappings.push_back({entity, prim, mi}); + } + } + } +} + bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* outPrim, const UvMap& uvmap, const char* name, MaterialInstance* mi) { outPrim->uvmap = uvmap; diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index b8ee8a0db2..1e528cc26d 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -246,17 +246,6 @@ struct FFilamentAsset : public FilamentAsset { void createAnimators(); - struct VariantMapping { - utils::Entity renderable; - size_t primitiveIndex; - filament::MaterialInstance* material; - }; - - struct Variant { - utils::CString name; - std::vector mappings; - }; - filament::Engine* mEngine; utils::NameComponentManager* mNameManager; utils::EntityManager* mEntityManager; diff --git a/libs/gltfio/src/FFilamentInstance.h b/libs/gltfio/src/FFilamentInstance.h index c565786723..5a1ec35efa 100644 --- a/libs/gltfio/src/FFilamentInstance.h +++ b/libs/gltfio/src/FFilamentInstance.h @@ -19,36 +19,53 @@ #include +#include #include +#include #include #include -#include #include #include "upcast.h" struct cgltf_node; +namespace filament { + class MaterialInstance; +} + namespace gltfio { struct FFilamentAsset; class Animator; struct Skin { - std::string name; + utils::CString name; std::vector inverseBindMatrices; std::vector joints; std::vector targets; }; +struct VariantMapping { + utils::Entity renderable; + size_t primitiveIndex; + filament::MaterialInstance* material; +}; + +struct Variant { + utils::CString name; + std::vector mappings; +}; + using SkinVector = std::vector; using NodeMap = tsl::robin_map; struct FFilamentInstance : public FilamentInstance { std::vector entities; + utils::FixedCapacityVector variants; utils::Entity root; Animator* animator; FFilamentAsset* owner; @@ -60,6 +77,7 @@ struct FFilamentInstance : public FilamentInstance { const char* getSkinNameAt(size_t skinIndex) const noexcept; size_t getJointCountAt(size_t skinIndex) const noexcept; const utils::Entity* getJointsAt(size_t skinIndex) const noexcept; + void applyMaterialVariant(size_t variantIndex) noexcept; }; FILAMENT_UPCAST(FilamentInstance) diff --git a/libs/gltfio/src/FilamentInstance.cpp b/libs/gltfio/src/FilamentInstance.cpp index ff9c984f8a..4b4ca7d880 100644 --- a/libs/gltfio/src/FilamentInstance.cpp +++ b/libs/gltfio/src/FilamentInstance.cpp @@ -61,6 +61,18 @@ const utils::Entity* FFilamentInstance::getJointsAt(size_t skinIndex) const noex return skins[skinIndex].joints.data(); } +void FFilamentInstance::applyMaterialVariant(size_t variantIndex) noexcept { + if (variantIndex >= variants.size()) { + return; + } + const auto& mappings = variants[variantIndex].mappings; + RenderableManager& rm = owner->mEngine->getRenderableManager(); + for (const auto& mapping : mappings) { + auto renderable = rm.getInstance(mapping.renderable); + rm.setMaterialInstanceAt(renderable, mapping.primitiveIndex, mapping.material); + } +} + FilamentAsset* FilamentInstance::getAsset() const noexcept { return upcast(this)->owner; } @@ -78,6 +90,10 @@ Entity FilamentInstance::getRoot() const noexcept { return upcast(this)->root; } +void FilamentInstance::applyMaterialVariant(size_t variantIndex) noexcept { + return upcast(this)->applyMaterialVariant(variantIndex); +} + Animator* FilamentInstance::getAnimator() noexcept { return upcast(this)->getAnimator(); } diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 80a8535237..5fbe74e6cf 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -158,7 +158,7 @@ void importSkins(const cgltf_data* gltf, const NodeMap& nodeMap, SkinVector& dst Skin& dstSkin = dstSkins[i]; const cgltf_skin& srcSkin = gltf->skins[i]; if (srcSkin.name) { - dstSkin.name = srcSkin.name; + dstSkin.name = CString(srcSkin.name); } // Build a list of transformables for this skin, one for each joint. diff --git a/samples/gltf_instances.cpp b/samples/gltf_instances.cpp index 03b96f80b6..37f55398a3 100644 --- a/samples/gltf_instances.cpp +++ b/samples/gltf_instances.cpp @@ -283,6 +283,13 @@ int main(int argc, char** argv) { static double previous = 0.0; if (now - previous > 1.0 && app.asset->getAssetInstanceCount() < 100) { FilamentInstance* instance = app.loader->createInstance(app.asset); + + // If the asset has variants, rotate through each variant. + const size_t variantCount = app.asset->getMaterialVariantCount(); + if (variantCount > 1) { + instance->applyMaterialVariant(app.instances.size() % variantCount); + } + app.instances.push_back(instance); arrangeIntoCircle(); previous = now; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 3d38d1f379..2eec57ce16 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -1872,6 +1872,7 @@ class_("gltfio$FilamentInstance") return EntityVector(ptr, ptr + self->getEntityCount()); }), allow_raw_pointers()) .function("getRoot", &FilamentInstance::getRoot) + .function("applyMaterialVariant", &FilamentInstance::applyMaterialVariant) .function("getAnimator", &FilamentInstance::getAnimator, allow_raw_pointers()); // This little wrapper exists to get around RTTI requirements in embind.