UTexture* to widget image, fails UTexture2D cast

Hi

So i’m trying to send the output render from Composure to my actor. I’ve got a class called PointerCompositingOutput which is a subclass of UCompositingElementOutput, where i can put my class as the output for Composure. It overrides the function responsible for handling the final render, and i’m setting the variable in my pawn object reference. I’m getting my reference to my actor in my scene through a TSoftObjectPtr.

In the BP of the pawn that PointerCompositingOutput sets, i did a GetMemorySize print result is 33177600. And printing IsValid, it returns false for a frame then returns true.


But when pressing play, i don’t see my image. In my widget im checking if it’s valid, if it’s not valid it uses an image from the content browser. The debug image works and does show up

Within my function, there are 2 if statements, it will either set the UTexture to the pawn or save it as an exr file. I can verify that the images it outputs is indeed the image i expected to see.
Within the if statement that sets the variable on my pawn, I’ve also bypassed my bool and have it set the variable every tick, but it doesn’t change the result.

cpp function override that handles composure UTexture

void UNcPointerCompositingOutput::RelayOutput_Implementation(UTexture* RenderResult, UComposurePostProcessingPassProxy* PostProcessProxy)
{
	
	ComposureRender = RenderResult;
	UWorld* World = GetWorld();

	UTexture* OutputImage = RenderResult;
	
	//Set variable of Pawn
	if (TargetActor.IsValid() && IsValid(RenderResult) && !WriteExrFiles && !stopSetting)
	{
		TargetActor->setTextureVal(ComposureRender);
		stopSetting = true;
		UE_LOG(LogTemp, Display, TEXT("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!its been set!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"));
	}
	
	//save Texture as EXR. Code copied from CompositingElementsOutput.cpp
	if (World && World->IsGameWorld() && WriteExrFiles) // Check if its in play mode or in editor mode
	{
		if (OutputImage && !OutputDirectiory.Path.IsEmpty() && !FilenameFormat.IsEmpty())
		{
			bool bShouldCapture = false;
			if (FrameNumber == 0)
			{
				bShouldCapture = true;
			}
			else
			{
				SecondsSinceLastCapture += FApp::GetDeltaTime();
				bShouldCapture = SecondsSinceLastCapture >= OutputFrameRate.AsInterval();
			}

			if (bShouldCapture)
			{
				FImageWriteOptions WriteOptions;
				WriteOptions.Format = EDesiredImageFormat::EXR;
				WriteOptions.bOverwriteFile = true;
				WriteOptions.bAsync = true;
				WriteOptions.CompressionQuality = (int32)Compression;

				const FString FrameIdString = FString::Printf(TEXT("%04d"), FrameNumber);
				FString Filename = FilenameFormat.Replace(TEXT("{frame}"), *FrameIdString);

				UImageWriteBlueprintLibrary::ExportToDisk(OutputImage, OutputDirectiory.Path / Filename, WriteOptions);

				++FrameNumber;
				SecondsSinceLastCapture = 0.0;
			}
		}
		else
		{
			InternalReset();
		}
	}
}

pawns setTextureVal function

void AVirtualProductionPawn::setTextureVal(UTexture* Tex)
{
	ComposureOutputRenderUTexture = Tex;
}

I’m very lost on this as i’ve spent nearly 2 days trying to figure this out, it also seems to fail when i cast the UTexture to UTexture2D in my pawns BP. Is there some formatting the UTexture that i could be missing?

Ultimately i’m looking to make my UTexture into a TArray as outlined in this glorious post.

Did a quick test on begin play of an actor where Tex is a UTexture.

	if (Tex != nullptr) {
		if (UTexture2D* tex2d = Cast<UTexture2D>(Tex)) {
			UE_LOG(LogTemp, Warning, TEXT("Conversion ok"));
		}
		else {
			UE_LOG(LogTemp, Error, TEXT("Conversion failed"));
		}
	}

The cast works no problem. Try adding a nullptr test of what you are trying to convert to make sure your not converting an uninitialized texture.

Hey again 3dRaven! This ones a real headscratcher.

