Tween Lights

This commit is contained in:
Josef 2025-05-27 22:07:15 +02:00
parent 66e92068eb
commit f025656b97
23 changed files with 651 additions and 64 deletions

View File

@ -0,0 +1,70 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenLight:SequenceAction
{
[Export]
public Light3D light3D;
[Export]
public TweenLightData tweenLightData;
[Export]
public TweenType tweenType = new TweenTimeCurve();
[Export]
public TimeLine timeLine;
protected override void _OnTrigger()
{
if ( light3D == null || tweenLightData == null )
{
return;
}
var tl = TimeLineManager.Ensure( timeLine );
var start = tl.position;
var fromData = new TweenLightData();
fromData.CopyFrom( light3D );
var lerpData = tweenLightData.Clone( true );
var sequenceID = DispatchStart();
var tweenType = this.tweenType;
if ( tweenType == null )
{
tweenType = TweenTimeCurve.defaultCurve;
}
TimeLineManager.ScheduleSpanIn( tl, 0, tweenType.GetTweenDuration(),
( span, type )=>
{
var timeNow = tl.position;
var elapsed = timeNow - start;
var state = tweenType.GetTweenPhaseForPhase( span.phase );
TweenLightData.LerpTo( fromData, tweenLightData, state, lerpData );
lerpData.CopyTo( light3D );
if ( type == TimeLineSpanUpdateType.End )
{
DispatchEnd( sequenceID );
}
}
);
}
}
}

View File

@ -0,0 +1 @@
uid://bhn2p16isptba

View File

@ -0,0 +1,86 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenLightData:Resource
{
[ExportGroup("Light")]
[Export]
public ColorValue lightColor;
[Export]
public FloatValue lightEnergy;
[Export]
public FloatValue lightSpecular;
[ExportGroup("Shadow")]
[Export]
public FloatValue shadowOpacity;
public virtual TweenLightData Clone( bool deepClone )
{
var clone = new TweenLightData();
clone.lightColor = ColorValue.Clone( lightColor, deepClone );
clone.lightEnergy = FloatValue.Clone( lightEnergy, deepClone );
clone.lightSpecular = FloatValue.Clone( lightSpecular, deepClone );
clone.shadowOpacity = FloatValue.Clone( shadowOpacity, deepClone );
return clone;
}
public virtual void CopyFrom( Light3D light3D )
{
lightColor = ColorValue.Create( light3D.LightColor );
lightEnergy = FloatValue.Create( light3D.LightEnergy );
lightSpecular = FloatValue.Create( light3D.LightSpecular );
shadowOpacity = FloatValue.Create( light3D.ShadowOpacity );
}
public virtual void CopyFrom( TweenLightData tweenLightData )
{
lightColor = tweenLightData.lightColor;
lightEnergy = tweenLightData.lightEnergy;
lightSpecular = tweenLightData.lightSpecular;
shadowOpacity = tweenLightData.shadowOpacity;
}
public virtual void CopyTo( Light3D light3D )
{
if ( lightColor != null )
{ light3D.LightColor = lightColor.value; }
if ( lightEnergy != null )
{ light3D.LightEnergy = lightEnergy.value; }
if ( lightSpecular != null )
{ light3D.LightSpecular = lightSpecular.value; }
if ( shadowOpacity != null )
{ light3D.ShadowOpacity = shadowOpacity.value; }
}
public static void LerpTo( TweenLightData a, TweenLightData b, float lerpAmount, TweenLightData output )
{
ColorValue.Lerp( a.lightColor, b.lightColor, lerpAmount, output.lightColor );
FloatValue.Lerp( a.lightEnergy, b.lightEnergy, lerpAmount, output.lightEnergy );
FloatValue.Lerp( a.lightSpecular, b.lightSpecular, lerpAmount, output.lightSpecular );
FloatValue.Lerp( a.shadowOpacity, b.shadowOpacity, lerpAmount, output.shadowOpacity );
}
}
}

View File

@ -0,0 +1 @@
uid://b6bnmseo2eq0q

View File

