gltfio: Allow Java / Kotlin clients to customize MaterialProvider.
This adds some significant JNI glue in order to make the Java API for gltfio materials much more consistent with C++, and create feature parity between the two languages. The motivation is to allow Java clients to provide their own ubershader-based solution or filamat-based solution. - The `MaterialProvider` Java class is now an interface. - The `UbershaderLoader` Java class is now a concrete implementation. - Users can now write their own provider class using Java / Kotlin. To streamline the amount of JNI work that occurs at run time, the MaterialProvider Java interface uses ints in a couple spots instead of enums. This sacrafices some readability for Java clients who wish to write their own provider, but makes the JNI much simpler. This replaces #3975.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,18 +23,151 @@
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/MaterialProvider.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include "common/NioUtils.h"
|
||||
|
||||
#include "MaterialKey.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace gltfio;
|
||||
using namespace utils;
|
||||
|
||||
class JavaMaterialProvider : public MaterialProvider {
|
||||
JNIEnv* const mEnv;
|
||||
const jobject mJavaProvider;
|
||||
mutable Material** mPreviousMaterials = nullptr;
|
||||
|
||||
jclass mMaterialKeyClass;
|
||||
|
||||
jmethodID mMaterialKeyConstructor;
|
||||
jmethodID mCreateMaterialInstance;
|
||||
jmethodID mGetMaterials;
|
||||
jmethodID mNeedsDummyData;
|
||||
jmethodID mDestroyMaterials;
|
||||
jmethodID mMaterialInstanceGetNativeObject;
|
||||
jmethodID mMaterialGetNativeObject;
|
||||
|
||||
public:
|
||||
JavaMaterialProvider(JNIEnv* env, jobject provider) : mEnv(env),
|
||||
mJavaProvider(env->NewGlobalRef(provider)) {
|
||||
mMaterialKeyClass = env->FindClass(JAVA_MATERIAL_KEY);
|
||||
mMaterialKeyClass = (jclass) env->NewGlobalRef(mMaterialKeyClass);
|
||||
assert_invariant(mMaterialKeyClass);
|
||||
|
||||
mMaterialKeyConstructor = env->GetMethodID(mMaterialKeyClass, "<init>", "()V");
|
||||
assert_invariant(mMaterialKeyConstructor);
|
||||
|
||||
jclass materialInstanceClass = env->FindClass("com/google/android/filament/MaterialInstance");
|
||||
mMaterialInstanceGetNativeObject = env->GetMethodID(materialInstanceClass, "getNativeObject", "()J");
|
||||
assert_invariant(mMaterialInstanceGetNativeObject);
|
||||
|
||||
jclass materialClass = env->FindClass("com/google/android/filament/Material");
|
||||
mMaterialGetNativeObject = env->GetMethodID(materialClass, "getNativeObject", "()J");
|
||||
assert_invariant(mMaterialGetNativeObject);
|
||||
|
||||
jclass providerClass = env->GetObjectClass(provider);
|
||||
|
||||
mCreateMaterialInstance = env->GetMethodID(providerClass, "createMaterialInstance",
|
||||
"(L" JAVA_MATERIAL_KEY ";[ILjava/lang/String;)Lcom/google/android/filament/MaterialInstance;");
|
||||
assert_invariant(mCreateMaterialInstance);
|
||||
|
||||
mGetMaterials = env->GetMethodID(providerClass, "getMaterials",
|
||||
"()[Lcom/google/android/filament/Material;");
|
||||
assert_invariant(mGetMaterials);
|
||||
|
||||
mNeedsDummyData = env->GetMethodID(providerClass, "needsDummyData","(I)Z");
|
||||
assert_invariant(mNeedsDummyData);
|
||||
|
||||
mDestroyMaterials = env->GetMethodID(providerClass, "destroyMaterials", "()V");
|
||||
assert_invariant(mDestroyMaterials);
|
||||
}
|
||||
|
||||
~JavaMaterialProvider() override {
|
||||
mEnv->DeleteGlobalRef(mMaterialKeyClass);
|
||||
mEnv->DeleteGlobalRef(mJavaProvider);
|
||||
delete mPreviousMaterials;
|
||||
}
|
||||
|
||||
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, const char* label) override {
|
||||
// Create a Java object for the material key and copy the native fields into it.
|
||||
jobject key = mEnv->NewObject(mMaterialKeyClass, mMaterialKeyConstructor);
|
||||
nativeToJava(mEnv, *config, key);
|
||||
|
||||
// Convert the optional label into a Java string.
|
||||
jstring stringLabel = label ? mEnv->NewStringUTF(label) : nullptr;
|
||||
|
||||
// Allocate space for the output argument.
|
||||
jintArray uvMapArray = mEnv->NewIntArray(8);
|
||||
|
||||
// Call the Java-based material provider.
|
||||
jobject materialInstance = mEnv->CallObjectMethod(mJavaProvider, mCreateMaterialInstance,
|
||||
key, uvMapArray, stringLabel);
|
||||
|
||||
// Copy the UvMap results from the JVM array into the native array.
|
||||
if (uvmap) {
|
||||
jint* elements = mEnv->GetIntArrayElements(uvMapArray, nullptr);
|
||||
for (size_t i = 0; i < uvmap->size(); i++) {
|
||||
(*uvmap)[i] = (UvSet) elements[i];
|
||||
}
|
||||
mEnv->ReleaseIntArrayElements(uvMapArray, elements, JNI_ABORT);
|
||||
}
|
||||
|
||||
// The config parameter is an in-out parameter so we need to copy the results from Java.
|
||||
nativeFromJava(mEnv, *config, key);
|
||||
|
||||
mEnv->DeleteLocalRef(key);
|
||||
mEnv->DeleteLocalRef(uvMapArray);
|
||||
|
||||
if (stringLabel) {
|
||||
mEnv->DeleteLocalRef(stringLabel);
|
||||
}
|
||||
|
||||
if (materialInstance == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (MaterialInstance*) mEnv->CallLongMethod(materialInstance, mMaterialInstanceGetNativeObject);
|
||||
}
|
||||
|
||||
const Material* const* getMaterials() const noexcept override {
|
||||
jobjectArray javaMaterials = (jobjectArray) mEnv->CallObjectMethod(mJavaProvider, mGetMaterials);
|
||||
|
||||
const size_t count = mEnv->GetArrayLength(javaMaterials);
|
||||
|
||||
delete mPreviousMaterials;
|
||||
using MaterialPointer = Material*;
|
||||
mPreviousMaterials = new MaterialPointer[count];
|
||||
|
||||
// Call "getNativeObject" on each material in order to pass them up into the native layer.
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
jobject javaMaterial = mEnv->GetObjectArrayElement(javaMaterials, i);
|
||||
jlong matPointer = mEnv->CallLongMethod(javaMaterial, mMaterialGetNativeObject);
|
||||
mPreviousMaterials[i] = (Material*) matPointer;
|
||||
}
|
||||
|
||||
return mPreviousMaterials;
|
||||
}
|
||||
|
||||
size_t getMaterialsCount() const noexcept override {
|
||||
jobjectArray javaMaterials = (jobjectArray) mEnv->CallObjectMethod(mJavaProvider, mGetMaterials);
|
||||
return mEnv->GetArrayLength(javaMaterials);
|
||||
}
|
||||
|
||||
void destroyMaterials() override {
|
||||
mEnv->CallObjectMethod(mJavaProvider, mDestroyMaterials);
|
||||
}
|
||||
|
||||
bool needsDummyData(VertexAttribute attrib) const noexcept override {
|
||||
return mEnv->CallBooleanMethod(mJavaProvider, mNeedsDummyData, (int) attrib);
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_AssetLoader_nCreateAssetLoader(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeProvider, jlong nativeEntities) {
|
||||
Java_com_google_android_filament_gltfio_AssetLoader_nCreateAssetLoader(JNIEnv* env, jclass,
|
||||
jlong nativeEngine, jobject provider, jlong nativeEntities) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
MaterialProvider* materials = (MaterialProvider*) nativeProvider;
|
||||
MaterialProvider* materials = new JavaMaterialProvider(env, provider);
|
||||
EntityManager* entities = (EntityManager*) nativeEntities;
|
||||
NameComponentManager* names = new NameComponentManager(*entities);
|
||||
return (jlong) AssetLoader::create({engine, materials, names, entities});
|
||||
@@ -44,6 +177,7 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_AssetLoader_nDestroyAssetLoader(JNIEnv*, jclass,
|
||||
jlong nativeLoader) {
|
||||
AssetLoader* loader = (AssetLoader*) nativeLoader;
|
||||
delete loader->getMaterialProvider();
|
||||
NameComponentManager* names = loader->getNames();
|
||||
AssetLoader::destroy(&loader);
|
||||
delete names;
|
||||
|
||||
Reference in New Issue
Block a user