I’m trying to render a dynamic material instance in a Slate Widget and nothing is displayed.
I have a widget derived from SImage
class KSGM_API SKBatteryWidget : public SImage
{
public:
SLATE_BEGIN_ARGS(SKBatteryWidget)
: _HUD(nullptr)
, _BatteryBrush(nullptr)
, _FillMatInst(nullptr)
, _Level(1.0f)
, _MaxLevel(1.0f)
{}
/** @brief The HUD used to create the dynamic material instance. */
SLATE_ARGUMENT(AKPlayerHUD*, HUD)
/** @brief The battery brush. */
SLATE_ATTRIBUTE(const FSlateBrush*, BatteryBrush)
/** @brief The fill material instance. */
SLATE_ARGUMENT(UMaterialInstance*, FillMatInst)
In the constructor, I create the dynamic material instance using the HUD pointer.
void SKBatteryWidget::Construct(const FArguments& InArgs)
{
// Create the dynamic material instance
if (InArgs._HUD != nullptr && InArgs._FillMatInst != nullptr)
FillMatInstDyn = UMaterialInstanceDynamic::Create(InArgs._FillMatInst, InArgs._HUD);
Level = InArgs._Level;
MaxLevel = InArgs._MaxLevel;
LevelParamName = InArgs._LevelParamName;
MaxLevelParamName = InArgs._MaxLevelParamName;
SImage::Construct(SImage::FArguments()
.Image(InArgs._BatteryBrush)
);
}
In the Tick function, I calculate the ratio and set the parameter to the dynamic material instance.
void SKBatteryWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
SLeafWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
CachedGeometry = AllottedGeometry;
// Calculate the ratio
float v = Level.Get();
float m = MaxLevel.Get();
Ratio = v / m;
if (FillMatInstDyn)
{
FillMatInstDyn->SetScalarParameterValue(LevelParamName, v);
FillMatInstDyn->SetScalarParameterValue(MaxLevelParamName, m);
}
}
In the OnPaint call-back, the brush of the battery is well displayed, but the dynamic material instance not.
int32 SKBatteryWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
LayerId = SImage::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
// Draw inside the battery based on the ratio
FSlateBrush FillBrush;
FillBrush.SetResourceObject(FillMatInstDyn);
FillBrush.DrawAs = ESlateBrushDrawType::Image;
// Calculate the rect we have to fill
FSlateRect rect = MyClippingRect;
float height = rect.GetSize().Y;
rect.Top = rect.Bottom - (Ratio * height);
LayerId++;
FSlateDrawElement::MakeBox(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
&FillBrush,
rect,
ESlateDrawEffect::None,
FillBrush.GetTint(InWidgetStyle) * InWidgetStyle.GetColorAndOpacityTint()
);
return LayerId;
}
Do I’m missing something or there is a bug rendering dynamic material instance in Slate Widget ?
Txs for your help,
D.