@ -1,4 +1,5 @@
using System;
using Godot;
@ -12,38 +13,36 @@ namespace Rokojori
public void OnAnimatorEnd(){}
public void OnAnimatorCancel(){}
[Export]
public Material material;
// [Export]
// public Material fromMaterial;
[Export]
public float duration;
public Material toMaterial;
[Export]
public Curve tweenCurve = MathX.Curve( 0, 1 );
public TweenType tweenType = new TweenTimeCurve();
[Export]
public TimeLine timeLine;
ColorProperty[] colors = [];
[Export]
public ColorProperty[] colors = [];
[Export]
public FloatProperty[] floats = [];
FloatProperty[] floats = [];
public enum MaterialAssignmentMode
{
No_Assignment,
Assignment,
Unique_Assignment
Assignment_Once,
Unique_Once,
Temporary_Duplicate_For_Each_Tween
}
[ExportGroup( "Target Assignment")]
[Export]
public MaterialAssignmentMode assignmentMode;
// [Export]
// public
MaterialAssignmentMode assignmentMode = MaterialAssignmentMode.Temporary_Duplicate_For_Each_Tween;
[ExportGroup( "Target")]
[Export]
public Node3D target;
@ -53,95 +52,140 @@ namespace Rokojori
[Export]
public int index = 0;
// [ExportToolButton( "Assign From-Material")]
// public Callable AssignFromMaterialButton => Callable.From(
// () =>
// {
// MaterialSurfaceContainer.SetMaterialInSlot( target, index, slot, fromMaterial );
// }
// );
[ExportToolButton( "Assign To-Material")]
public Callable AssignToMaterialButton => Callable.From(
() =>
{
MaterialSurfaceContainer.SetMaterialInSlot( target, index, slot, toMaterial );
}
);
[ExportToolButton( "Get Material Delta")]
public Callable GetMaterialDeltaButton => Callable.From(
() =>
{
var from = MaterialSurfaceContainer.GetMaterialInSlot<Material>( target, index, slot );
this.LogInfo( "Delta", MaterialDelta.Create( from, toMaterial ) );
}
);
Material _assignedMaterial = null;
protected override void _OnTrigger()
{
if ( material == null && _assignedMaterial == null )
if ( toMaterial == null && this._assignedMaterial == null )
{
return;
}
SetAssignedMaterial();
var fromMaterial = MaterialSurfaceContainer.From( target, this.index ).GetMaterialInSlot<Material>( slot );
SetAssignedMaterial( fromMaterial );
var tl = TimeLineManager.Ensure( timeLine );
var start = tl.position;
var delta = MaterialDelta.Create( fromMaterial, toMaterial );
this.LogInfo( "Delta:", delta );
colors = delta.colorProperties.ToArray();
floats = delta.floatProperties.ToArray();
var startColors = new Color[ colors.Length ];
var startFloats = new float[ floats.Length ];
var index = 0;
var assignedMaterial = _assignedMaterial;
var elementIndex = 0;
foreach ( var c in colors )
{
AnimationManager.StartAnimation( this, assignedMaterial, c.propertyName );
startColors[ index ] = c.propertyName.Get( assignedMaterial );
AnimationManager.StartAnimation( this, _assignedMaterial, c.propertyName );
startColors[ elementIndex ] = c.propertyName.Get( fromMaterial );
index ++;
elementIndex ++;
}
index = 0;
elementIndex = 0;
foreach ( var f in floats )
{
AnimationManager.StartAnimation( this, assignedMaterial, f.propertyName );
startFloats[ index ] = f.propertyName.Get( assignedMaterial );
index ++;
AnimationManager.StartAnimation( this, _assignedMaterial, f.propertyName );
startFloats[ elementIndex ] = f.propertyName.Get( fromMaterial );
elementIndex ++;
}
var sequenceID = DispatchStart();
TimeLineManager.ScheduleSpanIn( tl, 0, duration,
var tweenType = this.tweenType;
if ( tweenType == null )
{
tweenType = TweenTimeCurve.defaultCurve;
}
TimeLineManager.ScheduleSpanIn( tl, 0, tweenType.GetTweenDuration(),
( span, type )=>
{
var timeNow = tl.position;
var elapsed = timeNow - start;
var index = 0;
var state = span.phase;
if ( tweenCurve != null )
{
state = tweenCurve.Sample( state );
}
var elementIndex = 0;
var state = tweenType.GetTweenPhaseForPhase( span.phase );
foreach ( var c in colors )
{
if ( AnimationManager.IsAnimating( this, assignedMaterial, c.propertyName ) )
if ( AnimationManager.IsAnimating( this, _assignedMaterial, c.propertyName ) )
{
c.ApplyLerped( assignedMaterial, startColors[ index ], state );
c.ApplyLerped( _assignedMaterial, startColors[ elementIndex ], state );
}
index ++;
elementIndex ++;
}
index = 0;
elementIndex = 0;
foreach ( var f in floats )
{
if ( AnimationManager.IsAnimating( this, assignedMaterial, f.propertyName ) )
if ( AnimationManager.IsAnimating( this, _assignedMaterial, f.propertyName ) )
{
f.ApplyLerped( assignedMaterial, startFloats[ index ], state );
f.ApplyLerped( _assignedMaterial, startFloats[ elementIndex ], state );
}
index ++;
elementIndex ++;
}
if ( type == TimeLineSpanUpdateType.End )
{
foreach ( var c in colors )
{
AnimationManager.EndAnimation( this, assignedMaterial, c.propertyName );
AnimationManager.EndAnimation( this, _assignedMaterial, c.propertyName );
}
foreach ( var f in floats )
{
AnimationManager.EndAnimation( this, assignedMaterial, f.propertyName );
AnimationManager.EndAnimation( this, _assignedMaterial, f.propertyName );
}
if ( MaterialAssignmentMode.Temporary_Duplicate_For_Each_Tween == assignmentMode )
{
_assignedMaterial = null;
var msc = MaterialSurfaceContainer.From( target, index );
msc.SetMaterialInSlot( slot, toMaterial );
}
DispatchEnd( sequenceID );
}
}
@ -150,8 +194,16 @@ namespace Rokojori
}
void SetAssignedMaterial()
void SetAssignedMaterial( Material material )
{
if ( MaterialAssignmentMode.Temporary_Duplicate_For_Each_Tween == assignmentMode )
{
_assignedMaterial = (Material)material.Duplicate();
var tmsc = MaterialSurfaceContainer.From( target, index );
tmsc.SetMaterialInSlot( slot, _assignedMaterial );
return;
}
if ( _assignedMaterial != null )
{
return;
@ -159,15 +211,15 @@ namespace Rokojori
if ( MaterialAssignmentMode.No_Assignment == assignmentMode )
{
_assignedMaterial = material;
_assignedMaterial = toMaterial;
return;
}
_assignedMaterial = material;
_assignedMaterial = toMaterial;
if ( MaterialAssignmentMode.Unique_Assignment == assignmentMode )
if ( MaterialAssignmentMode.Unique_Once == assignmentMode )
{
_assignedMaterial = (Material)material.Duplicate();
_assignedMaterial = (Material)toMaterial.Duplicate();
}
var msc = MaterialSurfaceContainer.From( target, index );

View File

@ -13,9 +13,49 @@ namespace Rokojori
[Export]
public FlashEffect flashEffect;
[Export]
public Node3D[] targets = new Node3D[ 0 ];
Node3D[] _targets = [];
[Export]
public Node3D[] targets
{
get => _targets;
set
{
_targets = value;
_allTargets = null;
}
}
[Export]
public bool includeChildren = false;
bool refresh = false;
Node3D[] _allTargets;
public Node3D[] allTargets
{
get
{
if ( _allTargets != null )
{
return _allTargets;
}
if ( includeChildren )
{
_allTargets = Nodes.GetAnyChildren( _targets ).ToArray();
}
else
{
_allTargets = _targets;
}
_allTargets = Lists.Filter( Lists.From( allTargets ), t => t as GeometryInstance3D != null ).ToArray();
return _allTargets;
}
}
int animationID = -1;
int actionID = -1;
@ -73,7 +113,7 @@ namespace Rokojori
if ( FlashEffect.FlashLightMode.No_Light != flashEffect.lightMode )
{
light = targets[ 0 ].CreateChild<OmniLight3D>();
light = allTargets[ 0 ].CreateChild<OmniLight3D>();
light.LightColor = ComputeLightColor( color, 0 );
light.LightEnergy = 0;
light.OmniRange = flashEffect.lightRange;
@ -126,7 +166,7 @@ namespace Rokojori
_materials = new List<Material>();
Arrays.ForEach( targets,
Arrays.ForEach( allTargets,
t =>
{
var m = (Material) material.Duplicate( true );
@ -219,12 +259,6 @@ namespace Rokojori
public override void CancelAction( int id )
{
if ( false && actionID != id )
{
// RJLog.Log( "actionID != id", "id", id, "actionID", actionID );
return;
}
if ( light != null )
{
// RJLog.Log( "Removing Light", light.Name );
@ -243,14 +277,14 @@ namespace Rokojori
void RemoveMaterials()
{
if ( _materials.Count != targets.Length )
if ( _materials.Count != allTargets.Length )
{
return;
}
for ( int i = 0; i < targets.Length; i++ )
for ( int i = 0; i < allTargets.Length; i++ )
{
Materials.RemoveOverlay( targets[ i ], _materials[ i ] );
Materials.RemoveOverlay( allTargets[ i ], _materials[ i ] );
}
_materials.Clear();

View File

@ -0,0 +1,30 @@
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenTimeCurve : TweenType
{
public static readonly TweenTimeCurve defaultCurve = new TweenTimeCurve();
[Export]
public float duration = 0.5f;
[Export]
public Curve curve = MathX.Curve( 0, 1 );
public override float GetTweenDuration()
{
return duration;
}
public override float GetTweenPhaseForPhase( float phase )
{
return curve == null ? phase : curve.Sample( phase );
}
}
}

View File

@ -0,0 +1 @@
uid://lm37r3ycp1ls

View File

@ -0,0 +1,22 @@
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenType : Resource
{
public virtual float GetTweenDuration()
{
return 0.5f;
}
public virtual float GetTweenPhaseForPhase( float phase )
{
return phase;
}
}
}

View File

@ -0,0 +1 @@
uid://b7x1qbh5cosdl

View File

@ -135,6 +135,20 @@ namespace Rokojori
return list;
}
public static List<T> GetAnyChildren<T>( T[] nodes ) where T:Node
{
var list = new List<T>();
for ( int i = 0; i < nodes.Length; i++ )
{
list.Add( nodes[ i ] );
ForEach<T>( nodes[ i ], list.Add );
}
return list;
}
public static List<T> AllIn<T>( Node root, Func<T,bool> filter = null, bool includeRoot = true) where T:class
{
var list = new List<T>();

View File

@ -34,6 +34,18 @@ namespace Rokojori
public abstract T GetMaterialInSlot<T>( MaterialSlot slot ) where T:Material;
public abstract void SetMaterialInSlot( MaterialSlot slot, Material material );
public static void SetMaterialInSlot( Node3D owner, int surfaceIndex, MaterialSlot slot, Material material )
{
var c = MaterialSurfaceContainer.From( owner, surfaceIndex );
c.SetMaterialInSlot( slot, material );
}
public static T GetMaterialInSlot<T>( Node3D owner, int surfaceIndex, MaterialSlot slot ) where T:Material
{
var c = MaterialSurfaceContainer.From( owner, surfaceIndex );
return c.GetMaterialInSlot<T>( slot );
}
public void SetActiveMaterial( Material material )
{
SetMaterialInSlot( GetActiveMaterialSlot(), material );
@ -169,7 +181,7 @@ namespace Rokojori
public override void SetMaterialInSlot( MaterialSlot slot, Material material )
{
if ( surfaceIndex == -1 || MaterialSlot.None == slot )
if ( surfaceIndex == -1 || MaterialSlot.None == slot || material == null || node == null )
{
return;
}

View File

@ -0,0 +1,74 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class MaterialDelta:Resource
{
public List<ColorProperty> colorProperties = new List<ColorProperty>();
public List<FloatProperty> floatProperties = new List<FloatProperty>();
public override string ToString()
{
var sb = new StringBuilder();
colorProperties.ForEach( c => sb.Append( c.propertyName.propertyName + ", " ) );
floatProperties.ForEach( f => sb.Append( f.propertyName.propertyName + ", " ) );
return sb.ToString();
}
public static MaterialDelta Create( Material a, Material b )
{
if ( ! Materials.HaveSameShaderType( a, b ) )
{
return null;
}
var delta = new MaterialDelta();
var uniforms = Shaders.GetUniformMembers( a );
uniforms.ForEach(
( u )=>
{
if ( Variant.Type.Color == u.type )
{
var colorA = u.Get<Color>( a );
var colorB = u.Get<Color>( b );
if ( colorA == colorB )
{
return;
}
RJLog.Log( "Is Different", u.name, ":", u.type, ">>", colorA, colorB );
delta.colorProperties.Add( ColorProperty.Create( u.name, colorB ) );
}
if ( Variant.Type.Float == u.type )
{
var floatA = u.Get<float>( a );
var floatB = u.Get<float>( b );
if ( floatA == floatB )
{
return;
}
RJLog.Log( "Is Different", u.name, ":", u.type, ">>", floatA, floatB );
delta.floatProperties.Add( FloatProperty.Create( u.name, floatB ) );
}
}
);
return delta;
}
}
}

View File

@ -0,0 +1 @@
uid://bnbt4xcvfdb1b

View File

@ -7,6 +7,11 @@ namespace Rokojori
public class Materials
{
public static bool HaveSameShaderType( Material a, Material b )
{
return a.GetType() == b.GetType();
}
public static int CountOf( Node node )
{
var s = new StandardMaterial3D();

View File

@ -24,5 +24,13 @@ namespace Rokojori
var lerpedColor = from.Lerp( color, lerpState );
propertyName.Set( material, lerpedColor );
}
public static ColorProperty Create( string name, Color color )
{
var cp = new ColorProperty();
cp.propertyName = ColorPropertyName.Create( name );
cp.color = color;
return cp;
}
}
}

View File

@ -24,5 +24,13 @@ namespace Rokojori
var lerpedValue = Mathf.Lerp( from, value, lerpState );
propertyName.Set( material, lerpedValue );
}
public static FloatProperty Create( string name, float value )
{
var fp = new FloatProperty();
fp.propertyName = FloatPropertyName.Create( name );
fp.value = value;
return fp;
}
}
}

View File

@ -7,6 +7,41 @@ namespace Rokojori
public class Shaders
{
public static List<UniformMember> GetUniformMembersOfStandardMaterial()
{
var list = new List<UniformMember>();
var m = new StandardMaterial3D();
list.AddRange(
Lists.Map(
ReflectionHelper.GetDataMemberInfos<Color>( m ),
c => UniformMember.Create( c.Name, Variant.Type.Color )
)
);
list.AddRange(
Lists.Map(
ReflectionHelper.GetDataMemberInfos<float>( m ),
c => UniformMember.Create( c.Name, Variant.Type.Float )
)
);
return list;
}
public static List<UniformMember> GetUniformMembers( Material material )
{
if ( material is StandardMaterial3D )
{
return GetUniformMembersOfStandardMaterial();
}
var shaderMaterial = material as ShaderMaterial;
return GetUniformMembers( shaderMaterial.Shader );
}
public static List<UniformMember> GetUniformMembers( Shader shader )
{
var list = new List<UniformMember>();

View File

@ -1,6 +1,7 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.VisualBasic;
namespace Rokojori
{
@ -18,6 +19,14 @@ namespace Rokojori
return type + " " + name + "( '" + hint + "', '" + hintString + "', '" + usage + "')";
}
public static UniformMember Create( string name, Variant.Type type )
{
var um = new UniformMember ();
um.name = name;
um.type = type;
return um;
}
public string GetPropertyNameType()
{
if ( type == Variant.Type.Object )
@ -30,5 +39,27 @@ namespace Rokojori
}
}
public T Get<[MustBeVariant] T>( Material material )
{
return material is ShaderMaterial sm ? Get<T>( sm ) : Get<T>( (StandardMaterial3D) material );
}
public T Get<[MustBeVariant] T>( ShaderMaterial shaderMaterial )
{
return shaderMaterial.GetShaderParameter( name ).As<T>();
}
public T Get<[MustBeVariant] T>( StandardMaterial3D standardMaterial )
{
var property = new ShaderPropertyName();
property.propertyName = name;
return property._Get<T>( standardMaterial, default( T ) );
}
public void Set( ShaderMaterial shaderMaterial, Variant value )
{
shaderMaterial.SetShaderParameter( name, value );
}
}
}

View File

@ -0,0 +1,49 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ColorValue:Resource
{
[Export]
public Color value;
public override string ToString()
{
return "ColorValue( " + value + " )";
}
public static ColorValue Create( Color value )
{
var cv = new ColorValue();
cv.value = value;
return cv;
}
public static ColorValue Clone( ColorValue value, bool deepClone )
{
if ( deepClone )
{
return value == null ? null : Create( value.value );
}
return value;
}
public static void Lerp( ColorValue a, ColorValue b, float amount, ColorValue output )
{
if ( a == null || b == null || output == null )
{
return;
}
output.value = ColorX.Lerp( a.value, b.value, amount );
}
}
}

View File

@ -0,0 +1 @@
uid://evtt3x1yxlbw

View File

@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class FloatValue:Resource
{
[Export]
public float value;
public override string ToString()
{
return "FloatValue( " + RegexUtility.NumberToString( value ) + " )";
}
public static FloatValue Create( float value )
{
var fv = new FloatValue();
fv.value = value;
return fv;
}
public static FloatValue Clone( FloatValue value, bool deepClone )
{
if ( deepClone )
{
return value == null ? null : Create( value.value );
}
return value;
}
public static void Lerp( FloatValue a, FloatValue b, float amount, FloatValue output )
{
if ( a == null || b == null || output == null )
{
return;
}
output.value = Mathf.Lerp( a.value, b.value, amount );
}
}
}

View File

@ -0,0 +1 @@
uid://cfvigaosponfn