Skip to content

Comment charger une image via une URL dans Unreal Engine 5 ?

Posted on:5 mai 2023 at 02:00

L’article va être court, mais j’ai récemment travaillé dans le développement d’un plugin Unreal. Où il était nécessaire de charger des images depuis une URL d’un serveur web. Après plusieurs heures de recherches, je n’ai pas trouvé de solution clé en main pour faire cela en C++ (il existe des solutions simples, mais en blueprint uniquement, l’éditeur de script visuel de l’Unreal Engine).

Voilà donc le code qui en résulte:

UTexture2D* DownloadImage(const FString& URL)
{
    UTexture2D* Texture;

    TSharedPtr<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
    HttpRequest->SetVerb("GET");
    HttpRequest->SetURL(URL);
    HttpRequest->OnProcessRequestComplete().BindLambda([this, &Texture](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
    {
        if (bWasSuccessful && Response.IsValid())
        {
            // Create an instance of the image wrapper module
            IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));

            // Create an instance of the image wrapper
            TArray<uint8> ImageData = Response->GetContent();
            TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
            if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num()))
            {
                TArray<uint8> UncompressedImageData;
                if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedImageData))
                {
                    Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
                    Texture->UpdateResource();
                    FTexture2DMipMap& Mip = Texture->GetPlatformData()->Mips[0];
                    void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);
                    FMemory::Memcpy(Data, UncompressedImageData.GetData(), UncompressedImageData.Num());
                    Mip.BulkData.Unlock();
                    Texture->UpdateResource();
                }
            }
        }
    });
    HttpRequest->ProcessRequest();

    FHttpModule* Http = &FHttpModule::Get();
    Http->GetHttpManager().Flush(EHttpFlushReason::FullFlush);

    return Texture;
}

À noter que vous aurez besoin des modules HTTP et évidemment Core.
Comment ça s’utilise par la suite ? Rien de plus simple, il vous faut un Brush puis un élément où l’appliquer, par exemple un bouton.

SharedPtr<FDeferredCleanupSlateBrush> newTexture;
newTexture = FDeferredCleanupSlateBrush::CreateBrush(
    Cast<UTexture>(DownloadImage(Project.background.url)),
	FLinearColor(1.f, 1.f, 1.f),
	ESlateBrushTileType::NoTile,
	ESlateBrushImageType::FullColor
);

...

SNew(SImage)
    .Image(BrushImgTexture->GetSlateBrush())
    .DesiredSizeOverride(FVector2D(195, 95))

Et voilà ! Attention, le code présenté ici charge l’image de façon synchrone. Pas asynchrone. Donc cela peut avoir un impact sur les performances.