I would like to display an image generated from UStaticMesh in DataAset. I have implemented the IDetailCustomization interface and successfully added the SImage widget. However, I have a problem with generating the image.
I created an helper MeshCapture2D actor, which has a USceneCaptureComponent2D, and a UStaticMeshComponent. In the CustomizeDetails(IDetailLayoutBuilder& DetailLayout) DataAsset, I spawn this actor to get an image from the 3D model, but it looks like the SceneCaptureComponent and Mesh are not ready in the same frame right after spawning.
It also seems that even if I create the actor earlier, it still does not help. Calling the UStaticMeshComponent::SetStaticMesh method and calling CaptureScene in the same frame results in only the sky being rendered to the render target.
I wonder how to solve this problem. Can anyone help?
#include "AssetRegistry/AssetRegistryModule.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "GameFramework/RotatingMovementComponent.h"
#include "Materials/MaterialExpressionAppendVector.h"
#include "Materials/MaterialExpressionOneMinus.h"
#include "UObject/ConstructorHelpers.h"
// Sets default values
ASPMeshCapture2D::ASPMeshCapture2D()
{
SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Scene Root Componnent")));
RenderTargetTexture = NewObject<UTextureRenderTarget2D>();
RenderTargetTexture->ClearColor = FLinearColor::Transparent;
RenderTargetTexture->TargetGamma = 0.0f;
RenderTargetTexture->InitCustomFormat(1024, 1024, PF_FloatRGBA, false);
RenderTargetTexture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
RenderTargetTexture->LODBias = 0;
RenderTargetTexture->UpdateResourceImmediate(true);
GenerateMaterial(RenderTargetTexture);
PrimaryActorTick.bCanEverTick = true;
ItemMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ItemMesh"));
ItemMesh->SetRelativeLocation(FVector(38, 2.5, -5));
ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
ItemMesh->SetVisibleInSceneCaptureOnly(true);
ItemMesh->SetupAttachment(GetRootComponent());
SceneCaptureComponent2D = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("SceneCaptureComponent2D"));
SceneCaptureComponent2D->TextureTarget = RenderTargetTexture;
SceneCaptureComponent2D->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList;
SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_SceneColorHDR;
SceneCaptureComponent2D->ShowOnlyComponent(ItemMesh);
SceneCaptureComponent2D->SetupAttachment(GetRootComponent());
SceneCaptureComponent2D->SetAutoActivate(false);
SceneCaptureComponent2D->bCaptureEveryFrame = false;
}
// Called when the game starts or when spawned
void ASPMeshCapture2D::BeginPlay()
{
Super::BeginPlay();
}
void ASPMeshCapture2D::GenerateMaterial(UTexture* Texture)
{
Material = NewObject<UMaterial>(GetTransientPackage(), NAME_None, RF_Transient);
Material->AddToRoot();
Material->MaterialDomain = MD_UI;
Material->BlendMode = BLEND_Translucent;
UMaterialEditorOnlyData* MaterialEditorOnly = Material->GetEditorOnlyData();
{
auto TextureSamplerExpression = NewObject<UMaterialExpressionTextureSample>(Material);
TextureSamplerExpression->AddToRoot();
TextureSamplerExpression->Texture = Texture;
TextureSamplerExpression->SamplerType = EMaterialSamplerType::SAMPLERTYPE_Color;
TextureSamplerExpression->MaterialExpressionEditorX = -400;
TextureSamplerExpression->MaterialExpressionEditorY = -150;
FExpressionOutput& TextureSamplerOutput = TextureSamplerExpression->GetOutputs()[0];
FExpressionInput& MaterialInputFinalColor = MaterialEditorOnly->EmissiveColor;
FExpressionInput& MaterialInputOpacity = MaterialEditorOnly->Opacity;
MaterialInputFinalColor.Mask = 0;
MaterialInputFinalColor.MaskR = 1;
MaterialInputFinalColor.MaskG = 1;
MaterialInputFinalColor.MaskB = 1;
MaterialInputFinalColor.MaskA = 0;
auto OneMinusExpression = NewObject<UMaterialExpressionOneMinus>(Material);
OneMinusExpression->AddToRoot();
OneMinusExpression->Input.Expression = TextureSamplerExpression;
OneMinusExpression->Input.Mask = 0;
OneMinusExpression->Input.MaskR = 0;
OneMinusExpression->Input.MaskG = 0;
OneMinusExpression->Input.MaskB = 0;
OneMinusExpression->Input.MaskA = 1;
MaterialInputOpacity.Expression = OneMinusExpression;
MaterialInputFinalColor.Expression = TextureSamplerExpression;
Material->GetExpressionCollection().AddExpression(TextureSamplerExpression);
Material->GetExpressionCollection().AddExpression(OneMinusExpression);
}
Material->PostEditChange();
}
UMaterial* ASPMeshCapture2D::GenerateMeshPreview()
{
SceneCaptureComponent2D->CaptureScene();
return Material;
}