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"; /// /// 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). /// 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(); } /// /// 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. /// 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(ShaderPath); // MaterialOverride = shaderMaterial; // } // // private void ApplyTexture() // { // var shaderMaterial = MaterialOverride as ShaderMaterial; // // shaderMaterial.SetShaderParameter("sprite_texture", this.SpriteFrames.GetFrameTexture(Animation, Frame)); // } }