gltfio: material variants can now be applied to FilamentInstance.

This commit is contained in:
Philip Rideout
2022-03-25 14:02:10 -07:00
parent d879bbc80a
commit a82b3bedb2
12 changed files with 115 additions and 35 deletions

View File

@@ -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);
}

View File

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

View File

@@ -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);
}

View File

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

View File

@@ -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.
*

View File

@@ -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;

View File

@@ -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<VariantMapping> mappings;
};
filament::Engine* mEngine;
utils::NameComponentManager* mNameManager;
utils::EntityManager* mEntityManager;

View File

@@ -19,36 +19,53 @@
#include <gltfio/FilamentInstance.h>
#include <utils/CString.h>
#include <utils/Entity.h>
#include <utils/FixedCapacityVector.h>
#include <math/mat4.h>
#include <tsl/robin_map.h>
#include <string>
#include <vector>
#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<filament::math::mat4f> inverseBindMatrices;
std::vector<utils::Entity> joints;
std::vector<utils::Entity> targets;
};
struct VariantMapping {
utils::Entity renderable;
size_t primitiveIndex;
filament::MaterialInstance* material;
};
struct Variant {
utils::CString name;
std::vector<VariantMapping> mappings;
};
using SkinVector = std::vector<Skin>;
using NodeMap = tsl::robin_map<const cgltf_node*, utils::Entity>;
struct FFilamentInstance : public FilamentInstance {
std::vector<utils::Entity> entities;
utils::FixedCapacityVector<Variant> 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)

View File

@@ -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();
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -1872,6 +1872,7 @@ class_<FilamentInstance>("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.