From 8dc6fde58877533b77900e54a43f7bf0eacc285e Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Tue, 16 Aug 2022 10:35:28 -0700 Subject: [PATCH] gltfio API change: assets are now always 'instanced' etc This change was motivated by some internal work at Google and has the benefit of simplifying the gltfio API and implementation. There are 2 major API changes: (1) Consolidate separate loader entry points for GLB and GLTF. The distinction between GLB and GLTF can be made from the file content alone, because GLB has a 4-byte magic string in its header. There is no need for separate entry points. Clients do not (and should not) need to check the file name extension. (2) Remove the distinction between "instanced" and "non-instanced" glTF assets. In the new scheme, all assets have at least 1 instance. Broadly speaking, in gltfio an "asset" is a collection of Filament objects like textures and vertex buffers, while an "instance" is a collection of entities and components (e.g. the transform hierarchy). This API change makes life easier for clients because they no longer need to decide a priori if they will ever need to add instances. This change also moves some public-facing methods from FilamentAsset to FilamentInstance: - getSkinCount, getSkinNameAt - getJointCountAt, getJointsAt - attachSkin, detachSkin --- RELEASE_NOTES.md | 2 + .../android/filament/utils/ModelViewer.kt | 6 +- .../src/main/cpp/AssetLoader.cpp | 13 +- .../src/main/cpp/FilamentAsset.cpp | 54 --------- .../src/main/cpp/FilamentInstance.cpp | 54 +++++++++ .../android/filament/gltfio/AssetLoader.java | 27 ++--- .../filament/gltfio/FilamentAsset.java | 59 ---------- .../filament/gltfio/FilamentInstance.java | 68 ++++++++++- .../gltf-viewer/gltf-viewer/FILModelView.mm | 4 +- .../hello-gltf/FilamentView/App.cpp | 2 +- libs/gltfio/include/gltfio/Animator.h | 1 + libs/gltfio/include/gltfio/AssetLoader.h | 21 ++-- libs/gltfio/include/gltfio/FilamentAsset.h | 35 ------ libs/gltfio/include/gltfio/FilamentInstance.h | 35 ++++++ libs/gltfio/src/Animator.cpp | 6 - libs/gltfio/src/AssetLoader.cpp | 111 +++++------------- libs/gltfio/src/FFilamentAsset.h | 18 --- libs/gltfio/src/FFilamentInstance.h | 2 + libs/gltfio/src/FilamentAsset.cpp | 66 +---------- libs/gltfio/src/FilamentInstance.cpp | 39 ++++++ libs/gltfio/src/ResourceLoader.cpp | 26 ++-- samples/gltf_instances.cpp | 6 +- samples/gltf_viewer.cpp | 16 +-- web/filament-js/extensions.js | 17 +-- web/filament-js/filament-viewer.js | 11 +- web/filament-js/filament.d.ts | 9 +- web/filament-js/jsbindings.cpp | 38 +++--- web/samples/animation.html | 2 +- web/samples/helmet.html | 4 +- web/samples/morphing.html | 2 +- 30 files changed, 297 insertions(+), 457 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5fe74b5913..e647f364a2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ A new header is inserted each time a *tag* is created. ## main branch - engine: new feature level APIs, see `Engine::getSupportedFeatureLevel()` +- gltfio: add unified `AssetLoader::createAsset()` method [⚠️ **API Change**] +- gltfio: all assets are now "instanced" [⚠️ **API Change**] ## v1.25.6 diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt index 6e91620491..4b5c7f4e33 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt @@ -178,7 +178,7 @@ class ModelViewer( */ fun loadModelGlb(buffer: Buffer) { destroyModel() - asset = assetLoader.createAssetFromBinary(buffer) + asset = assetLoader.createAsset(buffer) asset?.let { asset -> resourceLoader.asyncBeginLoad(asset) animator = asset.animator @@ -193,7 +193,7 @@ class ModelViewer( */ fun loadModelGltf(buffer: Buffer, callback: (String) -> Buffer?) { destroyModel() - asset = assetLoader.createAssetFromJson(buffer) + asset = assetLoader.createAsset(buffer) asset?.let { asset -> for (uri in asset.resourceUris) { val resourceBuffer = callback(uri) @@ -216,7 +216,7 @@ class ModelViewer( */ fun loadModelGltfAsync(buffer: Buffer, callback: (String) -> Buffer) { destroyModel() - asset = assetLoader.createAssetFromJson(buffer) + asset = assetLoader.createAsset(buffer) fetchResourcesJob = CoroutineScope(Dispatchers.IO).launch { fetchResources(asset!!, callback) } diff --git a/android/gltfio-android/src/main/cpp/AssetLoader.cpp b/android/gltfio-android/src/main/cpp/AssetLoader.cpp index 6f3f16b9a6..90690129f6 100644 --- a/android/gltfio-android/src/main/cpp/AssetLoader.cpp +++ b/android/gltfio-android/src/main/cpp/AssetLoader.cpp @@ -207,20 +207,11 @@ Java_com_google_android_filament_gltfio_AssetLoader_nDestroyAssetLoader(JNIEnv*, } extern "C" JNIEXPORT jlong JNICALL -Java_com_google_android_filament_gltfio_AssetLoader_nCreateAssetFromBinary(JNIEnv* env, jclass, +Java_com_google_android_filament_gltfio_AssetLoader_nCreateAsset(JNIEnv* env, jclass, jlong nativeLoader, jobject javaBuffer, jint remaining) { AssetLoader* loader = (AssetLoader*) nativeLoader; AutoBuffer buffer(env, javaBuffer, remaining); - return (jlong) loader->createAssetFromBinary((const uint8_t *) buffer.getData(), - buffer.getSize()); -} - -extern "C" JNIEXPORT jlong JNICALL -Java_com_google_android_filament_gltfio_AssetLoader_nCreateAssetFromJson(JNIEnv* env, jclass, - jlong nativeLoader, jobject javaBuffer, jint remaining) { - AssetLoader* loader = (AssetLoader*) nativeLoader; - AutoBuffer buffer(env, javaBuffer, remaining); - return (jlong) loader->createAssetFromJson((const uint8_t *) buffer.getData(), + return (jlong) loader->createAsset((const uint8_t *) buffer.getData(), buffer.getSize()); } diff --git a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp index 9721a1448c..f32ecebb9e 100644 --- a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp +++ b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp @@ -228,44 +228,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetExtras(JNIEnv* env, jc return val ? env->NewStringUTF(val) : nullptr; } -extern "C" JNIEXPORT jint JNICALL -Java_com_google_android_filament_gltfio_FilamentAsset_nGetSkinCount(JNIEnv* , jclass, - jlong nativeAsset) { - FilamentAsset* asset = (FilamentAsset*) nativeAsset; - return (jint) asset->getSkinCount(); -} - -extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_gltfio_FilamentAsset_nGetSkinNames(JNIEnv* env, jclass, - jlong nativeAsset, jobjectArray result) { - FilamentAsset* asset = (FilamentAsset*) nativeAsset; - jsize available = env->GetArrayLength(result); - for (int i = 0; i < available; ++i) { - const char* name = asset->getSkinNameAt(i); - if (name) { - env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name)); - } - } -} - -extern "C" JNIEXPORT jint JNICALL -Java_com_google_android_filament_gltfio_FilamentAsset_nGetJointCountAt(JNIEnv* , jclass, - jlong nativeAsset, jint skinIndex) { - FilamentAsset* asset = (FilamentAsset*) nativeAsset; - return (jint) asset->getJointCountAt(skinIndex); -} - -extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_gltfio_FilamentAsset_nGetJointsAt(JNIEnv* env, jclass, - jlong nativeAsset, jint skinIndex, jintArray result) { - FilamentAsset* asset = (FilamentAsset*) nativeAsset; - jsize available = env->GetArrayLength(result); - Entity* entities = (Entity*) env->GetIntArrayElements(result, nullptr); - std::copy_n(asset->getJointsAt(skinIndex), - std::min(available, (jsize) asset->getJointCountAt(skinIndex)), entities); - env->ReleaseIntArrayElements(result, (jint*) entities, 0); -} - extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_gltfio_FilamentAsset_nGetAnimator(JNIEnv* , jclass, jlong nativeAsset) { @@ -340,19 +302,3 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nReleaseSourceData(JNIEnv* FilamentAsset* asset = (FilamentAsset*) nativeAsset; asset->releaseSourceData(); } - -extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_gltfio_FilamentAsset_nAttachSkin(JNIEnv* env, jclass, - jlong nativeAsset, jint skinIndex, jint targetEntity) { - FilamentAsset* asset = (FilamentAsset*) nativeAsset; - Entity target = Entity::import(targetEntity); - asset->attachSkin(skinIndex, target); -} - -extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_gltfio_FilamentAsset_nDetachSkin(JNIEnv* env, jclass, - jlong nativeAsset, jint skinIndex, jint targetEntity) { - FilamentAsset* asset = (FilamentAsset*) nativeAsset; - Entity target = Entity::import(targetEntity); - asset->detachSkin(skinIndex, target); -} diff --git a/android/gltfio-android/src/main/cpp/FilamentInstance.cpp b/android/gltfio-android/src/main/cpp/FilamentInstance.cpp index 62f9f7bbab..c35876681d 100644 --- a/android/gltfio-android/src/main/cpp/FilamentInstance.cpp +++ b/android/gltfio-android/src/main/cpp/FilamentInstance.cpp @@ -61,3 +61,57 @@ Java_com_google_android_filament_gltfio_FilamentInstance_nApplyMaterialVariant(J FilamentInstance* instance = (FilamentInstance*) nativeInstance; instance->applyMaterialVariant(variantIndex); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nAttachSkin(JNIEnv* env, jclass, + jlong nativeInstance, jint skinIndex, jint targetEntity) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + Entity target = Entity::import(targetEntity); + instance->attachSkin(skinIndex, target); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nDetachSkin(JNIEnv* env, jclass, + jlong nativeInstance, jint skinIndex, jint targetEntity) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + Entity target = Entity::import(targetEntity); + instance->detachSkin(skinIndex, target); +} + +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nGetSkinCount(JNIEnv* , jclass, + jlong nativeInstance) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + return (jint) instance->getSkinCount(); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nGetSkinNames(JNIEnv* env, jclass, + jlong nativeInstance, jobjectArray result) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + jsize available = env->GetArrayLength(result); + for (int i = 0; i < available; ++i) { + const char* name = instance->getSkinNameAt(i); + if (name) { + env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name)); + } + } +} + +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nGetJointCountAt(JNIEnv* , jclass, + jlong nativeInstance, jint skinIndex) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + return (jint) instance->getJointCountAt(skinIndex); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_gltfio_FilamentInstance_nGetJointsAt(JNIEnv* env, jclass, + jlong nativeInstance, jint skinIndex, jintArray result) { + FilamentInstance* instance = (FilamentInstance*) nativeInstance; + jsize available = env->GetArrayLength(result); + Entity* entities = (Entity*) env->GetIntArrayElements(result, nullptr); + std::copy_n(instance->getJointsAt(skinIndex), + std::min(available, (jsize) instance->getJointCountAt(skinIndex)), entities); + env->ReleaseIntArrayElements(result, (jint*) entities, 0); +} diff --git a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/AssetLoader.java b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/AssetLoader.java index ea43967756..3a3fc81cbc 100644 --- a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/AssetLoader.java +++ b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/AssetLoader.java @@ -26,8 +26,8 @@ import java.nio.Buffer; /** * Consumes a blob of glTF 2.0 content (either JSON or GLB) and produces a {@link FilamentAsset} - * object, which is a bundle of Filament entities, material instances, textures, vertex buffers, - * and index buffers. + * object, which is a bundle of Filament textures, vertex buffers, index buffers, etc. An asset is + * composed of 1 or more FilamentInstance objects which contain entities and components. * *

