diff --git a/Runtime/Godot/Editor/GodotBuiltInTypes/GodotBuiltInDataLibrary.cs b/Runtime/Godot/Editor/GodotBuiltInTypes/GodotBuiltInDataLibrary.cs new file mode 100644 index 0000000..5e36ad3 --- /dev/null +++ b/Runtime/Godot/Editor/GodotBuiltInTypes/GodotBuiltInDataLibrary.cs @@ -0,0 +1,14 @@ +using Godot; +using System.Text; +using System.Collections.Generic; + +namespace Rokojori +{ + public class GodotBuiltInDataLibrary + { + public static readonly List serializers = new List() + { + new SerializedGodotCurve() + }; + } +} \ No newline at end of file diff --git a/Runtime/Godot/Editor/GodotBuiltInTypes/GodotBuiltInSerializer.cs b/Runtime/Godot/Editor/GodotBuiltInTypes/GodotBuiltInSerializer.cs new file mode 100644 index 0000000..6990510 --- /dev/null +++ b/Runtime/Godot/Editor/GodotBuiltInTypes/GodotBuiltInSerializer.cs @@ -0,0 +1,19 @@ +using Godot; +using System.Text; +using System.Collections.Generic; + +namespace Rokojori +{ + public class GodotBuiltInSerializer + { + public virtual bool Handles( object obj ) + { + return false; + } + + public virtual object Serialize( object obj ) + { + return null; + } + } +} \ No newline at end of file diff --git a/Runtime/Godot/Editor/GodotBuiltInTypes/SerializedGodotCurve.cs b/Runtime/Godot/Editor/GodotBuiltInTypes/SerializedGodotCurve.cs new file mode 100644 index 0000000..5654750 --- /dev/null +++ b/Runtime/Godot/Editor/GodotBuiltInTypes/SerializedGodotCurve.cs @@ -0,0 +1,44 @@ +using Godot; +using System.Text; +using System.Collections.Generic; + +namespace Rokojori +{ + public class SerializedGodotCurve: GodotBuiltInSerializer + { + public override bool Handles( object obj ) + { + return obj as Curve != null; + } + + public override object Serialize( object obj ) + { + if ( obj == null ) + { + return null; + } + + var curve = obj as Curve; + + var curveData = new List(); + + for ( int i = 0; i < curve.PointCount; i++ ) + { + if ( i != 0 ) + { + curveData.Add( curve.GetPointLeftTangent( i ) ); + } + + curveData.Add( curve.GetPointPosition( i ).X ); + curveData.Add( curve.GetPointPosition( i ).Y ); + + if ( i != curve.PointCount -1 ) + { + curveData.Add( curve.GetPointRightTangent( i ) ); + } + } + + return curveData; + } + } +} \ No newline at end of file diff --git a/Runtime/Godot/Editor/GodotNodeData.cs b/Runtime/Godot/Editor/GodotNodeData.cs new file mode 100644 index 0000000..419c334 --- /dev/null +++ b/Runtime/Godot/Editor/GodotNodeData.cs @@ -0,0 +1,171 @@ +using Godot; +using System.Text; +using System.Collections.Generic; + +namespace Rokojori +{ + public class SerializedGodotObjectMember + { + public string name; + public object value; + + public override string ToString() + { + return "{" + name + ":" + RJLog.Stringify( value ) + "}"; + } + + public static bool debug = false; + + public override bool Equals( object? obj ) + { + var other = obj as SerializedGodotObjectMember; + + if ( other == null ) + { + if ( debug ){ RJLog.Log( "Other is null..." ); } + return false; + } + + if ( name != other.name ) + { + if ( debug ){ RJLog.Log( "Names are different" ); } + return false; + } + + if ( value == null && other.value == null ) + { + if ( debug ){ RJLog.Log( "Both null" ); } + return true; + } + + var result = value == other.value || + value.Equals( other.value ) || + Lists.AreListsAndEntriesEqual( value, other.value ) || + Arrays.AreArraysAndEntriesEqual( value, other.value ); + + if ( ! result && debug ) + { + if ( debug ) + { + RJLog.Log( name, "values not equals", value, other.value, value == other.value, value.Equals( other.value ) ); + } + } + + return result; + } + } + + public class SerializedGodotObject + { + public List members = new List(); + + public override string ToString() + { + return "SerializedGodotObject" + RJLog.Stringify( members ); + } + + public override bool Equals( object? obj ) + { + var other = obj as SerializedGodotObject; + + if ( members.Count != other.members.Count ) + { + // RJLog.Log( "Members not equals count" ); + return false; + } + + for ( int i = 0; i < members.Count; i++ ) + { + var member = members[ i ]; + var otherMember = other.members.Find( m => m.name == member.name ); + + if ( otherMember == null || ! member.Equals( otherMember ) ) + { + // if ( otherMember == null ) + // { + // RJLog.Log( member.name + " is not present" ); + // } + // else + // { + // SerializedGodotObjectMember.debug = true; + // var eq = member.Equals( otherMember ); + // SerializedGodotObjectMember.debug = false; + // RJLog.Log( eq, member.name + " is different, should be:", member.value, "but is: " + otherMember.value ); + // } + + return false; + } + + } + + return true; + } + + public static SerializedGodotObject Create( object obj, Dictionary cache = null ) + { + if ( cache == null ) + { + cache = new Dictionary(); + } + + var objectData = new SerializedGodotObject(); + + var serializer = GodotBuiltInDataLibrary.serializers.Find( s => s.Handles( obj ) ); + + if ( serializer != null ) + { + var serialized = new SerializedGodotObjectMember(); + serialized.name = "data"; + serialized.value = serializer.Serialize( obj ); + + objectData.members.Add( serialized ); + + return objectData; + } + + var nodeType = obj.GetType(); + var fields = nodeType.GetFields(); + + + + + for ( int i = 0; i < fields.Length; i++ ) + { + var isExportedVariable = System.Attribute.IsDefined( fields[ i ], typeof( Godot.ExportAttribute ) ); + + if ( ! isExportedVariable ) + { + continue; + } + + var name = fields[ i ].Name; + + if ( name.StartsWith( "X_" ) || name.StartsWith( "x_" ) ) + { + continue; + } + + var value = fields[ i ].GetValue( obj ); + + var resourceValue = value as Resource; + + if ( resourceValue != null ) + { + value = cache.ContainsKey( resourceValue ) ? cache[ resourceValue ] : + SerializedGodotObject.Create( resourceValue, cache ); + } + + var member = new SerializedGodotObjectMember(); + + member.name = name; + member.value = value; + + objectData.members.Add( member ); + } + + return objectData; + + } + } + +} \ No newline at end of file diff --git a/Runtime/Procedural/Assets/Grass/GrassPatch.cs b/Runtime/Procedural/Assets/Grass/GrassPatch.cs index 12832bf..b790139 100644 --- a/Runtime/Procedural/Assets/Grass/GrassPatch.cs +++ b/Runtime/Procedural/Assets/Grass/GrassPatch.cs @@ -121,6 +121,8 @@ namespace Rokojori [Export] public Vector2 positionToFilter = new Vector2( 0, 0 ); + SerializedGodotObject _cached; + public override void _Process( double delta ) { if ( ! ( update || updateAlways ) ) @@ -128,9 +130,21 @@ namespace Rokojori return; } + + var current = SerializedGodotObject.Create( this ); + + var isEquals = _cached != null && _cached.Equals( current ); + + if ( _cached != null && _cached.Equals( current ) ) + { + return; + } + update = false; CreatePatch(); + + _cached = current; } float _maxWidth = 0; diff --git a/Runtime/Tools/Arrays.cs b/Runtime/Tools/Arrays.cs index 6b43272..3769779 100644 --- a/Runtime/Tools/Arrays.cs +++ b/Runtime/Tools/Arrays.cs @@ -47,5 +47,38 @@ namespace Rokojori callback( it ); } } + + public static bool AreArraysAndEntriesEqual( object objA, object objB ) + { + if ( ! ( objA.GetType().IsArray && objB.GetType().IsArray ) ) + { + return false; + } + + return Lists.AreEntriesEqual( + Lists.FromEnumerable( objA as IEnumerable ), + Lists.FromEnumerable( objA as IEnumerable ) + ); + } + + public static bool AreEntriesEqual( T[] a, T[] b ) + { + if ( a.Length != b.Length ) + { + return false; + } + + for ( int i = 0; i < a.Length; i++ ) + { + var isEqual = EqualityComparer.Default.Equals( a[ i ], b[ i ]); + + if ( ! isEqual ) + { + return false; + } + } + + return true; + } } } \ No newline at end of file diff --git a/Runtime/Tools/Lists.cs b/Runtime/Tools/Lists.cs index b033369..9cf1259 100644 --- a/Runtime/Tools/Lists.cs +++ b/Runtime/Tools/Lists.cs @@ -250,6 +250,28 @@ namespace Rokojori return result; } + public static List FromEnumerable( IEnumerable enumerable ) + { + var listA = new List(); + + foreach ( var it in enumerable ) + { + listA.Add( (T) it ); + } + + return listA; + } + + public static bool AreListsAndEntriesEqual( object objA, object objB ) + { + if ( ! ( ReflectionHelper.IsList( objA ) && ReflectionHelper.IsList( objB ) ) ) + { + return false; + } + + return AreEntriesEqual( FromEnumerable( objA as IEnumerable ), FromEnumerable( objB as IEnumerable ) ); + } + public static bool AreEntriesEqual( List a, List b ) { if ( a.Count != b.Count )