gltfio: material variants can now be applied to FilamentInstance.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user