From 6afb3c06d2243b7373adcf3d76869694fed107da Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 18 Oct 2023 14:48:13 -0400 Subject: [PATCH] Working --- apps/cesium.omniverse.dev.kit | 2 +- exts/cesium.omniverse/mdl/cesium.mdl | 80 +++++++ .../include/cesium/omniverse/FabricMaterial.h | 7 +- .../omniverse/FabricMaterialDefinition.h | 10 +- .../cesium/omniverse/FabricResourceManager.h | 8 +- .../include/cesium/omniverse/FabricUtil.h | 5 + src/core/include/cesium/omniverse/Tokens.h | 2 + src/core/src/FabricMaterial.cpp | 61 ++++- src/core/src/FabricMaterialDefinition.cpp | 16 +- src/core/src/FabricPrepareRenderResources.cpp | 4 +- src/core/src/FabricResourceManager.cpp | 7 +- src/core/src/FabricUtil.cpp | 208 ++++++++++++++++++ 12 files changed, 386 insertions(+), 24 deletions(-) diff --git a/apps/cesium.omniverse.dev.kit b/apps/cesium.omniverse.dev.kit index c050db6d0..0149abe8d 100644 --- a/apps/cesium.omniverse.dev.kit +++ b/apps/cesium.omniverse.dev.kit @@ -14,7 +14,7 @@ app = true [settings] app.window.title = "Cesium for Omniverse Testing App" -app.useFabricSceneDelegate = true +app.useFabricSceneDelegate = false app.usdrt.scene_delegate.enableProxyCubes = false app.usdrt.scene_delegate.geometryStreaming.enabled = false omnihydra.parallelHydraSprimSync = false diff --git a/exts/cesium.omniverse/mdl/cesium.mdl b/exts/cesium.omniverse/mdl/cesium.mdl index 0c5c95190..ec65fb650 100644 --- a/exts/cesium.omniverse/mdl/cesium.mdl +++ b/exts/cesium.omniverse/mdl/cesium.mdl @@ -11,6 +11,86 @@ module [[ anno::display_name("Cesium MDL functions") ]]; +export enum up_axis_mode { + Y, + Z +}; + +float2 world_coordinate_2d(float2 min_world, float2 max_world, up_axis_mode up_axis) +{ + // Get the world pos of the pixel + float3 world_pos = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::position()); + float2 world_pos_horizontal = float2(world_pos.x, up_axis == Y ? world_pos.z : world_pos.y); + + // Return 0-1 UVs based on the min/max world coordinates provided + return (world_pos_horizontal - min_world) / (max_world - min_world); +} + +export float4 cesium_lookup_world_texture_float4(uniform texture_2d texture = texture_2d(), float2 min_world = float2(-5000.0, -5000.0), float2 max_world = float2(5000.0, 5000.0), up_axis_mode up_axis = Y) +[[ + anno::display_name("World-mapped texture lookup float4"), + anno::description("Returns float4 from a texture mapped to world UV coordinates"), + anno::author("Cesium GS Inc."), + anno::in_group("Cesium functions"), + anno::ui_order(300) +]] +{ + return tex::lookup_float4( + tex: texture, + coord: world_coordinate_2d(min_world, max_world, up_axis), + wrap_u: ::tex::wrap_clamp, + wrap_v: ::tex::wrap_clamp); +} + +export float3 cesium_lookup_world_texture_float3(uniform texture_2d texture = texture_2d(), float2 min_world = float2(-5000.0, -5000.0), float2 max_world = float2(5000.0, 5000.0), up_axis_mode up_axis = Y) +[[ + anno::display_name("World-mapped texture lookup float3"), + anno::description("Returns float3 from a texture mapped to world UV coordinates"), + anno::author("Cesium GS Inc."), + anno::in_group("Cesium functions"), + anno::ui_order(300) +]] +{ + return tex::lookup_float3( + tex: texture, + coord: world_coordinate_2d(min_world, max_world, up_axis), + wrap_u: ::tex::wrap_clamp, + wrap_v: ::tex::wrap_clamp); +} + +export color cesium_lookup_world_texture_color(uniform texture_2d texture = texture_2d(), float2 min_world = float2(-5000.0, -5000.0), float2 max_world = float2(5000.0, 5000.0), up_axis_mode up_axis = Y) +[[ + anno::display_name("World-mapped texture lookup color"), + anno::description("Returns color from a texture mapped to world UV coordinates"), + anno::author("Cesium GS Inc."), + anno::in_group("Cesium functions"), + anno::ui_order(300) +]] +{ + return tex::lookup_color( + tex: texture, + coord: world_coordinate_2d(min_world, max_world, up_axis), + wrap_u: ::tex::wrap_clamp, + wrap_v: ::tex::wrap_clamp); +} + +export color cesium_base_color_texture( + gltf_texture_lookup_value base_color_texture + [[ + anno::hidden() + ]] +) +[[ + anno::display_name("Base color texture lookup color"), + anno::description("Returns the base color texture of the tile. Returns [0, 0, 0] if the base color texture does not exist."), + anno::author("Cesium GS Inc."), + anno::in_group("Cesium functions"), + anno::ui_order(300) +]] +{ + return base_color_texture.valid ? color(base_color_texture.value.x, base_color_texture.value.y, base_color_texture.value.z) : color(0.0); +} + float4 alpha_blend(float4 src, float4 dst) { return src * float4(src.w, src.w, src.w, 1.0) + dst * (1.0 - src.w); } diff --git a/src/core/include/cesium/omniverse/FabricMaterial.h b/src/core/include/cesium/omniverse/FabricMaterial.h index ebe26343b..08b95c9d0 100644 --- a/src/core/include/cesium/omniverse/FabricMaterial.h +++ b/src/core/include/cesium/omniverse/FabricMaterial.h @@ -47,6 +47,7 @@ class FabricMaterial { private: void initialize(); + void initializeFromExistingMaterial(const omni::fabric::Path& path); void createMaterial(const omni::fabric::Path& materialPath); void createShader(const omni::fabric::Path& shaderPath, const omni::fabric::Path& materialPath); @@ -74,9 +75,9 @@ class FabricMaterial { const pxr::TfToken _defaultTransparentTextureAssetPathToken; const long _stageId; - omni::fabric::Path _shaderPath; - omni::fabric::Path _baseColorTexturePath; - std::vector _imageryLayerPaths; + std::vector _shaderPaths; + std::vector _baseColorTexturePaths; + std::vector> _imageryLayerPaths; std::vector _allPaths; }; diff --git a/src/core/include/cesium/omniverse/FabricMaterialDefinition.h b/src/core/include/cesium/omniverse/FabricMaterialDefinition.h index b59219159..e2f17ad9a 100644 --- a/src/core/include/cesium/omniverse/FabricMaterialDefinition.h +++ b/src/core/include/cesium/omniverse/FabricMaterialDefinition.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace cesium::omniverse { @@ -10,11 +11,17 @@ struct MaterialInfo; class FabricMaterialDefinition { public: - FabricMaterialDefinition(const MaterialInfo& materialInfo, uint64_t imageryLayerCount, bool disableTextures); + FabricMaterialDefinition( + const MaterialInfo& materialInfo, + uint64_t imageryLayerCount, + bool disableTextures, + const pxr::SdfPath& tilesetMaterialPath); [[nodiscard]] bool hasVertexColors() const; [[nodiscard]] bool hasBaseColorTexture() const; [[nodiscard]] uint64_t getImageryLayerCount() const; + [[nodiscard]] bool hasTilesetMaterial() const; + [[nodiscard]] const pxr::SdfPath& getTilesetMaterialPath() const; // Make sure to update this function when adding new fields to the class bool operator==(const FabricMaterialDefinition& other) const; @@ -23,6 +30,7 @@ class FabricMaterialDefinition { bool _hasVertexColors; bool _hasBaseColorTexture; uint64_t _imageryLayerCount; + pxr::SdfPath _tilesetMaterialPath; }; } // namespace cesium::omniverse diff --git a/src/core/include/cesium/omniverse/FabricResourceManager.h b/src/core/include/cesium/omniverse/FabricResourceManager.h index fa405963c..6596a8d62 100644 --- a/src/core/include/cesium/omniverse/FabricResourceManager.h +++ b/src/core/include/cesium/omniverse/FabricResourceManager.h @@ -61,8 +61,12 @@ class FabricResourceManager { bool smoothNormals, long stageId); - std::shared_ptr - acquireMaterial(const MaterialInfo& materialInfo, uint64_t imageryLayerCount, long stageId, int64_t tilesetId); + std::shared_ptr acquireMaterial( + const MaterialInfo& materialInfo, + uint64_t imageryLayerCount, + long stageId, + int64_t tilesetId, + const pxr::SdfPath& tilesetMaterialPath); std::shared_ptr acquireTexture(); diff --git a/src/core/include/cesium/omniverse/FabricUtil.h b/src/core/include/cesium/omniverse/FabricUtil.h index 04ccc1b4b..702cfbf68 100644 --- a/src/core/include/cesium/omniverse/FabricUtil.h +++ b/src/core/include/cesium/omniverse/FabricUtil.h @@ -35,5 +35,10 @@ void setTilesetId(const omni::fabric::Path& path, int64_t tilesetId); omni::fabric::Path toFabricPath(const pxr::SdfPath& path); omni::fabric::Path joinPaths(const omni::fabric::Path& absolutePath, const omni::fabric::Token& relativePath); bool isEmpty(const omni::fabric::Path& path); +std::vector +copyMaterial(const omni::fabric::Path& srcMaterialPath, const omni::fabric::Path& dstMaterialPath); +bool materialHasCesiumNodes(const omni::fabric::Path& path); +bool isCesiumNode(const omni::fabric::Token& mdlIdentifier); +omni::fabric::Token getMdlIdentifier(const omni::fabric::Path& path); } // namespace cesium::omniverse::FabricUtil diff --git a/src/core/include/cesium/omniverse/Tokens.h b/src/core/include/cesium/omniverse/Tokens.h index bf6bdb204..eaeed4965 100644 --- a/src/core/include/cesium/omniverse/Tokens.h +++ b/src/core/include/cesium/omniverse/Tokens.h @@ -15,6 +15,8 @@ __pragma(warning(push)) __pragma(warning(disable : 4003)) #define USD_TOKENS \ (base_color_texture) \ (cesium) \ + (cesium_base_color_texture) \ + (cesium_imagery_layer) \ (cesium_imagery_layer_resolver) \ (cesium_material) \ (cesium_texture_lookup) \ diff --git a/src/core/src/FabricMaterial.cpp b/src/core/src/FabricMaterial.cpp index 57ffae6f0..bc686f4e6 100644 --- a/src/core/src/FabricMaterial.cpp +++ b/src/core/src/FabricMaterial.cpp @@ -49,7 +49,13 @@ FabricMaterial::FabricMaterial( return; } - initialize(); + if (materialDefinition.hasTilesetMaterial()) { + const auto tilesetMaterialPath = FabricUtil::toFabricPath(materialDefinition.getTilesetMaterialPath()); + initializeFromExistingMaterial(tilesetMaterialPath); + } else { + initialize(); + } + reset(); } @@ -88,13 +94,13 @@ void FabricMaterial::initialize() { const auto shaderPath = FabricUtil::joinPaths(materialPath, FabricTokens::Shader); createShader(shaderPath, materialPath); - _shaderPath = shaderPath; + _shaderPaths.push_back(shaderPath); _allPaths.push_back(shaderPath); if (_materialDefinition.hasBaseColorTexture()) { const auto baseColorTexturePath = FabricUtil::joinPaths(materialPath, FabricTokens::base_color_texture); createTexture(baseColorTexturePath, shaderPath, FabricTokens::inputs_base_color_texture); - _baseColorTexturePath = baseColorTexturePath; + _baseColorTexturePaths.push_back(baseColorTexturePath); _allPaths.push_back(baseColorTexturePath); } @@ -103,7 +109,7 @@ void FabricMaterial::initialize() { if (imageryLayerCount == 1) { const auto imageryLayerPath = FabricUtil::joinPaths(materialPath, FabricTokens::imagery_layer_n[0]); createTexture(imageryLayerPath, shaderPath, FabricTokens::inputs_imagery_layers_texture); - _imageryLayerPaths.push_back(imageryLayerPath); + _imageryLayerPaths.push_back({imageryLayerPath}); _allPaths.push_back(imageryLayerPath); } else if (imageryLayerCount > 1) { const auto imageryLayerResolverPath = FabricUtil::joinPaths(materialPath, FabricTokens::imagery_layer_resolver); @@ -115,7 +121,7 @@ void FabricMaterial::initialize() { for (uint64_t i = 0; i < imageryLayerCount; i++) { const auto imageryLayerPath = FabricUtil::joinPaths(materialPath, FabricTokens::imagery_layer_n[i]); createTexture(imageryLayerPath, imageryLayerResolverPath, FabricTokens::inputs_imagery_layer_n[i]); - _imageryLayerPaths.push_back(imageryLayerPath); + _imageryLayerPaths.push_back({imageryLayerPath}); _allPaths.push_back(imageryLayerPath); } } @@ -125,6 +131,37 @@ void FabricMaterial::initialize() { } } +void FabricMaterial::initializeFromExistingMaterial(const omni::fabric::Path& srcMaterialPath) { + auto srw = UsdUtil::getFabricStageReaderWriter(); + + const auto& dstMaterialPath = _materialPath; + + const auto dstPaths = FabricUtil::copyMaterial(srcMaterialPath, dstMaterialPath); + + for (const auto& dstPath : dstPaths) { + srw.createAttribute(dstPath, FabricTokens::_cesium_tilesetId, FabricTypes::_cesium_tilesetId); + _allPaths.push_back(dstPath); + + const auto mdlIdentifier = FabricUtil::getMdlIdentifier(dstPath); + + if (mdlIdentifier == FabricTokens::cesium_base_color_texture) { + if (_materialDefinition.hasBaseColorTexture()) { + // Create a base color texture node to fill the empty slot + const auto baseColorTexturePath = + FabricUtil::joinPaths(dstMaterialPath, FabricTokens::base_color_texture); + createTexture(baseColorTexturePath, dstPath, FabricTokens::inputs_base_color_texture); + _allPaths.push_back(baseColorTexturePath); + _baseColorTexturePaths.push_back(baseColorTexturePath); + FabricResourceManager::getInstance().retainPath(baseColorTexturePath); + } + } + } + + for (const auto& path : _allPaths) { + FabricResourceManager::getInstance().retainPath(path); + } +} + void FabricMaterial::createMaterial(const omni::fabric::Path& materialPath) { auto srw = UsdUtil::getFabricStageReaderWriter(); srw.createPrim(materialPath); @@ -324,7 +361,9 @@ void FabricMaterial::setMaterial(int64_t tilesetId, const MaterialInfo& material auto srw = UsdUtil::getFabricStageReaderWriter(); - setShaderValues(_shaderPath, materialInfo); + for (auto& shaderPath : _shaderPaths) { + setShaderValues(shaderPath, materialInfo); + } for (const auto& path : _allPaths) { FabricUtil::setTilesetId(path, tilesetId); @@ -339,11 +378,9 @@ void FabricMaterial::setBaseColorTexture( return; } - if (FabricUtil::isEmpty(_baseColorTexturePath)) { - return; + for (auto& baseColorTexturePath : _baseColorTexturePaths) { + setTextureValues(baseColorTexturePath, textureAssetPathToken, textureInfo, texcoordIndex); } - - setTextureValues(_baseColorTexturePath, textureAssetPathToken, textureInfo, texcoordIndex); } void FabricMaterial::setImageryLayer( @@ -359,7 +396,9 @@ void FabricMaterial::setImageryLayer( return; } - setTextureValues(_imageryLayerPaths[imageryIndex], textureAssetPathToken, textureInfo, texcoordIndex); + for (auto& imageryLayerPath : _imageryLayerPaths[imageryIndex]) { + setTextureValues(imageryLayerPath, textureAssetPathToken, textureInfo, texcoordIndex); + } } void FabricMaterial::clearMaterial() { diff --git a/src/core/src/FabricMaterialDefinition.cpp b/src/core/src/FabricMaterialDefinition.cpp index 61e8bf05f..b726bd93c 100644 --- a/src/core/src/FabricMaterialDefinition.cpp +++ b/src/core/src/FabricMaterialDefinition.cpp @@ -14,7 +14,8 @@ namespace cesium::omniverse { FabricMaterialDefinition::FabricMaterialDefinition( const MaterialInfo& materialInfo, uint64_t imageryLayerCount, - bool disableTextures) { + bool disableTextures, + const pxr::SdfPath& tilesetMaterialPath) { auto hasBaseColorTexture = materialInfo.baseColorTexture.has_value(); @@ -26,6 +27,7 @@ FabricMaterialDefinition::FabricMaterialDefinition( _hasVertexColors = materialInfo.hasVertexColors; _hasBaseColorTexture = hasBaseColorTexture; _imageryLayerCount = imageryLayerCount; + _tilesetMaterialPath = tilesetMaterialPath; } bool FabricMaterialDefinition::hasVertexColors() const { @@ -40,6 +42,14 @@ uint64_t FabricMaterialDefinition::getImageryLayerCount() const { return _imageryLayerCount; } +bool FabricMaterialDefinition::hasTilesetMaterial() const { + return !_tilesetMaterialPath.IsEmpty(); +} + +const pxr::SdfPath& FabricMaterialDefinition::getTilesetMaterialPath() const { + return _tilesetMaterialPath; +} + // In C++ 20 we can use the default equality comparison (= default) bool FabricMaterialDefinition::operator==(const FabricMaterialDefinition& other) const { if (_hasVertexColors != other._hasVertexColors) { @@ -54,6 +64,10 @@ bool FabricMaterialDefinition::operator==(const FabricMaterialDefinition& other) return false; } + if (_tilesetMaterialPath != other._tilesetMaterialPath) { + return false; + } + return true; } diff --git a/src/core/src/FabricPrepareRenderResources.cpp b/src/core/src/FabricPrepareRenderResources.cpp index ecf679e06..48aec0fff 100644 --- a/src/core/src/FabricPrepareRenderResources.cpp +++ b/src/core/src/FabricPrepareRenderResources.cpp @@ -127,8 +127,8 @@ std::vector acquireFabricMeshes( if (shouldAcquireMaterial) { const auto materialInfo = GltfUtil::getMaterialInfo(model, primitive); - const auto fabricMaterial = - fabricResourceManager.acquireMaterial(materialInfo, imageryLayerCount, stageId, tileset.getTilesetId()); + const auto fabricMaterial = fabricResourceManager.acquireMaterial( + materialInfo, imageryLayerCount, stageId, tileset.getTilesetId(), tilesetMaterialPath); fabricMesh.material = fabricMaterial; fabricMesh.materialInfo = materialInfo; diff --git a/src/core/src/FabricResourceManager.cpp b/src/core/src/FabricResourceManager.cpp index c87be8af8..bfea28984 100644 --- a/src/core/src/FabricResourceManager.cpp +++ b/src/core/src/FabricResourceManager.cpp @@ -56,7 +56,7 @@ bool FabricResourceManager::shouldAcquireMaterial( } if (!tilesetMaterialPath.IsEmpty()) { - return false; + return FabricUtil::materialHasCesiumNodes(FabricUtil::toFabricPath(tilesetMaterialPath)); } return hasImagery || GltfUtil::hasMaterial(primitive); @@ -176,8 +176,9 @@ std::shared_ptr FabricResourceManager::acquireMaterial( const MaterialInfo& materialInfo, uint64_t imageryLayerCount, long stageId, - int64_t tilesetId) { - FabricMaterialDefinition materialDefinition(materialInfo, imageryLayerCount, _disableTextures); + int64_t tilesetId, + const pxr::SdfPath& tilesetMaterialPath) { + FabricMaterialDefinition materialDefinition(materialInfo, imageryLayerCount, _disableTextures, tilesetMaterialPath); if (useSharedMaterial(materialDefinition)) { return acquireSharedMaterial(materialInfo, materialDefinition, stageId, tilesetId); diff --git a/src/core/src/FabricUtil.cpp b/src/core/src/FabricUtil.cpp index 218f08fb5..8f3f12c63 100644 --- a/src/core/src/FabricUtil.cpp +++ b/src/core/src/FabricUtil.cpp @@ -684,4 +684,212 @@ bool isEmpty(const omni::fabric::Path& path) { return omni::fabric::PathC(path) == omni::fabric::kUninitializedPath; } +namespace { + +struct FabricConnection { + omni::fabric::Connection* connection; + omni::fabric::Token attributeName; +}; + +std::vector getConnections(const omni::fabric::Path& path) { + std::vector connections; + + auto srw = UsdUtil::getFabricStageReaderWriter(); + const auto attributes = srw.getAttributeNamesAndTypes(path); + const auto& names = attributes.first; + const auto& types = attributes.second; + + for (size_t i = 0; i < names.size(); i++) { + const auto& name = names[i]; + const auto& type = types[i]; + if (type.baseType == omni::fabric::BaseDataType::eConnection) { + const auto connection = srw.getConnection(path, name); + if (connection != nullptr) { + connections.push_back({connection, name}); + } + } + } + + return connections; +} + +bool isOutput(const omni::fabric::Token& attributeName) { + return attributeName == FabricTokens::outputs_out; +} + +bool isConnection(const omni::fabric::Type& attributeType) { + return attributeType.baseType == omni::fabric::BaseDataType::eConnection; +} + +bool isEmptyToken( + const omni::fabric::Path& path, + const omni::fabric::Token& attributeName, + const omni::fabric::Type& attributeType) { + auto srw = UsdUtil::getFabricStageReaderWriter(); + if (attributeType.baseType == omni::fabric::BaseDataType::eToken) { + const auto attributeValue = srw.getAttributeRd(path, attributeName); + if (attributeValue == nullptr || (*attributeValue).size() == 0) { + return true; + } + } + + return false; +} + +std::vector getAttributesToCopy(const omni::fabric::Path& path) { + std::vector attributeNames; + + auto srw = UsdUtil::getFabricStageReaderWriter(); + + const auto attributes = srw.getAttributeNamesAndTypes(path); + const auto& names = attributes.first; + const auto& types = attributes.second; + + for (size_t i = 0; i < names.size(); i++) { + const auto& name = names[i]; + const auto& type = types[i]; + + if (!isOutput(name) && !isConnection(type) && !isEmptyToken(path, name, type)) { + attributeNames.emplace_back(omni::fabric::TokenC(name)); + } + } + + return attributeNames; +} + +struct FabricAttribute { + omni::fabric::Token name; + omni::fabric::Type type; +}; + +std::vector getAttributesToCreate(const omni::fabric::Path& path) { + std::vector attributeNames; + + auto srw = UsdUtil::getFabricStageReaderWriter(); + + const auto attributes = srw.getAttributeNamesAndTypes(path); + const auto& names = attributes.first; + const auto& types = attributes.second; + + for (size_t i = 0; i < names.size(); i++) { + const auto& name = names[i]; + const auto& type = types[i]; + + if (isOutput(name) || isEmptyToken(path, name, type)) { + attributeNames.emplace_back(FabricAttribute{name, type}); + } + } + + return attributeNames; +} + +void getConnectedPrimsRecursive(const omni::fabric::Path& path, std::vector& connectedPaths) { + const auto connections = getConnections(path); + for (const auto& connection : connections) { + const auto it = std::find(connectedPaths.begin(), connectedPaths.end(), connection.connection->path); + if (it == connectedPaths.end()) { + connectedPaths.emplace_back(connection.connection->path); + getConnectedPrimsRecursive(connection.connection->path, connectedPaths); + } + } +} + +std::vector getPrimsInMaterialNetwork(const omni::fabric::Path& path) { + auto srw = UsdUtil::getFabricStageReaderWriter(); + std::vector paths; + paths.push_back(path); + getConnectedPrimsRecursive(path, paths); + return paths; +} + +} // namespace + +std::vector +copyMaterial(const omni::fabric::Path& srcMaterialPath, const omni::fabric::Path& dstMaterialPath) { + auto srw = UsdUtil::getFabricStageReaderWriter(); + const auto isrw = carb::getCachedInterface(); + + const auto srcPaths = getPrimsInMaterialNetwork(srcMaterialPath); + + std::vector dstPaths; + dstPaths.reserve(srcPaths.size()); + + for (const auto& srcPath : srcPaths) { + auto dstPath = omni::fabric::Path(); + + if (srcPath == srcMaterialPath) { + dstPath = dstMaterialPath; + } else { + dstPath = FabricUtil::joinPaths( + dstMaterialPath, omni::fabric::Token(UsdUtil::getSafeName(srcPath.getText()).c_str())); + } + + dstPaths.push_back(dstPath); + + srw.createPrim(dstPath); + + // This excludes connections, outputs, and empty tokens + // The material network will be reconnected later once all the prims have been copied + // The reason for excluding outputs and empty tokens is so that Omniverse doesn't print the warning + // [Warning] [omni.fabric.plugin] Warning: input has no valid data" + const auto attributesToCopy = getAttributesToCopy(srcPath); + + isrw->copySpecifiedAttributes( + srw.getId(), srcPath, attributesToCopy.data(), dstPath, attributesToCopy.data(), attributesToCopy.size()); + + // Add the outputs and empty tokens back. This doesn't print a warning. + const auto attributesToCreate = getAttributesToCreate(srcPath); + for (const auto& attribute : attributesToCreate) { + srw.createAttribute(dstPath, attribute.name, attribute.type); + } + } + + // Reconnect the prims + for (size_t i = 0; i < srcPaths.size(); i++) { + const auto& srcPath = srcPaths[i]; + const auto& dstPath = dstPaths[i]; + const auto connections = getConnections(srcPath); + for (const auto& connection : connections) { + const auto it = std::find(srcPaths.begin(), srcPaths.end(), connection.connection->path); + assert(it != srcPaths.end()); // Ensure that all connections are part of the material network + const auto index = it - srcPaths.begin(); + const auto dstConnection = + omni::fabric::Connection{omni::fabric::PathC(dstPaths[index]), connection.connection->attrName}; + srw.createConnection(dstPath, connection.attributeName, dstConnection); + } + } + + return dstPaths; +} + +bool materialHasCesiumNodes(const omni::fabric::Path& path) { + auto srw = UsdUtil::getFabricStageReaderWriter(); + const auto paths = getPrimsInMaterialNetwork(path); + + for (const auto& p : paths) { + const auto mdlIdentifier = getMdlIdentifier(p); + if (isCesiumNode(mdlIdentifier)) { + return true; + } + } + + return false; +} + +bool isCesiumNode(const omni::fabric::Token& mdlIdentifier) { + return mdlIdentifier == FabricTokens::cesium_base_color_texture; +} + +omni::fabric::Token getMdlIdentifier(const omni::fabric::Path& path) { + auto srw = UsdUtil::getFabricStageReaderWriter(); + if (srw.attributeExists(path, FabricTokens::info_mdl_sourceAsset_subIdentifier)) { + const auto infoMdlSourceAssetSubIdentifierFabric = + srw.getAttributeRd(path, FabricTokens::info_mdl_sourceAsset_subIdentifier); + if (infoMdlSourceAssetSubIdentifierFabric != nullptr) { + return *infoMdlSourceAssetSubIdentifierFabric; + } + } + return omni::fabric::Token{}; +} + } // namespace cesium::omniverse::FabricUtil