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())