From c7e4116154f3cfaaf4709213bbd9612ac387e84d Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Thu, 24 Jun 2021 08:57:01 -0700 Subject: [PATCH] gltfio: introduce support for extras strings. This allows clients to obtain the extras strings for the asset and for individual nodes. Note that extras for buffer views, accessors, etc are not supported, although C++ clients can access the raw cgltf data directly if they need to. --- .../src/main/cpp/FilamentAsset.cpp | 9 +++++ .../filament/gltfio/FilamentAsset.java | 11 +++++++ libs/gltfio/include/gltfio/FilamentAsset.h | 3 ++ libs/gltfio/src/AssetLoader.cpp | 33 ++++++++++++++----- libs/gltfio/src/FFilamentAsset.h | 8 ++++- libs/gltfio/src/FilamentAsset.cpp | 12 +++++++ web/filament-js/filament.d.ts | 1 + web/filament-js/jsbindings.cpp | 3 ++ 8 files changed, 70 insertions(+), 10 deletions(-) diff --git a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp index d4031df5c8..252af29582 100644 --- a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp +++ b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp @@ -192,6 +192,15 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetName(JNIEnv* env, jcla return val ? env->NewStringUTF(val) : nullptr; } +extern "C" JNIEXPORT jstring JNICALL +Java_com_google_android_filament_gltfio_FilamentAsset_nGetExtras(JNIEnv* env, jclass, + jlong nativeAsset, jint entityId) { + Entity entity = Entity::import(entityId); + FilamentAsset* asset = (FilamentAsset*) nativeAsset; + const auto val = asset->getExtras(entity); + return val ? env->NewStringUTF(val) : nullptr; +} + extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_gltfio_FilamentAsset_nGetAnimator(JNIEnv* , jclass, jlong 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 81b9560168..5f5105c2dd 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 @@ -186,6 +186,16 @@ public class FilamentAsset { return nGetName(getNativeObject(), entity); } + /** + * Gets the glTF extras string for the asset or a specific node. + * + * @param entity the entity corresponding to the glTF node, or 0 to get the asset-level string. + * @return the requested extras string, or null if it does not exist. + */ + public @Nullable String getExtras(@Entity int entity) { + return nGetExtras(mNativeObject, entity); + } + /** * Creates or retrieves the Animator interface for this asset. * @@ -252,6 +262,7 @@ public class FilamentAsset { 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 int nGetResourceUriCount(long nativeAsset); private static native void nGetResourceUris(long nativeAsset, String[] result); diff --git a/libs/gltfio/include/gltfio/FilamentAsset.h b/libs/gltfio/include/gltfio/FilamentAsset.h index e9ff11b96c..b26cf41514 100644 --- a/libs/gltfio/include/gltfio/FilamentAsset.h +++ b/libs/gltfio/include/gltfio/FilamentAsset.h @@ -189,6 +189,9 @@ public: size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities, size_t maxCount) const noexcept; + /** Gets the glTF extras string for a specific node, or for the asset, if it exists. */ + const char* getExtras(utils::Entity entity = {}) const noexcept; + /** * Lazily creates the animation engine or returns it from the cache. * diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index f64f8ed388..e0317d8189 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -46,9 +47,6 @@ #include -#include -#include - #define CGLTF_IMPLEMENTATION #include @@ -175,7 +173,7 @@ FFilamentAsset* FAssetLoader::createAssetFromJson(const uint8_t* bytes, uint32_t return mResult; } -FFilamentAsset* FAssetLoader::createAssetFromBinary(const uint8_t* bytes, uint32_t nbytes) { +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 @@ -184,11 +182,12 @@ FFilamentAsset* FAssetLoader::createAssetFromBinary(const uint8_t* bytes, uint32 // 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. - std::vector glbdata(bytes, bytes + nbytes); + 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(), nbytes, &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; @@ -200,7 +199,7 @@ FFilamentAsset* FAssetLoader::createAssetFromBinary(const uint8_t* bytes, uint32 return mResult; } -FFilamentAsset* FAssetLoader::createInstancedAsset(const uint8_t* bytes, uint32_t numBytes, +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."); @@ -211,10 +210,11 @@ FFilamentAsset* FAssetLoader::createInstancedAsset(const uint8_t* bytes, uint32_ // 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. - std::vector glbdata(bytes, bytes + numBytes); + utils::FixedCapacityVector glbdata(byteCount); + std::copy_n(bytes, byteCount, glbdata.data()); cgltf_data* sourceAsset; - cgltf_result result = cgltf_parse(&options, glbdata.data(), numBytes, &sourceAsset); + cgltf_result result = cgltf_parse(&options, glbdata.data(), byteCount, &sourceAsset); if (result != cgltf_result_success) { slog.e << "Unable to parse glTF file." << io::endl; return nullptr; @@ -281,6 +281,13 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances) mResult->mRoot = mEntityManager.create(); mTransformManager.create(mResult->mRoot); + // Check if the asset has an extras string. + const cgltf_asset& asset = srcAsset->asset; + const cgltf_size extras_size = asset.extras.end_offset - asset.extras.start_offset; + if (extras_size > 1) { + mResult->mAssetExtras = CString(srcAsset->json + asset.extras.start_offset, extras_size); + } + if (numInstances == 0) { // For each scene root, recursively create all entities. for (cgltf_size i = 0, len = scene->nodes_count; i < len; ++i) { @@ -364,6 +371,14 @@ void FAssetLoader::createEntity(const cgltf_node* node, Entity parent, bool enab auto parentTransform = mTransformManager.getInstance(parent); mTransformManager.create(entity, parentTransform, localTransform); + // Check if this node has an extras string. + const cgltf_size extras_size = node->extras.end_offset - node->extras.start_offset; + if (extras_size > 0) { + const cgltf_data* srcAsset = mResult->mSourceAsset->hierarchy; + mResult->mNodeExtras[entity] = CString( + srcAsset->json + node->extras.start_offset, extras_size); + } + // Update the asset's entity list and private node mapping. mResult->mEntities.push_back(entity); if (instance) { diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index b1857e6ae0..c548d20bfc 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -32,6 +32,8 @@ #include +#include +#include #include #include @@ -180,6 +182,8 @@ struct FFilamentAsset : public FilamentAsset { const char* getName(utils::Entity entity) const noexcept; + const char* getExtras(utils::Entity entity) const noexcept; + utils::Entity getFirstEntityByName(const char* name) noexcept; size_t getEntitiesByName(const char* name, utils::Entity* entities, @@ -243,6 +247,8 @@ struct FFilamentAsset : public FilamentAsset { bool mResourcesLoaded = false; DependencyGraph mDependencyGraph; tsl::htrie_map> mNameToEntity; + tsl::robin_map mNodeExtras; + utils::CString mAssetExtras; // Sentinels for situations where ResourceLoader needs to generate data. const cgltf_accessor mGenerateNormals = {}; @@ -254,7 +260,7 @@ struct FFilamentAsset : public FilamentAsset { ~SourceAsset() { cgltf_free(hierarchy); } cgltf_data* hierarchy; DracoCache dracoCache; - std::vector glbData; + utils::FixedCapacityVector glbData; }; // We used shared ownership for the raw cgltf data in order to permit ResourceLoader to diff --git a/libs/gltfio/src/FilamentAsset.cpp b/libs/gltfio/src/FilamentAsset.cpp index 5e3200b254..ed6a9a25ef 100644 --- a/libs/gltfio/src/FilamentAsset.cpp +++ b/libs/gltfio/src/FilamentAsset.cpp @@ -72,6 +72,14 @@ FFilamentAsset::~FFilamentAsset() { } } +const char* FFilamentAsset::getExtras(utils::Entity entity) const noexcept { + if (entity.isNull()) { + return mAssetExtras.c_str(); + } + const auto iter = mNodeExtras.find(entity); + return iter == mNodeExtras.cend() ? nullptr : iter->second.c_str(); +} + Animator* FFilamentAsset::getAnimator() noexcept { if (!mAnimator) { if (!mResourcesLoaded) { @@ -258,6 +266,10 @@ size_t FilamentAsset::getEntitiesByPrefix(const char* prefix, Entity* entities, return upcast(this)->getEntitiesByPrefix(prefix, entities, maxCount); } +const char* FilamentAsset::getExtras(Entity entity) const noexcept { + return upcast(this)->getExtras(entity); +} + Animator* FilamentAsset::getAnimator() noexcept { return upcast(this)->getAnimator(); } diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 462e87b4cd..6bcd27fde5 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -613,6 +613,7 @@ export class gltfio$FilamentAsset { public getResourceUris(): Vector; public getBoundingBox(): Aabb; public getName(entity: Entity): string; + public getExtras(entity: Entity): string; public getAnimator(): gltfio$Animator; public getWireframe(): Entity; public getEngine(): Engine; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 11da30a794..c91a50ecc4 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -1804,6 +1804,9 @@ class_("gltfio$FilamentAsset") .function("getName", EMBIND_LAMBDA(std::string, (FilamentAsset* self, utils::Entity entity), { return std::string(self->getName(entity)); }), allow_raw_pointers()) + .function("getExtras", EMBIND_LAMBDA(std::string, (FilamentAsset* self, utils::Entity entity), { + return std::string(self->getExtras(entity)); + }), allow_raw_pointers()) .function("getAnimator", &FilamentAsset::getAnimator, allow_raw_pointers()) .function("getWireframe", &FilamentAsset::getWireframe) .function("getEngine", &FilamentAsset::getEngine, allow_raw_pointers())