AssetLoader does not fetch external buffer data or create textures on its own. Clients can use * the provided {@link ResourceLoader} class for this, which obtains the URI list from the asset. @@ -51,7 +51,7 @@ import java.nio.Buffer; * filamentAsset = assets.open("models/lucy.gltf").use { input -> * val bytes = ByteArray(input.available()) * input.read(bytes) - * assetLoader.createAssetFromJson(ByteBuffer.wrap(bytes))!! + * assetLoader.createAsset(ByteBuffer.wrap(bytes))!! * } * * val resourceLoader = ResourceLoader(engine) @@ -115,20 +115,11 @@ public class AssetLoader { } /** - * Creates a {@link FilamentAsset} from the contents of a GLB file. + * Creates a {@link FilamentAsset} from the contents of a GLB or GLTF file. */ @Nullable - public FilamentAsset createAssetFromBinary(@NonNull Buffer buffer) { - long nativeAsset = nCreateAssetFromBinary(mNativeObject, buffer, buffer.remaining()); - return nativeAsset != 0 ? new FilamentAsset(mEngine, nativeAsset) : null; - } - - /** - * Creates a {@link FilamentAsset} from the contents of a GLTF file. - */ - @Nullable - public FilamentAsset createAssetFromJson(@NonNull Buffer buffer) { - long nativeAsset = nCreateAssetFromJson(mNativeObject, buffer, buffer.remaining()); + public FilamentAsset createAsset(@NonNull Buffer buffer) { + long nativeAsset = nCreateAsset(mNativeObject, buffer, buffer.remaining()); return nativeAsset != 0 ? new FilamentAsset(mEngine, nativeAsset) : null; } @@ -158,7 +149,7 @@ public class AssetLoader { } /** - * Adds a new instance to an instanced asset. + * Adds a new instance to the asset. * * Use this with caution. It is more efficient to pre-allocate a max number of instances, and * gradually add them to the scene as needed. Instances can also be "recycled" by removing and @@ -169,7 +160,6 @@ public class AssetLoader { * create/destroy churn, as noted above. * * This cannot be called after FilamentAsset#releaseSourceData(). - * This cannot be called on a non-instanced asset. * Animation is not supported in new instances. * See also AssetLoader#createInstancedAsset(). */ @@ -202,8 +192,7 @@ public class AssetLoader { private static native long nCreateAssetLoader(long nativeEngine, Object provider, long nativeEntities); private static native void nDestroyAssetLoader(long nativeLoader); - private static native long nCreateAssetFromBinary(long nativeLoader, Buffer buffer, int remaining); - private static native long nCreateAssetFromJson(long nativeLoader, Buffer buffer, int remaining); + private static native long nCreateAsset(long nativeLoader, Buffer buffer, int remaining); private static native long nCreateInstancedAsset(long nativeLoader, Buffer buffer, int remaining, long[] nativeInstances); private static native long nCreateInstance(long nativeLoader, long nativeAsset); 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 c314b500bd..a85d1303ef 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 @@ -225,58 +225,6 @@ public class FilamentAsset { return mAnimator; } - /** - * Gets the skin count of this asset. - */ - public int getSkinCount() { - return nGetSkinCount(getNativeObject()); - } - - /** - * Gets the skin name at skin index in this asset. - */ - public @NonNull String[] getSkinNames() { - String[] result = new String[getSkinCount()]; - nGetSkinNames(getNativeObject(), result); - return result; - } - - /** - * Attaches the given skin to the given node, which must have an associated mesh with - * BONE_INDICES and BONE_WEIGHTS attributes. - * - * This is a no-op if the given skin index or target is invalid. - */ - public void attachSkin(@IntRange(from = 0) int skinIndex, @Entity int target) { - nAttachSkin(getNativeObject(), skinIndex, target); - } - - /** - * Attaches the given skin to the given node, which must have an associated mesh with - * BONE_INDICES and BONE_WEIGHTS attributes. - * - * This is a no-op if the given skin index or target is invalid. - */ - public void detachSkin(@IntRange(from = 0) int skinIndex, @Entity int target) { - nDetachSkin(getNativeObject(), skinIndex, target); - } - - /** - * Gets the joint count at skin index in this asset. - */ - public int getJointCountAt(@IntRange(from = 0) int skinIndex) { - return nGetJointCountAt(getNativeObject(), skinIndex); - } - - /** - * Gets joints at skin index in this asset. - */ - public @NonNull @Entity int[] getJointsAt(@IntRange(from = 0) int skinIndex) { - int[] result = new int[getJointCountAt(skinIndex)]; - nGetJointsAt(getNativeObject(), skinIndex, result); - return result; - } - /** * Gets the names of all morph targets in the given entity. */ @@ -365,18 +313,11 @@ public class FilamentAsset { private static native int nGetMorphTargetCount(long nativeAsset, int entity); private static native void nGetMorphTargetNames(long nativeAsset, int entity, String[] result); - private static native void nAttachSkin(long nativeAsset, int skinIndex, int entity); - private static native void nDetachSkin(long nativeAsset, int skinIndex, int entity); - private static native void nGetBoundingBox(long nativeAsset, float[] box); private static native String nGetName(long nativeAsset, int entity); private static native String nGetExtras(long nativeAsset, int entity); private static native long nGetAnimator(long nativeAsset); private static native void nApplyMaterialVariant(long nativeAsset, int variantIndex); - private static native int nGetSkinCount(long nativeAsset); - private static native void nGetSkinNames(long nativeAsset, String[] result); - private static native int nGetJointCountAt(long nativeAsset, int skinIndex); - private static native void nGetJointsAt(long nativeAsset, int skinIndex, int[] result); private static native int nGetResourceUriCount(long nativeAsset); private static native void nGetResourceUris(long nativeAsset, String[] result); private static native void nReleaseSourceData(long nativeAsset); 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 be00ed8e05..48e288d4c1 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 @@ -85,6 +85,58 @@ public class FilamentInstance { return mAnimator; } + /** + * Gets the skin count of this instance. + */ + public int getSkinCount() { + return nGetSkinCount(getNativeObject()); + } + + /** + * Gets the skin name at skin index in this instance. + */ + public @NonNull String[] getSkinNames() { + String[] result = new String[getSkinCount()]; + nGetSkinNames(getNativeObject(), result); + return result; + } + + /** + * Attaches the given skin to the given node, which must have an associated mesh with + * BONE_INDICES and BONE_WEIGHTS attributes. + * + * This is a no-op if the given skin index or target is invalid. + */ + public void attachSkin(@IntRange(from = 0) int skinIndex, @Entity int target) { + nAttachSkin(getNativeObject(), skinIndex, target); + } + + /** + * Attaches the given skin to the given node, which must have an associated mesh with + * BONE_INDICES and BONE_WEIGHTS attributes. + * + * This is a no-op if the given skin index or target is invalid. + */ + public void detachSkin(@IntRange(from = 0) int skinIndex, @Entity int target) { + nDetachSkin(getNativeObject(), skinIndex, target); + } + + /** + * Gets the joint count at skin index in this instance. + */ + public int getJointCountAt(@IntRange(from = 0) int skinIndex) { + return nGetJointCountAt(getNativeObject(), skinIndex); + } + + /** + * Gets joints at skin index in this instance. + */ + public @NonNull @Entity int[] getJointsAt(@IntRange(from = 0) int skinIndex) { + int[] result = new int[getJointCountAt(skinIndex)]; + nGetJointsAt(getNativeObject(), skinIndex, result); + return result; + } + /** * Applies the given material variant to all primitives in this instance. * @@ -94,9 +146,15 @@ public class FilamentInstance { 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); + private static native int nGetRoot(long nativeInstance); + private static native int nGetEntityCount(long nativeInstance); + private static native void nGetEntities(long nativeInstance, int[] result); + private static native long nGetAnimator(long nativeInstance); + private static native void nApplyMaterialVariant(long nativeInstance, int variantIndex); + private static native void nGetJointsAt(long nativeInstance, int skinIndex, int[] result); + private static native int nGetSkinCount(long nativeInstance); + private static native void nGetSkinNames(long nativeInstance, String[] result); + private static native int nGetJointCountAt(long nativeInstance, int skinIndex); + private static native void nAttachSkin(long nativeInstance, int skinIndex, int entity); + private static native void nDetachSkin(long nativeInstance, int skinIndex, int entity); } diff --git a/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm b/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm index dedff5f543..d36e8b994e 100644 --- a/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm +++ b/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm @@ -198,7 +198,7 @@ const float kSensitivity = 100.0f; - (void)loadModelGlb:(NSData*)buffer { [self destroyModel]; - _asset = _assetLoader->createAssetFromBinary( + _asset = _assetLoader->createAsset( static_cast(buffer.bytes), static_cast(buffer.length)); if (!_asset) { @@ -213,7 +213,7 @@ const float kSensitivity = 100.0f; - (void)loadModelGltf:(NSData*)buffer callback:(ResourceCallback)callback { [self destroyModel]; - _asset = _assetLoader->createAssetFromJson( + _asset = _assetLoader->createAsset( static_cast(buffer.bytes), static_cast(buffer.length)); if (!_asset) { diff --git a/ios/samples/hello-gltf/hello-gltf/FilamentView/App.cpp b/ios/samples/hello-gltf/hello-gltf/FilamentView/App.cpp index 8637acdbb8..2fcb918929 100644 --- a/ios/samples/hello-gltf/hello-gltf/FilamentView/App.cpp +++ b/ios/samples/hello-gltf/hello-gltf/FilamentView/App.cpp @@ -137,7 +137,7 @@ void App::setupMesh() { std::cerr << "Unable to read glTF" << std::endl; exit(1); } - app.asset = app.assetLoader->createAssetFromBinary(buffer.data(), static_cast(size)); + app.asset = app.assetLoader->createAsset(buffer.data(), static_cast(size)); filament::gltfio::ResourceLoader({ .engine = engine, diff --git a/libs/gltfio/include/gltfio/Animator.h b/libs/gltfio/include/gltfio/Animator.h index 072d532a84..c8d3803f5f 100644 --- a/libs/gltfio/include/gltfio/Animator.h +++ b/libs/gltfio/include/gltfio/Animator.h @@ -104,6 +104,7 @@ private: friend struct FFilamentInstance; /*! \endcond */ + // If "instance" is null, then this is the primary animator. Animator(FFilamentAsset* asset, FFilamentInstance* instance); ~Animator(); diff --git a/libs/gltfio/include/gltfio/AssetLoader.h b/libs/gltfio/include/gltfio/AssetLoader.h index 327e4c9bc7..7023c0638e 100644 --- a/libs/gltfio/include/gltfio/AssetLoader.h +++ b/libs/gltfio/include/gltfio/AssetLoader.h @@ -69,8 +69,8 @@ struct AssetConfiguration { * \brief Consumes glTF content and produces FilamentAsset objects. * * AssetLoader consumes a blob of glTF 2.0 content (either JSON or GLB) and produces a FilamentAsset - * object, which is a bundle of Filament entities, material instances, textures, vertex buffers, - * and index buffers. + * object, which is a bundle of Filament textures, vertex buffers, index buffers, etc. An asset is + * composed of 1 or more FilamentInstance objects which contain entities and components. * * Clients must use AssetLoader to create and destroy FilamentAsset objects. This is similar to * how filament::Engine is used to create and destroy core objects like VertexBuffer. @@ -92,7 +92,7 @@ struct AssetConfiguration { * * // Parse the glTF content and create Filament entities. * std::vector content(...); - * FilamentAsset* asset = loader->createAssetFromJson(content.data(), content.size()); + * FilamentAsset* asset = loader->createAsset(content.data(), content.size()); * content.clear(); * * // Load buffers and textures from disk. @@ -148,16 +148,10 @@ public: static void destroy(AssetLoader** loader); /** - * Takes a pointer to the contents of a JSON-based glTF 2.0 file and returns a bundle - * of Filament objects. Returns null on failure. + * Takes a pointer to the contents of a GLB or a JSON-based glTF 2.0 file and returns an asset + * with one instance, or null on failure. */ - FilamentAsset* createAssetFromJson(const uint8_t* bytes, uint32_t nbytes); - - /** - * Takes a pointer to the contents of a GLB glTF 2.0 file and returns a bundle - * of Filament objects. Returns null on failure. - */ - FilamentAsset* createAssetFromBinary(const uint8_t* bytes, uint32_t nbytes); + FilamentAsset* createAsset(const uint8_t* bytes, uint32_t nbytes); /** * Consumes the contents of a glTF 2.0 file and produces a primary asset with one or more @@ -186,7 +180,7 @@ public: FilamentInstance** instances, size_t numInstances); /** - * Adds a new instance to an instanced asset. + * Adds a new instance to the asset. * * Use this with caution. It is more efficient to pre-allocate a max number of instances, and * gradually add them to the scene as needed. Instances can also be "recycled" by removing and @@ -197,7 +191,6 @@ public: * create/destroy churn, as noted above. * * This cannot be called after FilamentAsset::releaseSourceData(). - * This cannot be called on a non-instanced asset. * See also AssetLoader::createInstancedAsset(). */ FilamentInstance* createInstance(FilamentAsset* primary); diff --git a/libs/gltfio/include/gltfio/FilamentAsset.h b/libs/gltfio/include/gltfio/FilamentAsset.h index 31318d8dd6..b8a299481a 100644 --- a/libs/gltfio/include/gltfio/FilamentAsset.h +++ b/libs/gltfio/include/gltfio/FilamentAsset.h @@ -218,41 +218,6 @@ public: */ Animator* getAnimator() const noexcept; - /** - * Gets the number of skins. - */ - size_t getSkinCount() const noexcept; - - /** - * Gets the skin name at skin index. - */ - const char* getSkinNameAt(size_t skinIndex) const noexcept; - - /** - * Gets the number of joints at skin index. - */ - size_t getJointCountAt(size_t skinIndex) const noexcept; - - /** - * Gets joints at skin index. - */ - const Entity* getJointsAt(size_t skinIndex) const noexcept; - - /** - * Attaches the given skin to the given node, which must have an associated mesh with - * BONE_INDICES and BONE_WEIGHTS attributes. - * - * This is a no-op if the given skin index or target is invalid. - */ - void attachSkin(size_t skinIndex, Entity target) noexcept; - - /** - * Detaches the given skin from the given node. - * - * This is a no-op if the given skin index or target is invalid. - */ - void detachSkin(size_t skinIndex, Entity target) noexcept; - /** * Gets the morph target name at the given index in the given entity. */ diff --git a/libs/gltfio/include/gltfio/FilamentInstance.h b/libs/gltfio/include/gltfio/FilamentInstance.h index 60d8e8d960..b6de4eeac2 100644 --- a/libs/gltfio/include/gltfio/FilamentInstance.h +++ b/libs/gltfio/include/gltfio/FilamentInstance.h @@ -74,6 +74,41 @@ public: * The animator is owned by the asset and should not be manually deleted. */ Animator* getAnimator() noexcept; + + /** + * Gets the number of skins. + */ + size_t getSkinCount() const noexcept; + + /** + * Gets the skin name at skin index. + */ + const char* getSkinNameAt(size_t skinIndex) const noexcept; + + /** + * Gets the number of joints at skin index. + */ + size_t getJointCountAt(size_t skinIndex) const noexcept; + + /** + * Gets joints at skin index. + */ + const utils::Entity* getJointsAt(size_t skinIndex) const noexcept; + + /** + * Attaches the given skin to the given node, which must have an associated mesh with + * BONE_INDICES and BONE_WEIGHTS attributes. + * + * This is a no-op if the given skin index or target is invalid. + */ + void attachSkin(size_t skinIndex, utils::Entity target) noexcept; + + /** + * Detaches the given skin from the given node. + * + * This is a no-op if the given skin index or target is invalid. + */ + void detachSkin(size_t skinIndex, utils::Entity target) noexcept; }; } // namespace filament::gltfio diff --git a/libs/gltfio/src/Animator.cpp b/libs/gltfio/src/Animator.cpp index d375675867..df0ae7dbef 100644 --- a/libs/gltfio/src/Animator.cpp +++ b/libs/gltfio/src/Animator.cpp @@ -217,8 +217,6 @@ Animator::Animator(FFilamentAsset* asset, FFilamentInstance* instance) { // Import each glTF channel into a custom data structure. if (instance) { mImpl->addChannels(instance->nodeMap, srcAnim, dstAnim); - } else if (!asset->isInstanced()) { - mImpl->addChannels(asset->mNodeMap, srcAnim, dstAnim); } else { for (FFilamentInstance* instance : asset->mInstances) { mImpl->addChannels(instance->nodeMap, srcAnim, dstAnim); @@ -320,8 +318,6 @@ void Animator::resetBoneMatrices() { if (mImpl->instance) { update(mImpl->instance->skins, mImpl->boneMatrices); - } else if (!mImpl->asset->isInstanced()) { - update(mImpl->asset->mSkins, mImpl->boneMatrices); } else { for (FFilamentInstance* instance : mImpl->asset->mInstances) { update(instance->skins, mImpl->boneMatrices); @@ -363,8 +359,6 @@ void Animator::updateBoneMatrices() { if (mImpl->instance) { update(mImpl->instance->skins, mImpl->boneMatrices); - } else if (!mImpl->asset->isInstanced()) { - update(mImpl->asset->mSkins, mImpl->boneMatrices); } else { for (FFilamentInstance* instance : mImpl->asset->mInstances) { update(instance->skins, mImpl->boneMatrices); diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 5759ad8c12..6fd1f8371a 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -100,8 +100,7 @@ struct FAssetLoader : public AssetLoader { mEngine(*config.engine), mDefaultNodeName(config.defaultNodeName) {} - FFilamentAsset* createAssetFromJson(const uint8_t* bytes, uint32_t nbytes); - FFilamentAsset* createAssetFromBinary(const uint8_t* bytes, uint32_t nbytes); + FFilamentAsset* createAsset(const uint8_t* bytes, uint32_t nbytes); FFilamentAsset* createInstancedAsset(const uint8_t* bytes, uint32_t numBytes, FilamentInstance** instances, size_t numInstances); FilamentInstance* createInstance(FFilamentAsset* primary); @@ -172,8 +171,18 @@ struct FAssetLoader : public AssetLoader { FILAMENT_UPCAST(AssetLoader) -FFilamentAsset* FAssetLoader::createAssetFromJson(const uint8_t* bytes, uint32_t nbytes) { - cgltf_options options { cgltf_file_type_invalid }; +FFilamentAsset* FAssetLoader::createAsset(const uint8_t* bytes, uint32_t byteCount) { + FilamentInstance* instances; + return createInstancedAsset(bytes, byteCount, &instances, 1); +} + +FFilamentAsset* FAssetLoader::createInstancedAsset(const uint8_t* bytes, uint32_t byteCount, + FilamentInstance** instances, size_t numInstances) { + ASSERT_PRECONDITION(numInstances > 0, "Instance count must be 1 or more."); + + // This method can be used to load JSON or GLB. By using a default options struct, we are asking + // cgltf to examine the magic identifier to determine which type of file is being loaded. + cgltf_options options {}; if constexpr (!GLTFIO_USE_FILESYSTEM) { @@ -189,50 +198,6 @@ FFilamentAsset* FAssetLoader::createAssetFromJson(const uint8_t* bytes, uint32_t options.file.release = [](const cgltf_memory_options*, const cgltf_file_options*, void*) {}; } - cgltf_data* sourceAsset; - cgltf_result result = cgltf_parse(&options, bytes, nbytes, &sourceAsset); - if (result != cgltf_result_success) { - slog.e << "Unable to parse JSON file." << io::endl; - return nullptr; - } - createAsset(sourceAsset, 0); - return mResult; -} - -FFilamentAsset* FAssetLoader::createAssetFromBinary(const uint8_t* bytes, uint32_t byteCount) { - - // The cgltf library handles GLB efficiently by pointing all buffer views into the source data. - // However, we wish our API to be simple and safe, allowing clients to free up their source blob - // immediately, without worrying about when all the data has finished uploading asynchronously - // to the GPU. To achieve this we create a copy of the source blob and stash it inside the - // asset, asking cgltf to parse the copy. This allows us to free it at the correct time (i.e. - // after all GPU uploads have completed). Although it incurs a copy, the added safety of this - // API seems worthwhile. - utils::FixedCapacityVector glbdata(byteCount); - std::copy_n(bytes, byteCount, glbdata.data()); - - cgltf_options options { cgltf_file_type_glb }; - cgltf_data* sourceAsset; - cgltf_result result = cgltf_parse(&options, glbdata.data(), byteCount, &sourceAsset); - if (result != cgltf_result_success) { - slog.e << "Unable to parse glb file." << io::endl; - return nullptr; - } - createAsset(sourceAsset, 0); - if (mResult) { - glbdata.swap(mResult->mSourceAsset->glbData); - } - return mResult; -} - -FFilamentAsset* FAssetLoader::createInstancedAsset(const uint8_t* bytes, uint32_t byteCount, - FilamentInstance** instances, size_t numInstances) { - ASSERT_PRECONDITION(numInstances > 0, "Instance count must be 1 or more."); - - // This method can be used to load JSON or GLB. By using a default options struct, we are asking - // cgltf to examine the magic identifier to determine which type of file is being loaded. - cgltf_options options {}; - // Clients can free up their source blob immediately, but cgltf has pointers into the data that // need to stay valid. Therefore we create a copy of the source blob and stash it inside the // asset. @@ -258,10 +223,6 @@ FilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary) { slog.e << "Source data has been released; asset is frozen." << io::endl; return nullptr; } - if (!primary->isInstanced()) { - slog.e << "Cannot add an instance to a non-instanced asset." << io::endl; - return nullptr; - } const cgltf_data* srcAsset = primary->mSourceAsset->hierarchy; if (srcAsset->scenes == nullptr) { slog.e << "There is no scene in the asset." << io::endl; @@ -345,20 +306,13 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances) mResult->mRenderableCount = 0; - if (numInstances == 0) { - // For each scene root, recursively create all entities. - for (const auto& pair : mRootNodes) { - createEntity(srcAsset, pair.first, pair.second, 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, srcAsset) == nullptr) { - mError = true; - break; - } + // 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, srcAsset) == nullptr) { + mError = true; + break; } } @@ -371,7 +325,7 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances) // Find every unique resource URI and store a pointer to any of the cgltf-owned cstrings // that match the URI. These strings get freed during releaseSourceData(). - tsl::robin_map resourceUris; + tsl::robin_map resourceUris; auto addResourceUri = [&resourceUris](const char* uri) { if (uri) { resourceUris[uri] = uri; @@ -454,12 +408,8 @@ void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* no // Update the asset's entity list and private node mapping. mResult->mEntities.push_back(entity); - if (instance) { - instance->entities.push_back(entity); - instance->nodeMap[node] = entity; - } else { - mResult->mNodeMap[node] = entity; - } + instance->entities.push_back(entity); + instance->nodeMap[node] = entity; const char* name = getNodeName(node, mDefaultNodeName); @@ -644,11 +594,8 @@ void FAssetLoader::createMaterialVariants(const cgltf_data* srcAsset, const cglt 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}); - } + instance->variants[variantIndex].mappings.push_back({entity, prim, mi}); + mResult->mVariants[variantIndex].mappings.push_back({entity, prim, mi}); } } } @@ -1430,12 +1377,8 @@ void AssetLoader::destroy(AssetLoader** loader) { *loader = temp; } -FilamentAsset* AssetLoader::createAssetFromJson(uint8_t const* bytes, uint32_t nbytes) { - return upcast(this)->createAssetFromJson(bytes, nbytes); -} - -FilamentAsset* AssetLoader::createAssetFromBinary(uint8_t const* bytes, uint32_t nbytes) { - return upcast(this)->createAssetFromBinary(bytes, nbytes); +FilamentAsset* AssetLoader::createAsset(uint8_t const* bytes, uint32_t nbytes) { + return upcast(this)->createAsset(bytes, nbytes); } FilamentAsset* AssetLoader::createInstancedAsset(const uint8_t* bytes, uint32_t numBytes, diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index a2aee229a9..e4d8a3985c 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -210,18 +210,6 @@ struct FFilamentAsset : public FilamentAsset { Animator* getAnimator() const noexcept { return mAnimator; } - size_t getSkinCount() const noexcept; - - 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 attachSkin(size_t skinIndex, Entity target) noexcept; - - void detachSkin(size_t skinIndex, Entity target) noexcept; - const char* getMorphTargetNameAt(utils::Entity entity, size_t targetIndex) const noexcept; size_t getMorphTargetCountAt(utils::Entity entity) const noexcept; @@ -281,10 +269,6 @@ struct FFilamentAsset : public FilamentAsset { mDependencyGraph.addEdge(texture, tb.materialInstance, tb.materialParameter); } - bool isInstanced() const { - return mInstances.size() > 0; - } - void createAnimators(); filament::Engine* const mEngine; @@ -306,7 +290,6 @@ struct FFilamentAsset : public FilamentAsset { filament::Aabb mBoundingBox; utils::Entity mRoot; std::vector mInstances; - SkinVector mSkins; // unused for instanced assets Animator* mAnimator = nullptr; Wireframe* mWireframe = nullptr; @@ -341,7 +324,6 @@ struct FFilamentAsset : public FilamentAsset { std::vector mBufferSlots; std::vector mTextureSlots; std::vector mResourceUris; - NodeMap mNodeMap; // unused for instanced assets std::vector > mPrimitives; MatInstanceCache mMatInstanceCache; MeshCache mMeshCache; diff --git a/libs/gltfio/src/FFilamentInstance.h b/libs/gltfio/src/FFilamentInstance.h index 805dc0d960..af9806528e 100644 --- a/libs/gltfio/src/FFilamentInstance.h +++ b/libs/gltfio/src/FFilamentInstance.h @@ -86,6 +86,8 @@ 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 attachSkin(size_t skinIndex, utils::Entity target) noexcept; + void detachSkin(size_t skinIndex, utils::Entity target) noexcept; void applyMaterialVariant(size_t variantIndex) noexcept; }; diff --git a/libs/gltfio/src/FilamentAsset.cpp b/libs/gltfio/src/FilamentAsset.cpp index bf95fcb1e7..ace4131ef7 100644 --- a/libs/gltfio/src/FilamentAsset.cpp +++ b/libs/gltfio/src/FilamentAsset.cpp @@ -105,45 +105,6 @@ void FFilamentAsset::createAnimators() { } } -size_t FFilamentAsset::getSkinCount() const noexcept { - return mSkins.size(); -} - -const char* FFilamentAsset::getSkinNameAt(size_t skinIndex) const noexcept { - if (mSkins.size() <= skinIndex) { - return nullptr; - } - return mSkins[skinIndex].name.c_str(); -} - -size_t FFilamentAsset::getJointCountAt(size_t skinIndex) const noexcept { - if (mSkins.size() <= skinIndex) { - return 0; - } - return mSkins[skinIndex].joints.size(); -} - -const utils::Entity* FFilamentAsset::getJointsAt(size_t skinIndex) const noexcept { - if (mSkins.size() <= skinIndex) { - return nullptr; - } - return mSkins[skinIndex].joints.data(); -} - -void FFilamentAsset::attachSkin(size_t skinIndex, Entity target) noexcept { - if (UTILS_UNLIKELY(mSkins.size() <= skinIndex || target.isNull())) { - return; - } - mSkins[skinIndex].targets.insert(target); -} - -void FFilamentAsset::detachSkin(size_t skinIndex, Entity target) noexcept { - if (UTILS_UNLIKELY(mSkins.size() <= skinIndex || target.isNull())) { - return; - } - mSkins[skinIndex].targets.erase(target); -} - const char* FFilamentAsset::getMorphTargetNameAt(utils::Entity entity, size_t targetIndex) const noexcept { if (!mResourcesLoaded) { @@ -185,7 +146,7 @@ void FFilamentAsset::applyMaterialVariant(size_t variantIndex) noexcept { const std::vector& mappings = mVariants[variantIndex].mappings; RenderableManager& rm = mEngine->getRenderableManager(); for (const auto& mapping : mappings) { - auto instance = rm.getInstance(mapping.renderable); + RenderableManager::Instance instance = rm.getInstance(mapping.renderable); rm.setMaterialInstanceAt(instance, mapping.primitiveIndex, mapping.material); } } @@ -204,7 +165,6 @@ void FFilamentAsset::releaseSourceData() noexcept { mMatInstanceCache = {}; mMeshCache = {}; mResourceUris = {}; - mNodeMap = {}; mPrimitives = {}; mBufferSlots = {}; mTextureSlots = {}; @@ -397,30 +357,6 @@ Animator* FilamentAsset::getAnimator() const noexcept { return upcast(this)->getAnimator(); } -size_t FilamentAsset::getSkinCount() const noexcept { - return upcast(this)->getSkinCount(); -} - -const char* FilamentAsset::getSkinNameAt(size_t skinIndex) const noexcept { - return upcast(this)->getSkinNameAt(skinIndex); -} - -size_t FilamentAsset::getJointCountAt(size_t skinIndex) const noexcept { - return upcast(this)->getJointCountAt(skinIndex); -} - -const utils::Entity* FilamentAsset::getJointsAt(size_t skinIndex) const noexcept { - return upcast(this)->getJointsAt(skinIndex); -} - -void FilamentAsset::attachSkin(size_t skinIndex, Entity target) noexcept { - upcast(this)->attachSkin(skinIndex, target); -} - -void FilamentAsset::detachSkin(size_t skinIndex, Entity target) noexcept { - upcast(this)->detachSkin(skinIndex, target); -} - const char* FilamentAsset::getMorphTargetNameAt(utils::Entity entity, size_t targetIndex) const noexcept { return upcast(this)->getMorphTargetNameAt(entity, targetIndex); diff --git a/libs/gltfio/src/FilamentInstance.cpp b/libs/gltfio/src/FilamentInstance.cpp index 82f9ee51ba..fccd3f8ec6 100644 --- a/libs/gltfio/src/FilamentInstance.cpp +++ b/libs/gltfio/src/FilamentInstance.cpp @@ -61,6 +61,20 @@ const utils::Entity* FFilamentInstance::getJointsAt(size_t skinIndex) const noex return skins[skinIndex].joints.data(); } +void FFilamentInstance::attachSkin(size_t skinIndex, Entity target) noexcept { + if (UTILS_UNLIKELY(skins.size() <= skinIndex || target.isNull())) { + return; + } + skins[skinIndex].targets.insert(target); +} + +void FFilamentInstance::detachSkin(size_t skinIndex, Entity target) noexcept { + if (UTILS_UNLIKELY(skins.size() <= skinIndex || target.isNull())) { + return; + } + skins[skinIndex].targets.erase(target); +} + void FFilamentInstance::applyMaterialVariant(size_t variantIndex) noexcept { if (variantIndex >= variants.size()) { return; @@ -98,4 +112,29 @@ Animator* FilamentInstance::getAnimator() noexcept { return upcast(this)->getAnimator(); } +size_t FilamentInstance::getSkinCount() const noexcept { + return upcast(this)->getSkinCount(); +} + +const char* FilamentInstance::getSkinNameAt(size_t skinIndex) const noexcept { + return upcast(this)->getSkinNameAt(skinIndex); +} + +size_t FilamentInstance::getJointCountAt(size_t skinIndex) const noexcept { + return upcast(this)->getJointCountAt(skinIndex); +} + +const Entity* FilamentInstance::getJointsAt(size_t skinIndex) const noexcept { + return upcast(this)->getJointsAt(skinIndex); +} + +void FilamentInstance::attachSkin(size_t skinIndex, Entity target) noexcept { + return upcast(this)->attachSkin(skinIndex, target); +} + +void FilamentInstance::detachSkin(size_t skinIndex, Entity target) noexcept { + return upcast(this)->detachSkin(skinIndex, target); +} + + } // namespace filament::gltfio diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 0d6f63a2f9..dc1d6cbc5d 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -75,9 +75,7 @@ struct ResourceLoader::Impl { const bool mNormalizeSkinningWeights; const bool mRecomputeBoundingBoxes; const std::string mGltfPath; - - // TODO: this should be const - bool mIgnoreBindTransform; + const bool mIgnoreBindTransform; // User-provided resource data with URI string keys, populated with addResourceData(). // This is used on platforms without traditional file systems, such as Android, iOS, and WebGL. @@ -442,22 +440,14 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) { if (pImpl->mNormalizeSkinningWeights) { normalizeSkinningWeights(asset); } - if (!asset->isInstanced()) { - importSkins(gltf, asset->mNodeMap, asset->mSkins); - } else { - // NOTE: This takes care of up-front instances, but dynamically added instances also - // need to import the skin data, which is done in AssetLoader. - for (FFilamentInstance* instance : asset->mInstances) { - importSkins(gltf, instance->nodeMap, instance->skins); - } + // NOTE: This takes care of up-front instances, but dynamically added instances also + // need to import the skin data, which is done in AssetLoader. + for (FFilamentInstance* instance : asset->mInstances) { + importSkins(gltf, instance->nodeMap, instance->skins); } } if (pImpl->mRecomputeBoundingBoxes) { - // asset->mSkins is unused for instanced assets - if (!pImpl->mIgnoreBindTransform) { - pImpl->mIgnoreBindTransform = asset->isInstanced(); - } updateBoundingBoxes(asset); } @@ -766,7 +756,7 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { } } // Create a job description for morph targets. - NodeMap& nodeMap = asset->isInstanced() ? asset->mInstances[0]->nodeMap : asset->mNodeMap; + const NodeMap& nodeMap = asset->mInstances[0]->nodeMap; for (auto iter : nodeMap) { cgltf_node const* node = iter.first; cgltf_mesh const* mesh = node->mesh; @@ -869,7 +859,7 @@ void ResourceLoader::updateBoundingBoxes(FFilamentAsset* asset) const { SYSTRACE_CALL(); auto& rm = pImpl->mEngine->getRenderableManager(); auto& tm = pImpl->mEngine->getTransformManager(); - NodeMap& nodeMap = asset->isInstanced() ? asset->mInstances[0]->nodeMap : asset->mNodeMap; + const NodeMap& nodeMap = asset->mInstances[0]->nodeMap; // The purpose of the root node is to give the client a place for custom transforms. // Since it is not part of the source model, it should be ignored when computing the @@ -980,7 +970,7 @@ void ResourceLoader::updateBoundingBoxes(FFilamentAsset* asset) const { primitives.push_back({&mesh->primitives[index], nullptr, iter.second}); } if (cgltf_skin* const skin = iter.first->skin; skin) { - primitives.back().skin = &asset->mSkins[skin - baseSkin]; + primitives.back().skin = &asset->mInstances[0]->skins[skin - baseSkin]; } } } diff --git a/samples/gltf_instances.cpp b/samples/gltf_instances.cpp index 769f105cb8..828891c7fe 100644 --- a/samples/gltf_instances.cpp +++ b/samples/gltf_instances.cpp @@ -220,7 +220,11 @@ int main(int argc, char** argv) { app.resourceLoader->addTextureProvider("image/jpeg", app.stbDecoder); app.resourceLoader->addTextureProvider("image/ktx2", app.ktxDecoder); } - app.resourceLoader->asyncBeginLoad(app.asset); + + if (!app.resourceLoader->asyncBeginLoad(app.asset)) { + std::cerr << "Unable to start loading resources for " << filename << std::endl; + exit(1); + } // Load animation data. app.asset->getAnimator(); diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index dcba259552..0e85722232 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -413,11 +413,7 @@ int main(int argc, char** argv) { } // Parse the glTF file and create Filament entities. - if (filename.getExtension() == "glb") { - app.asset = app.assetLoader->createAssetFromBinary(buffer.data(), buffer.size()); - } else { - app.asset = app.assetLoader->createAssetFromJson(buffer.data(), buffer.size()); - } + app.asset = app.assetLoader->createAsset(buffer.data(), buffer.size()); buffer.clear(); buffer.shrink_to_fit(); @@ -436,6 +432,7 @@ int main(int argc, char** argv) { configuration.recomputeBoundingBoxes = app.recomputeAabb; configuration.ignoreBindTransform = app.ignoreBindTransform; configuration.normalizeSkinningWeights = true; + if (!app.resourceLoader) { app.resourceLoader = new gltfio::ResourceLoader(configuration); app.stbDecoder = createStbProvider(app.engine); @@ -444,7 +441,12 @@ int main(int argc, char** argv) { app.resourceLoader->addTextureProvider("image/jpeg", app.stbDecoder); app.resourceLoader->addTextureProvider("image/ktx2", app.ktxDecoder); } - app.resourceLoader->asyncBeginLoad(app.asset); + + if (!app.resourceLoader->asyncBeginLoad(app.asset)) { + std::cerr << "Unable to start loading resources for " << filename << std::endl; + exit(1); + } + app.asset->releaseSourceData(); auto ibl = FilamentApp::get().getIBL(); @@ -512,7 +514,7 @@ int main(int argc, char** argv) { app.assetLoader = AssetLoader::create({engine, app.materials, app.names }); app.mainCamera = &view->getCamera(); if (filename.isEmpty()) { - app.asset = app.assetLoader->createAssetFromBinary( + app.asset = app.assetLoader->createAsset( GLTF_DEMO_DAMAGEDHELMET_DATA, GLTF_DEMO_DAMAGEDHELMET_SIZE); } else { diff --git a/web/filament-js/extensions.js b/web/filament-js/extensions.js index a111ddeb9b..1a7c0e6a8f 100644 --- a/web/filament-js/extensions.js +++ b/web/filament-js/extensions.js @@ -517,22 +517,9 @@ Filament.loadClassExtensions = function() { return new Float32Array(arrayBuffer); }; - Filament.gltfio$AssetLoader.prototype.createAssetFromJson = function(buffer) { - if ('string' == typeof buffer && buffer.endsWith('.glb')) { - console.error('Please use createAssetFromBinary for glb files.'); - } + Filament.gltfio$AssetLoader.prototype.createAsset = function(buffer) { buffer = getBufferDescriptor(buffer); - const result = this._createAssetFromJson(buffer); - buffer.delete(); - return result; - }; - - Filament.gltfio$AssetLoader.prototype.createAssetFromBinary = function(buffer) { - if ('string' == typeof buffer && buffer.endsWith('.gltf')) { - console.error('Please use createAssetFromJson for gltf files.'); - } - buffer = getBufferDescriptor(buffer); - const result = this._createAssetFromBinary(buffer); + const result = this._createAsset(buffer); buffer.delete(); return result; }; diff --git a/web/filament-js/filament-viewer.js b/web/filament-js/filament-viewer.js index 01439952f4..714b78fc17 100644 --- a/web/filament-js/filament-viewer.js +++ b/web/filament-js/filament-viewer.js @@ -287,7 +287,7 @@ class FilamentViewer extends LitElement { // Dropping a glb file is simple because there are no external resources. if (this.srcBlob && this.srcBlob.name.endsWith(".glb")) { this.srcBlob.arrayBuffer().then(buffer => { - this.asset = this.loader.createAssetFromBinary(new Uint8Array(buffer)); + this.asset = this.loader.createAsset(new Uint8Array(buffer)); const aabb = this.asset.getBoundingBox(); this.assetRoot = this.asset.getRoot(); this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset); @@ -327,7 +327,7 @@ class FilamentViewer extends LitElement { }; this.srcBlob.arrayBuffer().then(buffer => { - this.asset = this.loader.createAssetFromJson(new Uint8Array(buffer)); + this.asset = this.loader.createAsset(new Uint8Array(buffer)); const aabb = this.asset.getBoundingBox(); this.assetRoot = this.asset.getRoot(); this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset); @@ -367,12 +367,7 @@ class FilamentViewer extends LitElement { return response.arrayBuffer(); }).then(arrayBuffer => { const modelData = new Uint8Array(arrayBuffer); - if (this.src.endsWith(".glb")) { - this.asset = this.loader.createAssetFromBinary(modelData); - } else { - this.asset = this.loader.createAssetFromJson(modelData); - } - + this.asset = this.loader.createAsset(modelData); const aabb = this.asset.getBoundingBox(); this.assetRoot = this.asset.getRoot(); this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset); diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 910b44c656..8fb78ba4ae 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -568,8 +568,7 @@ export class Ktx2Reader { } export class gltfio$AssetLoader { - public createAssetFromJson(urlOrBuffer: BufferReference): gltfio$FilamentAsset; - public createAssetFromBinary(urlOrBuffer: BufferReference): gltfio$FilamentAsset; + public createAsset(urlOrBuffer: BufferReference): gltfio$FilamentAsset; public createInstancedAsset(urlOrBuffer: BufferReference, instances: (gltfio$FilamentInstance | null)[]): gltfio$FilamentAsset; public destroyAsset(asset: gltfio$FilamentAsset): void; @@ -591,9 +590,6 @@ export class gltfio$FilamentAsset { public popRenderable(): Entity; public getMaterialInstances(): Vector; public getResourceUris(): Vector; - public getSkinNames(): Vector; - public attachSkin(skinIndex: number, entity: Entity): void; - public detachSkin(skinIndex: number, entity: Entity): void; public getBoundingBox(): Aabb; public getName(entity: Entity): string; public getExtras(entity: Entity): string; @@ -608,6 +604,9 @@ export class gltfio$FilamentInstance { public getEntities(): Vector; public getRoot(): Entity; public getAnimator(): gltfio$Animator; + public getSkinNames(): Vector; + public attachSkin(skinIndex: number, entity: Entity): void; + public detachSkin(skinIndex: number, entity: Entity): void; } export class gltfio$Animator { diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 6a6418592d..53be1a93a0 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -1785,17 +1785,6 @@ class_("gltfio$FilamentAsset") return retval; }), allow_raw_pointers()) - .function("getSkinNames", EMBIND_LAMBDA(std::vector, (FilamentAsset* self), { - std::vector names(self->getSkinCount()); - for (size_t i = 0; i < names.size(); ++i) { - names[i] = self->getSkinNameAt(i); - } - return names; - }), allow_raw_pointers()) - - .function("attachSkin", &FilamentAsset::attachSkin) - .function("detachSkin", &FilamentAsset::detachSkin) - .function("getBoundingBox", &FilamentAsset::getBoundingBox) .function("getName", EMBIND_LAMBDA(std::string, (FilamentAsset* self, utils::Entity entity), { return std::string(self->getName(entity)); @@ -1816,7 +1805,18 @@ class_("gltfio$FilamentInstance") }), allow_raw_pointers()) .function("getRoot", &FilamentInstance::getRoot) .function("applyMaterialVariant", &FilamentInstance::applyMaterialVariant) - .function("getAnimator", &FilamentInstance::getAnimator, allow_raw_pointers()); + .function("getAnimator", &FilamentInstance::getAnimator, allow_raw_pointers()) + + .function("getSkinNames", EMBIND_LAMBDA(std::vector, (FilamentInstance* self), { + std::vector names(self->getSkinCount()); + for (size_t i = 0; i < names.size(); ++i) { + names[i] = self->getSkinNameAt(i); + } + return names; + }), allow_raw_pointers()) + + .function("attachSkin", &FilamentInstance::attachSkin) + .function("detachSkin", &FilamentInstance::detachSkin); // These little wrappers exist to get around RTTI requirements in embind. @@ -1852,20 +1852,12 @@ class_("gltfio$AssetLoader") return AssetLoader::create({ engine, materials.provider, names }); }), allow_raw_pointers()) - /// createAssetFromJson ::method:: + /// createAsset ::method:: /// buffer ::argument:: asset string, or Uint8Array, or [Buffer] /// ::retval:: an instance of [FilamentAsset] - .function("_createAssetFromJson", EMBIND_LAMBDA(FilamentAsset*, + .function("_createAsset", EMBIND_LAMBDA(FilamentAsset*, (AssetLoader* self, BufferDescriptor buffer), { - return self->createAssetFromJson((const uint8_t*) buffer.bd->buffer, buffer.bd->size); - }), allow_raw_pointers()) - - /// createAssetFromBinary ::method:: - /// buffer ::argument:: asset string, or Uint8Array, or [Buffer] - /// ::retval:: an instance of [FilamentAsset] - .function("_createAssetFromBinary", EMBIND_LAMBDA(FilamentAsset*, - (AssetLoader* self, BufferDescriptor buffer), { - return self->createAssetFromBinary((const uint8_t*) buffer.bd->buffer, buffer.bd->size); + return self->createAsset((const uint8_t*) buffer.bd->buffer, buffer.bd->size); }), allow_raw_pointers()) /// createInstancedAsset ::method:: diff --git a/web/samples/animation.html b/web/samples/animation.html index b00ab8f008..a2c4409e4c 100644 --- a/web/samples/animation.html +++ b/web/samples/animation.html @@ -32,7 +32,7 @@ class App { const scene = this.scene = engine.createScene(); const loader = engine.createAssetLoader(); - const asset = this.asset = loader.createAssetFromJson(mesh_url); + const asset = this.asset = loader.createAsset(mesh_url); const sunlight = Filament.EntityManager.get().create(); Filament.LightManager.Builder(LightType.SUN).direction([0, 0, -1]).build(engine, sunlight); diff --git a/web/samples/helmet.html b/web/samples/helmet.html index 60b8751a3b..fe52f7eaf1 100644 --- a/web/samples/helmet.html +++ b/web/samples/helmet.html @@ -88,7 +88,7 @@ class App { const loader = this.loader = engine.createAssetLoader(); this.allowRefresh = false; - const asset = this.asset = loader.createAssetFromJson(mesh_url); + const asset = this.asset = loader.createAsset(mesh_url); this.assetRoot = this.asset.getRoot(); // Crudely indicate progress by printing the URI of each resource as it is loaded. @@ -138,7 +138,7 @@ class App { this.allowRefresh = false; this.scene.removeEntities(this.asset.getEntities()); this.loader.destroyAsset(this.asset); - this.asset = this.loader.createAssetFromJson(mesh_url); + this.asset = this.loader.createAsset(mesh_url); const onDone = () => { this.allowRefresh = true; } this.asset.loadResources(onDone); } diff --git a/web/samples/morphing.html b/web/samples/morphing.html index 6a5af723d9..2241b5df67 100644 --- a/web/samples/morphing.html +++ b/web/samples/morphing.html @@ -33,7 +33,7 @@ class App { this.trackball = new Trackball(canvas, {startSpin: 0.035}); const loader = engine.createAssetLoader(); - const asset = this.asset = loader.createAssetFromBinary(mesh_url); + const asset = this.asset = loader.createAsset(mesh_url); const onDone = () => { loader.delete();