I’m currently trying to have a modified border that renders as usual with all child widgets. After all childs were rendered, I want to set the pixels in the corners to Alpha=0 to retrieve Round corners, or other forms.
To do that I have a widget that inherits from SBorder and overwrites OnPaint methode:
int32 SBorderRound::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
// Call to parent first to draw the Border as usual
const bool bEnabled = ShouldBeEnabled(bParentEnabled);
int32 maxLayerId = SBorder::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bEnabled);
// Now cut of the corners of the rendering
const FSlateBrush* BrushResource = BorderImage.Get();
if (BrushResource && BrushResource->DrawAs != ESlateBrushDrawType::NoDrawType)
{
drawer->geometry = AllottedGeometry.ToPaintGeometry();
FSlateDrawElement::MakeCustom(
OutDrawElements,
LayerId,
drawer
);
}
return maxLayerId;
}
and a using custom draw element:
/**
* Custom Slater Drawer for SBorderRound
*/
class USERINTERFACE_API FBorderRoundDrawer : public ICustomSlateElement
{
public:
FBorderRoundDrawer();
~FBorderRoundDrawer();
virtual void DrawRenderThread(class FRHICommandListImmediate& RHICmdList, const void* textureTarget) override;
FPaintGeometry geometry;
private:
// Access only on RenderThread
FSlateCanvasRenderTarget* renderTarget;
};
void FBorderRoundDrawer::DrawRenderThread(class FRHICommandListImmediate& RHICmdList, const void* textureTarget)
{
// Alternative: https://answers.unrealengine.com/questions/214453/view.html
check(IsInRenderingThread());
FTexture2DRHIRef textureRHIRef = *(FTexture2DRHIRef*)textureTarget;
renderTarget->SetRenderTargetTexture(textureRHIRef);
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHILockTexture2D(textureRHIRef.GetReference(), 0, RLM_WriteOnly, DestStride, false, false);
// NOTE that the texture is not edited
RHIUnlockTexture2D(textureRHIRef.GetReference(), 0, false, false);
renderTarget->ClearRenderTargetTexture();
}
Wrapping the Child of SBorder in a custom UClass and placing it in UMG Editor results in:
The render result shows that ICustomSlateElement::DrawRenderThread is passed the texture for the entire window.
One problem is obviously that the whole screen is gone greyish, but I have no clue why that should have happend. The problem is
uint32 DestStride;
uint8* DestBuffer = (uint8*)RHILockTexture2D(textureRHIRef.GetReference(), 0, RLM_WriteOnly, DestStride, false, false);
// NOTE that the texture is not edited
RHIUnlockTexture2D(textureRHIRef.GetReference(), 0, false, false);
Commenting these lines, the render of the Window is fine. I did nothing to the texture, so why is it broken? I was never able to find the code where elements are actually drawn to a texture, so I have no clue how it is usually done.
According to
void FSlateRHIRenderingPolicy::DrawElements(
FRHICommandListImmediate& RHICmdList,
FSlateBackBuffer& BackBuffer,
FTexture2DRHIRef& ColorTarget,
FTexture2DRHIRef& DepthStencilTarget,
const TArray<FSlateRenderBatch>& RenderBatches,
const TArray<FSlateClippingState> RenderClipStates,
const FSlateRenderingOptions& Options)
{
...
// This element is custom and has no Slate geometry. Tell it to render itself now
CustomDrawer->DrawRenderThread(RHICmdList, &BackBuffer.GetRenderTargetTexture());
...
}
the entire texture for the window is passed to ICustomSlateElement::DrawRenderThread. Removing pixels here will definitly result in issues. How can I retrieve the layer that the Border was rendered to?
Edit:
Is there even a actual layer? Is the layer passed to SWidget::OnPaint only a index for sorting what gets rendered first? So If I want to round the corners of a Border, I actually need to NOT draw them in the first place?