gltfio: support material extras

This commit is contained in:
Alexey Pelykh
2021-07-28 18:09:34 +02:00
committed by Philip Rideout
parent 785f7de25d
commit 45054eece9
7 changed files with 52 additions and 30 deletions

View File

@@ -69,7 +69,7 @@ public:
jclass providerClass = env->GetObjectClass(provider);
mCreateMaterialInstance = env->GetMethodID(providerClass, "createMaterialInstance",
"(L" JAVA_MATERIAL_KEY ";[ILjava/lang/String;)Lcom/google/android/filament/MaterialInstance;");
"(L" JAVA_MATERIAL_KEY ";[ILjava/lang/String;Ljava/lang/String;)Lcom/google/android/filament/MaterialInstance;");
assert_invariant(mCreateMaterialInstance);
mGetMaterials = env->GetMethodID(providerClass, "getMaterials",
@@ -89,7 +89,7 @@ public:
delete mPreviousMaterials;
}
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, const char* label) override {
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, const char* label, const char* extras) override {
// Create a Java object for the material key and copy the native fields into it.
jobject javaKey = mEnv->NewObject(mMaterialKeyClass, mMaterialKeyConstructor);
@@ -99,12 +99,15 @@ public:
// Convert the optional label into a Java string.
jstring stringLabel = label ? mEnv->NewStringUTF(label) : nullptr;
// Convert the optional extras into a Java string.
jstring stringExtras = extras ? mEnv->NewStringUTF(extras) : nullptr;
// Allocate space for the output argument.
jintArray uvMapArray = mEnv->NewIntArray(8);
// Call the Java-based material provider.
jobject materialInstance = mEnv->CallObjectMethod(mJavaProvider, mCreateMaterialInstance,
javaKey, uvMapArray, stringLabel);
javaKey, uvMapArray, stringLabel, stringExtras);
// Copy the UvMap results from the JVM array into the native array.
if (uvmap) {
@@ -125,6 +128,10 @@ public:
mEnv->DeleteLocalRef(stringLabel);
}
if (stringExtras) {
mEnv->DeleteLocalRef(stringExtras);
}
if (materialInstance == nullptr) {
return nullptr;
}

View File

