|
|
|
// FIXME: Unable to cast object of type 'ReplacePalette' to type 'Godot.CollisionObject3D'.
|
|
|
|
public partial class ReplacePalette : Node3D
|
|
|
|
{
|
|
|
|
readonly struct TextureInfo(float min, float max)
|
|
|
|
{
|
|
|
|
public float MinBrightness { get; } = min;
|
|
|
|
public float MaxBrightness { get; } = max;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Shader _paletteShader;
|
|
|
|
static Dictionary<Texture2D, TextureInfo> _textureCache = [];
|
|
|
|
|
|
|
|
[Export] public Texture2D Palette { get; set; }
|
|
|
|
|
|
|
|
public override void _Ready()
|
|
|
|
{
|
|
|
|
_paletteShader ??= Load<Shader>("res://assets/shaders/palette_swap.gdshader");
|
|
|
|
ReplaceShaderRecursive(this, Palette);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReplaceShaderRecursive(Node node, Texture2D palette)
|
|
|
|
{
|
|
|
|
if (node is MeshInstance3D mesh) {
|
|
|
|
var oldMaterial = (StandardMaterial3D)mesh.Mesh.SurfaceGetMaterial(0);
|
|
|
|
var texture = oldMaterial.AlbedoTexture;
|
|
|
|
var info = GetOrCreateTextureInfo(texture);
|
|
|
|
|
|
|
|
var material = new ShaderMaterial { Shader = _paletteShader };
|
|
|
|
material.SetShaderParameter("palette", palette);
|
|
|
|
material.SetShaderParameter("albedo_texture", texture);
|
|
|
|
material.SetShaderParameter("min_brightness", info.MinBrightness);
|
|
|
|
material.SetShaderParameter("max_brightness", info.MaxBrightness);
|
|
|
|
mesh.MaterialOverride = material;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var child in node.GetChildren())
|
|
|
|
ReplaceShaderRecursive(child, palette);
|
|
|
|
}
|
|
|
|
|
|
|
|
static TextureInfo GetOrCreateTextureInfo(Texture2D texture)
|
|
|
|
{
|
|
|
|
if (!_textureCache.TryGetValue(texture, out var info)) {
|
|
|
|
var minBrightness = 1.0f;
|
|
|
|
var maxBrightness = 0.0f;
|
|
|
|
|
|
|
|
var image = texture.GetImage();
|
|
|
|
image.Convert(Image.Format.Rgba8);
|
|
|
|
var data = image.GetData().AsSpan();
|
|
|
|
for (var i = 0; i < data.Length; i += 4) {
|
|
|
|
var rgba = data.Slice(i, 4);
|
|
|
|
if (rgba[3] <= byte.MaxValue / 2) continue;
|
|
|
|
var brightness = Max(rgba[0], Max(rgba[1], rgba[2])) / (float)byte.MaxValue;
|
|
|
|
minBrightness = Min(minBrightness, brightness);
|
|
|
|
maxBrightness = Max(maxBrightness, brightness);
|
|
|
|
}
|
|
|
|
// for (var x = 0; x < image.GetWidth(); x++)
|
|
|
|
// for (var y = 0; y < image.GetHeight(); y++) {
|
|
|
|
// var color = image.GetPixel(x, y);
|
|
|
|
// // var brightness = Max(color.R, Max(color.G, color.B));
|
|
|
|
// color.ToHsv(out _, out _, out var brightness);
|
|
|
|
// minBrightness = Min(minBrightness, brightness);
|
|
|
|
// maxBrightness = Max(maxBrightness, brightness);
|
|
|
|
// }
|
|
|
|
|
|
|
|
info = new(minBrightness, maxBrightness);
|
|
|
|
_textureCache.Add(texture, info);
|
|
|
|
}
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
}
|