cirnogodot/Scripts/Components/Actors/3D/AnimatedShaderSprite3D.cs

142 lines
No EOL
4.1 KiB
C#

using Godot;
namespace Cirno.Scripts.Components.Actors._3D;
[Tool]
public partial class AnimatedShaderSprite3D : AnimatedSprite3D
{
private bool _isConnected;
private ShaderMaterial _shaderMaterial;
// Export the shader parameter name so the shader can use a different uniform name if needed.
[Export]
public string ShaderParameterName { get; set; } = "tex";
/// <summary>
/// Called when the node enters the scene tree. Ensure the shader receives the current
/// sprite frame right away (avoids the garbled default until the first frame change).
/// </summary>
public override void _Ready()
{
base._Ready();
ConnectSignals();
// Safely try to use the MaterialOverride as a ShaderMaterial. If it's not present
// we warn and skip applying parameters to avoid runtime exceptions.
if (MaterialOverride is ShaderMaterial sm)
{
_shaderMaterial = sm;
}
else
{
_shaderMaterial = null;
GD.PrintErr($"AnimatedShaderSprite3D: MaterialOverride is not a ShaderMaterial on '{Name}'. Shader parameters will not be applied.");
}
// Defer applying the current frame until after the engine finishes initialization
// so we avoid reading frames or materials before they're fully ready.
CallDeferred(nameof(ApplyCurrentFrame));
}
private void ConnectSignals()
{
if (_isConnected)
{
return;
}
_isConnected = true;
FrameChanged += HandleFrameChanged;
}
protected void HandleFrameChanged()
{
ApplyCurrentFrame();
}
/// <summary>
/// Safely set the shader parameter to the current animation/frame texture.
/// This method protects against missing SpriteFrames, missing animation names,
/// and null material so it won't crash at startup.
/// </summary>
private void ApplyCurrentFrame()
{
if (_shaderMaterial is null)
{
return;
}
var frames = SpriteFrames;
if (frames is null)
{
// No frames assigned; nothing to do.
return;
}
// Guard against missing animation or frame out-of-range.
try
{
var tex = frames.GetFrameTexture(Animation, Frame);
if (tex is not null)
{
_shaderMaterial.SetShaderParameter(ShaderParameterName, tex);
}
}
catch (System.Exception ex)
{
// Don't crash the editor/runtime for a missing animation name or bad frame index.
GD.PrintErr($"AnimatedShaderSprite3D: Could not apply frame texture on '{Name}': {ex.Message}");
}
}
protected override void Dispose(bool disposing)
{
DisconnectSignals();
base.Dispose(disposing);
}
private void DisconnectSignals()
{
if (!_isConnected)
{
return;
}
FrameChanged -= HandleFrameChanged;
}
// public override void _Process(double delta)
// {
// ((ShaderMaterial)MaterialOverride).SetShaderParameter("albedo_texture", SpriteFrames.GetFrameTexture(Animation, Frame));
//
// ((ShaderMaterial)MaterialOverride).SetShaderParameter("billboard_mode", (int)this.GetBillboardMode());
// }
// [Export(PropertyHint.File)] public string ShaderPath { get; private set; }
//
// public override void _Ready()
// {
// if (MaterialOverride is null)
// {
// ApplyMaterialOverride();
// }
//
// ApplyTexture();
// //texture_changed.connect(_apply_texture)
// }
//
// private void ApplyMaterialOverride()
// {
// var shaderMaterial = new ShaderMaterial();
// shaderMaterial.Shader = GD.Load<Shader>(ShaderPath);
// MaterialOverride = shaderMaterial;
// }
//
// private void ApplyTexture()
// {
// var shaderMaterial = MaterialOverride as ShaderMaterial;
//
// shaderMaterial.SetShaderParameter("sprite_texture", this.SpriteFrames.GetFrameTexture(Animation, Frame));
// }
}