rokojori_action_library/Runtime/VFX/FlashVFX/FlashVFX.cs

342 lines
9.3 KiB
C#

using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Flash.svg") ]
public partial class FlashVFX:SequenceAction, Animator
{
[Export]
public FlashPreset preset;
// [Export, ReadOnly]
// public string test = "3";
// [Export, NamedArray ]
// public FlareLayer[] layers = [];
[Export]
public Node[] targets;
[Export]
public bool includeChildren = false;
public enum MaterialAssignementMode
{
Replace_Per_Target,
Add_For_Each_Material
}
[ExportGroup("Material Assignment")]
[Export]
public MaterialAssignementMode assignementMode = MaterialAssignementMode.Add_For_Each_Material;
[Export]
public Node3D positionOffset;
public void OnAnimatorStart(){}
public void OnAnimatorEnd(){}
public void OnAnimatorCancel(){}
Dictionary<int,FlashVFXRunner> _running = new Dictionary<int, FlashVFXRunner>();
protected override void _OnTrigger()
{
if ( assignementMode == MaterialAssignementMode.Replace_Per_Target )
{
_OnTrigger_ReplacePerTarget();
}
if ( assignementMode == MaterialAssignementMode.Add_For_Each_Material )
{
_OnTrigger_AddForEachMaterial();
}
}
protected void _OnTrigger_ReplacePerTarget()
{
var id = DispatchStart();
var duration = preset.duration == null ? new SecondsDuration() : preset.duration;
var tl = TimeLineManager.Ensure( duration.timeLine );
var OVERLAY = GeometryInstance3D.PropertyName.MaterialOverlay;
var runner = new FlashVFXRunner();
runner.flashVFX = this;
runner.targets = VFXTools.GetGeometryInstance3Ds( targets, includeChildren );
var gradient = preset.colorGradient.GetGradient();
var gradientRepeat = preset.colorGradient.GetRepeat();
var gradientRepeatMode = preset.colorGradient.GetRepeatMode();
var opacityCurve = preset.opacityCurve;
if ( runner.targets.Count == 0 )
{
DispatchEnd( id );
return;
}
runner.materials = runner.targets.Map( gi =>
{
return preset.flashType.CreateAndInitializeMaterial( runner, gi, gradient.Sample( 0 ) );
}
);
_running[ id ] = runner;
runner.AddLight();
for ( int i = 0; i < runner.targets.Count; i++ )
{
var t = runner.targets[ i ];
AnimationManager.StartAnimation( this, t, OVERLAY );
t.MaterialOverlay = runner.materials[ i ];
runner.assignedMaterials[ t ] = runner.materials[ i ];
}
TimeLineManager.ScheduleSpanIn( tl, 0, duration.GetDurationInSeconds(),
( span, type )=>
{
var timeNow = tl.position;
var phase = span.phase;
var color = gradient.SampleRepeated( phase * gradientRepeat, gradientRepeatMode );
var opacity = opacityCurve.Sample( phase );
runner.UpdateLight( phase, color, opacity );
for ( int i = 0; i < runner.targets.Count; i++ )
{
var gi = runner.targets[ i ];
if ( ! AnimationManager.IsAnimating( this, gi, OVERLAY ) )
{
continue;
}
var material = runner.materials[ i ];
preset.flashType.ApplyFlashType( runner, span.phase, color, opacity, gi, material );
}
if ( type == TimeLineSpanUpdateType.End )
{
for ( int i = 0; i < runner.targets.Count; i++ )
{
var gi = runner.targets[ i ];
if ( gi.MaterialOverlay == runner.assignedMaterials[ gi ] )
{
gi.MaterialOverlay = null;
}
AnimationManager.EndAnimation( this, gi, OVERLAY, runner.materials[ i ] );
}
runner.RemoveLight();
DispatchEnd( id );
}
},
this
);
}
protected void _OnTrigger_AddForEachMaterial()
{
var id = DispatchStart();
var duration = preset.duration == null ? new SecondsDuration() : preset.duration;
var tl = TimeLineManager.Ensure( duration.timeLine );
var OVERLAY = GeometryInstance3D.PropertyName.MaterialOverlay;
var runner = new FlashVFXRunner();
runner.flashVFX = this;
runner.targets = VFXTools.GetGeometryInstance3Ds( targets, includeChildren );
if ( runner.targets.Count == 0 )
{
DispatchEnd( id );
return;
}
runner.AddLight();
var gradient = preset.colorGradient.GetGradient();
var gradientRepeat = preset.colorGradient.GetRepeat();
var gradientRepeatMode = preset.colorGradient.GetRepeatMode();
var opacityCurve = preset.opacityCurve;
Material nullMaterial = null;
for ( int i = 0; i < runner.targets.Count; i++ )
{
var t = runner.targets[ i ];
if ( t.MaterialOverlay == null )
{
if ( nullMaterial == null )
{
nullMaterial = preset.flashType.CreateAndInitializeMaterial( runner, null, gradient.Sample( 0 ) );
runner.materials.Add( nullMaterial );
runner.assignedMaterials[ t ] = nullMaterial;
}
}
else
{
var original = t.MaterialOverlay;
Material extended = null;
if ( ! runner.mappedMaterials.ContainsKey( original ) )
{
var mapped = preset.flashType.CreateAndInitializeMaterial( runner, null, gradient.Sample( 0 ) );
extended = t.MaterialOverlay.Duplicate() as Material;
Materials.AddToNextPass( extended, mapped );
runner.materials.Add( extended );
runner.mappedMaterials[ original ] = extended;
}
else
{
extended = runner.mappedMaterials[ original ];
}
runner.originalMaterials[ t ] = original;
runner.assignedMaterials[ t ] = extended;
t.MaterialOverlay = extended;
}
}
_running[ id ] = runner;
TimeLineManager.ScheduleSpanIn( tl, 0, duration.GetDurationInSeconds(),
( span, type )=>
{
var timeNow = tl.position;
var phase = span.phase;
var color = gradient.SampleRepeated( phase * gradientRepeat, gradientRepeatMode );
var opacity = opacityCurve.Sample( phase );
runner.UpdateLight( phase, color, opacity );
for ( int i = 0; i < runner.materials.Count; i++ )
{
var material = runner.materials[ i ];
preset.flashType.ApplyFlashType( runner, span.phase, color, opacity, null, material );
}
if ( type == TimeLineSpanUpdateType.End )
{
for ( int i = 0; i < runner.targets.Count; i++ )
{
var gi = runner.targets[ i ];
var material = runner.assignedMaterials[ gi ];
if ( gi.MaterialOverlay == material )
{
gi.MaterialOverlay = runner.originalMaterials.ContainsKey( gi ) ? runner.originalMaterials[ gi ] : null;
}
}
DispatchEnd( id );
runner.RemoveLight();
}
},
this
);
}
}
public class FlashVFXRunner
{
public FlashVFX flashVFX;
public FlashPreset preset => flashVFX.preset;
public List<GeometryInstance3D> targets = [];
public List<Material> materials = [];
public OmniLight3D light;
public Dictionary<GeometryInstance3D,Material> originalMaterials = new Dictionary<GeometryInstance3D, Material>();
public Dictionary<GeometryInstance3D,Material> assignedMaterials = new Dictionary<GeometryInstance3D, Material>();
public Dictionary<Material,Material> mappedMaterials = new Dictionary<Material, Material>();
public Vector3 GetFlashPosition( FlashPreset.PositionMode mode, Vector3 offset )
{
if ( FlashPreset.PositionMode.Position_Offset == mode )
{
return flashVFX.positionOffset.GlobalPosition + offset;
}
else if ( FlashPreset.PositionMode.Origin_Of_First == mode )
{
return targets[ 0 ].GlobalPosition + offset;
}
else if ( FlashPreset.PositionMode.Center_Of_All == mode )
{
return Node3DExtensions.GetCenter( targets ) + offset;
}
else
{
return offset;
}
}
public void AddLight()
{
if ( ! preset.lightEnabled )
{
return;
}
light = flashVFX.CreateChild<OmniLight3D>();
light.LightColor = new Color( 0, 0, 0 );
light.GlobalPosition = GetFlashPosition( preset.lightPosition, preset.lightPositionOffset );
light.OmniRange = preset.lightRange;
light.OmniAttenuation = preset.lightAttenutation;
light.LightEnergy = preset.lightEnergy;
light.ShadowEnabled = preset.lightShadowCasting;
}
public void UpdateLight( float phase, Color color, float opacity )
{
if ( light == null )
{
return;
}
light.LightColor = color * opacity;
}
public void RemoveLight()
{
if ( light == null )
{
return;
}
light.DeleteSelf();
}
}
}