Lexing/Cameras
This commit is contained in:
parent
e88bfc81af
commit
273f1caa35
|
@ -0,0 +1,11 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileHeader
|
||||||
|
{
|
||||||
|
public string type;
|
||||||
|
public List<SceneFileNamedValue> attributes = new List<SceneFileNamedValue>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileHeaderAttribute
|
||||||
|
{
|
||||||
|
protected string _attributeName;
|
||||||
|
|
||||||
|
public static SceneFileHeaderAttribute Create( string value )
|
||||||
|
{
|
||||||
|
var an = new SceneFileHeaderAttribute();
|
||||||
|
an._attributeName = value;
|
||||||
|
|
||||||
|
return an;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneFileNamedValue GetAttribute( SceneFileHeader header )
|
||||||
|
{
|
||||||
|
return header.attributes.Find( a => a.name == _attributeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Get( SceneFileHeader header, string alternative = null )
|
||||||
|
{
|
||||||
|
var attribute = GetAttribute( header );
|
||||||
|
|
||||||
|
if ( attribute == null )
|
||||||
|
{
|
||||||
|
return alternative;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attribute.value.Substring( 1, attribute.value.Length - 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetNumberValue( SceneFileHeader header, double alternative = 0 )
|
||||||
|
{
|
||||||
|
var attribute = GetAttribute( header );
|
||||||
|
|
||||||
|
if ( attribute == null )
|
||||||
|
{
|
||||||
|
return alternative;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegexUtility.ParseDouble( attribute.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SceneFileHeaderAttributeValue:SceneFileHeaderAttribute
|
||||||
|
{
|
||||||
|
public virtual void GetValue( SceneFileHeader header )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SceneFileHeaderAttributeValue<T>:SceneFileHeaderAttributeValue
|
||||||
|
{
|
||||||
|
T _value;
|
||||||
|
|
||||||
|
public T value => _value;
|
||||||
|
|
||||||
|
Func<SceneFileHeaderAttribute,SceneFileHeader,T> _getter;
|
||||||
|
|
||||||
|
public SceneFileHeaderAttributeValue( string name, Func<SceneFileHeaderAttribute,SceneFileHeader,T> getter )
|
||||||
|
{
|
||||||
|
_attributeName = name;
|
||||||
|
_getter = getter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GetValue( SceneFileHeader header )
|
||||||
|
{
|
||||||
|
_value = _getter( this, header );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SFHStringAttribute:SceneFileHeaderAttributeValue<string>
|
||||||
|
{
|
||||||
|
public SFHStringAttribute( string name )
|
||||||
|
:base( name, ( att, entry ) => att.Get( entry ) )
|
||||||
|
{}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SFHNumberAttribute:SceneFileHeaderAttributeValue<double>
|
||||||
|
{
|
||||||
|
public SFHNumberAttribute( string name )
|
||||||
|
:base( name, ( att, entry ) => att.GetNumberValue( entry ) )
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class EditableSFO:SceneFileObject
|
||||||
|
{
|
||||||
|
public static readonly string headerType = "editable";
|
||||||
|
|
||||||
|
public readonly SFHStringAttribute path = new SFHStringAttribute( "path" );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class ExtResourceSFO:SceneFileObject
|
||||||
|
{
|
||||||
|
public static readonly string headerType = "ext_resource";
|
||||||
|
|
||||||
|
public readonly SFHStringAttribute path = new SFHStringAttribute( "path" );
|
||||||
|
public readonly SFHStringAttribute uid = new SFHStringAttribute( "uid" );
|
||||||
|
public readonly SFHStringAttribute type = new SFHStringAttribute( "type" );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class GDSceneSFO:SceneFileObject
|
||||||
|
{
|
||||||
|
public static readonly string headerType = "gd_scene";
|
||||||
|
|
||||||
|
public readonly SFHNumberAttribute loadSteps = new SFHNumberAttribute( "loadSteps" );
|
||||||
|
public readonly SFHNumberAttribute format = new SFHNumberAttribute( "format" );
|
||||||
|
public readonly SFHStringAttribute uid = new SFHStringAttribute( "uid" );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class NodeSFO:SceneFileObject
|
||||||
|
{
|
||||||
|
public static readonly string headerType = "node";
|
||||||
|
|
||||||
|
|
||||||
|
protected NodeSFO _parentObject;
|
||||||
|
public readonly SFHStringAttribute name = new SFHStringAttribute( "name" );
|
||||||
|
public readonly SFHStringAttribute parent = new SFHStringAttribute( "parent" );
|
||||||
|
public readonly SFHStringAttribute type = new SFHStringAttribute( "type" );
|
||||||
|
protected string _elementPath;
|
||||||
|
public string elementPath => _elementPath;
|
||||||
|
|
||||||
|
|
||||||
|
protected override void _CreateFromHeaderEntry( SceneFileEntry headerEntry )
|
||||||
|
{
|
||||||
|
_elementPath = name.value;
|
||||||
|
var parentPath = parent.value;
|
||||||
|
|
||||||
|
if ( parentPath == null )
|
||||||
|
{
|
||||||
|
_elementPath = ".";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( parentPath != "." )
|
||||||
|
{
|
||||||
|
_elementPath = parentPath + "/" + name.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResolveParent( Dictionary<string,NodeSFO> nodeMap, NodeSFO rootNode )
|
||||||
|
{
|
||||||
|
var parentPath = parent.value;
|
||||||
|
|
||||||
|
if ( parentPath == "." )
|
||||||
|
{
|
||||||
|
_parentObject = rootNode;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( parentPath != "." && parentPath != null )
|
||||||
|
{
|
||||||
|
if ( nodeMap.ContainsKey( parentPath ) )
|
||||||
|
{
|
||||||
|
_parentObject = nodeMap[ parentPath ];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RJLog.Log( "Parent not found:", name.value, parentPath, elementPath );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileObject
|
||||||
|
{
|
||||||
|
protected SceneFileEntry _headerEntry;
|
||||||
|
protected List<SceneFileHeaderAttributeValue> _attributes = new List<SceneFileHeaderAttributeValue>();
|
||||||
|
|
||||||
|
public SceneFileObject()
|
||||||
|
{
|
||||||
|
SetAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateFromHeaderEntry( SceneFileEntry headerEntry )
|
||||||
|
{
|
||||||
|
ReadAttributes( headerEntry.header );
|
||||||
|
|
||||||
|
_CreateFromHeaderEntry( headerEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void _CreateFromHeaderEntry( SceneFileEntry headerEntry )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void SetAttributes()
|
||||||
|
{
|
||||||
|
_attributes = ReflectionHelper.GetFieldsOfType<SceneFileHeaderAttributeValue>( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ReadAttributes( SceneFileHeader entry )
|
||||||
|
{
|
||||||
|
_attributes.ForEach( a => a.GetValue( entry ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileObjectFactory
|
||||||
|
{
|
||||||
|
static Dictionary<string,Func<SceneFileObject>> factory =
|
||||||
|
new Dictionary<string,Func<SceneFileObject>>()
|
||||||
|
{
|
||||||
|
{ GDSceneSFO.headerType, () => new GDSceneSFO() },
|
||||||
|
{ ExtResourceSFO.headerType, () => new ExtResourceSFO() },
|
||||||
|
{ SubResourceSFO.headerType, () => new SubResourceSFO() },
|
||||||
|
{ NodeSFO.headerType, () => new NodeSFO() },
|
||||||
|
{ EditableSFO.headerType, () => new EditableSFO() }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static SceneFileObject Create( SceneFileEntry entry )
|
||||||
|
{
|
||||||
|
if ( entry.header == null )
|
||||||
|
{
|
||||||
|
RJLog.Log( "Has no header >>", entry.line );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( var vk in factory )
|
||||||
|
{
|
||||||
|
if ( entry.IsHeaderType( vk.Key ) )
|
||||||
|
{
|
||||||
|
var sfo = vk.Value();
|
||||||
|
sfo.CreateFromHeaderEntry( entry );
|
||||||
|
|
||||||
|
return sfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RJLog.Log( "Unknown header type >> ", entry.header.type, ":", entry.line );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SubResourceSFO:SceneFileObject
|
||||||
|
{
|
||||||
|
public static readonly string headerType = "sub_resource";
|
||||||
|
|
||||||
|
public readonly SFHStringAttribute id = new SFHStringAttribute( "uid" );
|
||||||
|
public readonly SFHStringAttribute type = new SFHStringAttribute( "type" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileHeaderParser
|
||||||
|
{
|
||||||
|
public bool hasError = true;
|
||||||
|
public string errorMessage;
|
||||||
|
string line;
|
||||||
|
|
||||||
|
public void Parse( SceneFileEntry entry )
|
||||||
|
{
|
||||||
|
|
||||||
|
line = entry.line.Substring( 1, entry.line.Length - 2 );
|
||||||
|
|
||||||
|
var lexer = new SceneFileLexer();
|
||||||
|
var tokens = lexer.LexToList( line );
|
||||||
|
|
||||||
|
if ( lexer.hasError || tokens.Count == 0)
|
||||||
|
{
|
||||||
|
errorMessage = "Lexing failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer.GrabMatches( tokens, line );
|
||||||
|
|
||||||
|
/*var info = "";
|
||||||
|
tokens.ForEach(
|
||||||
|
( tk )=>
|
||||||
|
{
|
||||||
|
info += tk.type + " " + tk.match + "\n";
|
||||||
|
}
|
||||||
|
);*/
|
||||||
|
|
||||||
|
var offset = 0;
|
||||||
|
var token = tokens[ 0 ];
|
||||||
|
|
||||||
|
if ( ! token.Is( LexerMatcherLibrary.CwordMatcher ) )
|
||||||
|
{
|
||||||
|
errorMessage = "First token is not a word: "+ token.type + " '" + token.match + "'";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var header = new SceneFileHeader();
|
||||||
|
|
||||||
|
entry.header = header;
|
||||||
|
|
||||||
|
header.type = tokens[ 0 ].match;
|
||||||
|
|
||||||
|
offset = 1; token = tokens[ offset ];
|
||||||
|
|
||||||
|
while ( offset < tokens.Count && token != null )
|
||||||
|
{
|
||||||
|
if ( ! token.Is( LexerMatcherLibrary.WhiteSpaceMatcher ) )
|
||||||
|
{
|
||||||
|
errorMessage = "Expected white space, but got: " + token.type + " '" + token.match + "'";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset ++; token = tokens[ offset ];
|
||||||
|
|
||||||
|
var assignmentResult = SceneFileLexer.ReadAssignment( tokens, offset );
|
||||||
|
|
||||||
|
if ( assignmentResult.hasError )
|
||||||
|
{
|
||||||
|
errorMessage = assignmentResult.errorMessage;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.attributes.Add( assignmentResult.GetNamedValue() );
|
||||||
|
|
||||||
|
offset = assignmentResult.endOffset;
|
||||||
|
token = tokens[ offset ];
|
||||||
|
|
||||||
|
|
||||||
|
/*if ( ! token.Is( LexerMatcherLibrary.CwordMatcher ) )
|
||||||
|
{
|
||||||
|
errorMessage = "Expected word for attribute name, but got: " + token.type + " '" + token.match + "'";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var attribute = new SceneFileNamedValue();
|
||||||
|
attribute.name = token.match;
|
||||||
|
|
||||||
|
offset ++; token = tokens[ offset ];
|
||||||
|
|
||||||
|
if ( ! token.Is( LexerMatcherLibrary.OperatorMatcher, "=" ) )
|
||||||
|
{
|
||||||
|
errorMessage = "Expected assignment operator, but got:" + token.type + " '" + token.match + "'";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset ++; token = tokens[ offset ];
|
||||||
|
|
||||||
|
if ( token.Is( LexerMatcherLibrary.NumberMatcher ) )
|
||||||
|
{
|
||||||
|
attribute.value = token.match;
|
||||||
|
header.attributes.Add( attribute );
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( token.Is( LexerMatcherLibrary.DoubleQuotedStringMatcher ) )
|
||||||
|
{
|
||||||
|
attribute.value = token.match;
|
||||||
|
header.attributes.Add( attribute );
|
||||||
|
}
|
||||||
|
else if ( token.Is( LexerMatcherLibrary.CFunctionMatcher ) )
|
||||||
|
{
|
||||||
|
var closer = LexerEvent.FindClosingBracket( tokens, offset + 1 );
|
||||||
|
|
||||||
|
if ( closer.type != LexerEvent.FindResultType.Found )
|
||||||
|
{
|
||||||
|
errorMessage = "Function with unmatched brackets: " + token.type + " '" + token.match + "'";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( int i = offset; i <= closer.index; i++ )
|
||||||
|
{
|
||||||
|
attribute.value += tokens[ i ].match;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = closer.index + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = "Unexpected token:" + token.type + " '" + token.match + "'";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
offset ++;
|
||||||
|
|
||||||
|
token = ( offset < tokens.Count - 1 ) ? tokens[ offset ] : null ;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//RJLog.Log( info );
|
||||||
|
|
||||||
|
hasError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileLexer:Lexer
|
||||||
|
{
|
||||||
|
public SceneFileLexer()
|
||||||
|
{
|
||||||
|
AddAllMatchers(
|
||||||
|
LexerMatcherLibrary.DoubleQuotedStringMatcher,
|
||||||
|
LexerMatcherLibrary.NumberMatcher,
|
||||||
|
LexerMatcherLibrary.NullMatcher,
|
||||||
|
LexerMatcherLibrary.BoolMatcher,
|
||||||
|
LexerMatcherLibrary.BreakMatcher,
|
||||||
|
LexerMatcherLibrary.WhiteSpaceMatcher,
|
||||||
|
LexerMatcherLibrary.BracketMatcher,
|
||||||
|
LexerMatcherLibrary.OperatorMatcher,
|
||||||
|
LexerMatcherLibrary.CFunctionMatcher,
|
||||||
|
LexerMatcherLibrary.CwordMatcher,
|
||||||
|
LexerMatcherLibrary.AnySymbolMatcher
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SceneFileAssignmentResult
|
||||||
|
{
|
||||||
|
public bool hasError = true;
|
||||||
|
public string errorMessage = "";
|
||||||
|
|
||||||
|
public string name;
|
||||||
|
public string value;
|
||||||
|
|
||||||
|
public SceneFileNamedValue GetNamedValue()
|
||||||
|
{
|
||||||
|
return SceneFileNamedValue.Create( name, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
public int startOffset;
|
||||||
|
public int endOffset;
|
||||||
|
|
||||||
|
public static SceneFileAssignmentResult Create( int startOffset )
|
||||||
|
{
|
||||||
|
var r = new SceneFileAssignmentResult();
|
||||||
|
|
||||||
|
r.startOffset = startOffset;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneFileAssignmentResult AsError( int offset, LexerEvent le, string message )
|
||||||
|
{
|
||||||
|
hasError = true;
|
||||||
|
endOffset = offset;
|
||||||
|
errorMessage = message + " Token: " + le.type + "'" + le.match + "'" ;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneFileAssignmentResult AsValue( int offset, LexerEvent le )
|
||||||
|
{
|
||||||
|
hasError = false;
|
||||||
|
endOffset = offset;
|
||||||
|
value = le.match;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneFileAssignmentResult AsValue( int offset, string value )
|
||||||
|
{
|
||||||
|
hasError = false;
|
||||||
|
endOffset = offset;
|
||||||
|
this.value = value;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SceneFileAssignmentResult ReadAssignment( List<LexerEvent> tokens, int offset )
|
||||||
|
{
|
||||||
|
var result = SceneFileAssignmentResult.Create( offset );
|
||||||
|
var token = tokens[ offset ];
|
||||||
|
|
||||||
|
if ( ! token.Is( LexerMatcherLibrary.CwordMatcher ) )
|
||||||
|
{
|
||||||
|
return result.AsError( offset, token, "Expected a word as name." ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.name = token.match;
|
||||||
|
|
||||||
|
offset ++; token = tokens[ offset ];
|
||||||
|
|
||||||
|
if ( ! token.Is( LexerMatcherLibrary.OperatorMatcher, "=" ) )
|
||||||
|
{
|
||||||
|
return result.AsError( offset, token, "Expected assignment operator." ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset ++; token = tokens[ offset ];
|
||||||
|
|
||||||
|
if ( token.Is( LexerMatcherLibrary.NumberMatcher ) ||
|
||||||
|
token.Is( LexerMatcherLibrary.DoubleQuotedStringMatcher )
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return result.AsValue( offset, token );
|
||||||
|
}
|
||||||
|
else if ( token.Is( LexerMatcherLibrary.CFunctionMatcher ) )
|
||||||
|
{
|
||||||
|
var start = offset + 1;
|
||||||
|
var closer = LexerEvent.FindClosingBracket( tokens, offset + 1 );
|
||||||
|
|
||||||
|
if ( closer.type != LexerEvent.FindResultType.Found )
|
||||||
|
{
|
||||||
|
return result.AsError( offset, token, "Unmatched brackets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = ( closer.index - start ) + 1;
|
||||||
|
|
||||||
|
var value = LexerEvent.GetMatchFromRange( tokens, start, length );
|
||||||
|
|
||||||
|
return result.AsValue( closer.index, value );
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return result.AsError( offset, token, "Unexpected token.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class GodotTextSceneLexer:Lexer
|
||||||
|
{
|
||||||
|
public static List<LexerEvent> Lex( string source )
|
||||||
|
{
|
||||||
|
var lexer = new GodotTextSceneLexer();
|
||||||
|
var events = lexer.LexToList( source );
|
||||||
|
|
||||||
|
if ( lexer.hasError )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
events.ForEach( ev => { ev.GrabMatch( source ); } );
|
||||||
|
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly LexerMatcher HeaderMatcher = LexerMatcher.CreateExtended(
|
||||||
|
"Header", "\\[.+\\]"
|
||||||
|
);
|
||||||
|
|
||||||
|
public static readonly LexerMatcher SeperatorMatcher = LexerMatcher.CreateExtended(
|
||||||
|
"Seperator", "\\l\\l"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ((?:\w|_|\/)+) = .+?(\r\n|\r|\n)
|
||||||
|
// ((?:\\w|_|\\/)+) = .+?(\\r\\n|\\r|\\n)
|
||||||
|
public static readonly LexerMatcher MemberMatcher = LexerMatcher.CreateExtended(
|
||||||
|
"Member", "(?:\\w|_|\\/)+ = .+?"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ".+?"\: .+?,(\r\n|\r|\n)
|
||||||
|
// \".+?\"\\: .+?,\\l
|
||||||
|
public static readonly LexerMatcher SubMemberMatcher = LexerMatcher.CreateExtended(
|
||||||
|
"SubMember", "\".+?\"\\: .+?"
|
||||||
|
);
|
||||||
|
|
||||||
|
public static readonly LexerMatcher SubMemberStartMatcher = LexerMatcher.CreateExtended(
|
||||||
|
"SubMemberStart", ".+\\[\\{"
|
||||||
|
);
|
||||||
|
|
||||||
|
public static readonly LexerMatcher SubMemberEndMatcher = LexerMatcher.CreateExtended(
|
||||||
|
"SubMemberEnd", "\\}\\]"
|
||||||
|
);
|
||||||
|
|
||||||
|
public GodotTextSceneLexer()
|
||||||
|
{
|
||||||
|
AddAllMatchers(
|
||||||
|
GodotTextSceneLexer.HeaderMatcher,
|
||||||
|
GodotTextSceneLexer.SeperatorMatcher,
|
||||||
|
GodotTextSceneLexer.SubMemberStartMatcher,
|
||||||
|
GodotTextSceneLexer.SubMemberEndMatcher,
|
||||||
|
GodotTextSceneLexer.SubMemberMatcher,
|
||||||
|
GodotTextSceneLexer.MemberMatcher,
|
||||||
|
LexerMatcherLibrary.AnySymbolMatcher
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileParser
|
||||||
|
{
|
||||||
|
public string source;
|
||||||
|
public SceneFile sceneFile;
|
||||||
|
|
||||||
|
public void Parse( string source )
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
|
||||||
|
var lines = RegexUtility.SplitLines( source );
|
||||||
|
var lexer = new GodotTextSceneLexer();
|
||||||
|
|
||||||
|
var lineIndex = 1;
|
||||||
|
|
||||||
|
sceneFile = new SceneFile();
|
||||||
|
|
||||||
|
lines.ForEach(
|
||||||
|
( ln )=>
|
||||||
|
{
|
||||||
|
var matcher = lexer.GetMatcher( ln );
|
||||||
|
|
||||||
|
if ( matcher == null )
|
||||||
|
{
|
||||||
|
sceneFile.entries.Add( SceneFile.Seperator() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var linePreview = ln;
|
||||||
|
sceneFile.entries.Add( SceneFile.Create( matcher.type, ln ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lineIndex ++;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
sceneFile.CreateObjects();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFile
|
||||||
|
{
|
||||||
|
public static SceneFileEntry Create( string type, string line )
|
||||||
|
{
|
||||||
|
var entry = new SceneFileEntry();
|
||||||
|
entry.type = type;
|
||||||
|
entry.line = line;
|
||||||
|
|
||||||
|
entry.Parse();
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SceneFileEntry Seperator()
|
||||||
|
{
|
||||||
|
return Create( GodotTextSceneLexer.SeperatorMatcher.type, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SceneFileEntry> entries = new List<SceneFileEntry>();
|
||||||
|
public List<SceneFileObject> objects = new List<SceneFileObject>();
|
||||||
|
|
||||||
|
public GDSceneSFO gdScene;
|
||||||
|
public NodeSFO rootNode;
|
||||||
|
public List<NodeSFO> nodes = new List<NodeSFO>();
|
||||||
|
public Dictionary<string,NodeSFO> nodeMap = new Dictionary<string, NodeSFO>();
|
||||||
|
public List<ExtResourceSFO> extResources = new List<ExtResourceSFO>();
|
||||||
|
public List<SubResourceSFO> subResourceSFOs = new List<SubResourceSFO>();
|
||||||
|
|
||||||
|
public SceneFile GetSerializableJSONVersion()
|
||||||
|
{
|
||||||
|
var file = new SceneFile();
|
||||||
|
file.entries = entries;
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateObjects()
|
||||||
|
{
|
||||||
|
entries.ForEach(
|
||||||
|
( e )=>
|
||||||
|
{
|
||||||
|
if ( ! e.hasHeader )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sfo = SceneFileObjectFactory.Create( e );
|
||||||
|
|
||||||
|
if ( sfo == null )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sfo is GDSceneSFO )
|
||||||
|
{
|
||||||
|
gdScene = (GDSceneSFO) sfo;
|
||||||
|
}
|
||||||
|
else if ( sfo is NodeSFO )
|
||||||
|
{
|
||||||
|
var node = (NodeSFO) sfo;
|
||||||
|
nodes.Add( node );
|
||||||
|
|
||||||
|
if ( node.elementPath == "." )
|
||||||
|
{
|
||||||
|
rootNode = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMap[ node.elementPath ] = node;
|
||||||
|
|
||||||
|
// RJLog.Log( "'" + node.elementPath + "'", node.name.value );
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( sfo is ExtResourceSFO )
|
||||||
|
{
|
||||||
|
extResources.Add( (ExtResourceSFO) sfo );
|
||||||
|
}
|
||||||
|
else if ( sfo is SubResourceSFO )
|
||||||
|
{
|
||||||
|
subResourceSFOs.Add( (SubResourceSFO) sfo );
|
||||||
|
}
|
||||||
|
|
||||||
|
objects.Add( sfo );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
nodes.ForEach(
|
||||||
|
( n )=>
|
||||||
|
{
|
||||||
|
n.ResolveParent( nodeMap, rootNode );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileEntry
|
||||||
|
{
|
||||||
|
public string type;
|
||||||
|
public string line;
|
||||||
|
public SceneFileHeader header;
|
||||||
|
public SceneFileNamedValue member;
|
||||||
|
public SceneFileNamedValue subMember;
|
||||||
|
public bool hasError = false;
|
||||||
|
|
||||||
|
|
||||||
|
public bool hasHeader => header != null;
|
||||||
|
|
||||||
|
public bool IsHeaderType( string type )
|
||||||
|
{
|
||||||
|
return header != null && header.type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Parse()
|
||||||
|
{
|
||||||
|
if ( type == GodotTextSceneLexer.HeaderMatcher.type )
|
||||||
|
{
|
||||||
|
var headerParser = new SceneFileHeaderParser();
|
||||||
|
headerParser.Parse( this );
|
||||||
|
|
||||||
|
if ( headerParser.hasError )
|
||||||
|
{
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class SceneFileNamedValue
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string value;
|
||||||
|
|
||||||
|
public static SceneFileNamedValue Create( string name, string value )
|
||||||
|
{
|
||||||
|
var nv = new SceneFileNamedValue();
|
||||||
|
nv.name = name;
|
||||||
|
nv.value = value;
|
||||||
|
return nv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
[Tool]
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class SceneFileReader:Node
|
||||||
|
{
|
||||||
|
[Export]
|
||||||
|
public string path = "";
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool load = false;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool exportJSON = false;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool exportHTML = false;
|
||||||
|
|
||||||
|
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
LoadScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadScene()
|
||||||
|
{
|
||||||
|
if ( ! load )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
load = false;
|
||||||
|
|
||||||
|
var text = FilesSync.LoadUTF8( path );
|
||||||
|
|
||||||
|
var parser = new SceneFileParser();
|
||||||
|
|
||||||
|
parser.Parse( text );
|
||||||
|
|
||||||
|
if ( exportJSON )
|
||||||
|
{
|
||||||
|
FilesSync.SaveJSON( path + ".json", parser.sceneFile.GetSerializableJSONVersion() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( exportHTML )
|
||||||
|
{
|
||||||
|
var nodes = parser.sceneFile.nodes;
|
||||||
|
RJLog.Log( "Nodes:", nodes.Count );
|
||||||
|
|
||||||
|
var doc = new HtmlDocument();
|
||||||
|
var html = doc.documentElement;
|
||||||
|
var body = html.querySelector( HtmlElementNodeName.body );
|
||||||
|
var head = html.querySelector( HtmlElementNodeName.head );
|
||||||
|
|
||||||
|
head.AddScript(
|
||||||
|
@"
|
||||||
|
|
||||||
|
function main()
|
||||||
|
{
|
||||||
|
let elements = document.querySelectorAll( '.closeable' );
|
||||||
|
|
||||||
|
for ( let i = 0; i < elements.length; i++ )
|
||||||
|
{
|
||||||
|
let element = elements[ i ];
|
||||||
|
|
||||||
|
element.addEventListener( 'click',
|
||||||
|
( ev )=>
|
||||||
|
{
|
||||||
|
|
||||||
|
let target = element.getAttribute( 'data-close-target' );
|
||||||
|
|
||||||
|
let targetElement = element;
|
||||||
|
|
||||||
|
let parentRegex = /^\.\.\s/;
|
||||||
|
|
||||||
|
while ( parentRegex.test( target ) )
|
||||||
|
{
|
||||||
|
target = target.replace( parentRegex, '' );
|
||||||
|
targetElement = targetElement.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rootRegex = /^\.\.\.\s/;
|
||||||
|
|
||||||
|
if ( rootRegex.test( target ) )
|
||||||
|
{
|
||||||
|
target = target.replace( rootRegex, '' );
|
||||||
|
targetElement = document.documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let closeTarget = element.parentElement.querySelector( target );
|
||||||
|
|
||||||
|
console.log( ev, target, closeTarget);
|
||||||
|
closeTarget.style.display = closeTarget.style.display === 'none' ? 'block' : 'none';
|
||||||
|
|
||||||
|
element.setAttribute( 'data-close-state', closeTarget.style.display );
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener( 'load', () => { main(); });
|
||||||
|
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
head.AddStyle(
|
||||||
|
@"
|
||||||
|
body
|
||||||
|
{
|
||||||
|
overflow-x: hidden;
|
||||||
|
margin: 2em 0em;
|
||||||
|
font-family: Helvetica, Arial, sans;
|
||||||
|
background: hsl(0,0%,10%);
|
||||||
|
color: hsl(0,0%,80%)
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeable
|
||||||
|
{
|
||||||
|
cursor: pointer;
|
||||||
|
opacity:1;
|
||||||
|
transition: opacity 300ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeable[data-close-state='none']
|
||||||
|
{
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
gd-name
|
||||||
|
{
|
||||||
|
border-radius: 0.5em;
|
||||||
|
padding: 1em 1em;
|
||||||
|
background-color: hsl(0,0%,20%);
|
||||||
|
margin: 0.5em 1em;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gd-type
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var elementMap = new Dictionary<string,HtmlElementNode>();
|
||||||
|
|
||||||
|
var GD_Node = HtmlElementNodeName.CreateNodeName( "gd-node" );
|
||||||
|
var GD_Name = HtmlElementNodeName.CreateNodeName( "gd-name" );
|
||||||
|
var GD_Type = HtmlElementNodeName.CreateNodeName( "gd-type" );
|
||||||
|
var GD_Children = HtmlElementNodeName.CreateNodeName( "gd-children" );
|
||||||
|
|
||||||
|
for ( int i = 0; i < nodes.Count; i++ )
|
||||||
|
{
|
||||||
|
var node = nodes[ i ];
|
||||||
|
|
||||||
|
//RJLog.Log( i, node.name.value );
|
||||||
|
|
||||||
|
var attachmentElement = body;
|
||||||
|
|
||||||
|
var elementPath = node.elementPath;
|
||||||
|
|
||||||
|
var parent = node.parent.value;
|
||||||
|
|
||||||
|
if ( parent != null && elementMap.ContainsKey( parent ) )
|
||||||
|
{
|
||||||
|
attachmentElement = elementMap[ parent ].querySelector( GD_Children );
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeElement = doc.Create( GD_Node );
|
||||||
|
|
||||||
|
nodeElement.SetAttribute( "class", "node" );
|
||||||
|
|
||||||
|
var nameElement = nodeElement.AddElement( GD_Name, node.name.value );
|
||||||
|
nameElement.SetAttribute( "class", "closeable" );
|
||||||
|
nameElement.SetAttribute( "data-close-target", ".. " + GD_Children.selector );
|
||||||
|
|
||||||
|
nodeElement.AddElement( GD_Type, node.type.value );
|
||||||
|
|
||||||
|
nodeElement.AddElement( GD_Children );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elementMap[ elementPath ] = nodeElement;
|
||||||
|
|
||||||
|
// RJLog.Log( "'" + elementPath + "'", node.name.value );
|
||||||
|
|
||||||
|
attachmentElement.AppendChild( nodeElement );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FilesSync.SaveUTF8( path + ".html", new HtmlSerializer().Serialize( doc.documentElement ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Rokojori
|
namespace Rokojori
|
||||||
{
|
{
|
||||||
|
@ -220,7 +220,7 @@ namespace Rokojori
|
||||||
|
|
||||||
if ( its > max )
|
if ( its > max )
|
||||||
{
|
{
|
||||||
throw new System.Exception();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
node = LastChild( node );
|
node = LastChild( node );
|
||||||
|
@ -240,7 +240,76 @@ namespace Rokojori
|
||||||
return NextNonChild( node );
|
return NextNonChild( node );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Iterate( N node, System.Action<N> callback, bool childrenOnly = false )
|
public int GetDepth( N node, Dictionary<N, int> depthMap = null )
|
||||||
|
{
|
||||||
|
if ( depthMap == null )
|
||||||
|
{
|
||||||
|
var depth = 0;
|
||||||
|
var it = Parent( node );
|
||||||
|
var maxDepth = 1000;
|
||||||
|
|
||||||
|
while ( it != null )
|
||||||
|
{
|
||||||
|
depth ++;
|
||||||
|
|
||||||
|
if ( depth == maxDepth )
|
||||||
|
{
|
||||||
|
RJLog.Log( "reached max depth", it );
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = Parent( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( depthMap.ContainsKey( node ) )
|
||||||
|
{
|
||||||
|
return depthMap[ node ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent = Parent( node );
|
||||||
|
|
||||||
|
if ( parent == null )
|
||||||
|
{
|
||||||
|
depthMap[ node ] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( depthMap.ContainsKey( parent ) )
|
||||||
|
{
|
||||||
|
var parentDepth = depthMap[ parent ];
|
||||||
|
var nodeDepth = parentDepth + 1;
|
||||||
|
|
||||||
|
depthMap[ node ] = nodeDepth;
|
||||||
|
|
||||||
|
return nodeDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
depthMap[ node ] = GetDepth( node );
|
||||||
|
|
||||||
|
return depthMap[ node ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DepthIterate( N node, Action<N, int> callback, bool childrenOnly = false, Dictionary<N, int> depthMap = null )
|
||||||
|
{
|
||||||
|
if ( depthMap == null )
|
||||||
|
{
|
||||||
|
depthMap = new Dictionary<N, int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var iterationCallback = ( N node )=>
|
||||||
|
{
|
||||||
|
var depth = GetDepth( node, depthMap );
|
||||||
|
callback( node, depth );
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterate( node, iterationCallback, childrenOnly );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Iterate( N node, Action<N> callback, bool childrenOnly = false )
|
||||||
{
|
{
|
||||||
var end = IterationEndOf( node );
|
var end = IterationEndOf( node );
|
||||||
var it = node;
|
var it = node;
|
||||||
|
@ -257,7 +326,7 @@ namespace Rokojori
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public N Find( N node, System.Predicate<N> predicate, bool childrenOnly )
|
public N Find( N node, Predicate<N> predicate, bool childrenOnly )
|
||||||
{
|
{
|
||||||
var end = IterationEndOf( node );
|
var end = IterationEndOf( node );
|
||||||
var it = node;
|
var it = node;
|
||||||
|
@ -280,9 +349,9 @@ namespace Rokojori
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Filter( List<N> list, N node, System.Predicate<N> predicate, bool childrenOnly )
|
public void Filter( List<N> list, N node, Predicate<N> predicate, bool childrenOnly )
|
||||||
{
|
{
|
||||||
System.Action<N> addToList = ( N n ) =>
|
Action<N> addToList = ( N n ) =>
|
||||||
{
|
{
|
||||||
if ( predicate( n ) )
|
if ( predicate( n ) )
|
||||||
{
|
{
|
||||||
|
@ -293,9 +362,9 @@ namespace Rokojori
|
||||||
Iterate( node, addToList, childrenOnly );
|
Iterate( node, addToList, childrenOnly );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FilterAndMap<U>( List<U> list, N node, System.Predicate<N> predicate, System.Func<N,U> mapper, bool childrenOnly )
|
public void FilterAndMap<U>( List<U> list, N node, Predicate<N> predicate, Func<N,U> mapper, bool childrenOnly )
|
||||||
{
|
{
|
||||||
System.Action<N> addToList = ( N n ) =>
|
Action<N> addToList = ( N n ) =>
|
||||||
{
|
{
|
||||||
if ( predicate( n ) )
|
if ( predicate( n ) )
|
||||||
{
|
{
|
||||||
|
@ -306,9 +375,9 @@ namespace Rokojori
|
||||||
Iterate( node, addToList, childrenOnly );
|
Iterate( node, addToList, childrenOnly );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Map<U>( List<U> list, N node, System.Predicate<N> predicate, System.Func<N,U> mapper, bool childrenOnly )
|
public void Map<U>( List<U> list, N node, Predicate<N> predicate, Func<N,U> mapper, bool childrenOnly )
|
||||||
{
|
{
|
||||||
System.Action<N> addToList = ( N n ) =>
|
Action<N> addToList = ( N n ) =>
|
||||||
{
|
{
|
||||||
list.Add( mapper( n ) );
|
list.Add( mapper( n ) );
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlAttributeNode:HtmlNode
|
||||||
|
{
|
||||||
|
public HtmlAttributeNode( HtmlDocument document, HtmlElementNode parent, string name, string value ):base( document, HtmlNode.NodeType.Attribute )
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_value = value;
|
||||||
|
_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
HtmlElementNode _parent;
|
||||||
|
string _name;
|
||||||
|
string _value;
|
||||||
|
|
||||||
|
public string name => _name;
|
||||||
|
public string value => _value;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlDocument
|
||||||
|
{
|
||||||
|
HtmlElementNode _documentElement;
|
||||||
|
public HtmlElementNode documentElement => _documentElement;
|
||||||
|
|
||||||
|
public HtmlDocument( bool addHeadAndBody = true )
|
||||||
|
{
|
||||||
|
_documentElement = HtmlElementNodeName.html.Create( this );
|
||||||
|
|
||||||
|
if ( addHeadAndBody )
|
||||||
|
{
|
||||||
|
_documentElement.AppendChild( HtmlElementNodeName.head.Create( this ) );
|
||||||
|
_documentElement.AppendChild( HtmlElementNodeName.body.Create( this ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode Create( HtmlElementNodeName nodeName, string text = null )
|
||||||
|
{
|
||||||
|
var element = nodeName.Create( this );
|
||||||
|
|
||||||
|
if ( text != null )
|
||||||
|
{
|
||||||
|
element.AddText( text );
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlTextNode CreateText( string text )
|
||||||
|
{
|
||||||
|
return new HtmlTextNode( this, text );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlElementNode:HtmlNode
|
||||||
|
{
|
||||||
|
public HtmlElementNode( HtmlDocument document, string nodeName ):base( document, HtmlNode.NodeType.Element )
|
||||||
|
{
|
||||||
|
_nodeName = nodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
string _nodeName;
|
||||||
|
public string nodeName => _nodeName;
|
||||||
|
|
||||||
|
List<HtmlNode> _children = new List<HtmlNode>();
|
||||||
|
public int numChildren => _children.Count;
|
||||||
|
|
||||||
|
public HtmlNode GetChildAt( int index )
|
||||||
|
{
|
||||||
|
return _children[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasOnlyTextNodes()
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < _children.Count; i++ )
|
||||||
|
{
|
||||||
|
if ( _children[ i ].nodeType != NodeType.Text )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _children.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveChild( HtmlNode node )
|
||||||
|
{
|
||||||
|
var childIndex = _children.IndexOf( node );
|
||||||
|
|
||||||
|
if ( childIndex == -1 )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node._SetParent( null );
|
||||||
|
_children.RemoveAt( childIndex );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppendChild( HtmlNode node )
|
||||||
|
{
|
||||||
|
if ( node.parentNode == this )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( node.parentNode != null )
|
||||||
|
{
|
||||||
|
var element = node.parentNode as HtmlElementNode;
|
||||||
|
element.RemoveChild( node );
|
||||||
|
}
|
||||||
|
|
||||||
|
_children.Add( node );
|
||||||
|
|
||||||
|
node._SetParent( this );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlTextNode AddText( string text )
|
||||||
|
{
|
||||||
|
var textNode = document.CreateText( text );
|
||||||
|
AppendChild( textNode );
|
||||||
|
|
||||||
|
return textNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode AddScript( string script )
|
||||||
|
{
|
||||||
|
return AddElement( HtmlElementNodeName.script, script );
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode AddStyle( string style )
|
||||||
|
{
|
||||||
|
return AddElement( HtmlElementNodeName.style, style );
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode AddElement( HtmlElementNodeName name, string text = null )
|
||||||
|
{
|
||||||
|
var en = document.Create( name, text );
|
||||||
|
AppendChild( en );
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttribute( string name, string value )
|
||||||
|
{
|
||||||
|
var att = new HtmlAttributeNode( document, this, name, value );
|
||||||
|
_attributes.Add( att );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<HtmlAttributeNode> _attributes = new List<HtmlAttributeNode>();
|
||||||
|
|
||||||
|
public int numAttributes => _attributes.Count;
|
||||||
|
|
||||||
|
public HtmlAttributeNode GetAttributeAt( int index )
|
||||||
|
{
|
||||||
|
return _attributes[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode querySelector( string selector )
|
||||||
|
{
|
||||||
|
var nodeName = HtmlElementNodeName.CreateNodeName( selector );
|
||||||
|
|
||||||
|
return querySelector( nodeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode querySelector( HtmlElementSelector selector )
|
||||||
|
{
|
||||||
|
|
||||||
|
var element = HtmlWalker.instance.Find( this,
|
||||||
|
n =>
|
||||||
|
{
|
||||||
|
var element = n as HtmlElementNode;
|
||||||
|
|
||||||
|
if ( element == null )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selector.Selects( element );
|
||||||
|
},
|
||||||
|
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
return element == null ? null : ( element as HtmlElementNode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlElementNodeName:HtmlElementSelector
|
||||||
|
{
|
||||||
|
public static readonly HtmlElementNodeName html = CreateNodeName( "html" );
|
||||||
|
public static readonly HtmlElementNodeName head = CreateNodeName( "head" );
|
||||||
|
public static readonly HtmlElementNodeName body = CreateNodeName( "body" );
|
||||||
|
public static readonly HtmlElementNodeName br = CreateNodeName( "br" );
|
||||||
|
public static readonly HtmlElementNodeName a = CreateNodeName( "a" );
|
||||||
|
public static readonly HtmlElementNodeName style = CreateNodeName( "style" );
|
||||||
|
public static readonly HtmlElementNodeName script = CreateNodeName( "script" );
|
||||||
|
|
||||||
|
string _nodeName;
|
||||||
|
|
||||||
|
public string selector => _nodeName;
|
||||||
|
|
||||||
|
public static HtmlElementNodeName CreateNodeName( string type )
|
||||||
|
{
|
||||||
|
var elementNodeType = new HtmlElementNodeName();
|
||||||
|
elementNodeType._nodeName = type;
|
||||||
|
|
||||||
|
return elementNodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElementNode Create( HtmlDocument document )
|
||||||
|
{
|
||||||
|
return new HtmlElementNode( document, _nodeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Selects( HtmlElementNode elementNode )
|
||||||
|
{
|
||||||
|
return elementNode.nodeName == _nodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public interface HtmlElementSelector
|
||||||
|
{
|
||||||
|
bool Selects( HtmlElementNode elementNode );
|
||||||
|
|
||||||
|
string selector { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public abstract class HtmlNode
|
||||||
|
{
|
||||||
|
public enum NodeType
|
||||||
|
{
|
||||||
|
Element = 1,
|
||||||
|
Attribute = 2,
|
||||||
|
Text = 3,
|
||||||
|
CDataSection = 4,
|
||||||
|
ProcessingInstruction = 7,
|
||||||
|
Comment = 8,
|
||||||
|
Document = 9,
|
||||||
|
DocumentType = 10,
|
||||||
|
DocumentFragment = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeType _nodeType;
|
||||||
|
public NodeType nodeType => _nodeType;
|
||||||
|
|
||||||
|
HtmlDocument _document;
|
||||||
|
public HtmlDocument document => _document;
|
||||||
|
|
||||||
|
protected HtmlNode _parentNode;
|
||||||
|
public HtmlNode parentNode => _parentNode;
|
||||||
|
public HtmlElementNode parentElement => _parentNode as HtmlElementNode;
|
||||||
|
public void _SetParent( HtmlNode node )
|
||||||
|
{
|
||||||
|
_parentNode = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string nodeValue => null;
|
||||||
|
public virtual string textContent => "";
|
||||||
|
|
||||||
|
public HtmlNode( HtmlDocument document, NodeType type )
|
||||||
|
{
|
||||||
|
_document = document;
|
||||||
|
_nodeType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlSerializer
|
||||||
|
{
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
HtmlWalker walker = new HtmlWalker();
|
||||||
|
string indent = " ";
|
||||||
|
Dictionary<HtmlNode, int> depthMap = new Dictionary<HtmlNode, int>();
|
||||||
|
Dictionary<int, string> indentMap = new Dictionary<int, string>();
|
||||||
|
List<HtmlElementNode> stack = new List<HtmlElementNode>();
|
||||||
|
|
||||||
|
|
||||||
|
public static string Escape( string rawText )
|
||||||
|
{
|
||||||
|
rawText = RegexUtility.Replace( rawText, "&", "&" );
|
||||||
|
rawText = RegexUtility.Replace( rawText, "<", "<" );
|
||||||
|
rawText = RegexUtility.Replace( rawText, ">", ">" );
|
||||||
|
rawText = RegexUtility.Replace( rawText, "\'", "'" );
|
||||||
|
rawText = RegexUtility.Replace( rawText, "\"", """ );
|
||||||
|
|
||||||
|
return rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Serialize( HtmlNode node )
|
||||||
|
{
|
||||||
|
|
||||||
|
sb.Append( "<!DOCTYPE html>" );
|
||||||
|
walker.DepthIterate( node,
|
||||||
|
|
||||||
|
( n, d ) =>
|
||||||
|
{
|
||||||
|
var depth = GetDepth( n );
|
||||||
|
ClosePreviousElements( depth );
|
||||||
|
|
||||||
|
var element = n as HtmlElementNode;
|
||||||
|
|
||||||
|
if ( element != null )
|
||||||
|
{
|
||||||
|
sb.Append( "\n" );
|
||||||
|
sb.Append( GetIndent( depth ) );
|
||||||
|
|
||||||
|
sb.Append( "<" + element.nodeName );
|
||||||
|
|
||||||
|
for ( int i = 0; i < element.numAttributes; i++ )
|
||||||
|
{
|
||||||
|
var attribute = element.GetAttributeAt( i );
|
||||||
|
|
||||||
|
sb.Append( " " );
|
||||||
|
sb.Append( attribute.name );
|
||||||
|
sb.Append( "=\"" );
|
||||||
|
sb.Append( Escape( attribute.value ) );
|
||||||
|
sb.Append( "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append( ">" );
|
||||||
|
|
||||||
|
stack.Add( element );
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
HtmlElementNodeName.style.Selects( n.parentElement ) ||
|
||||||
|
HtmlElementNodeName.script.Selects( n.parentElement )
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sb.Append( n.nodeValue );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append( Escape( n.nodeValue ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
false,
|
||||||
|
depthMap
|
||||||
|
);
|
||||||
|
|
||||||
|
ClosePreviousElements( -1 );
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetIndent( int depth )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( indentMap.ContainsKey( depth ) )
|
||||||
|
{
|
||||||
|
return indentMap[ depth ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( depth == 0 )
|
||||||
|
{
|
||||||
|
indentMap[ 0 ] = "";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( indentMap.ContainsKey( depth - 1 ) )
|
||||||
|
{
|
||||||
|
var smallerIndent = indentMap[ depth -1 ];
|
||||||
|
indentMap[ depth ] = smallerIndent + indent;
|
||||||
|
return indentMap[ depth ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
for ( int i = 0; i < depth; i++ )
|
||||||
|
{
|
||||||
|
sb.Append( indent );
|
||||||
|
}
|
||||||
|
|
||||||
|
indentMap[ depth ] = sb.ToString();
|
||||||
|
return indentMap[ depth ];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetDepth( HtmlNode n )
|
||||||
|
{
|
||||||
|
return walker.GetDepth( n, depthMap );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClosePreviousElements( int currentDepth )
|
||||||
|
{
|
||||||
|
for ( int i = stack.Count - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
var stackDepth = GetDepth( stack[ i ] );
|
||||||
|
|
||||||
|
if ( stackDepth >= currentDepth )
|
||||||
|
{
|
||||||
|
var element = stack[ i ];
|
||||||
|
stack.RemoveAt( i );
|
||||||
|
|
||||||
|
if ( NeedsLineBreak( element ) )
|
||||||
|
{
|
||||||
|
sb.Append( "\n" );
|
||||||
|
sb.Append( GetIndent( stackDepth ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append( "</" + element.nodeName + ">" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NeedsLineBreak( HtmlElementNode elementNode )
|
||||||
|
{
|
||||||
|
if ( elementNode.numChildren == 0 ||
|
||||||
|
|
||||||
|
elementNode.HasOnlyTextNodes() &&
|
||||||
|
! ( HtmlElementNodeName.script.Selects( elementNode ) ||
|
||||||
|
HtmlElementNodeName.style.Selects( elementNode )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlTextNode:HtmlNode
|
||||||
|
{
|
||||||
|
public HtmlTextNode( HtmlDocument document, string textContent ):base( document, HtmlNode.NodeType.Text )
|
||||||
|
{
|
||||||
|
_textContent = textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
string _textContent;
|
||||||
|
|
||||||
|
public override string nodeValue => _textContent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class HtmlWalker: TreeWalker<HtmlNode>
|
||||||
|
{
|
||||||
|
private static HtmlWalker _instance = new HtmlWalker();
|
||||||
|
public static HtmlWalker instance => _instance;
|
||||||
|
|
||||||
|
public override int NumChildren( HtmlNode node )
|
||||||
|
{
|
||||||
|
var elementNode = node as HtmlElementNode;
|
||||||
|
|
||||||
|
if ( elementNode != null )
|
||||||
|
{
|
||||||
|
return elementNode.numChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HtmlNode ChildAt( HtmlNode node, int index )
|
||||||
|
{
|
||||||
|
var elementNode = node as HtmlElementNode;
|
||||||
|
|
||||||
|
if ( elementNode != null )
|
||||||
|
{
|
||||||
|
return elementNode.GetChildAt( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HtmlNode Parent( HtmlNode node )
|
||||||
|
{
|
||||||
|
return node.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,6 +35,24 @@ namespace Rokojori
|
||||||
{
|
{
|
||||||
return node.GlobalBasis.X;
|
return node.GlobalBasis.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector3 GetYPlaneForward( Node3D node )
|
||||||
|
{
|
||||||
|
var forward = GetGlobalForward( node );
|
||||||
|
|
||||||
|
forward.Y = 0;
|
||||||
|
|
||||||
|
return forward.Normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3 GetYPlaneRight( Node3D node )
|
||||||
|
{
|
||||||
|
var right = GetGlobalRight( node );
|
||||||
|
|
||||||
|
right.Y = 0;
|
||||||
|
|
||||||
|
return right.Normalized();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -225,5 +225,15 @@ namespace Rokojori
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static float TimeLerp( float from, float to, float t, float timeDelta )
|
||||||
|
{
|
||||||
|
return Mathf.Lerp( from, to, 1f - Mathf.Pow( t, timeDelta ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float PolarAxis( bool negative, bool positive )
|
||||||
|
{
|
||||||
|
return ( negative ? -1 : 0 ) + ( positive ? 1 : 0 );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
[GlobalClass,Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJSensor.svg")]
|
||||||
|
public partial class CombineSensor : RJSensor
|
||||||
|
{
|
||||||
|
[Export]
|
||||||
|
public RJSensor[] sensors;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float axisActivationTreshold = 0.75f;
|
||||||
|
|
||||||
|
float _value = 0;
|
||||||
|
bool _wasActive = false;
|
||||||
|
bool _isActive = false;
|
||||||
|
|
||||||
|
public override float GetValue()
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsActive()
|
||||||
|
{
|
||||||
|
return _isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool WasActive()
|
||||||
|
{
|
||||||
|
return _wasActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateValue( float value )
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
|
||||||
|
_wasActive = _isActive;
|
||||||
|
_isActive = _value > axisActivationTreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
var value = 0f;
|
||||||
|
|
||||||
|
for ( int i = 0; i < sensors.Length; i++ )
|
||||||
|
{
|
||||||
|
value = Mathf.Max( value, sensors[ i ].GetValue() );
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateValue( value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class Sensors
|
||||||
|
{
|
||||||
|
public static bool IsActive( RJSensor sensor )
|
||||||
|
{
|
||||||
|
return sensor == null ? false : sensor.IsActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float GetValue( RJSensor sensor, float scale = 1 )
|
||||||
|
{
|
||||||
|
return sensor == null ? 0 : sensor.GetValue() * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float PolarAxis( RJSensor negative, RJSensor positive )
|
||||||
|
{
|
||||||
|
return GetValue( negative, -1 ) + GetValue( positive, 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
#include "res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Library/Math.gdshaderinc"
|
||||||
|
|
||||||
|
const float HCV_EPSILON = 1e-10;
|
||||||
|
const float HSL_EPSILON = 1e-10;
|
||||||
|
|
||||||
|
vec3 RGBtoHCV( vec3 rgb )
|
||||||
|
{
|
||||||
|
vec4 P = ( rgb.g < rgb.b ) ? vec4( rgb.bg, -1.0, 2.0/3.0 ) : vec4( rgb.gb, 0.0, -1.0/3.0 );
|
||||||
|
vec4 Q = ( rgb.r < P.x ) ? vec4( P.xyw, rgb.r ) : vec4( rgb.r, P.yzx );
|
||||||
|
float C = Q.x - min( Q.w, Q.y );
|
||||||
|
float H = abs( (Q.w - Q.y ) / ( 6.0 * C + HCV_EPSILON ) + Q.z );
|
||||||
|
return vec3( H, C, Q.x );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 RGBtoHSL( vec3 rgb )
|
||||||
|
{
|
||||||
|
vec3 HCV = RGBtoHCV( rgb );
|
||||||
|
float L = HCV.z - HCV.y * 0.5;
|
||||||
|
float S = HCV.y / ( 1.0 - abs( L * 2.0 - 1.0 ) + HSL_EPSILON );
|
||||||
|
return vec3( HCV.x, S, L );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 hueToRGB( float hue )
|
||||||
|
{
|
||||||
|
float R = abs( hue * 6.0 - 3.0 ) - 1.0;
|
||||||
|
float G = 2.0 - abs( hue * 6.0 - 2.0 );
|
||||||
|
float B = 2.0 - abs( hue * 6.0 - 4.0 );
|
||||||
|
return clamp( vec3( R,G,B ),0,1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 HSLtoRGB( vec3 hsl )
|
||||||
|
{
|
||||||
|
vec3 rgb = hueToRGB( hsl.x );
|
||||||
|
float C = ( 1.0 - abs( 2.0 * hsl.z - 1.0 )) * hsl.y;
|
||||||
|
return ( rgb - 0.5 ) * C + hsl.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 toLinear( vec3 sRGB )
|
||||||
|
{
|
||||||
|
return mix( pow( (sRGB + vec3( 0.055 )) * ( 1.0 / ( 1.0 + 0.055 )),vec3( 2.4 )),sRGB * ( 1.0 / 12.92 ),lessThan( sRGB,vec3( 0.04045 )) );
|
||||||
|
}
|
||||||
|
|
||||||
|
float modPolarDegrees( float value )
|
||||||
|
{
|
||||||
|
return mod( value + 180.0, 360.0 ) - 180.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float hue360ToYB( float hue )
|
||||||
|
{
|
||||||
|
return modPolarDegrees( hue - 240.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
float ybToHue360( float yb )
|
||||||
|
{
|
||||||
|
return mod( yb + 240.0, 360.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
float changeHue360( float hue, float change )
|
||||||
|
{
|
||||||
|
float yb = hue360ToYB( hue );
|
||||||
|
|
||||||
|
float absYB = abs( yb );
|
||||||
|
|
||||||
|
absYB = clamp( absYB + change, 0.0, 180.0 );
|
||||||
|
|
||||||
|
float realYB = sign( yb ) * absYB;
|
||||||
|
|
||||||
|
return ybToHue360( realYB );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 shiftHSL360( vec3 hslWithH360, vec3 offset, float blendRadius )
|
||||||
|
{
|
||||||
|
float distanceToYellow = min( 1.0, abs( hslWithH360.x - 60.0 ) / blendRadius );
|
||||||
|
float distanceToBlue = min( 1.0, abs( hslWithH360.x - 240.0 ) / blendRadius );
|
||||||
|
|
||||||
|
hslWithH360.x = changeHue360( hslWithH360.x, offset.x );
|
||||||
|
hslWithH360.y = clamp( hslWithH360.y + offset.y, 0, 1 );
|
||||||
|
hslWithH360.z = clamp( hslWithH360.z + offset.z, 0, 1 );
|
||||||
|
|
||||||
|
return hslWithH360;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 shiftHSL( vec3 hsl, vec3 offset, float blendRadius )
|
||||||
|
{
|
||||||
|
hsl.x *= 360.0;
|
||||||
|
offset.x *= 360.0;
|
||||||
|
hsl = shiftHSL360( hsl, offset, blendRadius );
|
||||||
|
hsl.x /= 360.0;
|
||||||
|
|
||||||
|
return hsl;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 RGBtoHSV( vec3 c )
|
||||||
|
{
|
||||||
|
vec4 K = vec4( 0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0 );
|
||||||
|
vec4 p = mix( vec4( c.bg, K.wz ), vec4( c.gb, K.xy ), step( c.b, c.g ));
|
||||||
|
vec4 q = mix( vec4( p.xyw, c.r ), vec4( c.r, p.yzx ), step( p.x, c.r ));
|
||||||
|
|
||||||
|
float d = q.x - min( q.w, q.y );
|
||||||
|
float e = 1.0e-10;
|
||||||
|
|
||||||
|
return vec3( abs( q.z + ( q.w - q.y ) / ( 6.0 * d + e )), d / ( q.x + e ), q.x );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 HSVtoRGB( vec3 c )
|
||||||
|
{
|
||||||
|
vec4 K = vec4( 1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0 );
|
||||||
|
vec3 p = abs( fract( c.xxx + K.xyz ) * 6.0 - K.www );
|
||||||
|
|
||||||
|
return c.z * mix( K.xxx, clamp( p - K.xxx, 0.0, 1.0 ), c.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 mix3( vec3 a, vec3 b, vec3 c, float t )
|
||||||
|
{
|
||||||
|
float weightA = mapClamped( t, 0, 0.5, 1, 0 );
|
||||||
|
float weightB = triangle( t );
|
||||||
|
float weightC = mapClamped( t, 0.5, 1, 0, 1 );
|
||||||
|
|
||||||
|
return a * weightA + b * weightB + c * weightC;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 mix3_v4( vec4 a, vec4 b, vec4 c, float t )
|
||||||
|
{
|
||||||
|
float weightA = mapClamped( t, 0, 0.5, 1, 0 );
|
||||||
|
float weightB = triangle( t );
|
||||||
|
float weightC = mapClamped( t, 0.5, 1, 0, 1 );
|
||||||
|
|
||||||
|
return a * weightA + b * weightB + c * weightC;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 scaleRGB( vec4 rgba, float scale )
|
||||||
|
{
|
||||||
|
return vec4( rgba.rgb * scale, rgba.a );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 fade( vec4 rgba, float fade )
|
||||||
|
{
|
||||||
|
return vec4( rgba.rgb, rgba.a * fade );
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
float clamp01( float value )
|
||||||
|
{
|
||||||
|
return clamp( value, 0.0, 1.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 clamp01_v4( vec4 value )
|
||||||
|
{
|
||||||
|
value.r = clamp01( value.r );
|
||||||
|
value.g = clamp01( value.g );
|
||||||
|
value.b = clamp01( value.b );
|
||||||
|
value.a = clamp01( value.a );
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float normalizeToRange( float value, float min, float max )
|
||||||
|
{
|
||||||
|
return ( value - min ) / ( max - min );
|
||||||
|
}
|
||||||
|
|
||||||
|
float normalizeToRange01( float value, float min, float max )
|
||||||
|
{
|
||||||
|
return clamp01( normalizeToRange( value, min, max ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
float map( float value, float inMin, float inMax, float outMin, float outMax )
|
||||||
|
{
|
||||||
|
return mix( outMin, outMax, normalizeToRange( value, inMin, inMax ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
float mapClamped( float value, float inMin, float inMax, float outMin, float outMax )
|
||||||
|
{
|
||||||
|
return mix( outMin, outMax, normalizeToRange01( value, inMin, inMax ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
float triangle( float value )
|
||||||
|
{
|
||||||
|
float inverted = 1.0 - value;
|
||||||
|
|
||||||
|
return min( value, inverted ) * 2.0;
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
#include "res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Library/Colors.gdshaderinc"
|
||||||
|
#include "res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Library/Math.gdshaderinc"
|
||||||
|
|
||||||
|
uniform sampler2D screenTexture:
|
||||||
|
hint_screen_texture,
|
||||||
|
repeat_disable,
|
||||||
|
filter_linear_mipmap;
|
||||||
|
|
||||||
|
uniform sampler2D rChannelCurve;
|
||||||
|
uniform float rChannelEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform sampler2D gChannelCurve;
|
||||||
|
uniform float gChannelEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform sampler2D bChannelCurve;
|
||||||
|
uniform float bChannelEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform sampler2D hChannelCurve;
|
||||||
|
uniform float hChannelEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform sampler2D sChannelCurve;
|
||||||
|
uniform float sChannelEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform sampler2D lChannelCurve;
|
||||||
|
uniform float lChannelEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform sampler2D lumaTemparatureShiftCurve;
|
||||||
|
uniform float warmenShift: hint_range(-1,1) = 0;
|
||||||
|
uniform float saturationShift: hint_range(-1,1) = 0;
|
||||||
|
uniform float luminanceShift: hint_range(-1,1) = 0;
|
||||||
|
uniform float lumaTemparatureShiftEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
|
||||||
|
uniform sampler2D lumaSaturationCurve;
|
||||||
|
uniform float lumaSaturationEffectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
uniform float effectStrength : hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
void fragment()
|
||||||
|
{
|
||||||
|
vec4 rgb = texture( screenTexture, UV );
|
||||||
|
vec4 originalRGB = rgb;
|
||||||
|
|
||||||
|
rgb.r = mix( rgb.r, texture( rChannelCurve, vec2( rgb.r, 0.0 ) ).x, rChannelEffectStrength );
|
||||||
|
rgb.g = mix( rgb.g, texture( gChannelCurve, vec2( rgb.g, 0.0 ) ).x, gChannelEffectStrength );
|
||||||
|
rgb.b = mix( rgb.b, texture( bChannelCurve, vec2( rgb.b, 0.0 ) ).x, bChannelEffectStrength );
|
||||||
|
|
||||||
|
rgb = clamp01_v4( rgb );
|
||||||
|
|
||||||
|
vec3 hsl = RGBtoHSL( rgb.rgb );
|
||||||
|
|
||||||
|
hsl.r = mix( hsl.r, hsl.r + texture( hChannelCurve, vec2( hsl.r, 0.0 ) ).x - 0.5, hChannelEffectStrength );
|
||||||
|
hsl.r = mod( hsl.r, 1.0 );
|
||||||
|
hsl.g = mix( hsl.g, texture( sChannelCurve, vec2( hsl.g, 0.0 ) ).x, sChannelEffectStrength );
|
||||||
|
hsl.b = mix( hsl.b, texture( lChannelCurve, vec2( hsl.b, 0.0 ) ).x, lChannelEffectStrength );
|
||||||
|
|
||||||
|
vec3 lumaTemparatureShift = vec3( warmenShift, -saturationShift, luminanceShift );
|
||||||
|
float lumaTemparatureShiftAmount = texture( lumaTemparatureShiftCurve, vec2( hsl.b, 0 ) ).x * 2.0 - 1.0;
|
||||||
|
lumaTemparatureShiftAmount = lumaTemparatureShiftAmount * lumaTemparatureShiftEffectStrength;
|
||||||
|
hsl = shiftHSL( hsl, lumaTemparatureShift * lumaTemparatureShiftAmount, 10 );
|
||||||
|
|
||||||
|
hsl.g = mix( hsl.g, hsl.g + texture( lumaSaturationCurve, vec2( hsl.b, 0.0 ) ).x - 0.5, hChannelEffectStrength );
|
||||||
|
rgb = vec4( HSLtoRGB( hsl ), rgb.a );
|
||||||
|
|
||||||
|
COLOR = mix( originalRGB, rgb, effectStrength );
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
#include "res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Library/Colors.gdshaderinc"
|
||||||
|
#include "res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Library/Math.gdshaderinc"
|
||||||
|
|
||||||
|
uniform sampler2D screenTexture:
|
||||||
|
hint_screen_texture,
|
||||||
|
repeat_disable,
|
||||||
|
filter_linear_mipmap;
|
||||||
|
|
||||||
|
|
||||||
|
uniform sampler2D overlay;
|
||||||
|
uniform float effectStrength: hint_range(0,1) = 1;
|
||||||
|
|
||||||
|
|
||||||
|
void fragment()
|
||||||
|
{
|
||||||
|
vec4 rgb = texture( screenTexture, UV );
|
||||||
|
vec4 originalRGB = rgb;
|
||||||
|
|
||||||
|
vec4 overlayColor = texture( overlay, UV );
|
||||||
|
vec4 additiveOverlayColor = overlayColor + rgb;
|
||||||
|
vec4 multipliedOverlayColor = overlayColor * rgb;
|
||||||
|
|
||||||
|
float overlayBlendType = length( overlayColor.rgb );
|
||||||
|
vec4 mixed = mix3_v4( multipliedOverlayColor, rgb, additiveOverlayColor, overlayBlendType );
|
||||||
|
|
||||||
|
rgb = mixed;
|
||||||
|
COLOR = mix( originalRGB, mixed, effectStrength );
|
||||||
|
}
|
|
@ -42,6 +42,30 @@ namespace Rokojori
|
||||||
AddMatcher( type, new Regex( regex ), mode, nextMode );
|
AddMatcher( type, new Regex( regex ), mode, nextMode );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LexerMatcher GetMatcher( string source, string mode ="" )
|
||||||
|
{
|
||||||
|
var matchers = _modes[ mode ];
|
||||||
|
var offset = 0;
|
||||||
|
|
||||||
|
for ( var i = 0; i < matchers.Count; i++ )
|
||||||
|
{
|
||||||
|
var matcher = matchers[ i ];
|
||||||
|
var matchLength = matcher.MatchLength( source, offset );
|
||||||
|
|
||||||
|
if ( matchLength > 0 )
|
||||||
|
{
|
||||||
|
return matcher;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GrabMatches( List<LexerEvent> events, string source )
|
||||||
|
{
|
||||||
|
events.ForEach( ev => { ev.GrabMatch( source ); } );
|
||||||
|
}
|
||||||
|
|
||||||
public void Lex( string source, System.Action<LexerEvent> callback, int offset = 0, string mode = "" )
|
public void Lex( string source, System.Action<LexerEvent> callback, int offset = 0, string mode = "" )
|
||||||
{
|
{
|
||||||
var ERROR_FLAG = -1;
|
var ERROR_FLAG = -1;
|
||||||
|
@ -49,13 +73,18 @@ namespace Rokojori
|
||||||
|
|
||||||
var lexerEvent = new LexerEvent( "", 0, -2 );
|
var lexerEvent = new LexerEvent( "", 0, -2 );
|
||||||
|
|
||||||
while ( offset < source.Length )
|
var numTries = 0;
|
||||||
|
var maxTries = 1000;
|
||||||
|
|
||||||
|
while ( offset < source.Length && numTries < maxTries)
|
||||||
{
|
{
|
||||||
|
numTries ++;
|
||||||
|
|
||||||
if ( ! _modes.ContainsKey( mode ) )
|
if ( ! _modes.ContainsKey( mode ) )
|
||||||
{
|
{
|
||||||
var errorMessage = "@Lexer-Error. Mode not found: '" + mode + "'";
|
var errorMessage = "@Lexer-Error. Mode not found: '" + mode + "'";
|
||||||
RJLog.Log( errorMessage, "@", offset );
|
RJLog.Log( errorMessage, "@", offset );
|
||||||
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
|
lexerEvent.Set( errorMessage, offset, ERROR_FLAG );
|
||||||
_hasError = true;
|
_hasError = true;
|
||||||
callback( lexerEvent );
|
callback( lexerEvent );
|
||||||
|
|
||||||
|
@ -72,7 +101,7 @@ namespace Rokojori
|
||||||
|
|
||||||
if ( matchLength > 0 )
|
if ( matchLength > 0 )
|
||||||
{
|
{
|
||||||
lexerEvent.set( matcher.type, offset, matchLength );
|
lexerEvent.Set( matcher.type, offset, matchLength );
|
||||||
//Logs.Log(matcher.type, ">>", "'"+source.Substring( offset, matchLength )+"'", "@", offset, matchLength );
|
//Logs.Log(matcher.type, ">>", "'"+source.Substring( offset, matchLength )+"'", "@", offset, matchLength );
|
||||||
callback( lexerEvent );
|
callback( lexerEvent );
|
||||||
|
|
||||||
|
@ -94,7 +123,7 @@ namespace Rokojori
|
||||||
{
|
{
|
||||||
var errorMessage = "@Lexer-Error. No match: '" + mode + "'";
|
var errorMessage = "@Lexer-Error. No match: '" + mode + "'";
|
||||||
RJLog.Log(errorMessage, "@", offset );
|
RJLog.Log(errorMessage, "@", offset );
|
||||||
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
|
lexerEvent.Set( errorMessage, offset, ERROR_FLAG );
|
||||||
_hasError = true;
|
_hasError = true;
|
||||||
callback( lexerEvent );
|
callback( lexerEvent );
|
||||||
|
|
||||||
|
@ -103,7 +132,13 @@ namespace Rokojori
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lexerEvent.set( mode, offset, DONE_FLAG );
|
if ( numTries >= maxTries )
|
||||||
|
{
|
||||||
|
lexerEvent.Set( mode, offset, ERROR_FLAG );
|
||||||
|
callback( lexerEvent );
|
||||||
|
}
|
||||||
|
|
||||||
|
lexerEvent.Set( mode, offset, DONE_FLAG );
|
||||||
callback( lexerEvent );
|
callback( lexerEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
|
||||||
namespace Rokojori
|
namespace Rokojori
|
||||||
|
@ -17,7 +17,7 @@ namespace Rokojori
|
||||||
|
|
||||||
public LexerEvent( string type, int offset, int length )
|
public LexerEvent( string type, int offset, int length )
|
||||||
{
|
{
|
||||||
set( type, offset, length );
|
Set( type, offset, length );
|
||||||
}
|
}
|
||||||
|
|
||||||
public string type { get { return _type; } }
|
public string type { get { return _type; } }
|
||||||
|
@ -34,7 +34,7 @@ namespace Rokojori
|
||||||
get { return this.length == -2; }
|
get { return this.length == -2; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set( string type, int offset, int length )
|
public void Set( string type, int offset, int length )
|
||||||
{
|
{
|
||||||
this._type = type;
|
this._type = type;
|
||||||
this._offset = offset;
|
this._offset = offset;
|
||||||
|
@ -65,5 +65,132 @@ namespace Rokojori
|
||||||
_match = source.Substring( offset, length );
|
_match = source.Substring( offset, length );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Is( string type, string match = null )
|
||||||
|
{
|
||||||
|
var typeCorrect = type == null || type == this.type;
|
||||||
|
|
||||||
|
if ( ! typeCorrect )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return match == null || match == this.match;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Is( LexerMatcher matcher, string match = null )
|
||||||
|
{
|
||||||
|
return Is( matcher == null ? null : matcher.type, match );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum FindResultType
|
||||||
|
{
|
||||||
|
Found,
|
||||||
|
KeepSearching,
|
||||||
|
NotFound,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FindResult
|
||||||
|
{
|
||||||
|
public FindResultType type = FindResultType.NotFound;
|
||||||
|
public int index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetMatchFromRange( List<LexerEvent> tokens, int offset, int length )
|
||||||
|
{
|
||||||
|
var match = new StringBuilder();
|
||||||
|
|
||||||
|
for ( int i = 0; i < length; i++ )
|
||||||
|
{
|
||||||
|
match.Append( tokens[ offset + i ].match );
|
||||||
|
}
|
||||||
|
|
||||||
|
return match.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static FindResult Find( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,FindResultType> evaluator )
|
||||||
|
{
|
||||||
|
var result = new FindResult();
|
||||||
|
|
||||||
|
for ( int i = offset; i < tokens.Count; i++ )
|
||||||
|
{
|
||||||
|
var tokenResult = evaluator( tokens[ i ] );
|
||||||
|
|
||||||
|
if ( tokenResult == FindResultType.Error ||
|
||||||
|
tokenResult == FindResultType.Found
|
||||||
|
)
|
||||||
|
{
|
||||||
|
result.type = tokenResult;
|
||||||
|
result.index = i;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
if ( bracketIndex == -1 )
|
||||||
|
{
|
||||||
|
var result = new FindResult();
|
||||||
|
result.type = FindResultType.NotFound;
|
||||||
|
result.index = 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FindResult FindCloser( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,int> counter )
|
||||||
|
{
|
||||||
|
var result = new FindResult();
|
||||||
|
var currentValue = 0;
|
||||||
|
|
||||||
|
for ( int i = offset; i < tokens.Count; i++ )
|
||||||
|
{
|
||||||
|
var countResult = counter( tokens[ i ] );
|
||||||
|
|
||||||
|
currentValue += countResult;
|
||||||
|
|
||||||
|
if ( currentValue == 0 )
|
||||||
|
{
|
||||||
|
result.type = FindResultType.Found;
|
||||||
|
result.index = i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,24 +23,24 @@ namespace Rokojori
|
||||||
public CSharpLexer()
|
public CSharpLexer()
|
||||||
{
|
{
|
||||||
AddAllMatchers(
|
AddAllMatchers(
|
||||||
LexerMatcherLibrary.SINGLE_LINE_COMMENT_MATCHER,
|
LexerMatcherLibrary.SingleLineCommentMatcher,
|
||||||
LexerMatcherLibrary.MULTI_LINE_COMMENT_MATCHER,
|
LexerMatcherLibrary.MultiLineCommentMatcher,
|
||||||
LexerMatcherLibrary.DOUBLE_QUOTED_STRING_MATCHER,
|
LexerMatcherLibrary.DoubleQuotedStringMatcher,
|
||||||
LexerMatcherLibrary.SINGLE_QUOTED_STRING_MATCHER,
|
LexerMatcherLibrary.SingleQuotedStringMatcher,
|
||||||
LexerMatcherLibrary.C_INSTRUCTION_MATCHER,
|
LexerMatcherLibrary.CInstructionMatcher,
|
||||||
LexerMatcherLibrary.NUMBER_MATCHER,
|
LexerMatcherLibrary.NumberMatcher,
|
||||||
LexerMatcherLibrary.NULL_MATCHER,
|
LexerMatcherLibrary.NullMatcher,
|
||||||
LexerMatcherLibrary.BOOL_MATCHER,
|
LexerMatcherLibrary.BoolMatcher,
|
||||||
LexerMatcherLibrary.BREAK_MATCHER,
|
LexerMatcherLibrary.BreakMatcher,
|
||||||
LexerMatcherLibrary.WHITESPACE_MATCHER,
|
LexerMatcherLibrary.WhiteSpaceMatcher,
|
||||||
LexerMatcherLibrary.LOGIC_MATCHER,
|
LexerMatcherLibrary.LogicMatcher,
|
||||||
LexerMatcherLibrary.BRACKET_MATCHER,
|
LexerMatcherLibrary.BracketMatcher,
|
||||||
LexerMatcherLibrary.ACCESS_MODIFIER_MATCHER,
|
LexerMatcherLibrary.AccessModifierMatcher,
|
||||||
LexerMatcherLibrary.CLASS_MATCHER,
|
LexerMatcherLibrary.ClassMatcher,
|
||||||
LexerMatcherLibrary.OPERATOR_MATCHER,
|
LexerMatcherLibrary.OperatorMatcher,
|
||||||
LexerMatcherLibrary.CFUNCTION_MATCHER,
|
LexerMatcherLibrary.CFunctionMatcher,
|
||||||
LexerMatcherLibrary.CWORD_MATCHER,
|
LexerMatcherLibrary.CwordMatcher,
|
||||||
LexerMatcherLibrary.ANY_SYMBOL_MATCHER
|
LexerMatcherLibrary.AnySymbolMatcher
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,11 @@ namespace Rokojori
|
||||||
_matcher = RegexUtility.MakeSticky( new Regex( matcher ) );
|
_matcher = RegexUtility.MakeSticky( new Regex( matcher ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LexerMatcher CreateExtended( string type, string matcher, string mode = "", string nextMode = "" )
|
||||||
|
{
|
||||||
|
return new LexerMatcher( type, RegexExtensions.Extend( matcher ), mode, nextMode );
|
||||||
|
}
|
||||||
|
|
||||||
public LexerMatcher CloneWithMode( string mode, string nextMode )
|
public LexerMatcher CloneWithMode( string mode, string nextMode )
|
||||||
{
|
{
|
||||||
return new LexerMatcher( _type, _matcher, mode, nextMode );
|
return new LexerMatcher( _type, _matcher, mode, nextMode );
|
||||||
|
|
|
@ -6,93 +6,90 @@ namespace Rokojori
|
||||||
{
|
{
|
||||||
public class LexerMatcherLibrary
|
public class LexerMatcherLibrary
|
||||||
{
|
{
|
||||||
public static readonly LexerMatcher CWORD_MATCHER =
|
public static readonly LexerMatcher CwordMatcher =
|
||||||
new LexerMatcher( "CWORD", @"[a-zA-Z_]\w*" );
|
new LexerMatcher( "CWord", @"[a-zA-Z_]\w*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher CFUNCTION_MATCHER =
|
public static readonly LexerMatcher CFunctionMatcher =
|
||||||
new LexerMatcher( "CFUNCTION", @"[a-zA-Z_]\w*(?=\s*\()" );
|
new LexerMatcher( "CFunction", @"[a-zA-Z_]\w*(?=\s*\()" );
|
||||||
|
|
||||||
// public static readonly LexerMatcher LexerMatcherCLASSNAME_MATCHER =
|
public static readonly LexerMatcher JSWordMatcher =
|
||||||
// new LexerMatcher( "CLASSNAME", @"(?<=(?:(?:class|interface|struct)\s+))[a-zA-Z_]\w*" );
|
new LexerMatcher( "JSWord", @"[a-zA-Z_\$]\w*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher JSWORD_MATCHER =
|
public static readonly LexerMatcher PHPWordMatcher =
|
||||||
new LexerMatcher( "JSWORD", @"[a-zA-Z_\$]\w*" );
|
new LexerMatcher( "PHPWord", @"\$?[a-zA-Z_]\w*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher PHPWORD_MATCHER =
|
|
||||||
new LexerMatcher( "PHPWORD", @"\$?[a-zA-Z_]\w*" );
|
|
||||||
|
|
||||||
|
|
||||||
public static readonly LexerMatcher CSS_CLASS_SELECTOR_MATCHER =
|
public static readonly LexerMatcher CSS_ClassSelectorMatcher =
|
||||||
new LexerMatcher( "CSS_CLASS_SELECTOR", @"\.[a-zA-Z_](\w|\-)*" );
|
new LexerMatcher( "CSS_ClassSelector", @"\.[a-zA-Z_](\w|\-)*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher CSS_ID_SELECTOR_MATCHER =
|
public static readonly LexerMatcher CSS_IDSelectorMatcher =
|
||||||
new LexerMatcher( "CSS_ID_SELECTOR", @"\#[a-zA-Z_](\w|\-)*" );
|
new LexerMatcher( "CSS_IDSelector", @"\#[a-zA-Z_](\w|\-)*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher CSS_WORD_MATCHER =
|
public static readonly LexerMatcher CSS_WordMatcher =
|
||||||
new LexerMatcher( "CSS_WORD", @"[a-zA-Z_](\w|\-)*" );
|
new LexerMatcher( "CSS_Word", @"[a-zA-Z_](\w|\-)*" );
|
||||||
|
|
||||||
|
|
||||||
public static readonly LexerMatcher HTML_CUSTOM_ELEMENT_MATCHER =
|
public static readonly LexerMatcher HTML_CustomElementMatcher =
|
||||||
new LexerMatcher( "HTML_CUSTOM_ELEMENT", @"[a-zA-Z]\w*\-(\w|\-)+" );
|
new LexerMatcher( "HTML_CustomElement", @"[a-zA-Z]\w*\-(\w|\-)+" );
|
||||||
|
|
||||||
public static readonly LexerMatcher DOUBLE_QUOTED_STRING_MATCHER =
|
public static readonly LexerMatcher DoubleQuotedStringMatcher =
|
||||||
new LexerMatcher( "DOUBLE_QUOTED_STRING", "\"(?:[^\"\\\\]|\\\\.)*\"" );
|
new LexerMatcher( "DoubleQuotedString", "\"(?:[^\"\\\\]|\\\\.)*\"" );
|
||||||
|
|
||||||
public static readonly LexerMatcher SINGLE_QUOTED_STRING_MATCHER =
|
public static readonly LexerMatcher SingleQuotedStringMatcher =
|
||||||
new LexerMatcher( "SINGLE_QUOTED_STRING", @"'(?:[^'\\]|\\.)*'" );
|
new LexerMatcher( "SingleQuotedString", @"'(?:[^'\\]|\\.)*'" );
|
||||||
|
|
||||||
public static readonly LexerMatcher NUMBER_MATCHER =
|
public static readonly LexerMatcher NumberMatcher =
|
||||||
new LexerMatcher( "NUMBER", @"(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?" );
|
new LexerMatcher( "Number", @"(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?" );
|
||||||
|
|
||||||
public static readonly LexerMatcher WHITESPACE_MATCHER =
|
public static readonly LexerMatcher WhiteSpaceMatcher =
|
||||||
new LexerMatcher( "WHITESPACE", @"\s+" );
|
new LexerMatcher( "WhiteSpace", @"\s+" );
|
||||||
|
|
||||||
public static readonly LexerMatcher BREAK_MATCHER =
|
public static readonly LexerMatcher BreakMatcher =
|
||||||
new LexerMatcher( "BREAK", @"(\r\n|\r|\n)" );
|
new LexerMatcher( "Break", @"(\r\n|\r|\n)" );
|
||||||
|
|
||||||
|
|
||||||
public static readonly LexerMatcher NULL_MATCHER =
|
public static readonly LexerMatcher NullMatcher =
|
||||||
new LexerMatcher( "NULL", "null" );
|
new LexerMatcher( "Null", "null" );
|
||||||
|
|
||||||
public static readonly LexerMatcher BOOL_MATCHER =
|
public static readonly LexerMatcher BoolMatcher =
|
||||||
new LexerMatcher( "BOOL", "true|false" );
|
new LexerMatcher( "Bool", "true|false" );
|
||||||
|
|
||||||
public static readonly LexerMatcher LOGIC_MATCHER =
|
public static readonly LexerMatcher LogicMatcher =
|
||||||
new LexerMatcher( "LOGIC", "if|else|switch|do|while|for|break|continue|return" );
|
new LexerMatcher( "Logic", "if|else|switch|do|while|for|break|continue|return" );
|
||||||
|
|
||||||
public static readonly LexerMatcher OPERATOR_MATCHER =
|
public static readonly LexerMatcher OperatorMatcher =
|
||||||
new LexerMatcher( "OPERATOR", "(?:\\=\\=)|(?:\\+\\+)|(?:\\-\\-)|\\+|\\-|\\*|\\/|\\^|\\||\\~|\\&|\\%|\\<|\\>|\\=|\\!|\\.|\\:|\\,|\\;" );
|
new LexerMatcher( "Operator", "(?:\\=\\=)|(?:\\+\\+)|(?:\\-\\-)|\\+|\\-|\\*|\\/|\\^|\\||\\~|\\&|\\%|\\<|\\>|\\=|\\!|\\.|\\:|\\,|\\;" );
|
||||||
|
|
||||||
public static readonly LexerMatcher BRACKET_MATCHER =
|
public static readonly LexerMatcher BracketMatcher =
|
||||||
new LexerMatcher( "BRACKET", @"\(|\)|\[|\]|\{|\}" );
|
new LexerMatcher( "Bracket", @"\(|\)|\[|\]|\{|\}" );
|
||||||
|
|
||||||
public static readonly LexerMatcher BLOCKSTART_MATCHER =
|
public static readonly LexerMatcher BlockStartMatcher =
|
||||||
new LexerMatcher( "BLOCKSTART", @"\{" );
|
new LexerMatcher( "BlockStart", @"\{" );
|
||||||
|
|
||||||
public static readonly LexerMatcher BLOCKEND_MATCHER =
|
public static readonly LexerMatcher BlockEndMatcher =
|
||||||
new LexerMatcher( "BLOCKEND", @"\}" );
|
new LexerMatcher( "BlockStart", @"\}" );
|
||||||
|
|
||||||
public static readonly LexerMatcher CLASS_MATCHER =
|
public static readonly LexerMatcher ClassMatcher =
|
||||||
new LexerMatcher( "CLASS", @"\bclass\b" );
|
new LexerMatcher( "Class", @"\bclass\b" );
|
||||||
|
|
||||||
public static readonly LexerMatcher ACCESS_MODIFIER_MATCHER =
|
public static readonly LexerMatcher AccessModifierMatcher =
|
||||||
new LexerMatcher( "ACCESS_MODIFIER", @"\b(?:public|protected|private)\b" );
|
new LexerMatcher( "AccessModifier", @"\b(?:public|protected|private)\b" );
|
||||||
|
|
||||||
|
|
||||||
public static readonly LexerMatcher SINGLE_LINE_COMMENT_MATCHER =
|
public static readonly LexerMatcher SingleLineCommentMatcher =
|
||||||
new LexerMatcher( "SINGLE_LINE_COMMENT", @"//.*" );
|
new LexerMatcher( "SingleLineComment", @"//.*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher C_INSTRUCTION_MATCHER =
|
public static readonly LexerMatcher CInstructionMatcher =
|
||||||
new LexerMatcher( "C_INSTRUCTION", @"\#.*" );
|
new LexerMatcher( "CInstruction", @"\#.*" );
|
||||||
|
|
||||||
public static readonly LexerMatcher MULTI_LINE_COMMENT_MATCHER =
|
public static readonly LexerMatcher MultiLineCommentMatcher =
|
||||||
new LexerMatcher( "MULTI_LINE_COMMENT", @"\/\*(.|(\r\n|\r|\n))*?\*\/" );
|
new LexerMatcher( "MultiLineComment", @"\/\*(.|(\r\n|\r|\n))*?\*\/" );
|
||||||
|
|
||||||
public static readonly LexerMatcher ANY_SYMBOL_MATCHER =
|
public static readonly LexerMatcher AnySymbolMatcher =
|
||||||
new LexerMatcher( "ANY_SYMBOL", @"." );
|
new LexerMatcher( "AnySymbol", @"." );
|
||||||
|
|
||||||
public static readonly LexerMatcher HASH_TAG =
|
public static readonly LexerMatcher HashTag =
|
||||||
new LexerMatcher( "HASH_TAG", @"\#(\w|-|\d)+" );
|
new LexerMatcher( "HashTag", @"\#(\w|-|\d)+" );
|
||||||
|
|
||||||
public static readonly LexerMatcher URL =
|
public static readonly LexerMatcher URL =
|
||||||
new LexerMatcher( "URL", @"https?\:\/\/(\w|\.|\-|\?|\=|\+|\/)+" );
|
new LexerMatcher( "URL", @"https?\:\/\/(\w|\.|\-|\?|\=|\+|\/)+" );
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Globalization;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
public class RegexExtensions
|
||||||
|
{
|
||||||
|
public static string Extend( string source )
|
||||||
|
{
|
||||||
|
//RJLog.Log( "Original:", source );
|
||||||
|
|
||||||
|
// l: Linebreak
|
||||||
|
source = RegexUtility.Replace( source, @"\\l", @"(?:\r\n|\r|\n)" );
|
||||||
|
|
||||||
|
// v: Variable
|
||||||
|
source = RegexUtility.Replace( source, @"\\l", @"(?:\w|_)+" );
|
||||||
|
|
||||||
|
//RJLog.Log( "Extended:", source );
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -384,6 +384,14 @@ namespace Rokojori
|
||||||
return Split( source, new Regex( regex ) );
|
return Split( source, new Regex( regex ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<string> SplitLines( string source )
|
||||||
|
{
|
||||||
|
var array = Regex.Split( source, "\r\n|\r|\n" );
|
||||||
|
var list = new List<string>();
|
||||||
|
list.AddRange( array );
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
public static string Remove( string source, string regex )
|
public static string Remove( string source, string regex )
|
||||||
{
|
{
|
||||||
return Replace( source, regex, "" );
|
return Replace( source, regex, "" );
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Rokojori
|
||||||
{
|
{
|
||||||
[Export]
|
[Export]
|
||||||
public Vector3 target;
|
public Vector3 target;
|
||||||
|
|
||||||
[Export]
|
[Export]
|
||||||
public float yaw = 0;
|
public float yaw = 0;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Rokojori
|
||||||
|
{
|
||||||
|
[Tool]
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class StrategyTopDownCamera:VirtualCamera3D
|
||||||
|
{
|
||||||
|
[Export]
|
||||||
|
public Vector3 target;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float yaw = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float pitch = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float distance = 10;
|
||||||
|
float smoothDistance = 10;
|
||||||
|
|
||||||
|
[ExportCategory("Move")]
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor moveUpButton;
|
||||||
|
[Export]
|
||||||
|
public RJSensor moveDownButton;
|
||||||
|
[Export]
|
||||||
|
public RJSensor moveRightButton;
|
||||||
|
[Export]
|
||||||
|
public RJSensor moveLeftButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float buttonMoveSpeed = 1f;
|
||||||
|
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor mouseMoveButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float mouseMoveSpeed = 0.001f;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool flipHorizontal = false;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool flipVertical = false;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool moveAtBorders = true;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float moveAtBorderSpeed = 0.001f;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float borderSizePercentage = 5;
|
||||||
|
|
||||||
|
[ExportCategory("Orbit")]
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor orbitRightButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor orbitLeftButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor orbitMouseButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float mouseOrbitSpeedScale = -0.5f;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float orbitSpeed = 0.001f;
|
||||||
|
|
||||||
|
|
||||||
|
[ExportCategory("Zoom")]
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float zoomStepInPercentage = 10;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float minDistance = 0.001f;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float maxDistance = 200f;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor zoomInButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor[] zoomInModifierButtons;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor zoomOutButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public RJSensor[] zoomOutModifierButtons;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public float zoomSmoothingCoefficient = 0.1f;
|
||||||
|
Smoother smoother = new Smoother();
|
||||||
|
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
Move();
|
||||||
|
Orbit( (float) delta );
|
||||||
|
Zoom();
|
||||||
|
|
||||||
|
Apply( (float) delta );
|
||||||
|
|
||||||
|
if ( ! hasMotionDelta )
|
||||||
|
{
|
||||||
|
motionDelta.X = 0;
|
||||||
|
motionDelta.Y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMotionDelta = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasMotionDelta = false;
|
||||||
|
Vector2 motionDelta = Vector2.Zero;
|
||||||
|
|
||||||
|
float borderMoveHorizontal = 0;
|
||||||
|
float borderMoveVertical = 0;
|
||||||
|
|
||||||
|
float GetBorderSize()
|
||||||
|
{
|
||||||
|
var size = GetViewport().GetVisibleRect().Size;
|
||||||
|
|
||||||
|
var borderX = ( size.X * borderSizePercentage ) / 100f;
|
||||||
|
var borderY = ( size.Y * borderSizePercentage ) / 100f;
|
||||||
|
|
||||||
|
return Mathf.Min( borderX, borderY );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Input( InputEvent inputEvent )
|
||||||
|
{
|
||||||
|
if ( inputEvent is InputEventMouseMotion )
|
||||||
|
{
|
||||||
|
var eventMouseMotion = inputEvent as InputEventMouseMotion;
|
||||||
|
motionDelta = eventMouseMotion.ScreenRelative;
|
||||||
|
|
||||||
|
if ( moveAtBorders )
|
||||||
|
{
|
||||||
|
var position = eventMouseMotion.Position;
|
||||||
|
|
||||||
|
var screenSize = GetViewport().GetVisibleRect().Size;
|
||||||
|
var borderSize = GetBorderSize();
|
||||||
|
|
||||||
|
borderMoveHorizontal = - MathX.PolarAxis( position.X < borderSize, position.X > screenSize.X - borderSize );
|
||||||
|
borderMoveVertical = - MathX.PolarAxis( position.Y < borderSize, position.Y > screenSize.Y - borderSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMotionDelta = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Move()
|
||||||
|
{
|
||||||
|
var deltaX = 0f;
|
||||||
|
var deltaY = 0f;
|
||||||
|
|
||||||
|
if ( Sensors.IsActive( mouseMoveButton ) )
|
||||||
|
{
|
||||||
|
deltaX = motionDelta.X * mouseMoveSpeed;
|
||||||
|
deltaY = motionDelta.Y * mouseMoveSpeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deltaX = Sensors.PolarAxis( moveLeftButton, moveRightButton ) + borderMoveHorizontal * moveAtBorderSpeed;
|
||||||
|
deltaY = Sensors.PolarAxis( moveUpButton, moveDownButton ) + borderMoveVertical * moveAtBorderSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
var forward = Math3D.GetYPlaneForward( this );
|
||||||
|
var right = Math3D.GetYPlaneRight( this );
|
||||||
|
|
||||||
|
var flipH = flipHorizontal ? -1 : 1;
|
||||||
|
var flipV = flipVertical ? -1 : 1;
|
||||||
|
|
||||||
|
var xAmount = deltaX * smoothDistance * right * flipH;
|
||||||
|
var zAmount = deltaY * smoothDistance * forward * flipV;
|
||||||
|
|
||||||
|
target += ( xAmount + zAmount );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Orbit( float delta )
|
||||||
|
{
|
||||||
|
var direction = Sensors.PolarAxis( orbitLeftButton, orbitRightButton );
|
||||||
|
|
||||||
|
if ( Sensors.IsActive( orbitMouseButton ) )
|
||||||
|
{
|
||||||
|
yaw += motionDelta.X * mouseOrbitSpeedScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
yaw += direction * orbitSpeed * delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Zoom()
|
||||||
|
{
|
||||||
|
var zoomButtonAxis = Sensors.PolarAxis( zoomOutButton, zoomInButton );
|
||||||
|
|
||||||
|
distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, zoomButtonAxis );
|
||||||
|
|
||||||
|
distance = Mathf.Clamp( distance, minDistance, maxDistance );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Apply( float delta )
|
||||||
|
{
|
||||||
|
smoothDistance = smoother.SmoothWithCoefficient( smoothDistance, distance, zoomSmoothingCoefficient, delta );
|
||||||
|
GlobalRotation = new Vector3( Mathf.DegToRad( pitch ), Mathf.DegToRad( yaw ), 0 );
|
||||||
|
|
||||||
|
var forward = Math3D.GetGlobalForward( this ) * smoothDistance;
|
||||||
|
GlobalPosition = target - forward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,6 +69,7 @@ namespace Rokojori
|
||||||
var position = new Vector3();
|
var position = new Vector3();
|
||||||
var up = new Vector3();
|
var up = new Vector3();
|
||||||
var forward = new Vector3();
|
var forward = new Vector3();
|
||||||
|
var fov = 0f;
|
||||||
|
|
||||||
_cameraSlots.ForEach(
|
_cameraSlots.ForEach(
|
||||||
c =>
|
c =>
|
||||||
|
@ -100,10 +101,12 @@ namespace Rokojori
|
||||||
position += priority * c.camera.GetCameraPosition();
|
position += priority * c.camera.GetCameraPosition();
|
||||||
up += priority * vUp;
|
up += priority * vUp;
|
||||||
forward += priority * vForward;
|
forward += priority * vForward;
|
||||||
|
fov += priority * c.camera.GetCameraFOV();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
position /= sumPriority;
|
position /= sumPriority;
|
||||||
|
fov /= sumPriority;
|
||||||
|
|
||||||
if ( forward.LengthSquared() == 0 )
|
if ( forward.LengthSquared() == 0 )
|
||||||
{
|
{
|
||||||
|
@ -126,6 +129,7 @@ namespace Rokojori
|
||||||
|
|
||||||
camera.GlobalPosition = position;
|
camera.GlobalPosition = position;
|
||||||
camera.LookAt( position - forward, up );
|
camera.LookAt( position - forward, up );
|
||||||
|
camera.Fov = fov;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue