How do I convert a UTexture2DDynamic to UTexture2D?

The Download Image node returns a UTexture2DDynamic but this plugin function that I use requires a UTexture2D. How do I convert between the two?

An answer in Blueprint or C++ will suffice.

Hi DanimalsOnParade

Since UTexture2D and UTexture2DDynamic are different stuff with UTexture as base, you cannot convert directly or cast, you have 2 options here!,

1.- Make a new node to transform between UTexture2DDynamic to UTexture2D as follows:


       UTexture2D* Texture2DDynamicToTexture2D(UTexture2DDynamic* Texture)
{
	UTexture2D* ResultTexture;
	ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
		FConvertTextures,
		FTexture2DDynamicResource*, TextureResource, static_cast<FTexture2DDynamicResource*>(Texture->Resource),
		UTexture2D*, ResultTexture, ResultTexture,
		{
			FTexture2DRHIParamRef TextureRHI = TextureResource->GetTexture2DRHI();
			int32 Width = TextureRHI->GetSizeX();
			int32 Height = TextureRHI->GetSizeY();

			uint32 DestStride = 0;
			uint8* ReadData = reinterpret_cast<uint8*>(RHILockTexture2D(TextureRHI, 0, RLM_ReadOnly, DestStride, false, false));

			ResultTexture = UTexture2D::CreateTransient(Width, Height);
			FTexture2DMipMap& Mip = ResultTexture->PlatformData->Mips[0];
			uint8* Data = (uint8*)Mip.BulkData.Lock(LOCK_READ_WRITE);
			FMemory::Memcpy(Data, ReadData, Width * Height * 4);
			Mip.BulkData.Unlock();
			ResultTexture->UpdateResource();

		});

	return ResultTexture;
}
  1. Make your own latent node like the AsyncTaskDownloadImage.cpp, and return a Texture2D* instead,

Summary
The problem with the first approach is very obvious you are duplicating textures in memory for a moment which is not bad if you dont mind, I would like the second method to be honest, is cleaner, only that memory and that is all

Hope helps
Cheers!

Hi ZkarmaKun thank you for your reply!

I tried your C++ code but it resulted in errors during the linking stage:

pastebin of errors

BasePlayerController.h

#pragma once

#include "CoreMinimal.h"
#include "Engine/Texture2D.h"
#include "Engine/Texture2DDynamic.h"
#include "GameFramework/PlayerController.h"
#include "BasePlayerController.generated.h"

/**
 * 
 */
UCLASS()
class UNIFORM_API ABasePlayerController : public APlayerController
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintCallable, Category = "BasePlayerController")
		UTexture2D* Texture2DDynamicToTexture2D(UTexture2DDynamic* Texture);
};

BasePlayerController.cpp

#include "BasePlayerController.h"
#include "Runtime/RHI/Public/RHI.h"


UTexture2D* ABasePlayerController::Texture2DDynamicToTexture2D(UTexture2DDynamic* Texture)
{
	UTexture2D* ResultTexture;
	ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
		FConvertTextures,
		FTexture2DDynamicResource*, TextureResource, static_cast<FTexture2DDynamicResource*>(Texture->Resource),
		UTexture2D*, ResultTexture, ResultTexture,
		{
			FTexture2DRHIParamRef TextureRHI = TextureResource->GetTexture2DRHI();
			int32 Width = TextureRHI->GetSizeX();
			int32 Height = TextureRHI->GetSizeY();

			uint32 DestStride = 0;
			uint8* ReadData = reinterpret_cast<uint8*>(RHILockTexture2D(TextureRHI, 0, RLM_ReadOnly, DestStride, false, false));

			ResultTexture = UTexture2D::CreateTransient(Width, Height);
			FTexture2DMipMap& Mip = ResultTexture->PlatformData->Mips[0];
			uint8* Data = (uint8*)Mip.BulkData.Lock(LOCK_READ_WRITE);
			FMemory::Memcpy(Data, ReadData, Width * Height * 4);
			Mip.BulkData.Unlock();
			ResultTexture->UpdateResource();

		});

	return ResultTexture;
}

Any ideas?

I solved those errors by added “RHI”, “RenderCore” to the PublicDependencyModuleNames, but the code isn’t working. How am I supposed to use it? Should it be in an Async blueprint like the Download Image node? The compiler complains that ResultTexture may not be initialized so whatever I initialize it to is what is sent to the plugin which makes me think I need it to be async or something.

I got it to work! The problem was in your code ResultTexture wasn’t instantiated so I instantiated it but the pointer was again changed in the render command. Marking this as accepted answer. Thank you!!

Final function for me for anyone else:

UTexture2D * ABasePlayerController::ConvertImage(UTexture2DDynamic * DynTex)
{
	int32 Width = DynTex->SizeX;
	int32 Height = DynTex->SizeY;

	UTexture2D* ResultTexture = UTexture2D::CreateTransient(Width, Height);

	ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
		FConvertTextures,
		FTexture2DDynamicResource*, TextureResource, static_cast<FTexture2DDynamicResource*>(DynTex->Resource),
		UTexture2D*, ResultTexture, ResultTexture,
		{
			FTexture2DRHIParamRef TextureRHI = TextureResource->GetTexture2DRHI();
			int32 Width = TextureRHI->GetSizeX();
			int32 Height = TextureRHI->GetSizeY();

			uint32 DestStride = 0;
			uint8* ReadData = reinterpret_cast<uint8*>(RHILockTexture2D(TextureRHI, 0, RLM_ReadOnly, DestStride, false, false));

			FTexture2DMipMap& Mip = ResultTexture->PlatformData->Mips[0];
			uint8* Data = (uint8*)Mip.BulkData.Lock(LOCK_READ_WRITE);
			FMemory::Memcpy(Data, ReadData, Width * Height * 4);
			Mip.BulkData.Unlock();
			ResultTexture->UpdateResource();
		});

	return ResultTexture;
}

Hi!
Does it work properly with any kind of texture? I’m experiencing problems with a downloaded texture, it distorts completely the image to the point of unrecognition. I was wondering if this method stopped working in a newer version of the engine.

Could someone confirm? Thank you! :slight_smile:

Hello, i´m trying to implement your code in ue4.26 but when compiling the code visual tells
the function takes too many arguments, I change the macro ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER with ENQUEUE_RENDER_COMMAND because in 4.26 it seems not to exit. Do you know why I can´t compile.
thanks