Docs Generator + Biquad/Smoother

This commit is contained in:
Josef 2025-07-11 10:16:45 +02:00
parent c3d7848d30
commit 375a05336a
37 changed files with 1597 additions and 103 deletions

View File

@ -169,8 +169,8 @@ namespace Rokojori
}
float reloadDuration = 2;
DateTime lastUpdateTime = DateTime.Now;
DateTime lastDisposalTime = DateTime.Now;
DateTimeOffset lastUpdateTime = DateTime.Now;
DateTimeOffset lastDisposalTime = DateTime.Now;
public void SaveCache( string relativeCachePath, object data )
{

View File

@ -170,9 +170,9 @@ namespace Rokojori
var smoothStrength = rawSmooth * 250f;
var current = TransformData.From( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation );
combined.position = Smoother.SmoothTimeInvariant( current.position, combined.position, delta, smoothStrength );
combined.rotation = Smoother.SmoothTimeInvariant( current.rotation, combined.rotation, delta, smoothStrength );
combined.scale = Smoother.SmoothTimeInvariant( current.scale, combined.scale, delta, smoothStrength );
combined.position = Smoother.SmoothTimeVariant( current.position, combined.position, delta, smoothStrength );
combined.rotation = Smoother.SmoothTimeVariant( current.rotation, combined.rotation, delta, smoothStrength );
combined.scale = Smoother.SmoothTimeVariant( current.scale, combined.scale, delta, smoothStrength );
}
combined.Set( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation );

View File