@@ -99,9 +99,10 @@ public interface MaterialProvider {
* @param uvmap Output argument that gets populated with a small table that maps from a glTF uv
* index to a Filament uv index (0 = UNUSED, 1 = UV0, 2 = UV1).
* @param label Optional tag that is not a part of the cache key.
* @param extras Optional extras as stringified JSON (not a part of the cache key).
*/
public @Nullable MaterialInstance createMaterialInstance(MaterialKey config,
@NonNull @Size(min = 8) int[] uvmap, @Nullable String label);
@NonNull @Size(min = 8) int[] uvmap, @Nullable String label, @Nullable String extras);
/**
* Creates and returns an array containing all cached materials.

View File

@@ -56,7 +56,7 @@ public class UbershaderLoader implements MaterialProvider {
}
public @Nullable MaterialInstance createMaterialInstance(MaterialKey config,
@NonNull @Size(min = 8) int[] uvmap, @Nullable String label) {
@NonNull @Size(min = 8) int[] uvmap, @Nullable String label, @Nullable String extras) {
long nativeMaterialInstance = nCreateMaterialInstance(mNativeObject, config, uvmap, label);
return nativeMaterialInstance == 0 ? null : new MaterialInstance(null, nativeMaterialInstance);
}

View File

@@ -133,9 +133,10 @@ public:
* @param uvmap Output argument that gets populated with a small table that maps from a glTF uv
* index to a Filament uv index.
* @param label Optional tag that is not a part of the cache key.
* @param extras Optional extras as stringified JSON (not a part of the cache key). Don't store the pointer.
*/
virtual filament::MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label = "material") = 0;
const char* label = "material", const char* extras = nullptr) = 0;
/**
* Gets a weak reference to the array of cached materials.

View File

@@ -125,16 +125,18 @@ struct FAssetLoader : public AssetLoader {
}
void createAsset(const cgltf_data* srcAsset, size_t numInstances);
FFilamentInstance* createInstance(FFilamentAsset* primary, const cgltf_scene* scene);
void createEntity(const cgltf_node* node, Entity parent, bool enableLight,
FFilamentInstance* instance);
void createRenderable(const cgltf_node* node, Entity entity, const char* name);
FFilamentInstance* createInstance(FFilamentAsset* primary, const cgltf_data* srcAsset,
const cgltf_scene* scene);
void createEntity(const cgltf_data* srcAsset, const cgltf_node* node, Entity parent,
bool enableLight, FFilamentInstance* instance);
void createRenderable(const cgltf_data* srcAsset, const cgltf_node* node, Entity entity,
const char* name);
bool createPrimitive(const cgltf_primitive* inPrim, Primitive* outPrim, const UvMap& uvmap,
const char* name, MaterialInstance* mi);
void createLight(const cgltf_light* light, Entity entity);
void createCamera(const cgltf_camera* camera, Entity entity);
MaterialInstance* createMaterialInstance(const cgltf_material* inputMat, UvMap* uvmap,
bool vertexColor);
MaterialInstance* createMaterialInstance(const cgltf_data* srcAsset,
const cgltf_material* inputMat, UvMap* uvmap, bool vertexColor);
void addTextureBinding(MaterialInstance* materialInstance, const char* parameterName,
const cgltf_texture* srcTexture, bool srgb);
bool primitiveHasVertexColor(const cgltf_primitive* inPrim) const;
@@ -241,7 +243,7 @@ FilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary) {
slog.e << "There is no scene in the asset." << io::endl;
return nullptr;
}
FFilamentInstance* instance = createInstance(primary, scene);
FFilamentInstance* instance = createInstance(primary, srcAsset, scene);
// Import the skin data. This is normally done by ResourceLoader but dynamically created
// instances are a bit special.
@@ -291,14 +293,14 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
// 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;
createEntity(nodes[i], mResult->mRoot, true, nullptr);
createEntity(srcAsset, nodes[i], mResult->mRoot, true, nullptr);
}
} else {
// Create a separate entity hierarchy for each instance. Note that MeshCache (vertex
// buffers and index buffers) and MatInstanceCache (materials and textures) help avoid
// needless duplication of resources.
for (size_t index = 0; index < numInstances; ++index) {
if (createInstance(mResult, scene) == nullptr) {
if (createInstance(mResult, srcAsset, scene) == nullptr) {
mError = true;
break;
}
@@ -331,7 +333,8 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
}
}
FFilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary, const cgltf_scene* scene) {
FFilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary,
const cgltf_data* srcAsset, const cgltf_scene* scene) {
auto rootTransform = mTransformManager.getInstance(primary->mRoot);
Entity instanceRoot = mEntityManager.create();
mTransformManager.create(instanceRoot, rootTransform);
@@ -347,13 +350,13 @@ FFilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary, const c
// 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;
createEntity(nodes[i], instanceRoot, false, instance);
createEntity(srcAsset, nodes[i], instanceRoot, false, instance);
}
return instance;
}
void FAssetLoader::createEntity(const cgltf_node* node, Entity parent, bool enableLight,
FFilamentInstance* instance) {
void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* node, Entity parent,
bool enableLight, FFilamentInstance* instance) {
Entity entity = mEntityManager.create();
// Always create a transform component to reflect the original hierarchy.
@@ -402,7 +405,7 @@ void FAssetLoader::createEntity(const cgltf_node* node, Entity parent, bool enab
// If the node has a mesh, then create a renderable component.
if (node->mesh) {
createRenderable(node, entity, name);
createRenderable(srcAsset, node, entity, name);
}
if (node->light && enableLight) {
@@ -414,11 +417,12 @@ void FAssetLoader::createEntity(const cgltf_node* node, Entity parent, bool enab
}
for (cgltf_size i = 0, len = node->children_count; i < len; ++i) {
createEntity(node->children[i], entity, enableLight, instance);
createEntity(srcAsset, node->children[i], entity, enableLight, instance);
}
}
void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const char* name) {
void FAssetLoader::createRenderable(const cgltf_data* srcAsset, const cgltf_node* node,
Entity entity, const char* name) {
const cgltf_mesh* mesh = node->mesh;
// Compute the transform relative to the root.
@@ -460,7 +464,8 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const
// Create a material instance for this primitive or fetch one from the cache.
UvMap uvmap {};
bool hasVertexColor = primitiveHasVertexColor(inputPrim);
MaterialInstance* mi = createMaterialInstance(inputPrim->material, &uvmap, hasVertexColor);
MaterialInstance* mi = createMaterialInstance(srcAsset, inputPrim->material, &uvmap,
hasVertexColor);
if (!mi) {
mError = true;
continue;
@@ -878,8 +883,8 @@ void FAssetLoader::createCamera(const cgltf_camera* camera, Entity entity) {
mResult->mCameraEntities.push_back(entity);
}
MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inputMat,
UvMap* uvmap, bool vertexColor) {
MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsset,
const cgltf_material* inputMat, UvMap* uvmap, bool vertexColor) {
intptr_t key = ((intptr_t) inputMat) ^ (vertexColor ? 1 : 0);
auto iter = mResult->mMatInstanceCache.find(key);
if (iter != mResult->mMatInstanceCache.end()) {
@@ -995,9 +1000,17 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp
break;
}
// Check if this material has an extras string.
CString extras;
const cgltf_size extras_size = inputMat->extras.end_offset - inputMat->extras.start_offset;
if (extras_size > 0) {
extras = CString(srcAsset->json + inputMat->extras.start_offset, extras_size);
}
// This not only creates a material instance, it modifies the material key according to our
// rendering constraints. For example, Filament only supports 2 sets of texture coordinates.
MaterialInstance* mi = mMaterials->createMaterialInstance(&matkey, uvmap, inputMat->name);
MaterialInstance* mi = mMaterials->createMaterialInstance(&matkey, uvmap, inputMat->name,
extras.c_str());
if (!mi) {
slog.e << "No material with the specified requirements exists." << io::endl;
return nullptr;

View File

@@ -37,7 +37,7 @@ public:
~MaterialGenerator() override;
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) override;
const char* label, const char* extras) override;
size_t getMaterialsCount() const noexcept override;
const Material* const* getMaterials() const noexcept override;
@@ -533,7 +533,7 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
}
MaterialInstance* MaterialGenerator::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) {
const char* label, const char* extras) {
constrainMaterial(config, uvmap);
auto iter = mCache.find(*config);
if (iter == mCache.end()) {

View File

@@ -45,7 +45,7 @@ public:
~UbershaderLoader() {}
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) override;
const char* label, const char* extras) override;
size_t getMaterialsCount() const noexcept override;
const Material* const* getMaterials() const noexcept override;
@@ -163,7 +163,7 @@ Material* UbershaderLoader::getMaterial(const MaterialKey& config) const {
}
MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
const char* label) {
const char* label, const char* extras) {
// Diagnostics are not supported with LOAD_UBERSHADERS, please use GENERATE_SHADERS instead.
if (config->enableDiagnostics) {
return nullptr;