Lexing/Cameras

This commit is contained in:
Josef 2024-07-25 07:40:31 +02:00
parent e88bfc81af
commit 273f1caa35
45 changed files with 2585 additions and 93 deletions

View File

@ -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>();
}
}

View File

@ -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 ) )
{}
}
}

View File

@ -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" );
}
}

View File

@ -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" );
}
}

View File

@ -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" );
}
}

View File

@ -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 );
}
}
}
}
}

View File

@ -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 ) );
}
}
}

View File

@ -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;
}
}
}

View File

@ -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" );
}
}

View File

@ -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;
}
}
}

View File

@ -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.");
}
}
}
}

View File

@ -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
);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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 );
}
);
}
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 ) );
}
}
}
}

View File

@ -1,6 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System;
namespace Rokojori
{
@ -220,7 +220,7 @@ namespace Rokojori
if ( its > max )
{
throw new System.Exception();
throw new Exception();
}
node = LastChild( node );
@ -240,7 +240,76 @@ namespace Rokojori
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 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 it = node;
@ -280,9 +349,9 @@ namespace Rokojori
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 ) )
{
@ -293,9 +362,9 @@ namespace Rokojori
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 ) )
{
@ -306,9 +375,9 @@ namespace Rokojori
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 ) );
};

View File

@ -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;
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

46
Runtime/Html/HtmlNode.cs Normal file
View File

@ -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;
}
}
}

View File

@ -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, "&", "&amp;" );
rawText = RegexUtility.Replace( rawText, "<", "&lt;" );
rawText = RegexUtility.Replace( rawText, ">", "&gt;" );
rawText = RegexUtility.Replace( rawText, "\'", "&apos;" );
rawText = RegexUtility.Replace( rawText, "\"", "&quot;" );
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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -35,6 +35,24 @@ namespace Rokojori
{
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();
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}

View File

@ -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;
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -42,6 +42,30 @@ namespace Rokojori
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 = "" )
{
var ERROR_FLAG = -1;
@ -49,13 +73,18 @@ namespace Rokojori
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 ) )
{
var errorMessage = "@Lexer-Error. Mode not found: '" + mode + "'";
RJLog.Log( errorMessage, "@", offset );
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
lexerEvent.Set( errorMessage, offset, ERROR_FLAG );
_hasError = true;
callback( lexerEvent );
@ -72,7 +101,7 @@ namespace Rokojori
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 );
callback( lexerEvent );
@ -94,7 +123,7 @@ namespace Rokojori
{
var errorMessage = "@Lexer-Error. No match: '" + mode + "'";
RJLog.Log(errorMessage, "@", offset );
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
lexerEvent.Set( errorMessage, offset, ERROR_FLAG );
_hasError = true;
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 );
}

View File

@ -1,7 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
namespace Rokojori
@ -17,7 +17,7 @@ namespace Rokojori
public LexerEvent( string type, int offset, int length )
{
set( type, offset, length );
Set( type, offset, length );
}
public string type { get { return _type; } }
@ -34,7 +34,7 @@ namespace Rokojori
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._offset = offset;
@ -65,5 +65,132 @@ namespace Rokojori
_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;
}
}
}

View File

