// Copyright 2020-2024 CesiumGS, Inc. and Contributors #pragma once #include "CesiumAsync/SharedAssetDepot.h" #include "CesiumGltf/Model.h" #include "CesiumGltf/Texture.h" #include "CesiumMetadataValueType.h" #include "CesiumTextureResource.h" #include "Engine/Texture.h" #include "Engine/Texture2D.h" #include "Engine/TextureDefines.h" #include "RHI.h" #include "Runtime/Launch/Resources/Version.h" #include "Templates/UniquePtr.h" #include #include namespace CesiumGltf { struct ImageAsset; struct Texture; } // namespace CesiumGltf namespace CesiumTextureUtility { // A slightly roundabout way to a hold a UTexture2D. // // We can't let Unreal's garbage collector be exclusively responsible for the // lifetime of our textures because it doesn't run often enough (and not at all // in the Editor). And we also need shared ownership of UTexture2Ds when a tile // is "upsampled" from its parent for raster overlays. So this class allows us // to control the lifetime of a UTexture2D via reference counting. // // Yes, this means we're controlling the lifetime of a garbage collected // `UTexture2D` object via reference counting. // // Instances of this class are created whenever we create a UTexture2D. A // pointer to the instance is held in `LoadedTextureResult` as well as in a // private extension added to the glTF `Texture` from which the UTexture2D was // created. struct ReferenceCountedUnrealTexture : CesiumUtility::ReferenceCountedThreadSafe { ReferenceCountedUnrealTexture() noexcept; ~ReferenceCountedUnrealTexture() noexcept; // The texture game object, once it's created. TObjectPtr getUnrealTexture() const; void setUnrealTexture(const TObjectPtr& p); // The renderer / RHI FTextureResource holding the pixel data. const FCesiumTextureResourceUniquePtr& getTextureResource() const; FCesiumTextureResourceUniquePtr& getTextureResource(); void setTextureResource(FCesiumTextureResourceUniquePtr&& p); private: TObjectPtr _pUnrealTexture; FCesiumTextureResourceUniquePtr _pTextureResource; }; /** * @brief Half-loaded Unreal texture with info on how to finish loading the * texture on the game thread and render thread. */ struct LoadedTextureResult { TextureAddress addressX; TextureAddress addressY; TextureFilter filter; TextureGroup group; bool sRGB{true}; // The index of the `CesiumGltf::Texture` instance with the glTF. Or -1 if // this result wasn't created from a texture in a glTF. int64_t textureIndex = -1; // The UTexture2D that has already been created, if any. CesiumUtility::IntrusivePointer pTexture; }; /** * Does the asynchronous part of renderer resource preparation for a `Texture` * in a glTF. Should be called in a worker thread. * * The `cesium.pixelData` will be removed from the image associated with the * texture so that it can be passed to Unreal's renderer thread without copying * it. * * @param model The glTF Model for which to load this texture. * @param texture The glTF Texture to load. This parameter is non-const because * a private extension will be added to this `Texture` in order to track the * associated Unreal texture. * @param sRGB True if the texture should be treated as sRGB; false if it should * be treated as linear. * {@link CesiumGltf::Model::images}, and all pointers must be initialized to * nullptr before the first call to `loadTextureFromModelAnyThreadPart` during * the glTF load process. */ TUniquePtr loadTextureFromModelAnyThreadPart( CesiumGltf::Model& model, CesiumGltf::Texture& texture, bool sRGB); /** * Does the asynchronous part of renderer resource preparation for a glTF * `Image` with the given `Sampler` settings. * * The `cesium.pixelData` will be removed from the image so that it can be * passed to Unreal's renderer thread without copying it. * * @param image The glTF image for each to create a texture. * @param sampler The sampler settings to use with the texture. * @param sRGB True if the texture should be treated as sRGB; false if it should * be treated as linear. */ TUniquePtr loadTextureFromImageAndSamplerAnyThreadPart( CesiumGltf::ImageAsset& image, const CesiumGltf::Sampler& sampler, bool sRGB); /** * @brief Does the asynchronous part of renderer resource preparation for * a texture.The given image _must_ be prepared before calling this method by * calling {@link ExtensionImageAssetUnreal::getOrCreate} and then waiting * for {@link ExtensionImageAssetUnreal::getFuture} to resolve. This method * should be called in a background thread. * * @param image The image. * @param addressX The X addressing mode. * @param addressY The Y addressing mode. * @param filter The sampler filtering to use for this texture. * @param useMipMapsIfAvailable true to use this image's mipmaps for sampling, * if they exist; false to ignore any mipmaps that might be present. * @param group The texture group of this texture. * @param sRGB Whether this texture uses a sRGB color space. * @param overridePixelFormat The explicit pixel format to use. If std::nullopt, * the pixel format is inferred from the image. * @return The loaded texture. */ TUniquePtr loadTextureAnyThreadPart( CesiumGltf::ImageAsset& image, TextureAddress addressX, TextureAddress addressY, TextureFilter filter, bool useMipMapsIfAvailable, TextureGroup group, bool sRGB, std::optional overridePixelFormat); /** * @brief Does the main-thread part of render resource preparation for this * image and queues up any required render-thread tasks to finish preparing the * image. * * @param model The model with which this texture is associated. This is used to * store a pointer to the created texture in an extension on the glTF texture so * that it can be reused later. * @param pHalfLoadedTexture The half-loaded renderer texture. * @return The Unreal texture result. */ CesiumUtility::IntrusivePointer loadTextureGameThreadPart( CesiumGltf::Model& model, LoadedTextureResult* pHalfLoadedTexture); /** * @brief Does the main-thread part of render resource preparation for this * image and queues up any required render-thread tasks to finish preparing the * image. * * @param pHalfLoadedTexture The half-loaded renderer texture. * @return The Unreal texture result. */ CesiumUtility::IntrusivePointer loadTextureGameThreadPart(LoadedTextureResult* pHalfLoadedTexture); /** * @brief Convert a glTF {@link CesiumGltf::Sampler::WrapS} value to an Unreal * `TextureAddress` value. * * @param wrapS The glTF wrapS value. * @returns The Unreal equivalent, or `TextureAddress::TA_Wrap` if the glTF * value is unknown or invalid. */ TextureAddress convertGltfWrapSToUnreal(int32_t wrapS); /** * @brief Convert a glTF {@link CesiumGltf::Sampler::WrapT} value to an Unreal * `TextureAddress` value. * * @param wrapT The glTF wrapT value. * @returns The Unreal equivalent, or `TextureAddress::TA_Wrap` if the glTF * value is unknown or invalid. */ TextureAddress convertGltfWrapTToUnreal(int32_t wrapT); std::optional getPixelFormatForImageAsset( const CesiumGltf::ImageAsset& imageCesium, const std::optional overridePixelFormat); std::optional getUnrealTextureFromGltfTexture(const CesiumGltf::Texture& texture); } // namespace CesiumTextureUtility