@ -118,6 +118,14 @@ namespace Rokojori
return rp;
}
public string absolutePath
{
get
{
return fullPath;
}
}
public string absoluteParentPath
{
get

172
Runtime/Math/Biquad.cs Normal file
View File

@ -0,0 +1,172 @@
using Godot;
namespace Rokojori
{
public enum BiquadType
{
LowPass,
BandPass,
HighPass,
Peak,
LowShelf,
HighShelf,
Notch
}
public class Biquad
{
public float a0 = 0;
public float a1 = 0;
public float a2 = 0;
public float a3 = 0;
public float a4 = 0;
public float x1 = 0;
public float x2 = 0;
public float y1 = 0;
public float y2 = 0;
float process( float inputSample )
{
var result = this.a0 * inputSample +
this.a1 * this.x1 +
this.a2 * this.x2 -
this.a3 * this.y1 -
this.a4 * this.y2;
this.x2 = this.x1;
this.x1 = inputSample;
this.y2 = this.y1;
this.y1 = result;
return result;
}
public static Biquad Create( BiquadType filterType, float gainDB, float frequency, float bandwidth, float sampleRate )
{
var filter = new Biquad();
float A= 0, omega= 0, sn= 0, cs= 0, alpha= 0, beta = 0;
float a0= 0, a1= 0, a2= 0, b0= 0, b1= 0, b2 = 0;
var M_LN2 = 0.69314718055994530942f;
A = Mathf.Pow( 10, gainDB / 40 );
omega = 2 * Mathf.Pi * frequency / sampleRate;
sn = Mathf.Sin( omega );
cs = Mathf.Cos( omega );
alpha = ( sn * Mathf.Sinh( M_LN2 / 2 * bandwidth * omega / sn ) );
beta = Mathf.Sqrt(A + A);
switch ( filterType )
{
case BiquadType.LowPass:
{
b0 = (1 - cs) /2;
b1 = 1 - cs;
b2 = (1 - cs) /2;
a0 = 1 + alpha;
a1 = -2 * cs;
a2 = 1 - alpha;
}
break;
case BiquadType.HighPass:
{
b0 = (1 + cs) /2;
b1 = -(1 + cs);
b2 = (1 + cs) /2;
a0 = 1 + alpha;
a1 = -2 * cs;
a2 = 1 - alpha;
}
break;
case BiquadType.BandPass:
{
b0 = alpha;
b1 = 0;
b2 = -alpha;
a0 = 1 + alpha;
a1 = -2 * cs;
a2 = 1 - alpha;
}
break;
case BiquadType.Notch:
{
b0 = 1;
b1 = -2 * cs;
b2 = 1;
a0 = 1 + alpha;
a1 = -2 * cs;
a2 = 1 - alpha;
}
break;
case BiquadType.Peak:
{
b0 = 1 + (alpha * A);
b1 = -2 * cs;
b2 = 1 - (alpha * A);
a0 = 1 + (alpha /A);
a1 = -2 * cs;
a2 = 1 - (alpha /A);
}
break;
case BiquadType.LowShelf:
{
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
a0 = (A + 1) + (A - 1) * cs + beta * sn;
a1 = -2 * ((A - 1) + (A + 1) * cs);
a2 = (A + 1) + (A - 1) * cs - beta * sn;
}
break;
case BiquadType.HighShelf:
{
b0 = A * ((A + 1) + (A - 1) * cs + beta * sn);
b1 = -2 * A * ((A - 1) + (A + 1) * cs);
b2 = A * ((A + 1) + (A - 1) * cs - beta * sn);
a0 = (A + 1) - (A - 1) * cs + beta * sn;
a1 = 2 * ((A - 1) - (A + 1) * cs);
a2 = (A + 1) - (A - 1) * cs - beta * sn;
}
break;
}
filter.a0 = b0 / a0;
filter.a1 = b1 / a0;
filter.a2 = b2 / a0;
filter.a3 = a1 / a0;
filter.a4 = a2 / a0;
filter.x1 = filter.x2 = 0;
filter.y1 = filter.y2 = 0;
return filter;
}
public static float filter( float inputSample, float[] coefficients, float[] buffer )
{
var result = coefficients[ 0 ] * inputSample +
coefficients[ 1 ] * buffer[ 0 ] +
coefficients[ 2 ] * buffer[ 1 ] -
coefficients[ 3 ] * buffer[ 2 ] -
coefficients[ 4 ] * buffer[ 3 ];
buffer[ 1 ] = buffer[ 0 ];
buffer[ 0 ] = inputSample;
buffer[ 3 ] = buffer[ 2 ];
buffer[ 2 ] = result;
return result;
}
}
}

View File

@ -4,7 +4,6 @@ using Godot;
namespace Rokojori
{
[System.Serializable]
public class Range
{
public float min;

92
Runtime/Math/RangeI.cs Normal file
View File

@ -0,0 +1,92 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class RangeI
{
public int min;
public int max;
public RangeI( int min, int max )
{
this.min = min;
this.max = max;
}
public void EnsureCorrectness()
{
if ( max < min )
{
var b = max; max = min; min = b;
}
}
public bool Contains( int value )
{
return min <= value && value <= max;
}
public bool Overlaps( RangeI other )
{
if ( other.max < min ) { return false; }
if ( other.min > max ) { return false; }
if ( other.Contains( min ) || other.Contains( max ) )
{
return true;
}
if ( Contains( other.min ) || Contains( other.max ) )
{
return true;
}
return false;
}
public RangeI Copy()
{
return new RangeI( min, max );
}
public int center { get { return ( max + min ) / 2; } }
public int range { get { return max - min; } }
public int length { get { return max - min; } }
public static bool Contains( float min, float max, float value )
{
return min <= value && value <= max;
}
public static bool ContainsExclusive( float min, float max, float value )
{
return min < value && value < max;
}
public static bool ContainsExclusiveMax( float min, float max, float value )
{
return min <= value && value < max;
}
public static bool Overlap( float minA, float maxA, float minB, float maxB )
{
if ( maxB < minA ) { return false; }
if ( minB > maxA ) { return false; }
if ( Contains( minB, maxB, minA ) || Contains( minB, maxB, maxA ) )
{
return true;
}
if ( Contains( minA, maxA, minB ) || Contains( minA, maxA, maxB ) )
{
return true;
}
return false;
}
}
}

View File

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

View File

@ -8,19 +8,19 @@ namespace Rokojori
{
float overflowDelta = 0;
public static float SmoothTimeInvariant( float value, float nextValue, float delta, float coefficient )
public static float SmoothTimeVariant( float value, float nextValue, float delta, float coefficient )
{
var lerpAmount = Mathf.Exp( -coefficient * delta );
return Mathf.Lerp( nextValue, value, lerpAmount );
}
public static Vector2 SmoothTimeInvariant( Vector2 value, Vector2 nextValue, float delta, float coefficient )
public static Vector2 SmoothTimeVariant( Vector2 value, Vector2 nextValue, float delta, float coefficient )
{
var lerpAmount = Mathf.Exp( -coefficient * delta );
return Math2D.Lerp( nextValue, value, lerpAmount );
}
public static Vector3 SmoothTimeInvariant( Vector3 value, Vector3 nextValue, float delta, float coefficient )
public static Vector3 SmoothTimeVariant( Vector3 value, Vector3 nextValue, float delta, float coefficient )
{
var lerpAmount = Mathf.Exp( -coefficient * delta );
return Math3D.Lerp( nextValue, value, lerpAmount );

View File

@ -19,12 +19,12 @@ namespace Rokojori
public int seed = 1984;
[Export]
public bool update;
[Export]
public bool updateAlways;
[ExportToolButton( "Create")]
public Callable createButton => Callable.From( ()=>
{
CreatePatch();
}
);
[Export]
public Material material;
@ -252,34 +252,6 @@ namespace Rokojori
[Export]
public Curve highCurve = MathX.Curve( 0, 1 );
// SerializedGodotObject _cached;
public override void _Process( double delta )
{
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
/*var current = SerializedGodotObject.Create( this );
var isEquals = _cached != null && _cached.Equals( current );
if ( _cached != null && _cached.Equals( current ) )
{
return;
}*/
CreatePatch();
// _cached = current;
}
float _maxWidth = 0;
Vector3 _patchOffset = Vector3.Zero;

View File

@ -132,6 +132,19 @@ namespace Rokojori
return false;
}
public bool IsAnyOf( params LexerMatcher[] matchers )
{
for ( int i = 0; i < matchers.Length; i++ )
{
if ( Is( matchers[ i ] ) )
{
return true;
}
}
return false;
}
public bool IsAny( LexerMatcher matcher, params string[] matches )
{
if ( matches == null || matches.Length == 0 )
@ -160,6 +173,9 @@ namespace Rokojori
public class FindResult
{
public FindResultType type = FindResultType.NotFound;
public bool found => FindResultType.Found == type;
public int index;
@ -225,11 +241,66 @@ namespace Rokojori
}
public static FindResult Find( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,FindResultType> evaluator )
public static List<List<LexerEvent>> FindSequences( List<LexerEvent> tokens, System.Func<int,bool,Trillean> matcher )
{
var sequences = new List<List<LexerEvent>>();
List<LexerEvent> currentSequence = null;
for ( int i = 0; i < tokens.Count; i++ )
{
var token = tokens[ i ];
var result = matcher( i, currentSequence != null );
if ( currentSequence != null )
{
if ( Trillean.True == result )
{
currentSequence.Add( token );
}
else if ( Trillean.False == result )
{
currentSequence.Add( token );
sequences.Add( currentSequence );
currentSequence = null;
}
}
else
{
if ( Trillean.True == result )
{
currentSequence = new List<LexerEvent>();
currentSequence.Add( token );
}
if ( Trillean.False == result )
{
sequences.Add( new List<LexerEvent>(){ tokens[ i ] } );
sequences.Add( null );
return sequences;
}
}
}
return sequences;
}
public static FindResult ReverseFind( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,FindResultType> evaluator )
{
return Find( tokens, offset, evaluator, false );
}
public static FindResult Find( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,FindResultType> evaluator, bool forward = true )
{
var result = new FindResult();
for ( int i = offset; i < tokens.Count; i++ )
var increment = forward ? 1 : -1;
var end = forward ? tokens.Count : -1;
for ( int i = offset; i != end && i >= 0 && i < tokens.Count; i += increment )
{
var tokenResult = evaluator( tokens[ i ] );
@ -289,11 +360,12 @@ namespace Rokojori
}
static List<string> openTypes = new List<string>(){ "(","[","{" };
static List<string> closingTypes = new List<string>(){ ")","]","}" };
public static FindResult FindClosingBracket( List<LexerEvent> tokens, int offset )
{
var openTypes = new List<string>(){ "(","[","{" };
var closingTypes = new List<string>(){ ")","]","}" };
var token = tokens[ offset ];
var bracketIndex = openTypes.IndexOf( token.match );
@ -325,26 +397,135 @@ namespace Rokojori
return FindCloser( tokens, offset, counter );
}
public static FindResult FindCloser( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,int> counter )
public static FindResult FindCloser( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,int> counter, bool forward = true )
{
var result = new FindResult();
var currentValue = 0;
for ( int i = offset; i < tokens.Count; i++ )
if ( forward )
{
var countResult = counter( tokens[ i ] );
currentValue += countResult;
if ( currentValue == 0 )
for ( int i = offset; i < tokens.Count; i++ )
{
result.type = FindResultType.Found;
result.index = i;
return result;
var countResult = counter( tokens[ i ] );
currentValue += countResult;
if ( currentValue == 0 )
{
result.type = FindResultType.Found;
result.index = i;
return result;
}
}
}
else
{
for ( int i = offset; i >= 0; i-- )
{
var countResult = counter( tokens[ i ] );
currentValue += countResult;
if ( currentValue == 0 )
{
result.type = FindResultType.Found;
result.index = i;
return result;
}
}
}
return result;
}
public static FindResult FindOpeningBracket( List<LexerEvent> tokens, int offset, string blockBracket = "{" )
{
var result = Find( tokens, offset,
( le ) =>
{
return le.Is( LexerMatcherLibrary.BracketMatcher, blockBracket ) ? FindResultType.Found : FindResultType.KeepSearching;
}
);
return result;
}
public static FindResult ReverseFindOpeningBracket( List<LexerEvent> tokens, int offset )
{
var token = tokens[ offset ];
var bracketIndex = closingTypes.IndexOf( token.match );
if ( bracketIndex == -1 )
{
var result = new FindResult( FindResultType.NotFound, offset );
return result;
}
var opener = openTypes[ bracketIndex ];
var closer = closingTypes[ bracketIndex ];
var counter = ( LexerEvent le ) =>
{
if ( le.Is( LexerMatcherLibrary.BracketMatcher, closer ) )
{
return 1;
}
if ( le.Is( LexerMatcherLibrary.BracketMatcher, opener ) )
{
return -1;
}
return 0;
};
return FindCloser( tokens, offset, counter, false );
}
public static List<RangeI> GetBlocks( List<LexerEvent> tokens )
{
var index = 0;
List<RangeI> blocks = new List<RangeI>();
while ( index < tokens.Count )
{
var openResult = FindOpeningBracket( tokens, index );
if ( ! openResult.found || openResult.index < index )
{
// RJLog.Log( "No OpeningBracket after", index );
if ( index == 0 )
{
RJLog.Log( tokens.Map( t => t.type + ": '" + t.match + "'" ).Join( "\n" ) );
}
return blocks;
}
var closeResult = FindClosingBracket( tokens, openResult.index );
if ( ! closeResult.found || closeResult.index <= openResult.index )
{
// RJLog.Log( "No ClosingBracket after", index );
return null;
}
var range = new RangeI( openResult.index, closeResult.index );
blocks.Add( range );
index = closeResult.index + 1;
}
return blocks;
}
}
}

View File

@ -20,6 +20,39 @@ namespace Rokojori
return events;
}
public static List<List<LexerEvent>> GetAllObjectDefinitions( string source )
{
var tokens = Lex( source ).Filter( e => ! ( e.isDone || e.isError ) );
var sequences = LexerEvent.FindSequences( tokens,
( int index, bool inSequence ) =>
{
var le = tokens[ index ];
if (
le.Is( LexerMatcherLibrary.ClassMatcher ) ||
le.Is( LexerMatcherLibrary.StructMatcher ) ||
le.Is( LexerMatcherLibrary.InterfaceMatcher ) ||
le.Is( LexerMatcherLibrary.RecordMatcher ) ||
le.Is( LexerMatcherLibrary.EnumMatcher )
)
{
return Trillean.True;
}
if ( inSequence && le.Is( LexerMatcherLibrary.CwordMatcher ) )
{
return Trillean.False;
}
return Trillean.Any;
}
);
return sequences;
}
public CSharpLexer()
{
AddAllMatchers(
@ -37,6 +70,10 @@ namespace Rokojori
LexerMatcherLibrary.BracketMatcher,
LexerMatcherLibrary.AccessModifierMatcher,
LexerMatcherLibrary.ClassMatcher,
LexerMatcherLibrary.EnumMatcher,
LexerMatcherLibrary.StructMatcher,
LexerMatcherLibrary.InterfaceMatcher,
LexerMatcherLibrary.RecordMatcher,
LexerMatcherLibrary.OperatorMatcher,
LexerMatcherLibrary.CFunctionMatcher,
LexerMatcherLibrary.CwordMatcher,

View File

@ -0,0 +1,71 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class GDShaderLexer:Lexer
{
public static List<LexerEvent> Lex( string source )
{
var lexer = new GDShaderLexer();
var events = lexer.LexToList( source );
if ( lexer.hasError )
{
return null;
}
events.ForEach( ev => { ev.GrabMatch( source ); } );
return events;
}
public static readonly LexerMatcher RenderSettingsFlagMatcher =
new LexerMatcher( "RenderSettingsFlag", @"\b(?:shader_type|render_mode)\b" );
public static readonly LexerMatcher TypeMatcher =
new LexerMatcher( "ShaderType", @"\b(?:spatial|canvas_item|particles|sky|fog)\b" );
public static readonly LexerMatcher UsageMatcher =
new LexerMatcher( "Usage", @"\b(?:uniform|varying)\b" );
public static readonly LexerMatcher InOutMatcher =
new LexerMatcher( "InOut", @"\b(?:inout|in|out)\b" );
// public static readonly LexerMatcher RenderModeMatcher =
// new LexerMatcher( "RenderMode", @"\b(?:spatial|canvas|particles)\b" );
public static LexerMatcher[] ignore =
[
LexerMatcherLibrary.SingleLineCommentMatcher,
LexerMatcherLibrary.MultiLineCommentMatcher,
LexerMatcherLibrary.BreakMatcher,
LexerMatcherLibrary.WhiteSpaceMatcher
];
public GDShaderLexer()
{
AddAllMatchers(
LexerMatcherLibrary.SingleLineCommentMatcher,
LexerMatcherLibrary.MultiLineCommentMatcher,
LexerMatcherLibrary.DoubleQuotedStringMatcher,
LexerMatcherLibrary.CInstructionMatcher,
RenderSettingsFlagMatcher,
TypeMatcher,
UsageMatcher,
InOutMatcher,
LexerMatcherLibrary.NumberMatcher,
LexerMatcherLibrary.BoolMatcher,
LexerMatcherLibrary.BreakMatcher,
LexerMatcherLibrary.WhiteSpaceMatcher,
LexerMatcherLibrary.LogicMatcher,
LexerMatcherLibrary.BracketMatcher,
LexerMatcherLibrary.AccessModifierMatcher,
LexerMatcherLibrary.StructMatcher,
LexerMatcherLibrary.OperatorMatcher,
LexerMatcherLibrary.CFunctionMatcher,
LexerMatcherLibrary.CwordMatcher,
LexerMatcherLibrary.AnySymbolMatcher
);
}
}
}

View File

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

View File

@ -0,0 +1,132 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class LexerList
{
protected List<LexerEvent> _events;
public List<LexerEvent> events => _events;
public LexerList Filter( System.Predicate<LexerEvent> le )
{
return Create( events.Filter( e => le( e ) ) );
}
public LexerList GetAll( LexerMatcher lexerMatcher )
{
return Create( events.Filter( e => e.Is( lexerMatcher ) ) );
}
public List<RangeI> Seperate( int start, int end, LexerMatcher[] ignore = null )
{
return Seperate( ",", start, end, ignore );
}
public List<RangeI> Seperate( string seperator, int start, int end, LexerMatcher[] ignore = null )
{
var list = new List<RangeI>();
var currentStart = -1;
for ( int i = start; i <= end; i++ )
{
if ( _events[ i ].MatchIs( seperator ) )
{
if ( currentStart != -1 )
{
list.Add( new RangeI( currentStart, i - 1 ) );
}
currentStart = -1;
}
else if ( ignore != null && _events[ i ].IsAnyOf( ignore ) )
{
continue;
}
else if ( currentStart == -1 )
{
currentStart = i;
}
}
if ( currentStart != -1 )
{
list.Add( new RangeI( currentStart, end ) );
}
return list;
}
public void ForEach( LexerMatcher lexerMatcher, System.Action<LexerEvent> action )
{
events.ForEach(
( e )=>
{
if ( ! e.Is( lexerMatcher ) )
{
return;
}
action( e );
}
);
}
public LexerEvent.FindResult FindOpeningBracket( int offset, string blockBracket = "{" )
{
return LexerEvent.FindOpeningBracket( _events, offset, blockBracket );
}
public LexerEvent.FindResult ReverseFindOpeningBracket( int offset )
{
return LexerEvent.ReverseFindOpeningBracket( _events, offset );
}
public LexerEvent.FindResult ReverseFind( int offset, System.Func<LexerEvent,LexerEvent.FindResultType> evaluator )
{
return LexerEvent.Find( _events, offset, evaluator, false );
}
public LexerEvent.FindResult Find( int offset, System.Func<LexerEvent,LexerEvent.FindResultType> evaluator )
{
return LexerEvent.Find( _events, offset, evaluator, true );
}
public List<List<LexerEvent>> FindSequences( System.Func<int,bool,Trillean> matcher )
{
return LexerEvent.FindSequences( _events, matcher );
}
public List<RangeI> GetBlocks()
{
return LexerEvent.GetBlocks( _events );
}
public LexerList Range( RangeI range )
{
return Range( range.min, range.max );
}
public LexerList Range( int start, int end = -1 )
{
if ( end == -1 )
{
end = _events.Count;
}
return Create( _events.Sub( start, end - start + 1 ) );
}
public static LexerList Create( List<LexerEvent> lexerEvents )
{
var list = new LexerList();
list._events = lexerEvents;
return list;
}
}
}

View File

@ -72,8 +72,20 @@ namespace Rokojori
public static readonly LexerMatcher ClassMatcher =
new LexerMatcher( "Class", @"\bclass\b" );
public static readonly LexerMatcher EnumMatcher =
new LexerMatcher( "Enum", @"\benum\b" );
public static readonly LexerMatcher InterfaceMatcher =
new LexerMatcher( "Interface", @"\binterface\b" );
public static readonly LexerMatcher StructMatcher =
new LexerMatcher( "Struct", @"\bstruct\b" );
public static readonly LexerMatcher RecordMatcher =
new LexerMatcher( "Record", @"\brecord\b" );
public static readonly LexerMatcher AccessModifierMatcher =
new LexerMatcher( "AccessModifier", @"\b(?:public|protected|private)\b" );
new LexerMatcher( "AccessModifier", @"\b(?:public|protected|private|const)\b" );
public static readonly LexerMatcher SingleLineCommentMatcher =
new LexerMatcher( "SingleLineComment", @"//.*" );

View File

@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
@ -24,5 +25,43 @@ namespace Rokojori
return path;
}
public static string Replace( this string source, Regex regex, string replacement )
{
return regex.Replace( source, replacement );
}
public static string SubstringAfterMatching( this string source, string match )
{
var index = source.IndexOf( match );
return index == -1 ? source : source.Substring( index + match.Length );
}
public static long ToLong( this string source )
{
return long.Parse( source );
}
public static string ReplaceStart( this string source, string start, string replacement = "" )
{
if ( source.StartsWith( start ) )
{
return replacement + source.Substring( start.Length );
}
return source;
}
public static string ReplaceEnd( this string source, string ending, string replacement = "" )
{
if ( source.EndsWith( ending ) )
{
return source.Substring( 0, source.Length - ending.Length ) + replacement;
}
return source;
}
}
}

