Godot Version
v4.6.1 Mono
Question
I have a shader that swaps color palette. It takes in two textures, one for source palette and other for target. The shader will then swap every color on the image that maps onto the source palette with the target palette. The shader works as intended.
However, when trying to swap out these textures at run time i have weird issues. It works exactly as intended for my Control nodes that uses TextureRect to display an Icon (that uses the shader, so the colors in the icon are swapped correctly). However, it does not work as intended for Sprite2D, even though im using the shader the same way.
For my Sprite2D, i can only set shader parameters in _Ready, if i try to do it elsewhere it doesnt work, unless i share the same material across all instances (by sett LocalToScene in material to false)
It works for my ItemSlotGui, which is a control node.
public partial class ItemSlotGui : GenericButton
{
TextureRect Icon;
ShaderMaterial _material;
public override void _Ready()
{
Icon = FindChild("Icon") as TextureRect;
_material = (ShaderMaterial)Icon.Material;
Icon.Material = _material;
}
public void SetItem(Item item, Item compareItem = null)
{
if (item != null)
{
if (item.Material != ItemMaterial.None)
{
Texture2D palette = GuiManager.Instance.MaterialPalettes[item.Material]; //Gets a "color palette" texture
_material.SetShaderParameter("target_palette_texture", palette);
Icon.Material = _material;
}
else
{
Icon.Material = null;
}
}
}
}
It does not work for ArmorPart, which is a Sprite2D.
public partial class ArmorPart : Sprite2D
{
private ShaderMaterial _material;
public override void _Ready()
{
_material = (ShaderMaterial)Material;
var pal = GuiManager.Instance.MaterialPalettes[ItemMaterial.Iron];
_material.SetShaderParameter("target_palette_texture", pal); //When setting the Parameter in _Ready, it works.
GD.Print($"[_Ready] Material RID: {_material.GetRid()} " + " / " + Name); //This prints the same RID as the Print in SetShader.
}
public void SyncFramesFrom(Sprite2D source)
{
Hframes = source.Hframes;
Vframes = source.Vframes;
}
public void SetShader(ItemMaterial material)
{
GD.Print($"Material RID: {Material?.GetRid()}"); //Same RID as below and as in _Ready
GD.Print($"_material RID: {_material?.GetRid()}"); //Same RID as above and as in _Ready
var pal = GuiManager.Instance.MaterialPalettes[material];
_material.SetShaderParameter("target_palette_texture", pal); //This doesnt work, even though its the same thing i do in ItemSlotGui and in _Ready.
QueueRedraw(); //Tried doing this, but doesnt change anything
}
}
However, ArmorPart does set the shader correctly if “Local to Scene” is false in the material setting for the scene (ArmorPart scene). However, different armorparts should be able to have different shaders, so “Local to Scene” has to be true.
I also tried doing:
_material = (ShaderMaterial)Material.Duplicate();
Material = _material;
Inside the _Ready for ArmorPart, this gave me the same result (only the _Ready Parameter change matters)