So here’s a screenshot of that result and here’s where those prints are being called from.

  • RelayOutput_Implementation printing after it passes the if (updated if to check for nullptr)
  • Pawn Blueprint printing GetMemorySize
  • Pawn Blueprint printing is valid
  • Pawn Blueprint printing Cast To Texture2D failed cast
  • Pawn C++ SetTextureValue on failed cast.
    image

the settexture function
image

I’ve also added != nullptr check when it sets the variable and passing through the RenderResult.

But also if the variable is a nullptr, should the memory value be 33MB?

Try setting a breakpoint where you are setting the UTexture and where you are assigning
ComposureOutputRenderUTexture = Tex;
Put a break point at the start of the function and see if your can hover over Tex once the breakpoint hits and see what it’s value is.

Seeing that the input value is of type UTexture it shouldn’t fail.

No a nullptr wouldn’t be 33MB.

I wonder if WriteOptions.bAsync = true; isn’t messing with the output.

With most async functions you would have a callback to continue further progress.

Could it be that the export is not finished while you advance to the next part of the code?

Edit:

After checking FImageWriteOptions there is a OnComplete delegate like i suspected. You should hook it up to a function and move the rest of the code there, or turn off async.

Right now your code is continuing before the write to disk is ready.

OnComplete

So with the break points it looks like it’s all there.

BUT, i can’t believe i didnt do this before as i assumed the function override was saying it’s true class. Running the variable through the Get Class blueprint node is returning a TextureRenderTarget2D.

As for the OnComplete delegate. If i was sending this straight to my Widget to display the image, would it need that delegate, i thought it would try and put the image on the widget as soon as the variable updates. If it was an actual UTexture and not a TextureRenderTarget2D.
I assume this is needed if i’m writing to disk and not trying to pass the data around and display in different areas?


UTexture2D* UImageFunctionLibrary::CreateTexture(UTextureRenderTarget2D* TextureRenderTarget) {

	// Creates Texture2D to store TextureRenderTarget content
	UTexture2D* Texture = UTexture2D::CreateTransient(TextureRenderTarget->SizeX, TextureRenderTarget->SizeY, PF_B8G8R8A8);
#if WITH_EDITORONLY_DATA
	Texture->MipGenSettings = TMGS_NoMipmaps;
#endif
	Texture->SRGB = TextureRenderTarget->SRGB;

	// Read the pixels from the RenderTarget and store them in a FColor array
	TArray<FColor> SurfData;
	FRenderTarget* RenderTarget = TextureRenderTarget->GameThread_GetRenderTargetResource();
	RenderTarget->ReadPixels(SurfData);

	// Lock and copies the data between the textures
	void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
	const int32 TextureDataSize = SurfData.Num() * 4;
	FMemory::Memcpy(TextureData, SurfData.GetData(), TextureDataSize);
	Texture->GetPlatformData()->Mips[0].BulkData.Unlock();
	// Apply Texture changes to GPU memory
	Texture->UpdateResource();
	return Texture;
}

Here’s an old function of mine to convert UTextureRenderTarget2D to a UTexture2D. Might be useful.

1 Like

Wow that code is great. Thanks so much, my jaw dropped checking this! This seems to potentially kill 2 birds with 1 stone as to what i’m trying to do.
i think it’s working as it’s setting the class now as a UTexture2D and the data seems right when querying the XY size.

Wanted to double check though. As i’m taking this UTexture2D and setting the image variable inside my widget which has an “Image” in the canvas panel. Would i expect to see the texture on this as it’s updating every tick? Using the Widget reflector i can see that there is a Texure transient that it’s pointing too and every now and again the little preview window shows an image! I wanted to see the Texture2D image in the Widget as a way to verify that it is indeed all there and correct.

It would be better if the update to the widget was event driven. Only update it when the texture changes.

You could do a custom class based on uuserwidget with a bindwidget image exposed to c++. You could then access it from within your code.

So to emulate a event i got the engine time code seconds%10 and Frames%15 so it only has a small amount of time to update. I do now see a white square.

May there be other ways to view the Texture2D to validate?

You could temporarily pass in an actor class and set it’s material slot 0 with a new dynamic material. You would need to change the dynamic materials texture parameter and pass in the Utexture.

A bit of a bother but it would work.

Or go the route of passing in the custom uuserwidget with the bound image parameter and set brush from texture on it with the new UTexture.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.