@ -23,24 +23,24 @@ namespace Rokojori
public CSharpLexer()
{
AddAllMatchers(
LexerMatcherLibrary.SINGLE_LINE_COMMENT_MATCHER,
LexerMatcherLibrary.MULTI_LINE_COMMENT_MATCHER,
LexerMatcherLibrary.DOUBLE_QUOTED_STRING_MATCHER,
LexerMatcherLibrary.SINGLE_QUOTED_STRING_MATCHER,
LexerMatcherLibrary.C_INSTRUCTION_MATCHER,
LexerMatcherLibrary.NUMBER_MATCHER,
LexerMatcherLibrary.NULL_MATCHER,
LexerMatcherLibrary.BOOL_MATCHER,
LexerMatcherLibrary.BREAK_MATCHER,
LexerMatcherLibrary.WHITESPACE_MATCHER,
LexerMatcherLibrary.LOGIC_MATCHER,
LexerMatcherLibrary.BRACKET_MATCHER,
LexerMatcherLibrary.ACCESS_MODIFIER_MATCHER,
LexerMatcherLibrary.CLASS_MATCHER,
LexerMatcherLibrary.OPERATOR_MATCHER,
LexerMatcherLibrary.CFUNCTION_MATCHER,
LexerMatcherLibrary.CWORD_MATCHER,
LexerMatcherLibrary.ANY_SYMBOL_MATCHER
LexerMatcherLibrary.SingleLineCommentMatcher,
LexerMatcherLibrary.MultiLineCommentMatcher,
LexerMatcherLibrary.DoubleQuotedStringMatcher,
LexerMatcherLibrary.SingleQuotedStringMatcher,
LexerMatcherLibrary.CInstructionMatcher,
LexerMatcherLibrary.NumberMatcher,
LexerMatcherLibrary.NullMatcher,
LexerMatcherLibrary.BoolMatcher,
LexerMatcherLibrary.BreakMatcher,
LexerMatcherLibrary.WhiteSpaceMatcher,
LexerMatcherLibrary.LogicMatcher,
LexerMatcherLibrary.BracketMatcher,
LexerMatcherLibrary.AccessModifierMatcher,
LexerMatcherLibrary.ClassMatcher,
LexerMatcherLibrary.OperatorMatcher,
LexerMatcherLibrary.CFunctionMatcher,
LexerMatcherLibrary.CwordMatcher,
LexerMatcherLibrary.AnySymbolMatcher
);
}
}

View File