View File

@ -0,0 +1,29 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
[GlobalClass]
[Tool]
public partial class Date:Resource
{
public int year = 1;
public int month = 1;
public int day = 1;
public int hours = 0;
public int minutes = 0;
public int second = 0;
public float milliseconds = 0;
public int utcHoursOffset = 0;
public int utcMinutesOffset = 0;
public float utcMillisecondsOffset = 0;
}
}

View File

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

View File

@ -11,15 +11,20 @@ namespace Rokojori
public static class DateMath
{
public static bool IsNewerThan( this DateTimeOffset a, DateTimeOffset b )
{
var difference = GetDifference( a, b );
return difference > 0;
}
public static float GetDifference( this DateTime a, DateTime b )
public static float GetDifference( this DateTimeOffset a, DateTimeOffset b )
{
return (float) ( ( a - b ).TotalSeconds );
}
public static bool HasExpired( this DateTime oldTime, float duration )
public static bool HasExpired( this DateTimeOffset oldTime, float duration )
{
var difference = GetDifference( DateTime.Now, oldTime );
var difference = GetDifference( DateTimeOffset.Now, oldTime );
return difference >= duration;
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Text;
using System;
using Godot;
using System.Threading.Tasks;
namespace Rokojori
{
@ -458,6 +459,11 @@ namespace Rokojori
return GetLast( list );
}
public static T ReverseAt<T>( this List<T> list, int index )
{
return list[ list.Count - 1 - index ];
}
public static void RemoveIncreasingSortedIndices<T>( List<T> list, List<int> increasinglySortedRemovals )
{
for ( var i = increasinglySortedRemovals.Count - 1; i >= 0; i-- )
@ -658,6 +664,27 @@ namespace Rokojori
return mapped;
}
public static async Task<List<T>> FilterAsync<T>( this List<T> list, Func<T,Task<bool>> filter )
{
var outputList = new List<T>();
var index = 0;
foreach ( var it in list )
{
var result = await filter( it );
if ( result )
{
outputList.Add( it );
}
index++;
}
return outputList;
}
public static void Filter<T>( List<T> inputList, List<T> list, Func<T,int,bool> filter )
{
for ( int i = 0; i < inputList.Count; i++ )

View File

@ -15,6 +15,11 @@ namespace Rokojori
public static class TrilleanLogic
{
public static Trillean FromBool( bool value )
{
return value ? Trillean.True : Trillean.False;
}
public static bool ToBool( Trillean value, bool any = true )
{
if ( Trillean.Any == value )

View File

@ -4,6 +4,7 @@ using Rokojori;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
namespace Rokojori.Tools
{
@ -15,6 +16,125 @@ namespace Rokojori.Tools
public class Git
{
public static async Task<System.DateTimeOffset?> GetFileChangedTime( string path )
{
var arguments = new List<string>()
{
"log -1 --format=\"%ct\" -- " + path.EscapeAsPathForCommandLine()
};
var response = await Run( arguments, RegexUtility.ParentPath( path ) );
if ( response.exitCode != 0 )
{
return null;
}
try
{
var time = System.DateTimeOffset.FromUnixTimeSeconds( response.rawResponse.ToLong() );
return time;
}
catch( System.Exception e )
{
RJLog.Error( e );
}
return null;
}
public static async Task<bool> IsFileNewerThan( string path, System.DateTimeOffset time )
{
var _fileChangedTime = await GetFileChangedTime( path );
if ( _fileChangedTime == null )
{
return false;
}
var fileChangedTime = (DateTimeOffset) _fileChangedTime;
return fileChangedTime.IsNewerThan( time );
}
public static async Task<GitResponse> Run( List<string> arguments, string workingDirectory = null )
{
var response = new GitResponse();
var joinedArgs = arguments.Join( " ");
RJLog.Log( "GIT", joinedArgs );
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "git",
Arguments = joinedArgs,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
if ( workingDirectory != null )
{
process.StartInfo.WorkingDirectory = workingDirectory;
}
process.Start();
var outputResult = new List<string>();
var errorResult = new List<string>();
process.OutputDataReceived += (sender, e) =>
{
if ( e.Data == null )
{
return;
}
RJLog.Log( e.Data );
outputResult.Add( e.Data );
};
process.ErrorDataReceived += (sender, e) =>
{
if ( e.Data == null )
{
return;
}
RJLog.Error( e.Data );
errorResult.Add( e.Data );
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync();
response.exitCode = process.ExitCode;
if ( process.ExitCode == 0 )
{
response.rawResponse = outputResult.Join( "" );
}
else
{
response.rawResponse = errorResult.Join( "" );
}
return response;
}
public static async Task<GitResponse> GetStatus( string path )
{
var process = new Process

View File

@ -4,6 +4,7 @@ using Rokojori;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
namespace Rokojori.Tools
{
@ -12,7 +13,7 @@ namespace Rokojori.Tools
public partial class GitTest:Node
{
[ExportToolButton("Status")]
public Callable StatusButton => Callable.From(
public Callable statusButton => Callable.From(
()=>
{
GetStatus();
@ -23,8 +24,38 @@ namespace Rokojori.Tools
{
var response = await Git.GetStatus( ProjectSettings.GlobalizePath( "res://") );
this.LogInfo( response.exitCode, ">>", response.rawResponse );
}
[ExportGroup( "File")]
[Export]
public string filePath;
[ExportToolButton("Get Change Time")]
public Callable changeTimeButton => Callable.From(
()=>
{
GetTime();
}
);
async void GetTime()
{
var optionalTime = await Git.GetFileChangedTime( ProjectSettings.GlobalizePath( filePath ) );
if ( optionalTime != null )
{
var time = (DateTimeOffset) optionalTime;
this.LogInfo( "LOCAL:", time.ToLocalTime(), " || UTC:", time,"\n>>", filePath );
}
else
{
this.LogInfo( "Could not receive time info" );
}
}
}
}
#endif

View File

@ -79,12 +79,25 @@ namespace Rokojori.Tools
process.OutputDataReceived += (sender, e) =>
{
if ( e.Data == null )
{
return;
}
RJLog.Log( e.Data );
outputResult.Add( e.Data );
};
process.ErrorDataReceived += (sender, e) =>
{
if ( e.Data == null )
{
return;
}
RJLog.Error( e.Data );
errorResult.Add( e.Data );
};

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Linq;
namespace Rokojori.DocGenerator
{
@ -40,23 +41,32 @@ namespace Rokojori.DocGenerator
return "CSharp.svg";
}
public ClassDocInfo Create( string filePath, Type type, List<string> icons )
public ClassDocInfo Create( string filePath, Type classType, string definitionType, List<string> icons )
{
var data = filePath == null ? "" : FilesSync.LoadUTF8( filePath );
var comments = new DocComments();
comments.Grab( data );
RJLog.Log( type.Name, "Comments:", comments.entries.Count );
var typeName = GetDocsType.Of( classType );
var baseTypeName = classType.BaseType;
var typeNameSpace = classType.Namespace;
info.name = type.Name;
info.csNameSpace = type.Namespace;
RJLog.Log( typeName, "Comments:", comments.entries.Count );
info.doc = comments.FindDoc( "class", type.Name );
info.generics = Lists.Map( type.GenericTypeArguments, t => t + "" );
info.name = typeName;
info.csNameSpace = typeNameSpace;
info.definitionType = definitionType;
info.sourcePath = filePath.SubstringAfterMatching( "addons/rokojori_action_library/");
info.doc = comments.FindDoc( "class", typeName );
var genericArguments = classType.GetGenericArguments().ToList();
info.generics = genericArguments.Map( t => t.Name );
info.interfaces = classType.GetInterfaces().ToList().Map( i => GetDocsType.Of( i ) );
var allBindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
var fields = Lists.ToList( type.GetFields( allBindings ) );
var fields = Lists.ToList( classType.GetFields( allBindings ) );
fields = Lists.Filter( fields, f => ! f.IsPrivate );
var godotTypes = new List<string>()
@ -67,7 +77,7 @@ namespace Rokojori.DocGenerator
var it = type.BaseType;
var it = baseTypeName;
while ( it != null )
{
@ -86,7 +96,7 @@ namespace Rokojori.DocGenerator
}
else
{
info.extendingClasses.Add( it.Name );
info.extendingClasses.Add( GetDocsType.Of( it ) );
it = it.BaseType;
}
@ -95,14 +105,59 @@ namespace Rokojori.DocGenerator
info.icon = GetIcon( data, info.name, info.extendingClasses, icons );
var constructors = Lists.ToList( classType.GetConstructors( allBindings ) );
constructors = Lists.Filter( constructors, m => ! m.IsPrivate );
constructors.ForEach(
( con ) =>
{
var memberInfo = MemberInfo.CreateConstructor();
memberInfo.name = typeName;
memberInfo.dataType = typeName;
memberInfo.csNameSpace = typeNameSpace;
// memberInfo.generics = info.generics;
// memberInfo.doc = comments.FindDoc( "constructor", con.Name );
memberInfo.modifiers.Add( con.IsPublic ? "public" : "protected" );
var parameters = Lists.ToList( con.GetParameters() );
parameters.ForEach(
( p )=>
{
var parameterType = new ParameterType();
parameterType.name = p.Name;
parameterType.type = GetDocsType.Of( p.ParameterType );
parameterType.csNameSpace = p.ParameterType.Namespace;
parameterType.generics = Lists.Map( p.ParameterType.GetGenericArguments(), t => t + "" );
memberInfo.parameters.Add( parameterType );
}
);
info.memberInfos.Add( memberInfo );
}
);
fields.ForEach(
( f )=>
{
if ( f.Name == "value__" )
{
return;
}
var memberInfo = MemberInfo.CreateField();
memberInfo.name = f.Name;
memberInfo.csNameSpace = f.FieldType.Namespace;
memberInfo.dataType = f.FieldType.Name;
memberInfo.dataType = GetDocsType.Of( f.FieldType );
memberInfo.generics = Lists.Map( f.FieldType.GetGenericArguments(), t => t + "" );
memberInfo.doc = comments.FindDoc( "field", f.Name );
@ -119,7 +174,7 @@ namespace Rokojori.DocGenerator
);
var methods = Lists.ToList( type.GetMethods( allBindings ) );
var methods = Lists.ToList( classType.GetMethods( allBindings ) );
methods = Lists.Filter( methods, m => ! m.IsPrivate );
var godotInternalMethods = new List<string>()
@ -132,7 +187,8 @@ namespace Rokojori.DocGenerator
"GetGodotPropertyList",
"GetGodotPropertyDefaultValues",
"SaveGodotObjectData",
"RestoreGodotObjectData"
"RestoreGodotObjectData",
"InvokeGodotClassStaticMethod"
};
methods.ForEach(
@ -145,8 +201,10 @@ namespace Rokojori.DocGenerator
var memberInfo = MemberInfo.CreateMethod();
memberInfo.isAccessor = GetDocsType.isAccessorMethod( m );
memberInfo.name = m.Name;
memberInfo.dataType = m.ReturnType.Name;
memberInfo.dataType = GetDocsType.Of( m.ReturnType );
memberInfo.csNameSpace = m.ReturnType.Namespace;
memberInfo.generics = Lists.Map( m.GetGenericArguments(), t => t + "" );
memberInfo.doc = comments.FindDoc( "method", m.Name );
@ -164,9 +222,9 @@ namespace Rokojori.DocGenerator
{
var parameterType = new ParameterType();
parameterType.name = p.Name;
parameterType.type = p.ParameterType.Name;
parameterType.type = GetDocsType.Of( p.ParameterType );
parameterType.csNameSpace = p.ParameterType.Namespace;
parameterType.generics = Lists.Map( p.ParameterType.GenericTypeArguments, t => t + "" );
parameterType.generics = Lists.Map( p.ParameterType.GetGenericArguments(), t => t + "" );
memberInfo.parameters.Add( parameterType );

View File

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

View File

@ -9,7 +9,8 @@ namespace Rokojori.DocGenerator
public string name;
public string type;
public string csNameSpace;
public List<string> generics;
public List<string> modifiers = [];
public List<string> generics = [];
}
public class MemberInfo
@ -19,6 +20,11 @@ namespace Rokojori.DocGenerator
public string name;
public string dataType;
public string csNameSpace;
public string path;
public bool isAccessor;
public List<string> attributes = new List<string>();
public int lineIndex;
public int lineOffset;
public List<string> modifiers = new List<string>();
public List<string> generics;
@ -26,6 +32,9 @@ namespace Rokojori.DocGenerator
public static readonly string Field = "Field";
public static readonly string Method = "Method";
public static readonly string Constructor = "Constructor";
public static readonly string Uniform = "Uniform";
public static readonly string Varying = "Varying";
public static MemberInfo Create( string memberType )
{
@ -43,6 +52,21 @@ namespace Rokojori.DocGenerator
{
return MemberInfo.Create( MemberInfo.Method );
}
public static MemberInfo CreateConstructor()
{
return MemberInfo.Create( MemberInfo.Constructor );
}
public static MemberInfo CreateUniform()
{
return MemberInfo.Create( MemberInfo.Uniform );
}
public static MemberInfo CreateVarying()
{
return MemberInfo.Create( MemberInfo.Varying );
}
}
public class ClassDocInfo
@ -51,8 +75,14 @@ namespace Rokojori.DocGenerator
public string name ="";
public string doc;
public string icon;
public string sourcePath;
public string definitionType;
public List<string> generics = new List<string>();
public List<string> interfaces = new List<string>();
public List<string> extendingClasses = new List<string>();
public List<MemberInfo> memberInfos = new List<MemberInfo>();
}
}

View File

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

View File

@ -2,6 +2,7 @@
using Godot;
using System.Collections.Generic;
using System;
namespace Rokojori.DocGenerator
{
@ -29,6 +30,21 @@ namespace Rokojori.DocGenerator
[Export]
public string outputPath ="res://addons/rokojori_action_library/Tools/docs/output";
[Export]
public bool clearDirectory = false;
[Export]
public bool c_sharp = true;
[Export]
public bool shaders = true;
[Export]
public bool processOnlyNewerThanCheckTime = true;
[Export]
public float hoursCheckTime;
[ExportToolButton( "Create")]
public Callable createButton => Callable.From( ()=>{ Generate(); } );
@ -47,11 +63,21 @@ namespace Rokojori.DocGenerator
void Generate()
{
var absoluteLibraryPath = ProjectSettings.GlobalizePath( "res://addons/rokojori_action_library/Runtime" );
var absoluteIconPath = ProjectSettings.GlobalizePath( "res://addons/rokojori_action_library/Icons" );
var absoluteOutputPath = ProjectSettings.GlobalizePath( outputPath );
if ( clearDirectory )
{
FilesSync.Delete( absoluteOutputPath );
FilesSync.EnsureDirectoryExists( absoluteOutputPath );
}
var generator = new DocGenerator();
generator.shaders = shaders;
generator.cSharp = c_sharp;
var icons = Lists.Map( FilesSync.GetFiles( absoluteIconPath, ( fp => fp.fileExtension == ".svg" ) ), fp => fp.fullFileName );
generator.Generate( absoluteLibraryPath, absoluteOutputPath, icons );
}

View File

@ -78,7 +78,7 @@ namespace Rokojori.DocGenerator
public string FindDoc( string type, string name )
{
var e= entries.Find( e => e.type == type && e.name == name );
var e = entries.Find( e => e.type == type && e.name == name );
return e == null ? null : e.doc;
}

View File

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

View File

@ -4,40 +4,81 @@ using Godot;
using Rokojori;
using System.Collections.Generic;
using System;
using Microsoft.VisualBasic;
using Rokojori.Tools;
using System.Threading.Tasks;
namespace Rokojori.DocGenerator
{
public class ClassTypeEntry
{
public Type type;
public string definitionType;
public string path;
}
public class ShaderTypeEntry
{
public string path;
}
public class DocGenerator
{
string path = "";
string outputPath = "";
List<string> icons = new List<string>();
public bool checkChangeTime = false;
public DateTimeOffset changeTime;
public List<FilePath> classFiles = new List<FilePath>();
public List<ClassTypeEntry> classTypes = new List<ClassTypeEntry>();
public List<ShaderTypeEntry> shaderTypes = new List<ShaderTypeEntry>();
public bool shaders = true;
public bool cSharp = true;
public void Generate( string path, string outputPath, List<string> icons )
public async void Generate( string path, string outputPath, List<string> icons )
{
this.path = path;
this.outputPath = outputPath;
this.icons = icons;
GetFiles();
await GetFiles();
CreateTypesFromFiles();
IncludeDefaultTypes();
// IncludeDefaultTypes();
GenerateDocsFromTypes();
}
void GetFiles()
async Task GetFiles()
{
classFiles = FilesSync.GetFiles( path, f => f.fileExtension == ".cs", true );
classFiles = FilesSync.GetFiles( path,
f =>
{
var isCS = f.fileExtension == ".cs";
var isShader = f.fileExtension == ".gdshader" || f.fileExtension == ".gdshaderinc";
if ( isShader )
{
RJLog.Log( "SHADER: FILE", f.absolutePath );
}
return ( cSharp && isCS ) || ( shaders && isShader );
},
true
);
if ( checkChangeTime )
{
classFiles = await classFiles.FilterAsync(
async ( f ) =>
{
return await Git.IsFileNewerThan( f.absolutePath, changeTime );
}
);
}
}
void IncludeDefaultTypes()
@ -67,45 +108,111 @@ namespace Rokojori.DocGenerator
void CreateTypesFromFiles()
{
var genericType = new EventSlot<int>().GetType();
var namespaceLabel = "Rokojori";
var namespaceLabel = "Rokojori";
var assembly = genericType.Assembly;
classFiles.ForEach(
( cf )=>
{
var name = cf.fileName;
var type = ReflectionHelper.GetTypeByName( namespaceLabel + "." + name );
if ( type == null )
if ( cf.fileExtension == ".cs" )
{
type = ReflectionHelper.GetTypeByNameFromAssembly( name, assembly );
var fileContent = FilesSync.LoadUTF8( cf.fullPath );
var definitions = CSharpLexer.GetAllObjectDefinitions( fileContent );
RJLog.Log( cf.fileName, ">>", definitions.Join( "," ) );
for ( int i = 0; i < definitions.Count; i++ )
{
var sequence = definitions[ i ];
var definitionType = sequence[ 0 ];
var definitionName = sequence[ 1 ];
RJLog.Log( "Adding definition:", definitionName.match );
AddCSharpsClassType( cf, definitionName.match, definitionType.match, namespaceLabel, assembly );
}
}
else if ( cf.fileExtension == ".gdshader" || cf.fileExtension == ".gdshaderinc" )
{
var shaderType = new ShaderTypeEntry();
shaderType.path = cf.absolutePath;
shaderTypes.Add( shaderType );
RJLog.Log( "SHADER: Adding definition:", cf.absolutePath );
}
if ( type != null )
{
var entry = new ClassTypeEntry();
entry.type = type;
entry.path = cf.fullPath;
classTypes.Add( entry);
}
}
);
}
void AddCSharpsClassType( FilePath cf, string name, string definitionType, string namespaceLabel, System.Reflection.Assembly assembly )
{
// var name = cf.fileName;
var type = ReflectionHelper.GetTypeByName( namespaceLabel + "." + name );
if ( type == null )
{
type = ReflectionHelper.GetTypeByNameFromAssembly( name, assembly );
}
if ( type == null )
{
return;
}
var entry = new ClassTypeEntry();
entry.type = type;
entry.path = cf.fullPath;
entry.definitionType = definitionType;
classTypes.Add( entry);
}
void GenerateDocsFromTypes()
{
FilesSync.EnsureDirectoryExists( outputPath );
classTypes.ForEach(
( c )=>
{
var cdg = new ClassDocGenerator();
var cinfo = cdg.Create( c.path, c.type, icons );
var cinfo = cdg.Create( c.path, c.type, c.definitionType, icons );
var outputPathName = cinfo.name.Replace( "`", "-" ).Replace( "<", "-" ).Replace( ">", "-" );
var outputPath = FilePath.Join( this.outputPath, cinfo.name + ".json" );
var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" );
FilesSync.SaveJSON( outputPath, cinfo );
}
);
RJLog.Log( "SHADER: shaderTypes", shaderTypes.Count );
shaderTypes.ForEach(
( s ) =>
{
try
{
RJLog.Log( "SHADER: DOC", s.path );
var sdg = new ShaderDocGenerator();
var cinfo = sdg.Create( s.path );
var prefix = cinfo.definitionType + "." ;
var outputPathName = prefix + cinfo.name;
var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" );
RJLog.Log( "SHADER: SAVING", s.path, ">>", outputPathName );
FilesSync.SaveJSON( outputPath, cinfo );
}
catch ( System.Exception e )
{
RJLog.Error( s.path );
RJLog.Error( e );
}
}
);
}
}
}

View File

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

57
Tools/docs/GetDocsType.cs Normal file
View File

@ -0,0 +1,57 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
using System;
using System.Text.RegularExpressions;
using System.Linq;
namespace Rokojori.DocGenerator
{
public static class GetDocsType
{
public static bool isAccessorMethod( System.Reflection.MethodInfo m )
{
var isSetAccessor = m.DeclaringType.GetProperties().Any( prop => prop.GetSetMethod() == m );
if ( isSetAccessor )
{
return true;
}
var isGetAccessor = m.DeclaringType.GetProperties().Any( prop => prop.GetGetMethod() == m );
return isGetAccessor;
}
public static Dictionary<string,string> replacements = new Dictionary<string, string>()
{
{ "Int32", "int" },
{ "Single", "float" },
{ "Boolean", "bool" }
};
public static string Of( Type type )
{
var docType = type.Name;
if ( replacements.ContainsKey( docType ) )
{
docType = replacements[ docType ];
}
if ( docType.Contains( "Action" ) && type.Namespace.Contains( "System" ) )
{
docType = docType.Replace( "Action", "System.Action" );
}
if ( docType.Contains( "`" ) )
{
var generics = Lists.Map( type.GetGenericArguments(), t => Of( t ) );
var replacement = "<" + generics.Join( "," ) + ">";
docType = docType.Replace( new Regex( "`.*$" ), replacement );
}
return docType;
}
}
}

View File

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

View File

@ -0,0 +1,262 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Linq;
namespace Rokojori.DocGenerator
{
public class ShaderDocGenerator
{
public static readonly string GDShaderExtension = ".gdshader";
public static readonly string GDShaderIncludeExtension = ".gdshaderinc";
ClassDocInfo info = new ClassDocInfo();
List<LexerEvent> _lexerEvents;
LexerList tokens;
public ClassDocInfo Create( string filePath )
{
var fileContent = FilesSync.LoadUTF8( filePath );
_lexerEvents = GDShaderLexer.Lex( fileContent );
tokens = LexerList.Create( _lexerEvents );
if ( filePath.EndsWith( GDShaderIncludeExtension ) )
{
info.definitionType = "GDShaderInclude";
}
else
{
info.definitionType = "GDShader";
}
info.name = RegexUtility.TrimToLastPathFragment( filePath ).ReplaceEnd( GDShaderIncludeExtension, "" ).ReplaceEnd( GDShaderExtension, "" );
GetIncludes();
GetUniforms();
GetBlocks();
return info;
}
void GetIncludes()
{
var includePrexix = "#include ";
RJLog.Log( "Processing includes:", tokens.GetAll( LexerMatcherLibrary.CInstructionMatcher ).events.Count );
tokens.ForEach( LexerMatcherLibrary.CInstructionMatcher,
( t )=>
{
var match = t.match;
RJLog.Log( "Processing include:", match );
if ( ! match.StartsWith( includePrexix ) )
{
RJLog.Log( "Processing include:", match );
return;
}
var path = match.ReplaceStart( includePrexix, "" );
path = path.Substring( 1, path.Length - 2 );
var name = RegexUtility.TrimToLastPathFragment( path ).TrimSuffix( ".gdshaderinc");
var memberInfo = MemberInfo.Create( "ShaderInclude" );
memberInfo.name = name;
memberInfo.path = path;
memberInfo.dataType = "GDShaderInclude";
info.memberInfos.Add( memberInfo );
}
);
}
void GetUniforms()
{
var sequences = tokens.FindSequences(
( int tokenIndex, bool inSequence ) =>
{
var token = tokens.events[ tokenIndex ];
if ( ! inSequence )
{
if ( token.Is( GDShaderLexer.UsageMatcher ) )
{
return Trillean.True;
}
return Trillean.Any;
}
else
{
return TrilleanLogic.FromBool( ! token.Is( LexerMatcherLibrary.OperatorMatcher, ";" ) );
}
}
);
// RJLog.Log( "Uniforms/Varyings:", sequences.Count );
sequences.ForEach(
( s )=>
{
if ( s == null )
{
return;
}
var filtered = s.Filter( s => ! s.IsAnyOf( GDShaderLexer.ignore ) );
// RJLog.Log( "'" + filtered.Map( s => s.match ).Join( "', '") + "'" );
if ( filtered.Count < 3 )
{
return;
}
var first = filtered[ 0 ];
var isUniform = first.MatchIs( "uniform" );
var memberInfo = isUniform ? MemberInfo.CreateUniform() : MemberInfo.CreateVarying();
memberInfo.dataType = filtered[ 1 ].match;
memberInfo.name = filtered[ 2 ].match;
// LINE INFO
info.memberInfos.Add( memberInfo );
}
);
}
void GetBlocks()
{
var blocks = tokens.GetBlocks();
if ( blocks == null )
{
return;
}
// RJLog.Log( "Num blocks:", blocks.Count );
blocks.ForEach( b => ProcessBlock( b ) );
}
void ProcessBlock( RangeI blockRange )
{
var blockStart = blockRange.min;
var firstTokenBeforeResult = tokens.ReverseFind( blockStart - 1,
( t ) =>
{
// RJLog.Log( "Token type:", t );
if ( t.IsAnyOf( GDShaderLexer.ignore ) )
{
return LexerEvent.FindResultType.KeepSearching;
}
if ( !
(
t.Is( LexerMatcherLibrary.BracketMatcher, ")" ) ||
t.Is( LexerMatcherLibrary.CwordMatcher )
)
)
{
// RJLog.Log( "Invalid token type:", t );
return LexerEvent.FindResultType.Error;
}
return LexerEvent.FindResultType.Found;
}
);
if ( ! firstTokenBeforeResult.found )
{
// RJLog.Log( "No first token before block found!" );
return;
}
var firstToken = _lexerEvents[ firstTokenBeforeResult.index ];
var isMethod = firstToken.Is( LexerMatcherLibrary.BracketMatcher, ")" );
if ( isMethod )
{
var closeBracketResult = firstTokenBeforeResult;
var openBracketResult = tokens.ReverseFindOpeningBracket( closeBracketResult.index );
if ( ! openBracketResult.found )
{
return;
}
var nameToken = tokens.ReverseFind( openBracketResult.index,
( t ) =>
{
return t.Is( LexerMatcherLibrary.CFunctionMatcher ) ? LexerEvent.FindResultType.Found : LexerEvent.FindResultType.KeepSearching;
}
);
if ( ! nameToken.found )
{
return;
}
var typeToken = tokens.ReverseFind( nameToken.index - 1,
( t ) =>
{
return t.Is( LexerMatcherLibrary.CwordMatcher ) ? LexerEvent.FindResultType.Found : LexerEvent.FindResultType.KeepSearching;
}
);
var memberInfo = MemberInfo.CreateMethod();
memberInfo.name = tokens.events[ nameToken.index ].match;
memberInfo.dataType = tokens.events[ typeToken.index ].match;
var parameters = tokens.Seperate( openBracketResult.index + 1, closeBracketResult.index - 1, GDShaderLexer.ignore );
parameters.ForEach(
( p )=>
{
var pValues = tokens.Range( p ).Filter( t => ! t.IsAnyOf( GDShaderLexer.ignore ) );
var parameterType = new ParameterType();
parameterType.name = pValues.events.ReverseAt( 0 ).match;
parameterType.type = pValues.events.ReverseAt( 1 ).match;
if ( pValues.events.Count > 2 )
{
parameterType.modifiers.Add( pValues.events.ReverseAt( 2 ).match );
}
memberInfo.parameters.Add( parameterType );
}
);
info.memberInfos.Add( memberInfo );
// RJLog.Log( "Method found!", memberInfo.memberType, memberInfo.name );
}
else
{
// RJLog.Log( "No method found!", firstToken );
}
}
}
}

View File

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