diff --git a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp index 26ec2cb82..a75563ad1 100644 --- a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp +++ b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp @@ -19,6 +19,7 @@ #include "CesiumGeospatial/GlobeTransforms.h" #include "CesiumGltf/ImageCesium.h" #include "CesiumGltf/Ktx2TranscodeTargets.h" +#include "CesiumGltf/SharedAssetDepot.h" #include "CesiumGltfComponent.h" #include "CesiumGltfPointsSceneProxyUpdater.h" #include "CesiumGltfPrimitiveComponent.h" @@ -792,8 +793,7 @@ class UnrealResourcePreparer pOptions->group, // TODO: sRGB should probably be configurable on the raster overlay. true, - std::nullopt, - nullptr); + std::nullopt); return texture.Release(); } diff --git a/Source/CesiumRuntime/Private/CesiumEncodedFeaturesMetadata.cpp b/Source/CesiumRuntime/Private/CesiumEncodedFeaturesMetadata.cpp index d419da552..97e53942b 100644 --- a/Source/CesiumRuntime/Private/CesiumEncodedFeaturesMetadata.cpp +++ b/Source/CesiumRuntime/Private/CesiumEncodedFeaturesMetadata.cpp @@ -153,8 +153,7 @@ std::optional encodeFeatureIdTexture( TEXTUREGROUP_8BitData, false, // TODO: currently this is always the case, but doesn't have to be - EPixelFormat::PF_R8G8B8A8_UINT, - nullptr))); + EPixelFormat::PF_R8G8B8A8_UINT))); featureIdTextureMap.Emplace( pFeatureIdImage, encodedFeatureIdTexture.pTexture); @@ -568,16 +567,16 @@ EncodedPropertyTable encodePropertyTableAnyThreadPart( encodedFormat.bytesPerChannel * encodedFormat.channels); } + CesiumGltf::ImageCesium imageCopy(image); encodedProperty.pTexture = loadTextureAnyThreadPart( - image, + imageCopy, TextureAddress::TA_Clamp, TextureAddress::TA_Clamp, TextureFilter::TF_Nearest, false, TEXTUREGROUP_8BitData, false, - encodedFormat.format, - nullptr); + encodedFormat.format); } if (pDescription->PropertyDetails.bHasOffset) { @@ -706,8 +705,7 @@ EncodedPropertyTexture encodePropertyTextureAnyThreadPart( false, // This assumes that the texture's image only contains one byte // per channel. - EPixelFormat::PF_R8G8B8A8_UINT, - nullptr))); + EPixelFormat::PF_R8G8B8A8_UINT))); propertyTexturePropertyMap.Emplace(pImage, encodedProperty.pTexture); } }; diff --git a/Source/CesiumRuntime/Private/CesiumEncodedMetadataUtility.cpp b/Source/CesiumRuntime/Private/CesiumEncodedMetadataUtility.cpp index 53fe9a576..4ec4068f8 100644 --- a/Source/CesiumRuntime/Private/CesiumEncodedMetadataUtility.cpp +++ b/Source/CesiumRuntime/Private/CesiumEncodedMetadataUtility.cpp @@ -277,16 +277,16 @@ EncodedMetadataFeatureTable encodeMetadataFeatureTableAnyThreadPart( } } + CesiumGltf::ImageCesium imageCopy(image); encodedProperty.pTexture = loadTextureAnyThreadPart( - image, + imageCopy, TextureAddress::TA_Clamp, TextureAddress::TA_Clamp, TextureFilter::TF_Nearest, false, TEXTUREGROUP_8BitData, false, - encodedFormat.format, - nullptr); + encodedFormat.format); } return encodedFeatureTable; @@ -425,8 +425,7 @@ EncodedFeatureTexture encodeFeatureTextureAnyThreadPart( // R8G8B8A8 form, but this does not necessarily need to be the // case in the future. isNormalized ? EPixelFormat::PF_R8G8B8A8 - : EPixelFormat::PF_R8G8B8A8_UINT, - nullptr))); + : EPixelFormat::PF_R8G8B8A8_UINT))); featureTexturePropertyMap.Emplace( pImage, encodedFeatureTextureProperty.pTexture); @@ -529,8 +528,7 @@ EncodedMetadataPrimitive encodeMetadataPrimitiveAnyThreadPart( false, // TODO: currently this is always the case, but doesn't have // to be - EPixelFormat::PF_R8G8B8A8_UINT, - nullptr))); + EPixelFormat::PF_R8G8B8A8_UINT))); featureIdTextureMap.Emplace( pFeatureIdImage, encodedFeatureIdTexture.pTexture); diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index 036f52816..f0d6a9dd8 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -401,8 +401,7 @@ template static TUniquePtr loadTexture( CesiumGltf::Model& model, const std::optional& gltfTexture, - bool sRGB, - std::vector& textureResources) { + bool sRGB) { if (!gltfTexture || gltfTexture.value().index < 0 || gltfTexture.value().index >= model.textures.size()) { if (gltfTexture && gltfTexture.value().index >= 0) { @@ -418,18 +417,13 @@ static TUniquePtr loadTexture( int32_t textureIndex = gltfTexture.value().index; CesiumGltf::Texture& texture = model.textures[textureIndex]; - return loadTextureFromModelAnyThreadPart( - model, - texture, - sRGB, - textureResources); + return loadTextureFromModelAnyThreadPart(model, texture, sRGB); } static void applyWaterMask( Model& model, const MeshPrimitive& primitive, - LoadPrimitiveResult& primitiveResult, - std::vector& textureResources) { + LoadPrimitiveResult& primitiveResult) { // Initialize water mask if needed. auto onlyWaterIt = primitive.extras.find("OnlyWater"); auto onlyLandIt = primitive.extras.find("OnlyLand"); @@ -451,11 +445,8 @@ static void applyWaterMask( waterMaskInfo.index = waterMaskTextureId; if (waterMaskTextureId >= 0 && waterMaskTextureId < model.textures.size()) { - primitiveResult.waterMaskTexture = loadTexture( - model, - std::make_optional(waterMaskInfo), - false, - textureResources); + primitiveResult.waterMaskTexture = + loadTexture(model, std::make_optional(waterMaskInfo), false); } } } @@ -911,8 +902,7 @@ static void loadPrimitiveFeaturesMetadata( CesiumGltf::MeshPrimitive& primitive, bool duplicateVertices, TArray& vertices, - const TArray& indices, - std::vector& textureResources) { + const TArray& indices) { ExtensionExtMeshFeatures* pFeatures = primitive.getExtension(); @@ -1130,8 +1120,7 @@ static void loadPrimitive( const CreatePrimitiveOptions& options, const Accessor& positionAccessor, const AccessorView& positionView, - const TIndexAccessor& indicesView, - std::vector& textureResources) { + const TIndexAccessor& indicesView) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::loadPrimitive) @@ -1272,7 +1261,7 @@ static void loadPrimitive( } } - applyWaterMask(model, primitive, primitiveResult, textureResources); + applyWaterMask(model, primitive, primitiveResult); // The water effect works by animating the normal, and the normal is // expressed in tangent space. So if we have water, we need tangents. @@ -1424,27 +1413,22 @@ static void loadPrimitive( primitive, duplicateVertices, StaticMeshBuildVertices, - indices, - textureResources); + indices); { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::loadTextures) - primitiveResult.baseColorTexture = loadTexture( - model, - pbrMetallicRoughness.baseColorTexture, - true, - textureResources); + primitiveResult.baseColorTexture = + loadTexture(model, pbrMetallicRoughness.baseColorTexture, true); primitiveResult.metallicRoughnessTexture = loadTexture( model, pbrMetallicRoughness.metallicRoughnessTexture, - false, - textureResources); + false); primitiveResult.normalTexture = - loadTexture(model, material.normalTexture, false, textureResources); + loadTexture(model, material.normalTexture, false); primitiveResult.occlusionTexture = - loadTexture(model, material.occlusionTexture, false, textureResources); + loadTexture(model, material.occlusionTexture, false); primitiveResult.emissiveTexture = - loadTexture(model, material.emissiveTexture, true, textureResources); + loadTexture(model, material.emissiveTexture, true); } { @@ -1692,8 +1676,7 @@ static void loadIndexedPrimitive( const glm::dmat4x4& transform, const CreatePrimitiveOptions& options, const Accessor& positionAccessor, - const AccessorView& positionView, - std::vector& textureResources) { + const AccessorView& positionView) { const Model& model = *options.pMeshOptions->pNodeOptions->pModelOptions->pModel; const MeshPrimitive& primitive = *options.pPrimitive; @@ -1708,8 +1691,7 @@ static void loadIndexedPrimitive( options, positionAccessor, positionView, - indexAccessor, - textureResources); + indexAccessor); primitiveResult.IndexAccessor = indexAccessor; } else if ( indexAccessorGltf.componentType == @@ -1721,8 +1703,7 @@ static void loadIndexedPrimitive( options, positionAccessor, positionView, - indexAccessor, - textureResources); + indexAccessor); primitiveResult.IndexAccessor = indexAccessor; } else if ( indexAccessorGltf.componentType == @@ -1734,8 +1715,7 @@ static void loadIndexedPrimitive( options, positionAccessor, positionView, - indexAccessor, - textureResources); + indexAccessor); primitiveResult.IndexAccessor = indexAccessor; } else { UE_LOG( @@ -1749,8 +1729,7 @@ static void loadIndexedPrimitive( static void loadPrimitive( LoadPrimitiveResult& result, const glm::dmat4x4& transform, - const CreatePrimitiveOptions& options, - std::vector& textureResources) { + const CreatePrimitiveOptions& options) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::loadPrimitive) const Model& model = @@ -1785,16 +1764,14 @@ static void loadPrimitive( options, *pPositionAccessor, positionView, - syntheticIndexBuffer, - textureResources); + syntheticIndexBuffer); } else { loadIndexedPrimitive( result, transform, options, *pPositionAccessor, - positionView, - textureResources); + positionView); } result.PositionAccessor = std::move(positionView); } @@ -1802,8 +1779,7 @@ static void loadPrimitive( static void loadMesh( std::optional& result, const glm::dmat4x4& transform, - const CreateMeshOptions& options, - std::vector& textureResources) { + const CreateMeshOptions& options) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::loadMesh) @@ -1815,11 +1791,7 @@ static void loadMesh( for (CesiumGltf::MeshPrimitive& primitive : mesh.primitives) { CreatePrimitiveOptions primitiveOptions = {&options, &*result, &primitive}; auto& primitiveResult = result->primitiveResults.emplace_back(); - loadPrimitive( - primitiveResult, - transform, - primitiveOptions, - textureResources); + loadPrimitive(primitiveResult, transform, primitiveOptions); // if it doesn't have render data, then it can't be loaded if (!primitiveResult.RenderData) { @@ -1974,8 +1946,7 @@ static void loadInstancingData( static void loadNode( std::vector& loadNodeResults, const glm::dmat4x4& transform, - const CreateNodeOptions& options, - std::vector& textureResources) { + const CreateNodeOptions& options) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::loadNode) @@ -2055,7 +2026,7 @@ static void loadNode( loadInstancingData(model, result, pGpuInstancingExtension); } CreateMeshOptions meshOptions = {&options, &result, &model.meshes[meshId]}; - loadMesh(result.meshResult, nodeTransform, meshOptions, textureResources); + loadMesh(result.meshResult, nodeTransform, meshOptions); } for (int childNodeId : node.children) { @@ -2064,11 +2035,7 @@ static void loadNode( options.pModelOptions, options.pHalfConstructedModelResult, &model.nodes[childNodeId]}; - loadNode( - loadNodeResults, - nodeTransform, - childNodeOptions, - textureResources); + loadNode(loadNodeResults, nodeTransform, childNodeOptions); } } } @@ -2214,8 +2181,7 @@ loadModelMetadata(LoadModelResult& result, const CreateModelOptions& options) { static void loadModelAnyThreadPart( LoadModelResult& result, const glm::dmat4x4& transform, - const CreateModelOptions& options, - std::vector& textureResources) { + const CreateModelOptions& options) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::loadModelAnyThreadPart) Model& model = *options.pModel; @@ -2249,11 +2215,11 @@ static void loadModelAnyThreadPart( continue; Image* pImage = model.getSafe(&model.images, texture.source); - if (!pImage || pImage->cesium.pixelData.empty()) + if (!pImage || pImage->cesium->pixelData.empty()) continue; std::optional errorMessage = - CesiumGltfReader::GltfReader::generateMipMaps(pImage->cesium); + CesiumGltfReader::GltfReader::generateMipMaps(*pImage->cesium); if (errorMessage) { UE_LOG( LogCesium, @@ -2278,27 +2244,19 @@ static void loadModelAnyThreadPart( const Scene& defaultScene = model.scenes[model.scene]; for (int nodeId : defaultScene.nodes) { CreateNodeOptions nodeOptions = {&options, &result, &model.nodes[nodeId]}; - loadNode( - result.nodeResults, - rootTransform, - nodeOptions, - textureResources); + loadNode(result.nodeResults, rootTransform, nodeOptions); } } else if (model.scenes.size() > 0) { // There's no default, so show the first scene const Scene& defaultScene = model.scenes[0]; for (int nodeId : defaultScene.nodes) { CreateNodeOptions nodeOptions = {&options, &result, &model.nodes[nodeId]}; - loadNode( - result.nodeResults, - rootTransform, - nodeOptions, - textureResources); + loadNode(result.nodeResults, rootTransform, nodeOptions); } } else if (model.nodes.size() > 0) { // No scenes at all, use the first node as the root node. CreateNodeOptions nodeOptions = {&options, &result, &model.nodes[0]}; - loadNode(result.nodeResults, rootTransform, nodeOptions, textureResources); + loadNode(result.nodeResults, rootTransform, nodeOptions); } else if (model.meshes.size() > 0) { // No nodes either, show all the meshes. for (Mesh& mesh : model.meshes) { @@ -2308,11 +2266,7 @@ static void loadModelAnyThreadPart( &dummyNodeOptions, &dummyNodeResult, &mesh}; - loadMesh( - dummyNodeResult.meshResult, - rootTransform, - meshOptions, - textureResources); + loadMesh(dummyNodeResult.meshResult, rootTransform, meshOptions); } } } @@ -3446,15 +3400,8 @@ static void loadPrimitiveGameThreadPart( UCesiumGltfComponent::CreateOffGameThread( const glm::dmat4x4& Transform, const CreateModelOptions& Options) { - std::vector textureResources; - textureResources.resize(Options.pModel->images.size(), nullptr); - auto pResult = MakeUnique(); - loadModelAnyThreadPart( - pResult->loadModelResult, - Transform, - Options, - textureResources); + loadModelAnyThreadPart(pResult->loadModelResult, Transform, Options); return pResult; } diff --git a/Source/CesiumRuntime/Private/CesiumTextureResource.cpp b/Source/CesiumRuntime/Private/CesiumTextureResource.cpp index 1bbeb5a59..cbfe302a5 100644 --- a/Source/CesiumRuntime/Private/CesiumTextureResource.cpp +++ b/Source/CesiumRuntime/Private/CesiumTextureResource.cpp @@ -285,7 +285,7 @@ FTextureRHIRef FCesiumUseExistingTextureResource::InitializeTextureRHI() { } FCesiumCreateNewTextureResource::FCesiumCreateNewTextureResource( - CesiumGltf::ImageCesium&& image, + CesiumGltf::ImageCesium& image, TextureGroup textureGroup, uint32 width, uint32 height, diff --git a/Source/CesiumRuntime/Private/CesiumTextureResource.h b/Source/CesiumRuntime/Private/CesiumTextureResource.h index 1ee0e5889..91a47b5ee 100644 --- a/Source/CesiumRuntime/Private/CesiumTextureResource.h +++ b/Source/CesiumRuntime/Private/CesiumTextureResource.h @@ -6,6 +6,7 @@ #include "Engine/Texture.h" #include "TextureResource.h" #include +#include /** * The base class for Cesium texture resources, making Cesium's texture data @@ -110,7 +111,7 @@ class FCesiumUseExistingTextureResource : public FCesiumTextureResourceBase { class FCesiumCreateNewTextureResource : public FCesiumTextureResourceBase { public: FCesiumCreateNewTextureResource( - CesiumGltf::ImageCesium&& image, + CesiumGltf::ImageCesium& image, TextureGroup textureGroup, uint32 width, uint32 height, diff --git a/Source/CesiumRuntime/Private/CesiumTextureUtility.cpp b/Source/CesiumRuntime/Private/CesiumTextureUtility.cpp index 5aceb4bb4..f7e084adb 100644 --- a/Source/CesiumRuntime/Private/CesiumTextureUtility.cpp +++ b/Source/CesiumRuntime/Private/CesiumTextureUtility.cpp @@ -139,6 +139,15 @@ struct ExtensionUnrealTexture { pTexture; }; +struct ExtensionUnrealTextureResource { + static inline constexpr const char* TypeName = + "ExtensionUnrealTextureResource"; + static inline constexpr const char* ExtensionName = + "PRIVATE_unreal_texture_resource"; + + FCesiumTextureResourceBase* pTextureResource; +}; + } // namespace namespace CesiumTextureUtility { @@ -198,13 +207,20 @@ void ReferenceCountedUnrealTexture::setTextureResource( this->_pTextureResource = std::move(p); } +CesiumGltf::SharedAsset +ReferenceCountedUnrealTexture::getSharedImage() const { + return this->_pImageCesium; +} + +void ReferenceCountedUnrealTexture::setSharedImage( + CesiumGltf::SharedAsset& image) { + this->_pImageCesium = image; +} + TUniquePtr loadTextureFromModelAnyThreadPart( CesiumGltf::Model& model, CesiumGltf::Texture& texture, - bool sRGB, - std::vector& textureResources) { - check(textureResources.size() == model.images.size()); - + bool sRGB) { int64_t textureIndex = model.textures.empty() ? -1 : &texture - &model.textures[0]; if (textureIndex < 0 || size_t(textureIndex) >= model.textures.size()) { @@ -213,13 +229,12 @@ TUniquePtr loadTextureFromModelAnyThreadPart( ExtensionUnrealTexture& extension = texture.addExtension(); - if (extension.pTexture && (extension.pTexture->getUnrealTexture() || extension.pTexture->getTextureResource())) { // There's an existing Unreal texture for this glTF texture. This will - // happen if this texture is used by multiple primitives on the same model. - // It will also be the case when this model was upsampled from a parent - // tile. + // happen if this texture is used by multiple primitives on the same + // model. It will also be the case when this model was upsampled from a + // parent tile. TUniquePtr pResult = MakeUnique(); pResult->pTexture = extension.pTexture; pResult->textureIndex = textureIndex; @@ -273,44 +288,25 @@ TUniquePtr loadTextureFromModelAnyThreadPart( } CesiumGltf::Image& image = model.images[source]; - const CesiumGltf::ImageCesium& imageCesium = image.cesium; const CesiumGltf::Sampler& sampler = model.getSafe(model.samplers, texture.sampler); - FCesiumTextureResourceBase* pExistingImageResource = nullptr; - - if (image.cesium.pixelData.empty() && source >= 0 && - source < textureResources.size()) { - // An RHI texture has already been created for this image; reuse it. - pExistingImageResource = textureResources[source]; - } - TUniquePtr pResult = - loadTextureFromImageAndSamplerAnyThreadPart( - image, - sampler, - sRGB, - pExistingImageResource); + loadTextureFromImageAndSamplerAnyThreadPart(image.cesium, sampler, sRGB); + if (pResult) { extension.pTexture = pResult->pTexture; // Note the index of this texture within the glTF. pResult->textureIndex = textureIndex; - - if (source >= 0 && source < textureResources.size()) { - // Make the RHI resource known so it can be used by other textures that - // reference this same image. - textureResources[source] = pResult->pTexture->getTextureResource().Get(); - } } return pResult; } TUniquePtr loadTextureFromImageAndSamplerAnyThreadPart( - CesiumGltf::Image& image, + CesiumGltf::SharedAsset& image, const CesiumGltf::Sampler& sampler, - bool sRGB, - FCesiumTextureResourceBase* pExistingImageResource) { + bool sRGB) { TextureAddress addressX = convertGltfWrapSToUnreal(sampler.wrapS); TextureAddress addressY = convertGltfWrapTToUnreal(sampler.wrapT); @@ -369,8 +365,8 @@ TUniquePtr loadTextureFromImageAndSamplerAnyThreadPart( break; } - return loadTextureAnyThreadPart( - image.cesium, + auto loadResult = loadTextureAnyThreadPart( + *image, addressX, addressY, filter, @@ -378,8 +374,13 @@ TUniquePtr loadTextureFromImageAndSamplerAnyThreadPart( // TODO: allow texture group to be configured on Cesium3DTileset. TEXTUREGROUP_World, sRGB, - std::nullopt, - pExistingImageResource); + std::nullopt); + + if (loadResult != nullptr) { + loadResult->pTexture->setSharedImage(image); + } + + return loadResult; } static UTexture2D* CreateTexture2D(LoadedTextureResult* pHalfLoadedTexture) { @@ -419,8 +420,7 @@ TUniquePtr loadTextureAnyThreadPart( bool useMipMapsIfAvailable, TextureGroup group, bool sRGB, - std::optional overridePixelFormat, - FCesiumTextureResourceBase* pExistingImageResource) { + std::optional overridePixelFormat) { EPixelFormat pixelFormat; if (imageCesium.compressedPixelFormat != GpuCompressedPixelFormat::NONE) { switch (imageCesium.compressedPixelFormat) { @@ -492,10 +492,13 @@ TUniquePtr loadTextureAnyThreadPart( // for caching purposes. imageCesium.sizeBytes = int64_t(imageCesium.pixelData.size()); - if (pExistingImageResource) { + ExtensionUnrealTextureResource& extension = + imageCesium.addExtension(); + + if (extension.pTextureResource != nullptr) { pResult->pTexture->setTextureResource( MakeUnique( - pExistingImageResource, + extension.pTextureResource, group, imageCesium.width, imageCesium.height, @@ -544,7 +547,7 @@ TUniquePtr loadTextureAnyThreadPart( pResult->pTexture->setTextureResource( MakeUnique( - std::move(imageCesium), + imageCesium, group, imageCesium.width, imageCesium.height, @@ -558,6 +561,7 @@ TUniquePtr loadTextureAnyThreadPart( } check(pResult->pTexture->getTextureResource() != nullptr); + extension.pTextureResource = pResult->pTexture->getTextureResource().Get(); return pResult; } diff --git a/Source/CesiumRuntime/Private/CesiumTextureUtility.h b/Source/CesiumRuntime/Private/CesiumTextureUtility.h index 2592dabc3..bc657eaa4 100644 --- a/Source/CesiumRuntime/Private/CesiumTextureUtility.h +++ b/Source/CesiumRuntime/Private/CesiumTextureUtility.h @@ -3,6 +3,7 @@ #pragma once #include "CesiumGltf/Model.h" +#include "CesiumGltf/SharedAssetDepot.h" #include "CesiumMetadataValueType.h" #include "CesiumTextureResource.h" #include "Engine/Texture.h" @@ -50,9 +51,14 @@ struct ReferenceCountedUnrealTexture TUniquePtr& getTextureResource(); void setTextureResource(TUniquePtr&& p); + /// The SharedAsset that this texture was created from. + CesiumGltf::SharedAsset getSharedImage() const; + void setSharedImage(CesiumGltf::SharedAsset& image); + private: TObjectPtr _pUnrealTexture; TUniquePtr _pTextureResource; + CesiumGltf::SharedAsset _pImageCesium; }; /** @@ -97,8 +103,7 @@ struct LoadedTextureResult { TUniquePtr loadTextureFromModelAnyThreadPart( CesiumGltf::Model& model, CesiumGltf::Texture& texture, - bool sRGB, - std::vector& textureResources); + bool sRGB); /** * Does the asynchronous part of renderer resource preparation for a glTF @@ -117,10 +122,9 @@ TUniquePtr loadTextureFromModelAnyThreadPart( * and can be empty. */ TUniquePtr loadTextureFromImageAndSamplerAnyThreadPart( - CesiumGltf::Image& image, + CesiumGltf::SharedAsset& image, const CesiumGltf::Sampler& sampler, - bool sRGB, - FCesiumTextureResourceBase* pExistingImageResource); + bool sRGB); /** * @brief Does the asynchronous part of renderer resource preparation for @@ -153,8 +157,7 @@ TUniquePtr loadTextureAnyThreadPart( bool useMipMapsIfAvailable, TextureGroup group, bool sRGB, - std::optional overridePixelFormat, - FCesiumTextureResourceBase* pExistingImageResource); + std::optional overridePixelFormat); /** * @brief Does the main-thread part of render resource preparation for this diff --git a/Source/CesiumRuntime/Private/Tests/Cesium3DTileset.spec.cpp b/Source/CesiumRuntime/Private/Tests/Cesium3DTileset.spec.cpp new file mode 100644 index 000000000..a9d3d53af --- /dev/null +++ b/Source/CesiumRuntime/Private/Tests/Cesium3DTileset.spec.cpp @@ -0,0 +1,30 @@ +// Copyright 2020-2024 CesiumGS, Inc. and Contributors + +#include "Cesium3DTileset.h" +#include "CesiumTestHelpers.h" +#include "Engine/World.h" +#include "Misc/AutomationTest.h" + +BEGIN_DEFINE_SPEC( + FCesium3DTilesetSpec, + "Cesium.Unit.3DTileset", + EAutomationTestFlags::ApplicationContextMask | + EAutomationTestFlags::ProductFilter); +END_DEFINE_SPEC(FCesium3DTilesetSpec); + +void FCesium3DTilesetSpec::Define() { + Describe("SharedImages", [this]() { + It("should only load two textures", [this]() { + UWorld* World = GEditor->PlayWorld; + check(World); + + ACesium3DTileset* tileset = World->SpawnActor(); + tileset->SetTilesetSource(ETilesetSource::FromUrl); + tileset->SetUrl(FString::Printf( + TEXT( + "file:///%s/cesium-unreal/extern/cesium-native/Cesium3DTilesSelection/test/data/SharedImages"), + *FPaths::ProjectPluginsDir())); + tileset->SetActorLabel(TEXT("SharedImages")); + }); + }); +} diff --git a/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp b/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp index 4daa30cda..9ff986f6e 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp @@ -136,9 +136,9 @@ void FCesiumFeatureIdTextureSpec::Define() { It("constructs valid instance for texture with nonexistent texcoord attribute", [this]() { Image& image = model.images.emplace_back(); - image.cesium.width = image.cesium.height = 1; - image.cesium.channels = 1; - image.cesium.pixelData.push_back(std::byte(42)); + image.cesium->width = image.cesium->height = 1; + image.cesium->channels = 1; + image.cesium->pixelData.push_back(std::byte(42)); Sampler& sampler = model.samplers.emplace_back(); sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; @@ -175,9 +175,9 @@ void FCesiumFeatureIdTextureSpec::Define() { It("constructs valid instance for texture with invalid texcoord accessor", [this]() { Image& image = model.images.emplace_back(); - image.cesium.width = image.cesium.height = 1; - image.cesium.channels = 1; - image.cesium.pixelData.push_back(std::byte(42)); + image.cesium->width = image.cesium->height = 1; + image.cesium->channels = 1; + image.cesium->pixelData.push_back(std::byte(42)); Sampler& sampler = model.samplers.emplace_back(); sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; diff --git a/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.cpp b/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.cpp index 45c4d8149..47cb9a9d7 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.cpp @@ -70,12 +70,12 @@ CesiumGltf::FeatureId& AddFeatureIDsAsTextureToModel( const int32_t samplerWrapS, const int32_t samplerWrapT) { CesiumGltf::Image& image = model.images.emplace_back(); - image.cesium.bytesPerChannel = 1; - image.cesium.channels = 1; - image.cesium.width = imageWidth; - image.cesium.height = imageHeight; + image.cesium->bytesPerChannel = 1; + image.cesium->channels = 1; + image.cesium->width = imageWidth; + image.cesium->height = imageHeight; - std::vector& data = image.cesium.pixelData; + std::vector& data = image.cesium->pixelData; data.resize(imageWidth * imageHeight); std::memcpy(data.data(), featureIDs.data(), data.size()); diff --git a/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h b/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h index b5dd72df8..325b85c25 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h +++ b/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h @@ -200,15 +200,15 @@ CesiumGltf::PropertyTextureProperty& AddPropertyTexturePropertyToModel( classProperty.componentType = componentType; CesiumGltf::Image& image = model.images.emplace_back(); - image.cesium.width = 2; - image.cesium.height = 2; - image.cesium.channels = sizeof(T); - image.cesium.bytesPerChannel = 1; - image.cesium.pixelData.resize(values.size() * sizeof(T)); + image.cesium->width = 2; + image.cesium->height = 2; + image.cesium->channels = sizeof(T); + image.cesium->bytesPerChannel = 1; + image.cesium->pixelData.resize(values.size() * sizeof(T)); std::memcpy( - image.cesium.pixelData.data(), + image.cesium->pixelData.data(), values.data(), - image.cesium.pixelData.size()); + image.cesium->pixelData.size()); CesiumGltf::Sampler& sampler = model.samplers.emplace_back(); sampler.wrapS = CesiumGltf::Sampler::WrapS::CLAMP_TO_EDGE; diff --git a/Source/CesiumRuntime/Private/Tests/CesiumTextureUtility.spec.cpp b/Source/CesiumRuntime/Private/Tests/CesiumTextureUtility.spec.cpp index 10b8ba83b..b6204f49d 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumTextureUtility.spec.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumTextureUtility.spec.cpp @@ -15,7 +15,7 @@ BEGIN_DEFINE_SPEC( EAutomationTestFlags::ProductFilter | EAutomationTestFlags::NonNullRHI) std::vector originalPixels; std::vector originalMipPixels; -ImageCesium imageCesium; +SharedAsset imageCesium; void RunTests(); @@ -44,18 +44,18 @@ void CesiumTextureUtilitySpec::Define() { 0x24, 0x44, 0x84, 0xF4, 0x25, 0x45, 0x85, 0xF5}; originalMipPixels.clear(); - imageCesium = {}; - imageCesium.width = 3; - imageCesium.height = 2; + imageCesium = SharedAsset(ImageCesium{}); + imageCesium->width = 3; + imageCesium->height = 2; TestEqual( "image buffer size is correct", originalPixels.size(), - imageCesium.width * imageCesium.height * imageCesium.bytesPerChannel * - imageCesium.channels); - imageCesium.pixelData.resize(originalPixels.size()); + imageCesium->width * imageCesium->height * + imageCesium->bytesPerChannel * imageCesium->channels); + imageCesium->pixelData.resize(originalPixels.size()); std::memcpy( - imageCesium.pixelData.data(), + imageCesium->pixelData.data(), originalPixels.data(), originalPixels.size()); }); @@ -66,30 +66,30 @@ void CesiumTextureUtilitySpec::Define() { Describe("With Mips", [this]() { BeforeEach([this]() { imageCesium = {}; - imageCesium.width = 3; - imageCesium.height = 2; + imageCesium->width = 3; + imageCesium->height = 2; // Original image (3x2) originalPixels = {0x20, 0x40, 0x80, 0xF0, 0x21, 0x41, 0x81, 0xF1, 0x22, 0x42, 0x82, 0xF2, 0x23, 0x43, 0x83, 0xF3, 0x24, 0x44, 0x84, 0xF4, 0x25, 0x45, 0x85, 0xF5}; - imageCesium.mipPositions.emplace_back( + imageCesium->mipPositions.emplace_back( ImageCesiumMipPosition{0, originalPixels.size()}); // Mip 1 (1x1) originalMipPixels = {0x26, 0x46, 0x86, 0xF6}; - imageCesium.mipPositions.emplace_back(ImageCesiumMipPosition{ - imageCesium.mipPositions[0].byteSize, + imageCesium->mipPositions.emplace_back(ImageCesiumMipPosition{ + imageCesium->mipPositions[0].byteSize, originalMipPixels.size()}); - imageCesium.pixelData.resize( + imageCesium->pixelData.resize( originalPixels.size() + originalMipPixels.size()); std::memcpy( - imageCesium.pixelData.data(), + imageCesium->pixelData.data(), originalPixels.data(), originalPixels.size()); std::memcpy( - imageCesium.pixelData.data() + originalPixels.size(), + imageCesium->pixelData.data() + originalPixels.size(), originalMipPixels.data(), originalMipPixels.size()); }); @@ -101,15 +101,14 @@ void CesiumTextureUtilitySpec::Define() { void CesiumTextureUtilitySpec::RunTests() { It("ImageCesium non-sRGB", [this]() { TUniquePtr pHalfLoaded = loadTextureAnyThreadPart( - imageCesium, + *imageCesium, TextureAddress::TA_Mirror, TextureAddress::TA_Wrap, TextureFilter::TF_Bilinear, true, TextureGroup::TEXTUREGROUP_Cinematic, false, - std::nullopt, - nullptr); + std::nullopt); TestNotNull("pHalfLoaded", pHalfLoaded.Get()); IntrusivePointer pRefCountedTexture = @@ -126,15 +125,14 @@ void CesiumTextureUtilitySpec::RunTests() { It("ImageCesium sRGB", [this]() { TUniquePtr pHalfLoaded = loadTextureAnyThreadPart( - imageCesium, + *imageCesium, TextureAddress::TA_Clamp, TextureAddress::TA_Mirror, TextureFilter::TF_Trilinear, true, TextureGroup::TEXTUREGROUP_Bokeh, true, - std::nullopt, - nullptr); + std::nullopt); TestNotNull("pHalfLoaded", pHalfLoaded.Get()); IntrusivePointer pRefCountedTexture = @@ -150,9 +148,6 @@ void CesiumTextureUtilitySpec::RunTests() { }); It("Image and Sampler", [this]() { - Image image; - image.cesium = imageCesium; - Sampler sampler; sampler.minFilter = Sampler::MinFilter::NEAREST; sampler.magFilter = Sampler::MagFilter::NEAREST; @@ -161,10 +156,9 @@ void CesiumTextureUtilitySpec::RunTests() { TUniquePtr pHalfLoaded = loadTextureFromImageAndSamplerAnyThreadPart( - image, + imageCesium, sampler, - false, - nullptr); + false); TestNotNull("pHalfLoaded", pHalfLoaded.Get()); IntrusivePointer pRefCountedTexture = @@ -195,21 +189,10 @@ void CesiumTextureUtilitySpec::RunTests() { texture.source = 0; texture.sampler = 0; - std::vector textureResources; - textureResources.resize(model.images.size(), nullptr); - TUniquePtr pHalfLoaded = - loadTextureFromModelAnyThreadPart( - model, - texture, - true, - textureResources); + loadTextureFromModelAnyThreadPart(model, texture, true); TestNotNull("pHalfLoaded", pHalfLoaded.Get()); TestNotNull("pHalfLoaded->pTexture", pHalfLoaded->pTexture.get()); - TestEqual( - "textureResources[0]", - textureResources[0], - pHalfLoaded->pTexture->getTextureResource().Get()); IntrusivePointer pRefCountedTexture = loadTextureGameThreadPart(model, pHalfLoaded.Get()); @@ -249,34 +232,15 @@ void CesiumTextureUtilitySpec::RunTests() { texture2.source = 0; texture2.sampler = 1; - std::vector textureResources; - textureResources.resize(model.images.size(), nullptr); - TUniquePtr pHalfLoaded1 = - loadTextureFromModelAnyThreadPart( - model, - model.textures[0], - true, - textureResources); + loadTextureFromModelAnyThreadPart(model, model.textures[0], true); TestNotNull("pHalfLoaded1", pHalfLoaded1.Get()); TestNotNull("pHalfLoaded1->pTexture", pHalfLoaded1->pTexture.get()); - TestEqual( - "textureResources[0]", - textureResources[0], - pHalfLoaded1->pTexture->getTextureResource().Get()); TUniquePtr pHalfLoaded2 = - loadTextureFromModelAnyThreadPart( - model, - model.textures[1], - false, - textureResources); + loadTextureFromModelAnyThreadPart(model, model.textures[1], false); TestNotNull("pHalfLoaded2", pHalfLoaded2.Get()); TestNotNull("pHalfLoaded2->pTexture", pHalfLoaded2->pTexture.get()); - TestEqual( - "textureResources[0]", - textureResources[0], - pHalfLoaded2->pTexture->getTextureResource().Get()); IntrusivePointer pRefCountedTexture1 = loadTextureGameThreadPart(model, pHalfLoaded1.Get()); @@ -325,21 +289,10 @@ void CesiumTextureUtilitySpec::RunTests() { texture.source = 0; texture.sampler = 0; - std::vector textureResources; - textureResources.resize(model.images.size(), nullptr); - TUniquePtr pHalfLoaded = - loadTextureFromModelAnyThreadPart( - model, - texture, - true, - textureResources); + loadTextureFromModelAnyThreadPart(model, texture, true); TestNotNull("pHalfLoaded", pHalfLoaded.Get()); TestNotNull("pHalfLoaded->pTexture", pHalfLoaded->pTexture.get()); - TestEqual( - "textureResources[0]", - textureResources[0], - pHalfLoaded->pTexture->getTextureResource().Get()); IntrusivePointer pRefCountedTexture = loadTextureGameThreadPart(model, pHalfLoaded.Get()); @@ -352,19 +305,12 @@ void CesiumTextureUtilitySpec::RunTests() { CheckFilter(pRefCountedTexture, TextureFilter::TF_Default); CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_World); - std::vector textureResources2; - textureResources2.resize(model.images.size(), nullptr); - // Copy the model and load the same texture again. // This time there's no more pixel data, so it's necessary to use the // previously-created texture. Model model2 = model; TUniquePtr pHalfLoaded2 = - loadTextureFromModelAnyThreadPart( - model2, - model.textures[0], - true, - textureResources2); + loadTextureFromModelAnyThreadPart(model2, model.textures[0], true); TestNotNull("pHalfLoaded2", pHalfLoaded2.Get()); TestNotNull("pHalfLoaded2->pTexture", pHalfLoaded2->pTexture.get()); TestNull( @@ -392,21 +338,10 @@ void CesiumTextureUtilitySpec::RunTests() { texture.source = 0; texture.sampler = 0; - std::vector textureResources; - textureResources.resize(model.images.size(), nullptr); - TUniquePtr pHalfLoaded = - loadTextureFromModelAnyThreadPart( - model, - texture, - true, - textureResources); + loadTextureFromModelAnyThreadPart(model, texture, true); TestNotNull("pHalfLoaded", pHalfLoaded.Get()); TestNotNull("pHalfLoaded->pTexture", pHalfLoaded->pTexture.get()); - TestEqual( - "textureResources[0]", - textureResources[0], - pHalfLoaded->pTexture->getTextureResource().Get()); IntrusivePointer pRefCountedTexture = loadTextureGameThreadPart(model, pHalfLoaded.Get()); @@ -419,18 +354,11 @@ void CesiumTextureUtilitySpec::RunTests() { CheckFilter(pRefCountedTexture, TextureFilter::TF_Default); CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_World); - std::vector textureResources2; - textureResources2.resize(model.images.size(), nullptr); - // Load the same texture again. // This time there's no more pixel data, so it's necessary to use the // previously-created texture. TUniquePtr pHalfLoaded2 = - loadTextureFromModelAnyThreadPart( - model, - model.textures[0], - true, - textureResources2); + loadTextureFromModelAnyThreadPart(model, model.textures[0], true); TestNotNull("pHalfLoaded2", pHalfLoaded2.Get()); TestNotNull("pHalfLoaded2->pTexture", pHalfLoaded2->pTexture.get()); TestNull( diff --git a/extern/cesium-native b/extern/cesium-native index 43187540a..e79b31379 160000 --- a/extern/cesium-native +++ b/extern/cesium-native @@ -1 +1 @@ -Subproject commit 43187540a5199d3b5fa8693fca90a1b81bf73c80 +Subproject commit e79b31379c22e1e91b321cda06cf44e79ca543d4