@ -42,6 +42,11 @@ namespace Rokojori
_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 )
{
return new LexerMatcher( _type, _matcher, mode, nextMode );

View File

@ -6,93 +6,90 @@ namespace Rokojori
{
public class LexerMatcherLibrary
{
public static readonly LexerMatcher CWORD_MATCHER =
new LexerMatcher( "CWORD", @"[a-zA-Z_]\w*" );
public static readonly LexerMatcher CwordMatcher =
new LexerMatcher( "CWord", @"[a-zA-Z_]\w*" );
public static readonly LexerMatcher CFUNCTION_MATCHER =
new LexerMatcher( "CFUNCTION", @"[a-zA-Z_]\w*(?=\s*\()" );
public static readonly LexerMatcher CFunctionMatcher =
new LexerMatcher( "CFunction", @"[a-zA-Z_]\w*(?=\s*\()" );
// public static readonly LexerMatcher LexerMatcherCLASSNAME_MATCHER =
// new LexerMatcher( "CLASSNAME", @"(?<=(?:(?:class|interface|struct)\s+))[a-zA-Z_]\w*" );
public static readonly LexerMatcher JSWordMatcher =
new LexerMatcher( "JSWord", @"[a-zA-Z_\$]\w*" );
public static readonly LexerMatcher JSWORD_MATCHER =
new LexerMatcher( "JSWORD", @"[a-zA-Z_\$]\w*" );
public static readonly LexerMatcher PHPWORD_MATCHER =
new LexerMatcher( "PHPWORD", @"\$?[a-zA-Z_]\w*" );
public static readonly LexerMatcher PHPWordMatcher =
new LexerMatcher( "PHPWord", @"\$?[a-zA-Z_]\w*" );
public static readonly LexerMatcher CSS_CLASS_SELECTOR_MATCHER =
new LexerMatcher( "CSS_CLASS_SELECTOR", @"\.[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_ClassSelectorMatcher =
new LexerMatcher( "CSS_ClassSelector", @"\.[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_ID_SELECTOR_MATCHER =
new LexerMatcher( "CSS_ID_SELECTOR", @"\#[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_IDSelectorMatcher =
new LexerMatcher( "CSS_IDSelector", @"\#[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_WORD_MATCHER =
new LexerMatcher( "CSS_WORD", @"[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher CSS_WordMatcher =
new LexerMatcher( "CSS_Word", @"[a-zA-Z_](\w|\-)*" );
public static readonly LexerMatcher HTML_CUSTOM_ELEMENT_MATCHER =
new LexerMatcher( "HTML_CUSTOM_ELEMENT", @"[a-zA-Z]\w*\-(\w|\-)+" );
public static readonly LexerMatcher HTML_CustomElementMatcher =
new LexerMatcher( "HTML_CustomElement", @"[a-zA-Z]\w*\-(\w|\-)+" );
public static readonly LexerMatcher DOUBLE_QUOTED_STRING_MATCHER =
new LexerMatcher( "DOUBLE_QUOTED_STRING", "\"(?:[^\"\\\\]|\\\\.)*\"" );
public static readonly LexerMatcher DoubleQuotedStringMatcher =
new LexerMatcher( "DoubleQuotedString", "\"(?:[^\"\\\\]|\\\\.)*\"" );
public static readonly LexerMatcher SINGLE_QUOTED_STRING_MATCHER =
new LexerMatcher( "SINGLE_QUOTED_STRING", @"'(?:[^'\\]|\\.)*'" );
public static readonly LexerMatcher SingleQuotedStringMatcher =
new LexerMatcher( "SingleQuotedString", @"'(?:[^'\\]|\\.)*'" );
public static readonly LexerMatcher NUMBER_MATCHER =
new LexerMatcher( "NUMBER", @"(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?" );
public static readonly LexerMatcher NumberMatcher =
new LexerMatcher( "Number", @"(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?" );
public static readonly LexerMatcher WHITESPACE_MATCHER =
new LexerMatcher( "WHITESPACE", @"\s+" );
public static readonly LexerMatcher WhiteSpaceMatcher =
new LexerMatcher( "WhiteSpace", @"\s+" );
public static readonly LexerMatcher BREAK_MATCHER =
new LexerMatcher( "BREAK", @"(\r\n|\r|\n)" );
public static readonly LexerMatcher BreakMatcher =
new LexerMatcher( "Break", @"(\r\n|\r|\n)" );
public static readonly LexerMatcher NULL_MATCHER =
new LexerMatcher( "NULL", "null" );
public static readonly LexerMatcher NullMatcher =
new LexerMatcher( "Null", "null" );
public static readonly LexerMatcher BOOL_MATCHER =
new LexerMatcher( "BOOL", "true|false" );
public static readonly LexerMatcher BoolMatcher =
new LexerMatcher( "Bool", "true|false" );
public static readonly LexerMatcher LOGIC_MATCHER =
new LexerMatcher( "LOGIC", "if|else|switch|do|while|for|break|continue|return" );
public static readonly LexerMatcher LogicMatcher =
new LexerMatcher( "Logic", "if|else|switch|do|while|for|break|continue|return" );
public static readonly LexerMatcher OPERATOR_MATCHER =
new LexerMatcher( "OPERATOR", "(?:\\=\\=)|(?:\\+\\+)|(?:\\-\\-)|\\+|\\-|\\*|\\/|\\^|\\||\\~|\\&|\\%|\\<|\\>|\\=|\\!|\\.|\\:|\\,|\\;" );
public static readonly LexerMatcher OperatorMatcher =
new LexerMatcher( "Operator", "(?:\\=\\=)|(?:\\+\\+)|(?:\\-\\-)|\\+|\\-|\\*|\\/|\\^|\\||\\~|\\&|\\%|\\<|\\>|\\=|\\!|\\.|\\:|\\,|\\;" );
public static readonly LexerMatcher BRACKET_MATCHER =
new LexerMatcher( "BRACKET", @"\(|\)|\[|\]|\{|\}" );
public static readonly LexerMatcher BracketMatcher =
new LexerMatcher( "Bracket", @"\(|\)|\[|\]|\{|\}" );
public static readonly LexerMatcher BLOCKSTART_MATCHER =
new LexerMatcher( "BLOCKSTART", @"\{" );
public static readonly LexerMatcher BlockStartMatcher =
new LexerMatcher( "BlockStart", @"\{" );
public static readonly LexerMatcher BLOCKEND_MATCHER =
new LexerMatcher( "BLOCKEND", @"\}" );
public static readonly LexerMatcher BlockEndMatcher =
new LexerMatcher( "BlockStart", @"\}" );
public static readonly LexerMatcher CLASS_MATCHER =
new LexerMatcher( "CLASS", @"\bclass\b" );
public static readonly LexerMatcher ClassMatcher =
new LexerMatcher( "Class", @"\bclass\b" );
public static readonly LexerMatcher ACCESS_MODIFIER_MATCHER =
new LexerMatcher( "ACCESS_MODIFIER", @"\b(?:public|protected|private)\b" );
public static readonly LexerMatcher AccessModifierMatcher =
new LexerMatcher( "AccessModifier", @"\b(?:public|protected|private)\b" );
public static readonly LexerMatcher SINGLE_LINE_COMMENT_MATCHER =
new LexerMatcher( "SINGLE_LINE_COMMENT", @"//.*" );
public static readonly LexerMatcher SingleLineCommentMatcher =
new LexerMatcher( "SingleLineComment", @"//.*" );
public static readonly LexerMatcher C_INSTRUCTION_MATCHER =
new LexerMatcher( "C_INSTRUCTION", @"\#.*" );
public static readonly LexerMatcher CInstructionMatcher =
new LexerMatcher( "CInstruction", @"\#.*" );
public static readonly LexerMatcher MULTI_LINE_COMMENT_MATCHER =
new LexerMatcher( "MULTI_LINE_COMMENT", @"\/\*(.|(\r\n|\r|\n))*?\*\/" );
public static readonly LexerMatcher MultiLineCommentMatcher =
new LexerMatcher( "MultiLineComment", @"\/\*(.|(\r\n|\r|\n))*?\*\/" );
public static readonly LexerMatcher ANY_SYMBOL_MATCHER =
new LexerMatcher( "ANY_SYMBOL", @"." );
public static readonly LexerMatcher AnySymbolMatcher =
new LexerMatcher( "AnySymbol", @"." );
public static readonly LexerMatcher HASH_TAG =
new LexerMatcher( "HASH_TAG", @"\#(\w|-|\d)+" );
public static readonly LexerMatcher HashTag =
new LexerMatcher( "HashTag", @"\#(\w|-|\d)+" );
public static readonly LexerMatcher URL =
new LexerMatcher( "URL", @"https?\:\/\/(\w|\.|\-|\?|\=|\+|\/)+" );

View File

@ -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;
}
}
}

View File

@ -384,6 +384,14 @@ namespace Rokojori
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 )
{
return Replace( source, regex, "" );

View File

@ -14,6 +14,7 @@ namespace Rokojori
{
[Export]
public Vector3 target;
[Export]
public float yaw = 0;

View File

@ -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;
}
}
}

View File

@ -69,6 +69,7 @@ namespace Rokojori
var position = new Vector3();
var up = new Vector3();
var forward = new Vector3();
var fov = 0f;
_cameraSlots.ForEach(
c =>
@ -100,10 +101,12 @@ namespace Rokojori
position += priority * c.camera.GetCameraPosition();
up += priority * vUp;
forward += priority * vForward;
fov += priority * c.camera.GetCameraFOV();
}
);
position /= sumPriority;
fov /= sumPriority;
if ( forward.LengthSquared() == 0 )
{
@ -126,6 +129,7 @@ namespace Rokojori
camera.GlobalPosition = position;
camera.LookAt( position - forward, up );
camera.Fov = fov;
}