Math/Procederul Updates

This commit is contained in:
Josef 2024-09-14 08:41:52 +02:00
parent 575431f868
commit fed16cb75c
107 changed files with 8278 additions and 270 deletions

83
Icons/Scatterer.svg Normal file
View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg42222"
sodipodi:docname="Scatterer.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs42226" />
<sodipodi:namedview
id="namedview42224"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="14.305085"
inkscape:cy="10.745763"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg42222" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path43509"
cx="6.5593214"
cy="10.899628"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle43563"
cx="2.8305085"
cy="12.933526"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle46711"
cx="10.355931"
cy="13.103018"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle46713"
cx="13.237288"
cy="10.391153"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle46715"
cx="9.7118645"
cy="7.5436959"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle46717"
cx="3"
cy="7.3742042"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle46719"
cx="6.3559322"
cy="3.3742044"
r="1.9322034" />
<circle
style="fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle46721"
cx="12.322034"
cy="3.3064077"
r="1.9322034" />
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://clm1p530y0sh0"
path="res://.godot/imported/Scatterer.svg-faa0f406c786220743edbcf86085b917.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Scatterer.svg"
dest_files=["res://.godot/imported/Scatterer.svg-faa0f406c786220743edbcf86085b917.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

52
Icons/Spline.svg Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg42222"
sodipodi:docname="Spline.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs42226" />
<sodipodi:namedview
id="namedview42224"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="20.85965"
inkscape:cx="15.532379"
inkscape:cy="15.796046"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg42222" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#fc7f7f;stroke-width:2;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
d="M 12.047939,3.2118215 C 10.778939,2.3699272 5.6943781,1.9062831 4.5423729,2.9152542 3.5900977,3.7492939 3.3064257,5.5473527 4.0263787,6.6776739 4.8897493,8.0331597 10.029541,8.1436155 12.186233,8.798812 c 2.069946,0.6288435 1.37427,2.899163 0.534562,3.913053 -1.676235,2.02394 -6.6161891,1.149842 -7.8733374,0.406779"
id="path42345"
sodipodi:nodetypes="csssac" />
<circle
style="opacity:1;fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path43509"
cx="12.881356"
cy="3.4915257"
r="1.9322034" />
<circle
style="opacity:1;fill:#fc7f7f;fill-opacity:1;stroke:none;stroke-width:4.22222;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="circle43563"
cx="3.3559334"
cy="12.169491"
r="1.9322034" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

37
Icons/Spline.svg.import Normal file
View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c4t5wy2e8guge"
path="res://.godot/imported/Spline.svg-24ecac2761c76806ec867265b5304b23.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Spline.svg"
dest_files=["res://.godot/imported/Spline.svg-24ecac2761c76806ec867265b5304b23.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,56 @@
using Godot;
namespace Rokojori
{
public enum ColorBlendModeType
{
Alpha,
Add,
Multiply,
Replace
}
public class ColorBlendMode
{
public static Color Blend( ColorBlendModeType type, Color bottom, Color top )
{
switch ( type )
{
case ColorBlendModeType.Alpha: return Alpha( bottom, top );
case ColorBlendModeType.Multiply: return Multiply( bottom, top );
case ColorBlendModeType.Add: return Add( bottom, top );
case ColorBlendModeType.Replace: return Replace( bottom, top );
}
return new Color( 1, 1, 1, 1 );
}
public static Color Alpha( Color bottom, Color top )
{
var a = top.A + bottom.A * ( 1f - top.A );
var colorBottom = ColorX.RGB( bottom );
var colorTop = ColorX.RGB( top );
var colorRGB = ( colorTop * top.A + colorBottom * bottom.A * ( 1f - top.A ) ) / a;
return ColorX.Create( colorRGB, a );
}
public static Color Multiply( Color bottom, Color top )
{
return bottom * top;
}
public static Color Add( Color bottom, Color top )
{
return bottom + top;
}
public static Color Replace( Color bottom, Color top )
{
return top;
}
}
}

29
Runtime/Colors/ColorX.cs Normal file
View File

@ -0,0 +1,29 @@
using Godot;
namespace Rokojori
{
public class ColorX
{
public static Color Lerp( Color a, Color b, float amount )
{
return a.Lerp( b, amount );
}
public static Vector3 RGB( Color c )
{
return new Vector3( c.R, c.G, c.B );
}
public static Vector2 RA( Color c )
{
return new Vector2( c.R, c.A);
}
public static Color Create( Vector3 rgb, float a )
{
return new Color( rgb.X, rgb.Y, rgb.Z, a );
}
}
}

View File

@ -75,6 +75,8 @@ namespace Rokojori
ForEach<T>( root, callback );
}
public static List<T> AllInScene<T>( Func<T,bool> filter = null) where T:class
{
var list = new List<T>();
@ -144,6 +146,35 @@ namespace Rokojori
return GetDirectChild<T>( parent );
}
public static void RemoveAndDelete( Node node )
{
var parent = node.GetParent();
if ( parent != null )
{
parent.RemoveChild( node );
}
node.QueueFree();
}
public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false )
{
if ( parent == null )
{
return;
}
var numChildren = parent.GetChildCount( includeInternal );
for ( int i = numChildren - 1; i >= 0; i-- )
{
var node = parent.GetChild( i, includeInternal );
parent.RemoveChild( node );
node.QueueFree();
}
}
public static T GetDirectChild<T>( Node parent ) where T:Node
{
if ( parent == null )
@ -219,6 +250,40 @@ namespace Rokojori
}
}
public static List<U> MapDirectChildren<T,U>( Node parent, System.Func<T,U> mapper ) where T:Node
{
var list = new List<U>();
ForEachDirectChild<T>( parent, c => list.Add( mapper( c ) ) );
return list;
}
public static int TypeIndex<T>( Node parent, T child ) where T:Node
{
var counter = 0;
var numChildren = parent.GetChildCount();
for ( int i = 0; i < numChildren; i++ )
{
var node = parent.GetChild( i );
if ( node is T )
{
if ( node == child )
{
return counter;
}
counter++;
}
}
return -1;
}
static NodesWalker nodesWalker = new NodesWalker();
public static T GetAnyChild<T>( Node parent ) where T:Node

View File

@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class Box2
{
public Vector2 min = Vector2.Zero;
public Vector2 max = Vector2.Zero;
public Box2( Vector2 min, Vector2 max )
{
this.min = min;
this.max = max;
}
public Box2()
{}
public bool ContainsPoint( Vector2 point )
{
return ContainsPoint( min, max, point );
}
public Box2 Clone()
{
return new Box2( min, max );
}
public void UnionWith( Box2 other )
{
min = min.Min( other.min );
max = max.Max( other.max );
}
public void Grow( Vector2 abs )
{
min -= abs;
max += abs;
}
public void GrowByPoint( Vector2 p )
{
min = min.Min( p );
max = max.Max( p );
}
public Vector2 size => max - min;
public void GrowRelativeToSize( float amount )
{
Grow( size * amount );
}
public static bool ContainsPoint( Vector2 min, Vector2 max, Vector2 point )
{
if ( ! Range.Contains( min.X, max.X, point.X ) )
{
return false;
}
if ( ! Range.Contains( min.Y, max.Y, point.Y ) )
{
return false;
}
return true;
}
public void EnsureCorrectness()
{
if ( max.X < min.X )
{
var b = max.X; max.X = min.X; min.X = b;
}
if ( max.Y < min.Y )
{
var b = max.Y; max.Y = min.Y; min.Y = b;
}
}
}
}

View File

@ -26,6 +26,16 @@ namespace Rokojori
return b;
}
public Box2 AsXZBox2()
{
var b = new Box2();
b.min = Math2D.XZ( min );
b.max = Math2D.XZ( max );
return b;
}
public Vector3 Constrain( Vector3 point )
{
point = min.Max( point );

View File

@ -0,0 +1,44 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
namespace Rokojori
{
public class Capsule2
{
public Vector2 start = Vector2.Zero;
public Vector2 end = Vector2.Zero;
public float radius = 1;
public Capsule2( Vector2 start, Vector2 end, float radius )
{
this.start = start;
this.end = end;
this.radius = radius;
}
public Capsule2 Copy()
{
return new Capsule2( start, end, radius );
}
public Vector2 center
{
get { return start.Lerp( end, 0.5f ); }
}
public Line2 centerLine
{
get { return new Line2( start, end ); }
}
public float DistanceToPoint( Vector2 p )
{
var lineDistance = centerLine.DistanceToPoint( p );
return Mathf.Max( 0, lineDistance - radius );
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class Circle
{
public Vector2 center = Vector2.Zero;
public float radius = 1;
}
}

View File

@ -0,0 +1,146 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class Convex2Group
{
public List<Convex2> items;
public Box2 boundingBox;
public static Convex2Group From( List<Convex2> items )
{
var group = new Convex2Group();
group.items = items;
group.boundingBox = items[ 0 ].boundingBox.Clone();
items.ForEach( it => group.boundingBox.UnionWith( it.boundingBox ) );
// RJLog.Log( group.boundingBox );
return group;
}
public static Convex2Group FromPath( Path2 path )
{
return From( Convex2.PathToConvexList( path ) );
}
public bool ContainsPoint( Vector2 point )
{
if ( ! boundingBox.ContainsPoint( point ) )
{
return false;
}
for ( int i = 0; i < items.Count; i++ )
{
if ( items[ i ].ContainsPoint( point ) )
{
return true;
}
}
return false;
}
}
public class Convex2
{
public List<Vector2> points;
public List<Vector2> normals;
public List<Vector2> directions;
public Box2 boundingBox;
public bool clockwise;
public bool ContainsPoint( Vector2 point, bool checkBounds = true )
{
if ( checkBounds && ! boundingBox.ContainsPoint( point ) )
{
return false;
}
var positive = 0;
var negative = 0;
for ( int i = 0; i < points.Count; i++ )
{
var p = points[ i ];
var d = directions[ i ].Cross( point - p );
if ( d > 0 )
{
positive++;
}
if ( d < 0 )
{
negative++;
}
if ( positive > 0 && negative > 0 )
{
return false;
}
}
return true;
}
public static List<Convex2> PathToConvexList( Path2 path )
{
var convexPolys = Geometry2D.DecomposePolygonInConvex( path.points.ToArray() );
var list = new List<Convex2>();
for ( int i = 0; i < convexPolys.Count; i++ )
{
var clockwise = Geometry2D.IsPolygonClockwise( convexPolys[ i ] );
var cnv = FromConvexHull( convexPolys[ i ], clockwise );
list.Add( cnv );
}
// RJLog.Log( "PathToConvexList:", list.Count );
return list;
}
public static Convex2 FromConvexHull( Vector2[] points, bool isClockwise = true )
{
return FromConvexHull( new List<Vector2>( points ), isClockwise );
}
public static Convex2 FromConvexHull( List<Vector2> points, bool isClockwise = true )
{
var cnv = new Convex2();
cnv.points = points;
cnv.normals = new List<Vector2>();
cnv.directions = new List<Vector2>();
cnv.clockwise = isClockwise;
cnv.boundingBox = new Box2( points[ 0 ], points[ 0 ] );
for ( int i = 0; i < points.Count; i++ )
{
var next = ( i + 1 ) % points.Count;
cnv.boundingBox.GrowByPoint( points[ i ] );
var direction = points[ next ] - points[ i ];
var normal = Math2D.Rotate90Degrees( direction.Normalized(), ! isClockwise );
cnv.normals.Add( normal );
cnv.directions.Add( direction );
}
return cnv;
}
}
}

View File

@ -6,7 +6,7 @@ namespace Rokojori
{
public abstract class Curve3
{
float _gradientSamplingRange = Mathf.Pow( 10f, -13f );
float _gradientSamplingRange = Mathf.Pow( 10f, -8f );
public abstract Vector3 SampleAt( float t );
public virtual void SampleMultiple( int numSamples, List<Vector3> output )
@ -28,8 +28,12 @@ namespace Rokojori
public virtual Vector3 GradientAt( float t )
{
return GradientAt( t, _gradientSamplingRange );
}
return SampleAt( t + _gradientSamplingRange ) - SampleAt( t - _gradientSamplingRange );
public virtual Vector3 GradientAt( float t, float samplingRange )
{
return SampleAt( t + samplingRange ) - SampleAt( t - samplingRange );
}
public virtual float ComputeLength( int numSamples )
@ -71,6 +75,88 @@ namespace Rokojori
return Mathf.Max( minSamples, numSamples );
}
public Vector3 GetClosestPositionTo( Vector3 position, int numSegments = 20, int depth = 0 )
{
var parameter = GetClosestParameterTo( position, numSegments, depth );
return SampleAt( parameter );
}
public float GetClosestParameterTo( Vector3 point, int numSegments = 20, int depth = 3 )
{
return GetClosestParameterTo( 0, 1, point, numSegments, depth, 0 );
}
public float GetDistanceTo( Vector3 point, int numSegments = 20, int depth = 3 )
{
var p = GetClosestPositionTo( point, numSegments, depth );
return ( p - point ).Length();
}
protected float GetClosestParameterTo( float tStart, float tEnd, Vector3 position, int numSegments, int maxDepth, int currentDepth = 0 )
{
var tNormalizer = ( tEnd - tStart ) / (float) ( numSegments - 1 );
var closestIndex = -1;
var closestDistance = float.MaxValue;
var minT = 0f;
var maxT = 1f;
var startPoint = SampleAt( tStart );
var line = new Line3();
var lastT = tStart;
for ( int i = 1; i < numSegments; i++ )
{
var t = tNormalizer * i + tStart;
var nextCurvePoint = SampleAt( t );
line.Set( startPoint, nextCurvePoint );
var closestPoint = line.ClosestPointToPoint( position );
var distance = ( position - closestPoint ).LengthSquared();
if ( distance < closestDistance )
{
closestDistance = distance;
closestIndex = i - 1;
minT = lastT;
maxT = t;
}
startPoint = nextCurvePoint;
lastT = t;
}
if ( maxDepth != currentDepth )
{
return GetClosestParameterTo( minT, maxT, position, numSegments, maxDepth, currentDepth + 1 );
}
var tNormalizer2 = ( maxT - minT ) / (float)(numSegments - 1 );
closestDistance = float.MaxValue;
var closestParameter = 0.5f;
for ( int i = 0; i < numSegments; i++ )
{
var detailedT = i * tNormalizer2 + minT;
var sampledPoint = SampleAt( detailedT );
var distance = ( sampledPoint - position ).LengthSquared();
if ( distance < closestDistance )
{
closestParameter = detailedT;
closestDistance = distance;
}
}
return closestParameter;
}
}

View File

@ -9,9 +9,9 @@ namespace Rokojori
/*
P = Point,
P = Point
S = Sphere
L = Line,
L = Line
C = Capsule

View File

@ -30,11 +30,11 @@ namespace Rokojori
public class LerpCurve3:Curve3
{
List<Vector3> list;
List<Vector3> points;
public LerpCurve3( List<Vector3> list )
{
this.list = list;
this.points = list;
if ( list.Count == 0 )
{
@ -45,12 +45,12 @@ namespace Rokojori
public override float ComputeLength( int numSamples )
{
var length = 0f;
var end = list.Count - 1;
var end = points.Count - 1;
for ( int i = 0; i < end; i++ )
{
var p0 = list[ i ];
var p1 = list[ i + 1 ];
var p0 = points[ i ];
var p1 = points[ i + 1 ];
length += p0.DistanceTo( p1 );
}
@ -58,19 +58,104 @@ namespace Rokojori
return length;
}
public int numSegments => points.Count -1;
public float GetSegmentDistance( int i )
{
return ( points[ i + 1 ] - points[ i ] ).Length();
}
public float GetMinimumSegmentDistance()
{
var min = GetSegmentDistance( 0 );
for ( int i = 1; i < numSegments; i++ )
{
min = Mathf.Min( min, GetSegmentDistance( i ) );
}
return min;
}
public LerpCurve3 CreateMinimumSpaced( float minSpace )
{
var lastPoint = points.Count - 1;
var spacedPoints = new List<Vector3>();
var line = new Line3();
for ( int i = 0; i < lastPoint; i++ )
{
line.start = points[ i ];
line.end = points[ i + 1 ];
var direction = line.direction;
var length = direction.Length();
var numPoints = Mathf.CeilToInt( length / minSpace );
for ( int j = 0; j < numPoints - 1; j++ )
{
var t = j / (float)( numPoints - 1 );
spacedPoints.Add( line.SampleAt( t ) );
}
}
spacedPoints.Add( points[ points.Count - 1 ] );
return new LerpCurve3( spacedPoints );
}
public static LerpCurve3 SampledFrom( Curve3 curve, int numSamples )
{
var points = new List<Vector3>();
for ( int i = 0; i < numSamples; i++ )
{
var t = i / ( (float) numSamples - 1 );
var p = curve.SampleAt( t );
points.Add( p );
}
return new LerpCurve3( points );
}
public static LerpCurve3 SampledWithResolution( Curve3 curve, float resolution, int lengthSampleResolution = 20 )
{
var length = curve.ComputeLength( lengthSampleResolution );
var numSamples = Mathf.CeilToInt( length * resolution );
return SampledFrom( curve, numSamples );
}
public static LerpCurve3 SampledEqually( Curve3 curve, float minDistance, float smallestSegmentDivision = 2, int lengthSampleResolution = 20 )
{
var lerpCurve = SampledWithResolution( curve, minDistance, lengthSampleResolution );
var smallestSegment = lerpCurve.GetMinimumSegmentDistance() / smallestSegmentDivision;
var minSpace = Mathf.Min( smallestSegment, minDistance );
return lerpCurve.CreateMinimumSpaced( minSpace );
}
public float UndistortParameter( float parameter )
{
var completeLength = ComputeLength( 0 );
var end = list.Count - 1;
var normalizer = 1f / ( list.Count - 1 );
var end = points.Count - 1;
var normalizer = 1f / ( points.Count - 1 );
var length = 0f;
for ( int i = 0; i < end; i++ )
{
var p0 = list[ i ];
var p1 = list[ i + 1 ];
var p0 = points[ i ];
var p1 = points[ i + 1 ];
var t0 = i * normalizer;
var t1 = ( i + 1 ) * normalizer;
var distance = p0.DistanceTo( p1 );
@ -93,7 +178,7 @@ namespace Rokojori
{
var output = new List<SubdivisionData>();
var num = list.Count - 1;
var num = points.Count - 1;
if ( close )
{
@ -102,8 +187,8 @@ namespace Rokojori
for ( var i = 0; i < num; i++ )
{
var start = list[ i ];
var end = list[ i == list.Count ? 0 : i + 1 ];
var start = points[ i ];
var end = points[ i == points.Count ? 0 : i + 1 ];
var dir = ( end - start );
var length = dir.Length();
@ -123,8 +208,8 @@ namespace Rokojori
if ( ! close )
{
var start = list[ list.Count - 2];
var end = list[ list.Count - 1];
var start = points[ points.Count - 2];
var end = points[ points.Count - 1];
var dir = ( end - start );
@ -151,33 +236,33 @@ namespace Rokojori
{
if ( t < 0 )
{
return list[ 0 ];
return points[ 0 ];
}
if ( t >= 1 )
{
return list[ list.Count - 1 ];
return points[ points.Count - 1 ];
}
if ( list.Count == 1 )
if ( points.Count == 1 )
{
return list[ 0 ];
return points[ 0 ];
}
var listIndex = t * ( list.Count - 1 );
var listIndex = t * ( points.Count - 1 );
var flooredIndex = (int) Mathf.Floor( listIndex );
var lerpAmount = listIndex - flooredIndex;
if ( flooredIndex < 0 || ( flooredIndex >= list.Count - 1 ) )
if ( flooredIndex < 0 || ( flooredIndex >= points.Count - 1 ) )
{
RJLog.Log( "Out of range index:",flooredIndex, " t:", t, " listIndex", listIndex, "Count:", list.Count );
RJLog.Log( "Out of range index:",flooredIndex, " t:", t, " listIndex", listIndex, "Count:", points.Count );
}
var lower = list[ flooredIndex ];
var upper = list[ flooredIndex + 1 ];
var lower = points[ flooredIndex ];
var upper = points[ flooredIndex + 1 ];
return Math3D.LerpUnclamped( lower, upper, lerpAmount );
@ -218,14 +303,14 @@ namespace Rokojori
line = new Line3( Vector3.Zero, Vector3.Zero );
}
var end = list.Count - 1;
var end = points.Count - 1;
var parameterNormalizer = 1f / ( list.Count - 1f );
var parameterNormalizer = 1f / ( points.Count - 1f );
for ( int i = 0; i < end; i++ )
{
line.start = list[ i ];
line.end = list[ i + 1 ];
line.start = points[ i ];
line.end = points[ i + 1 ];
var currentParameter = line.ClostestParameterToPoint( point );
var currentClosestPoint = line.GetPointAtParameter( currentParameter );

View File

@ -0,0 +1,155 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class Line2
{
public Vector2 start = Vector2.Zero;
public Vector2 end = Vector2.Zero;
public Line2( Vector2 start, Vector2 end )
{
this.start = start;
this.end = end;
}
public Line2(){}
public float A{ get { return end.Y - start.Y; } }
public float B{ get { return start.X - end.X; } }
public float C{ get { return A * start.X + B * start.Y; } }
public Vector2 direction { get { return end - start; } }
public Vector2 reverseDirection { get { return start - end; } }
public float angle
{
get {
var lineDirection = direction;
return Mathf.Atan2( lineDirection.Y, lineDirection.X );
}
}
public float reverseAngle
{
get {
var lineDirection = reverseDirection;
return Mathf.Atan2( lineDirection.Y, lineDirection.X );
}
}
public Vector2? InfiniteIntersectionOf( Line2 line2 )
{
var A1 = A;
var B1 = B;
var A2 = line2.A;
var B2 = line2.B;
var determinant = A1 * B2 - A2 * B1;
if ( Mathf.Abs( determinant ) < Mathf.Epsilon )
{
return null;
}
var inverseDeterminant = 1f / determinant;
var C1 = A1 * start.X + B1 * start.Y;
var C2 = A2 * line2.start.X + B2 * line2.start.Y;
var x = ( B2 * C1 - B1 * C2 ) * inverseDeterminant;
var y = ( A1 * C2 - A2 * C1 ) * inverseDeterminant;
return new Vector2( x, y );
}
public Vector2? IntersectionOf( Line2 other )
{
var possibleIntersection = InfiniteIntersectionOf( other );
if ( possibleIntersection == null )
{
return null;
}
var point = (Vector2) possibleIntersection;
if ( ! IsValueInRange( point.X, start.X, end.X ) )
{
return null;
}
if ( ! IsValueInRange( point.Y, start.Y, end.Y ) )
{
return null;
}
if ( ! IsValueInRange( point.X, other.start.X, other.end.X ) )
{
return null;
}
if ( ! IsValueInRange( point.Y, other.start.Y, other.end.Y ) )
{
return null;
}
return point;
}
public bool IntersectsWith( Line2 other )
{
return IntersectionOf( other ) != null;
}
bool IsValueInRange( float value, float start, float end )
{
var min = Mathf.Min( start, end );
var max = Mathf.Max( start, end );
return min <= value && value <= max;
}
public Vector2 GetPointAtParameter( float t )
{
return start + direction * t;
}
public Vector2 ClosestPointToPoint( Vector2 point )
{
var parameter = MathX.Clamp01( ClostestParameterToPoint( point ) );
return GetPointAtParameter( parameter );
}
public float ClostestParameterToPoint( Vector2 point )
{
var startP = point - start;
var startEnd = end - start;
var startEnd2 = Math2D.Dot( startEnd, startEnd );
var startEnd_startP = Math2D.Dot( startEnd, startP );
if ( startEnd2 == 0 )
{
return 0;
}
var t = startEnd_startP / startEnd2;
return t;
}
public float DistanceToPoint( Vector2 point )
{
return ( point - ClosestPointToPoint( point ) ).Length();
}
}
}

View File

@ -15,6 +15,19 @@ namespace Rokojori
this.end = end;
}
public Line3(){}
public static Line3 Create( Vector3 start, Vector3 end )
{
return new Line3( start, end );
}
public void Set( Vector3 start, Vector3 end )
{
this.start = start;
this.end = end;
}
public override Vector3 SampleAt( float t )
{
return start.Lerp( end, t );
@ -80,6 +93,14 @@ namespace Rokojori
return GetPointAtParameter( parameter );
}
public static Vector3 ClosestPointOf( Vector3 p, Vector3 lineStart, Vector3 lineEnd )
{
var line = new Line3();
line.Set( lineStart, lineEnd );
return line.ClosestPointToPoint( p );
}
public float ClostestParameterToPoint( Vector3 point )
{
var startP = point - start;
@ -119,6 +140,51 @@ namespace Rokojori
return new Vector3[]{ x, y };
}
public Vector3 ClosestPointTo( Line3 other )
{
var parameters = ClosestParametersToLine( other );
return SampleAt( parameters.X );
}
public Vector3 ClosestPointTo( params Vector3[] points )
{
var d = float.MaxValue;
var id = -1;
for ( int i = 0; i < points.Length; i++ )
{
var pd = DistanceToPoint( points[ i ] );
if ( pd < d )
{
id = i;
d = pd;
}
}
return points[ id ];
}
public Vector3 ClosestPointTo( List<Vector3> points )
{
var d = float.MaxValue;
var id = -1;
for ( int i = 0; i < points.Count; i++ )
{
var pd = DistanceToPoint( points[ i ] );
if ( pd < d )
{
id = i;
d = pd;
}
}
return points[ id ];
}
public Vector2 ClosestParametersToLine( Line3 s )
{
float epsilon = 0.00000001f;

View File

@ -0,0 +1,380 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public enum PointInPathResult
{
INSIDE,
OUTSIDE,
ERROR
}
public class Path2
{
float POINT_TO_CLOSE_DISTANCE = 0.0001f;
float ANGLE_TO_SIMILAR_TRESHOLD = 0.00001f / 180f;
int NUM_POINT_IN_PATH_TEST_RETRIES = 5;
List<Vector2> _points = new List<Vector2>();
public List<Vector2> points => _points;
public Path2( List<Vector2> points )
{
this._points = points;
}
public int numPoints => _points.Count;
public bool empty => _points.Count == 0;
public bool cacheBounds = true;
Vector2? min;
Vector2? max;
public Vector2 leftTop
{
get
{
if ( cacheBounds && min != null )
{
return (Vector2) min;
}
var _min = _points[ 0 ];
for ( int i = 1; i < _points.Count; i++ )
{
var point = _points[ i ];
_min.X = Mathf.Min( point.X, _min.X );
_min.Y = Mathf.Min( point.Y, _min.Y );
}
min = _min;
return (Vector2) min;
}
}
public Vector2 rightBottom
{
get
{
if ( cacheBounds && max != null )
{
return (Vector2) max;
}
var _max = _points[ 0 ];
for ( int i = 1; i < _points.Count; i++ )
{
var point = _points[ i ];
_max.X = Mathf.Max( point.X, _max.X );
_max.Y = Mathf.Max( point.Y, _max.Y );
}
max = _max;
return (Vector2) max;
}
}
public void AddToNavigationPolygon( NavigationPolygon polygon )
{
var convexPolygons = Geometry2D.DecomposePolygonInConvex( points.ToArray() );
for ( int i = 0; i < convexPolygons.Count; i++ )
{
var vertices = convexPolygons[ i ];
polygon.SetVertices( vertices );
polygon.AddPolygon( CreateIndices( vertices.Length ) );
}
}
public static int[] CreateIndices( int amount )
{
var indices = new int[ amount ];
for ( int i = 0; i < amount; i++ )
{
indices[ i ] = i;
}
return indices;
}
public static Path2 AsXZFrom( Curve3D curve3D, bool closed, int resolution = 20 )
{
var points = new List<Vector2>();
var numPoints = resolution;
var normalizer = 1f / resolution;
for ( int i = 0; i < numPoints; i++ )
{
points.Add( Math2D.XZ( curve3D.Samplef( i * normalizer ) ) );
}
if ( closed )
{
points.Add( points[ 0 ] );
}
return new Path2( points );
}
public static Path2 AsXZFrom( Curve3 curve3, bool closed, int resolution = 20 )
{
var points = new List<Vector2>();
var numPoints = resolution;
var normalizer = 1f / resolution;
for ( int i = 0; i < numPoints; i++ )
{
points.Add( Math2D.XZ( curve3.SampleAt( i * normalizer ) ) );
}
if ( closed )
{
points.Add( points[ 0 ] );
}
return new Path2( points );
}
public bool PointInPath( Vector2 p, bool fast = true, bool checkBoundingBox = true )
{
if ( fast )
{
return PointInPathFast( p, checkBoundingBox );
}
else
{
return PointInPathReliable( p, checkBoundingBox );
}
}
public bool PointInPathFast( Vector2 point, bool checkBoundingBox = true )
{
var min = this.leftTop;
var max = this.rightBottom;
if ( checkBoundingBox )
{
if ( ! Box2.ContainsPoint( min, max, point ) )
{
return false;
}
}
return CheckPointInPathFast( point, max + new Vector2( 122.133544f, 129.45423f ) );
}
public bool PointInPathReliable( Vector2 point, bool checkBoundingBox = true )
{
var min = this.leftTop;
var max = this.rightBottom;
if ( checkBoundingBox )
{
if ( ! Box2.ContainsPoint( min, max, point ) )
{
return false;
}
}
var endPoint = max + new Vector2( 0.1234235f, 0.4211322f );
var result = CheckPointInPath( point, endPoint );
if ( PointInPathResult.ERROR != result )
{
return PointInPathResult.INSIDE == result;
}
var rightTop = new Vector2( max.X, min.Y );
endPoint = rightTop + new Vector2( 0.03234235f, -0.90211322f );
result = CheckPointInPath( point, endPoint );
if ( PointInPathResult.ERROR != result )
{
return PointInPathResult.INSIDE == result;
}
var centerTop = new Vector2( ( min.X + max.X ) * 0.5f , min.Y );
endPoint = centerTop + new Vector2( 0.013234235f, -0.90211322f );
result = CheckPointInPath( point, endPoint );
if ( PointInPathResult.ERROR != result )
{
return PointInPathResult.INSIDE == result;
}
var rightMiddle = new Vector2( max.X , ( min.Y + max.Y ) * 0.5f);
endPoint = rightMiddle + new Vector2( 0.013234235f, 0.00211322f );
result = CheckPointInPath( point, endPoint );
if ( PointInPathResult.ERROR != result )
{
return PointInPathResult.INSIDE == result;
}
endPoint = leftTop + new Vector2( -0.01053535f, -0.41005465f );
result = CheckPointInPath( point, endPoint );
if ( PointInPathResult.ERROR != result )
{
return PointInPathResult.INSIDE == result;
}
for ( int i = 0; i < NUM_POINT_IN_PATH_TEST_RETRIES; i++ )
{
var randomX = GodotRandom.Get().Next();
var randomY = GodotRandom.Get().Next();
var randomPoint = max + new Vector2( randomX, randomY );
result = CheckPointInPath( point, endPoint );
if ( PointInPathResult.ERROR != result )
{
return PointInPathResult.INSIDE == result;
}
}
return false;
}
public bool CheckPointInPathFast( Vector2 point, Vector2 endPoint )
{
var pointLine = new Line2( point, endPoint );
var iterationLine = new Line2();
var intersections = 0;
for ( int i = 0; i < _points.Count; i++ )
{
var start = _points[ i ];
var nextIndex = ( i == _points.Count - 1 ) ? 0 : ( i + 1 );
var end = _points[ nextIndex ];
iterationLine.start = start;
iterationLine.end = end;
var intersects = pointLine.IntersectsWith( iterationLine );
if ( intersects )
{
intersections ++;
}
}
return intersections % 2 != 0;
}
PointInPathResult CheckPointInPath( Vector2 point, Vector2 endPoint )
{
var pointLine = new Line2( point, endPoint );
var pointAngle = pointLine.angle;
var pointAngle2 = pointLine.reverseAngle;
var iterationLine = new Line2();
var intersections = 0;
for ( int i = 0; i < _points.Count; i++ )
{
var start = _points[ i ];
if ( pointLine.DistanceToPoint( start ) < POINT_TO_CLOSE_DISTANCE )
{
return PointInPathResult.ERROR;
}
var nextIndex = ( i == _points.Count - 1 ) ? 0 : ( i + 1 );
var end = _points[ nextIndex ];
iterationLine.start = _points[ i ];
iterationLine.end = _points[ nextIndex ];
var iterationAngle = iterationLine.angle;
var angleDifference = AbsoluteDeltaAngleFromRadiansToDegrees( pointAngle, iterationAngle );
if ( angleDifference < ANGLE_TO_SIMILAR_TRESHOLD )
{
return PointInPathResult.ERROR;
}
angleDifference = AbsoluteDeltaAngleFromRadiansToDegrees( pointAngle2, iterationAngle );
if ( angleDifference < ANGLE_TO_SIMILAR_TRESHOLD )
{
return PointInPathResult.ERROR;
}
var intersects = pointLine.IntersectsWith( iterationLine );
if ( intersects )
{
intersections ++;
}
}
if ( intersections % 2 != 0 )
{
return PointInPathResult.INSIDE;
}
return PointInPathResult.OUTSIDE;
}
public static Path2 Circle( Vector2 center, float radius = 1, int resolution = 36 )
{
var points = new List<Vector2>();
for ( int i = 0; i < resolution; i++ )
{
var t = i / ( float ) ( resolution );
var phase = t * Mathf.Pi * 2;
var x = Mathf.Cos( phase ) * radius;
var y = Mathf.Sin( phase ) * radius;
points.Add( new Vector2( x, y ) + center );
}
return new Path2( points );
}
public static float AbsoluteDeltaAngleFromRadiansToDegrees( float rA, float rB )
{
var dA = Mathf.RadToDeg( rA );
var dB = Mathf.RadToDeg( rB );
return MathX.AbsoluteDeltaAngle( dA, dB );
}
}
}

View File

@ -0,0 +1,122 @@
using Godot;
namespace Rokojori
{
public class Plane3
{
public Vector3 normal = Vector3.Up;
public float constant = 0;
public Plane3(){}
public void Set( Vector3 normal, float constant = 0 )
{
this.normal = normal;
this.constant = constant;
}
public Vector3 ConstrainToPlane( Vector3 p, float distance = 10000 )
{
var line3 = new Line3();
line3.Set( p - normal * distance, p + normal * distance );
var intersection = IntersectLine( line3 );
if ( intersection == null )
{
return p;
}
return (Vector3) intersection;
}
public Vector3 ClosestPointTo( Vector3 p )
{
return ConstrainToPlane( p );
}
public float DistanceTo( Vector3 p, float normalIntersectionDistance = 5000 )
{
var line = Line3.Create( p - normal * normalIntersectionDistance, p + normal * normalIntersectionDistance );
var isc = IntersectLine( line );
if ( isc == null )
{
return float.PositiveInfinity;
}
var xp = (Vector3)isc;
return ( p - xp ).Length();
}
public Ray3 GetIntersectionRay( Plane3 other )
{
var lineDirection = normal.Cross( other.normal );
var det = lineDirection.LengthSquared();
if ( det == 0 )
{
return null;
}
var r = new Ray3();
r.offset = ( ( lineDirection.Cross( other.normal ) * constant ) +
( normal.Cross( lineDirection ) * other.constant ) ) / det;
r.direction = lineDirection;
return r;
}
public Vector3? IntersectLine( Line3 line )
{
var direction = line.direction;
var denominator = Math3D.Dot( normal, direction );
if ( denominator == 0 )
{
return null;
}
var t = - ( Math3D.Dot( line.start, normal ) + constant ) / denominator;
if ( t < 0 || t > 1 )
{
return null;
}
return direction * t + line.start;
}
public static Plane3 GetZeroUp()
{
var plane = new Plane3();
plane.Set( Vector3.Up, 0 );
return plane;
}
public void SetFromNormalAndCoplanarPoint( Vector3 normal, Vector3 point )
{
this.normal = normal;
this.constant = - Math3D.Dot( point, this.normal );
}
public void SetFromCoplanarPoints( Vector3 a, Vector3 b, Vector3 c )
{
var normal = Math3D.ComputeNormal( a, b, c );
SetFromNormalAndCoplanarPoint( normal, a );
}
public static Plane3 FromCoplanarPoints( Vector3 a, Vector3 b, Vector3 c )
{
var p = new Plane3();
p.SetFromCoplanarPoints( a, b, c );
return p;
}
}
}

View File

@ -0,0 +1,100 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class Pose
{
bool _needsUpdate = true;
Quaternion _rotation = Quaternion.Identity;
public Quaternion rotation
{
get => _rotation;
set
{
_rotation = value;
_needsUpdate = true;
}
}
Vector3 _position = Vector3.Zero;
public Vector3 position
{
get => _position;
set
{
_position = value;
_needsUpdate = true;
}
}
Basis _basis;
void Update()
{
if ( ! _needsUpdate )
{
return;
}
_needsUpdate = false;
_basis = new Basis( _rotation );
}
public Vector3 forward
{
get
{
Update();
return -_basis.Z;
}
}
public Vector3 right
{
get
{
Update();
return _basis.X;
}
}
public Vector3 up
{
get
{
Update();
return _basis.Y;
}
}
public Pose()
{
}
public Pose ToGlobal( Node3D n )
{
var p = new Pose();
p.rotation = rotation * n.GetGlobalQuaternion();
p.position = n.ToGlobal( position );
return p;
}
public Vector3 Apply( Vector3 p )
{
p = rotation * p;
p += position;
return p;
}
}
}

View File

@ -0,0 +1,12 @@
using Godot;
namespace Rokojori
{
public class Ray3
{
public Vector3 offset;
public Vector3 direction;
}
}

View File

@ -0,0 +1,64 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class SAT2
{
public static void Project( Vector2 normal, List<Vector2> points, Range outputProjectionRange )
{
var min = float.MaxValue;
var max = - float.MaxValue;
for ( var i = 0; i < points.Count; i++ )
{
var dot = Math2D.Dot( normal, points[ i ] );
min = Mathf.Min( min, dot );
max = Mathf.Max( max, dot );
}
outputProjectionRange.min = min;
outputProjectionRange.max = max;
}
public static bool IsIntersecting(
List<Vector2> normalsA, List<Vector2> pointsA,
List<Vector2> normalsB, List<Vector2> pointsB )
{
var projectionRangeA = new Range( 0, 1 );
var projectionRangeB = new Range( 0, 1 );
for ( var i = 0; i < normalsA.Count; i++ )
{
var axis = normalsA[ i ];
Project( axis, pointsA, projectionRangeA );
Project( axis, pointsB, projectionRangeB );
if ( ! projectionRangeA.Overlaps( projectionRangeB ) )
{
return false;
}
}
for ( var i = 0; i < normalsB.Count; i++ )
{
var axis = normalsB[ i ];
Project( axis, pointsA, projectionRangeA );
Project( axis, pointsB, projectionRangeB );
if ( ! projectionRangeA.Overlaps( projectionRangeB ) )
{
return false;
}
}
return true;
}
}
}

View File

@ -0,0 +1,83 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class SAT3
{
public static bool IsIntersecting(
List<Vector3> pointsA, List<Vector3> faceNormalsA, List<Vector3> edgeDirectionsA,
List<Vector3> pointsB, List<Vector3> faceNormalsB, List<Vector3> edgeDirectionsB
)
{
for ( var i = 0; i < faceNormalsA.Count; i++ )
{
var axis = faceNormalsA[ i ];
if ( ProjectionsHaveGap( axis, pointsA, pointsB ) )
{
return false;
}
}
for ( var i = 0; i < faceNormalsB.Count; i++ )
{
var axis = faceNormalsB[ i ];
if ( ProjectionsHaveGap( axis, pointsA, pointsB ) )
{
return false;
}
}
for ( var i = 0; i < edgeDirectionsA.Count; i++ )
{
var edgeA = edgeDirectionsA[ i ];
for ( var j = 0; j < edgeDirectionsB.Count; j++ )
{
var edgeB = edgeDirectionsB[ j ];
var axis = edgeA.Cross( edgeB );
if ( ProjectionsHaveGap( axis, pointsA, pointsB ) )
{
return false;
}
}
}
return true;
}
static bool ProjectionsHaveGap( Vector3 axis, List<Vector3> a, List<Vector3> b )
{
var projectionRangeA = Project( axis, a );
var projectionRangeB = Project( axis, b );
return ! projectionRangeA.Overlaps( projectionRangeB );
}
public static Range Project( Vector3 axis, List<Vector3> points )
{
var min = float.MaxValue;
var max = - float.MaxValue;
for ( var i = 0; i < points.Count; i++ )
{
var dot = Math3D.Dot( axis, points[ i ] );
min = Mathf.Min( min, dot );
max = Mathf.Max( max, dot );
}
return new Range( min, max );
}
}
}

View File

@ -15,6 +15,17 @@ namespace Rokojori
this.radius = radius;
}
public Sphere(){}
public Sphere Create( Vector3 center, float radius )
{
var sphere = new Sphere();
sphere.center = center;
sphere.radius = radius;
return sphere;
}
public Sphere Copy()
{
return new Sphere( center, radius );

View File

@ -0,0 +1,158 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
namespace Rokojori
{
public class SplineCurveTangent
{
public Vector3 position;
public float weight = 1f;
public SplineCurveTangent Clone()
{
var t = new SplineCurveTangent();
t.position = position;
t.weight = weight;
return t;
}
}
public class SplineCurvePoint
{
public Vector3 position;
public Quaternion rotation;
public float weight = 1f;
public SplineCurveTangent tangentBefore = new SplineCurveTangent();
public SplineCurveTangent tangentNext = new SplineCurveTangent();
public SplineCurvePoint Clone()
{
var scp = new SplineCurvePoint();
scp.position = position;
scp.rotation = rotation;
scp.weight = weight;
scp.tangentBefore = tangentBefore.Clone();
scp.tangentNext = tangentNext.Clone();
return scp;
}
public SplineCurvePoint CloneForXZ( float y )
{
var cloned = Clone();
cloned.position.Y = y;
cloned.tangentBefore.position.Y = 0;
cloned.tangentNext.position.Y = 0;
return cloned;
}
}
public class SplineCurve: Curve3
{
List<SplineCurvePoint> _points = new List<SplineCurvePoint>();
public List<SplineCurvePoint> points => _points;
public static SplineCurve From( List<SplineCurvePoint> points )
{
var splineCurve = new SplineCurve();
splineCurve._points = points;
return splineCurve;
}
public Vector3 MinPointPosition()
{
var min = _points[ 0 ].position;
points.ForEach( p => min = min.Min( p.position ) );
return min;
}
public Vector3 MaxPointPosition()
{
var max = _points[ 0 ].position;
points.ForEach( p => max = max.Max( p.position ) );
return max;
}
public SplineCurve Clone()
{
var splineCurve = new SplineCurve();
splineCurve._points = Lists.Map( points, p => p.Clone() );
return splineCurve;
}
public SplineCurve CloneForXZ( float y )
{
var splineCurve = new SplineCurve();
splineCurve._points = Lists.Map( points, p => p.CloneForXZ( y ) );
return splineCurve;
}
public Vector3 GetByPointIndex( float pointIndex )
{
if ( pointIndex <= 0 )
{
return _points[ 0 ].position;
}
if ( pointIndex >= ( _points.Count - 1 ) )
{
return _points[ _points.Count - 1 ].position;
}
var lower = Mathf.FloorToInt( pointIndex );
var higher = Mathf.CeilToInt( pointIndex );
var lerpAmount = pointIndex - lower;
return RationalCubicBezier.Compute(
lerpAmount,
_points[ lower ].position,
_points[ lower ].tangentNext.position,
_points[ higher ].tangentBefore.position,
_points[ higher ].position,
_points[ lower ].weight,
_points[ lower ].tangentNext.weight,
_points[ higher ].tangentBefore.weight,
_points[ higher ].weight
);
}
public override Vector3 SampleAt( float t )
{
if ( _points.Count <= 0 )
{
return Vector3.Zero;
}
var index = NormalizedToPointIndex( t );
return GetByPointIndex( index );
}
public float NormalizedToPointIndex( float normalized )
{
return normalized * ( _points.Count - 1 );
}
public float PointIndexToNormalized( float pointIndex )
{
return pointIndex / ( _points.Count - 1 );
}
}
}

View File

@ -0,0 +1,137 @@
using Godot;
using System.Collections.Generic;
using System;
namespace Rokojori
{
public class SplineCurveCreator
{
bool closed;
public SplineCurve Create( List<SplinePoint> splinePoints, bool close )
{
closed = close;
var points = new List<SplineCurvePoint>();
for ( int i = 0; i < splinePoints.Count; i++ )
{
points.Add( CreatePoint( splinePoints, i ) );
}
if ( closed )
{
points.Add( CreatePoint( splinePoints, 0 ) );
}
return SplineCurve.From( points );
}
SplineCurvePoint CreatePoint( List<SplinePoint> splinePoints, int index )
{
var splineCurvePoint = new SplineCurvePoint();
var splinePoint = splinePoints[ index ];
splineCurvePoint.position = splinePoint.GlobalPosition;
splineCurvePoint.weight = 1f;
splineCurvePoint.tangentBefore.position = GetTangentPosition( splinePoints, index, true );
splineCurvePoint.tangentNext.position = GetTangentPosition( splinePoints, index, false );
splineCurvePoint.tangentBefore.weight = splinePoint.tangentBeforeWeight;
splineCurvePoint.tangentNext.weight = splinePoint.tangentNextWeight;
return splineCurvePoint;
}
public Vector3 GetTangentPosition( List<SplinePoint> splinePoints, int index, bool before )
{
var splinePoint = splinePoints[ index ];
if ( splinePoint.tangentMode == SplinePointTangentMode.Custom )
{
return before ? splinePoint.tangentBefore.GlobalPosition :
splinePoint.tangentNext.GlobalPosition;
}
var previousIndex = index - 1;
if ( previousIndex == -1 )
{
previousIndex = closed ? splinePoints.Count -1 : 0;
}
var nextIndex = index + 1;
if ( nextIndex == splinePoints.Count )
{
nextIndex = closed ? 0 : splinePoints.Count - 1;
}
var previous = splinePoints[ previousIndex ];
var next = splinePoints[ nextIndex ];
var previousPosition = previous.GlobalPosition;
var nextPosition = next.GlobalPosition;
var point = splinePoint.GlobalPosition;
var overshootPrevention = splinePoint.overshootPrevention;
var tangentScale = splinePoint.tangentScale;
var symmetricTangentLength = splinePoint.symmetricTangentLength;
if ( overshootPrevention > 0 )
{
var previousDirection = ( previousPosition - point ) ;
var nextDirection = ( nextPosition - point );
var previousLength = previousDirection.Length();
var nextLength = nextDirection.Length();
if ( previousLength > nextLength )
{
previousPosition = Math3D.Lerp(
previousPosition, point + previousDirection.Normalized() * nextLength,
overshootPrevention
);
}
else
{
nextPosition = Math3D.Lerp(
nextPosition, point + nextDirection.Normalized() * previousLength,
overshootPrevention
);
}
}
var direction = nextPosition - previousPosition;
var length = 0f;
var lengthBefore = ( point - previousPosition ).Length();
var lengthNext = ( point - nextPosition ).Length();
var lengthAverage = ( lengthBefore + lengthNext ) * 0.5f;
if ( symmetricTangentLength > 0 )
{
lengthBefore = Mathf.Lerp( lengthBefore, lengthAverage, symmetricTangentLength );
lengthNext = Mathf.Lerp( lengthNext, lengthAverage, symmetricTangentLength );
}
if ( before )
{
length = -lengthBefore;
}
else
{
length = lengthNext;
}
return point + direction.Normalized() * length * 0.33333f * tangentScale;
}
}
}

View File

@ -5,22 +5,6 @@ using System.Collections.Generic;
namespace Rokojori
{
public enum TriangleSide
{
AB,
BC,
CA
}
public class TriangleSides
{
public static readonly TriangleSide[] ALL =
{
TriangleSide.AB,
TriangleSide.BC,
TriangleSide.CA
};
}
public class Triangle3
{
@ -33,6 +17,8 @@ namespace Rokojori
this.a = a;
this.b = b;
this.c = c;
_needsUpdate = true;
}
public float perimeter
@ -47,14 +33,45 @@ namespace Rokojori
}
}
public float semiperimeter
public float semiperimeter => perimeter * 0.5f;
Vector3 _center;
Sphere _boundingSphere;
Plane3 _plane;
bool _needsUpdate = false;
public void NeedsUpdate()
{
_needsUpdate = true;
}
public Sphere boundingSphere
{
get
{
return perimeter * 0.5f;
Update();
return _boundingSphere;
}
}
void Update()
{
if ( ! _needsUpdate )
{
return;
}
var m = Math3D.Center( a, b, c );
var radius = Mathf.Sqrt( MathX.Min( ( m - a ).LengthSquared(), ( m - b ).LengthSquared(), ( m -c ).LengthSquared() ) );
_boundingSphere = new Sphere( m, radius );
_plane = new Plane3();
_plane.SetFromCoplanarPoints( a, b, c );
_needsUpdate = false;
}
public static float ComputeTriangleArea( Vector3 a, Vector3 b, Vector3 c )
{
var sideA = ( b - a ).Length();
@ -71,26 +88,266 @@ namespace Rokojori
return areaValue;
}
public float area
public float area => ComputeTriangleArea( a, b, c );
public bool Intersects( Line3 line )
{
get
var planePoint = _plane.IntersectLine( line );
if ( planePoint == null )
{
return ComputeTriangleArea( a, b, c );
}
return false;
}
return InsideTriangle( (Vector3) planePoint, a, b, c );
}
public Line3 GetSide( TriangleSide side )
public Vector3? GetIntersection( Line3 line )
{
switch ( side )
Update();
var planePoint = _plane.IntersectLine( line );
if ( planePoint == null )
{
case TriangleSide.AB: return new Line3( a, b );
case TriangleSide.BC: return new Line3( b, c );
case TriangleSide.CA: return new Line3( c, a );
return null;
}
if ( ! InsideTriangle( (Vector3) planePoint, a, b, c ) )
{
return null;
}
return planePoint;
}
public bool PointInside( Vector3 p )
{
return InsideTriangle( p, a, b, c );
}
public Vector3 ClosestPoint( Vector3 p )
{
Update();
var planePoint = _plane.ClosestPointTo( p );
if ( PointInside( planePoint ) )
{
return planePoint;
}
return ClosestPointToEdges( p );
}
public Vector3? GetPointOnPlaneInsideTriangle( Vector3 p )
{
Update();
var planePoint = _plane.ClosestPointTo( p );
if ( PointInside( planePoint ) )
{
return planePoint;
}
return null;
}
Vector3[] closestPoints = new Vector3[3];
public Vector3 ClosestPointToEdges( Vector3 p )
{
closestPoints[ 0 ] = Line3.ClosestPointOf( p, a, b );
closestPoints[ 1 ] = Line3.ClosestPointOf( p, b, c );
closestPoints[ 2 ] = Line3.ClosestPointOf( p, c, a );
return Math3D.GetClosest( p, closestPoints );
}
public Vector3 ClosestPoint( Line3 line )
{
var intersection = GetIntersection( line );
if ( intersection != null )
{
return (Vector3) intersection;
}
var ab = new Line3( a, b ).ClosestPointTo( line );
var bc = new Line3( b, c ).ClosestPointTo( line );
var ca = new Line3( c, a ).ClosestPointTo( line );
return line.ClosestPointTo( ab, bc, ca );
}
public Vector3 GetPoint( int i )
{
if ( i == 0 )
{
return a;
}
else if ( i == 1 )
{
return b;
}
else if ( i == 2 )
{
return c;
}
return a;
}
void SetPointsToLine( Line3 line, int index )
{
line.Set( GetPoint( index ), GetPoint( ( index + 1 ) % 3 ) );
}
public Line3 GetIntersectionLine( Triangle3 other )
{
var ownSphere = boundingSphere;
var otherSphere = other.boundingSphere;
if ( ! ownSphere.IntersectsSphere( otherSphere ) )
{
return null;
}
var line = new Line3();
var intersections = new List<Vector3>();
for ( int i = 0; i < 3; i++ )
{
SetPointsToLine( line, i );
var intersection = other.GetIntersection( line );
if ( intersection != null )
{
intersections.Add( (Vector3) intersection );
}
}
if ( intersections.Count == 2 )
{
line.Set( intersections[ 0 ], intersections[ 1 ] );
return line;
}
for ( int i = 0; i < 3; i++ )
{
other.SetPointsToLine( line, i );
var intersection = GetIntersection( line );
if ( intersection != null )
{
intersections.Add( (Vector3) intersection );
}
}
if ( intersections.Count == 2 )
{
line.Set( intersections[ 0 ], intersections[ 1 ] );
return line;
}
return null;
}
public bool Intersects( Triangle3 other )
{
var ownSphere = boundingSphere;
var otherSphere = other.boundingSphere;
if ( ! ownSphere.IntersectsSphere( otherSphere ) )
{
return false;
}
var line = new Line3();
for ( int i = 0; i < 3; i++ )
{
if ( PointInside( other.GetPoint( i ) ) )
{
return true;
}
if ( other.PointInside( GetPoint( i ) ) )
{
return true;
}
SetPointsToLine( line, i );
if ( other.Intersects( line ) )
{
return true;
}
other.SetPointsToLine( line, i );
if ( Intersects( line ) )
{
return true;
}
}
return false;
}
public static bool InsideTriangle( Vector3 point, Vector3 a, Vector3 b, Vector3 c )
{
var bary = GetBaryCentricCoordinate( point, a, b, c );
if ( bary == null )
{
return false;
}
var values = (Vector3) bary;
var insideV = Range.Contains( values.Y, 0, 1 );
var insideU = Range.Contains( values.Z, 0, 1 );
var insideX = Range.Contains( values.X, 0, 1 );
var result = insideX && insideV && insideU;
return result;
}
public static Vector3? GetBaryCentricCoordinate( Vector3 point, Vector3 a, Vector3 b, Vector3 c )
{
var _v0 = c - a;
var _v1 = b - a;
var _v2 = point - a;
var dot00 = Math3D.Dot( _v0, _v0 );
var dot01 = Math3D.Dot( _v0, _v1 );
var dot02 = Math3D.Dot( _v0, _v2 );
var dot11 = Math3D.Dot( _v1, _v1 );
var dot12 = Math3D.Dot( _v1, _v2 );
var denom = ( dot00 * dot11 - dot01 * dot01 );
if ( denom == 0 )
{
return null;
}
var invDenom = 1f / denom;
var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
return new Vector3( 1f - u - v, v, u );
}
}
}

50
Runtime/Math/Math2D.cs Normal file
View File

@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System.Text;
using System;
namespace Rokojori
{
public class Math2D
{
public static float Dot( Vector2 a, Vector2 b )
{
return a.Dot( b );
}
public static Vector2 XZ( Vector3 v )
{
return new Vector2( v.X, v.Z );
}
public static Vector2 Fract( Vector2 a )
{
return new Vector2( MathX.Fract( a.X ), MathX.Fract( a.Y ) );
}
public static Vector2 SmoothStep( Vector2 a, Vector2 b, Vector2 t )
{
var x = MathX.Smoothstep( a.X, b.X, t.X );
var y = MathX.Smoothstep( a.Y, b.Y, t.Y );
return new Vector2( x, y );
}
public static Vector2 Rotate90DegreesRight( Vector2 v )
{
// 1, 0.5 => -0.5, 1 : -y, x
return new Vector2( -v.Y, v.X );
}
public static Vector2 Rotate90DegreesLeft( Vector2 v )
{
// -0.5, 1 => 1, 0.5 : y, -x
return new Vector2( v.Y, -v.X );
}
public static Vector2 Rotate90Degrees( Vector2 v, bool clockwise )
{
return clockwise ? Rotate90DegreesRight( v ) : Rotate90DegreesLeft( v );
}
}
}

View File

@ -6,8 +6,146 @@ using System;
namespace Rokojori
{
public class Math3D
public static class Math3D
{
public static Vector3 OnCircleXZ( float radians, float size = 1 )
{
var x = Mathf.Cos( radians ) * size;
var z = Mathf.Sin( radians ) * size;
return new Vector3( x, 0, z );
}
public static Vector3 XYasXZ( Vector2 v )
{
return new Vector3( v.X, 0, v.Y );
}
public static float Dot( Vector3 a, Vector3 b )
{
return a.Dot( b );
}
public static Vector3 Cross( Vector3 a, Vector3 b )
{
return a.Cross( b );
}
public static Vector3 Fract( Vector3 a )
{
return new Vector3( MathX.Fract( a.X ), MathX.Fract( a.Y ), MathX.Fract( a.Z ) );
}
public static bool IsExactlyZero( Vector3 v )
{
return v.X == 0 && v.Y == 0 && v.Z == 0;
}
public static Vector3 ComputeNormalFast( Vector3 a, Vector3 b, Vector3 c )
{
return Math3D.Cross( c - b , a - b ).Normalized();
}
public static Vector3 ComputeNormal( Vector3 a, Vector3 b, Vector3 c )
{
var cross = Math3D.Cross( c - b , a - b );
if ( Math3D.IsExactlyZero( cross ) )
{
cross = Math3D.Cross( b - c , a - c );
}
if ( Math3D.IsExactlyZero( cross ) )
{
cross = Math3D.Cross( c - a , b - a );
}
return cross.Normalized();
}
public static Vector3 SmoothStep( Vector3 a, Vector3 b, Vector3 t )
{
var x = MathX.Smoothstep( a.X, b.X, t.X );
var y = MathX.Smoothstep( a.Y, b.Y, t.Y );
var z = MathX.Smoothstep( a.Z, b.Z, t.Z );
return new Vector3( x, y, z );
}
public static Vector3 Center( params Vector3[] points )
{
var center = Vector3.Zero;
if ( points.Length == 0 )
{
return center;
}
for ( int i = 0; i < points.Length; i++ )
{
center += points[ i ];
}
return center / points.Length;
}
public static Vector3 Center( List<Vector3> points )
{
var center = Vector3.Zero;
if ( points.Count == 0 )
{
return center;
}
for ( int i = 0; i < points.Count; i++ )
{
center += points[ i ];
}
return center / points.Count;
}
public static Vector3 GetClosest( Vector3 from, params Vector3[] points )
{
var distance = float.MaxValue;
var index = -1;
for ( int i = 0; i < points.Length; i++ )
{
var d = from.DistanceSquaredTo( points[ i ] );
if ( d < distance )
{
index = i;
distance = d;
}
}
return points[ index ];
}
public static Vector3 GetClosest( Vector3 from, List<Vector3> points )
{
var distance = float.MaxValue;
var index = -1;
for ( int i = 0; i < points.Count; i++ )
{
var d = from.DistanceSquaredTo( points[ i ] );
if ( d < distance )
{
index = i;
distance = d;
}
}
return points[ index ];
}
public static Vector3 LerpUnclamped( Vector3 a, Vector3 b, float amount )
{
return a + amount * ( b - a );
@ -18,12 +156,115 @@ namespace Rokojori
return LerpUnclamped( a, b, MathX.Clamp01( amount ) );
}
public static Quaternion GetGlobalRotation( Node3D node )
public static Quaternion GetGlobalQuaternion( this Node3D node )
{
return node.GlobalBasis.GetRotationQuaternion();
return GetGlobalRotationFrom( node );
}
public static void SetGlobalRotation( Node3D node, Quaternion quaternion )
public static Quaternion GetGlobalRotationFrom( Node3D node )
{
var quaternion = node.GlobalBasis.GetRotationQuaternion();
return quaternion;
}
public static Vector3 MinPosition( Node3D a, Node3D b )
{
return a.GlobalPosition.Min( b.GlobalPosition );
}
public static Vector3 MaxPosition( Node3D a, Node3D b )
{
return a.GlobalPosition.Max( b.GlobalPosition );
}
public static Vector3 SnapRounded( Vector3 v, Vector3 snapping )
{
v.X = MathX.SnapRounded( v.X, snapping.X );
v.Y = MathX.SnapRounded( v.Y, snapping.Y );
v.Z = MathX.SnapRounded( v.Z, snapping.Z );
return v;
}
public static Vector3 SnapCeiled( Vector3 v, Vector3 snapping )
{
v.X = MathX.SnapCeiled( v.X, snapping.X );
v.Y = MathX.SnapCeiled( v.Y, snapping.Y );
v.Z = MathX.SnapCeiled( v.Z, snapping.Z );
return v;
}
public static Vector3 SnapFloored( Vector3 v, Vector3 snapping )
{
v.X = MathX.SnapFloored( v.X, snapping.X );
v.Y = MathX.SnapFloored( v.Y, snapping.Y );
v.Z = MathX.SnapFloored( v.Z, snapping.Z );
return v;
}
public static Quaternion AlignUp( Quaternion rotation, Vector3 upDirection )
{
var basis = new Basis( rotation );
var aligned = AlignUp( basis, upDirection );
return aligned.GetRotationQuaternion();
}
public static Basis AlignUp( Basis basis, Vector3 upDirection )
{
basis.Y = upDirection;
basis.X = - basis.Z.Cross( upDirection );
return basis.Orthonormalized();
}
public static Vector3 Lerp( Vector3 a, Vector3 b, float lerp )
{
return a.Lerp( b, lerp );
}
public static Quaternion LookRotation( Vector3 direction, Vector3 up )
{
if ( direction.Length() == 0 )
{
return Quaternion.Identity;
}
var t = new Transform3D();
t.Basis = Basis.Identity;
t.Origin = Vector3.Zero;
t = t.LookingAt( direction, up );
return t.Basis.GetRotationQuaternion();
}
public static Vector3 LerpComponents( Vector3 a, Vector3 b, Vector3 t )
{
return new Vector3( Mathf.Lerp( a.X, b.X, t.X ), Mathf.Lerp( a.Y, b.Y, t.Y ), Mathf.Lerp( a.Z, b.Z, t.Z ) );
}
public static Vector4 AsVector4( this Quaternion q)
{
return new Vector4( q.X, q.Y, q.Z, q.W );
}
public static void SetGlobalQuaternion( this Node3D node, Quaternion quaternion )
{
var offset = node.GlobalPosition;
var localScale = node.Scale;
node.GlobalBasis = new Basis( quaternion );
node.GlobalPosition = offset;
node.Scale = localScale;
//SetGlobalRotationTo( node, quaternion );
}
public static void SetGlobalRotationTo( Node3D node, Quaternion quaternion )
{
var forward = quaternion * Vector3.Forward;
var up = quaternion * Vector3.Up;
@ -31,16 +272,62 @@ namespace Rokojori
node.LookAt( node.GlobalPosition + forward, up );
}
public static void LookTowards( this Node3D node, Vector3 forwardDirection, Vector3 upDirection, Quaternion rotation )
{
//forwardDirection = rotation * forwardDirection;
//upDirection = rotation * upDirection;
node.LookTowards( forwardDirection, upDirection );
node.SetGlobalQuaternion( node.GetGlobalQuaternion() * rotation );
}
public static void LookTowards( this Node3D node, Vector3 forward, Vector3 up )
{
node.LookAt( forward + node.GlobalPosition, up );
}
public static Quaternion GetDifference( this Quaternion q, Quaternion other )
{
return GetQuaternionDifference( q, other );
}
public static Quaternion GetQuaternionDifference( Quaternion a, Quaternion b )
{
return b.Inverse() * a;
}
public static Quaternion GetQuaternionFraction( Quaternion q, float fraction )
{
return Quaternion.Identity.Slerp( q, fraction );
}
public static Vector3 GlobalForward( this Node3D node )
{
return GetGlobalForward( node );
}
public static Vector3 GetGlobalForward( Node3D node )
{
return -node.GlobalBasis.Z;
}
public static Vector3 GlobalUp( this Node3D node )
{
return GetGlobalUp( node );
}
public static Vector3 GetGlobalUp( Node3D node )
{
return node.GlobalBasis.Y;
}
public static Vector3 GlobalRight( this Node3D node )
{
return GetGlobalRight( node );
}
public static Vector3 GetGlobalRight( Node3D node )
{
return node.GlobalBasis.X;
@ -63,6 +350,54 @@ namespace Rokojori
return right.Normalized();
}
public static void SetGlobalX( this Node3D node, float x )
{
var gp = node.GlobalPosition;
gp.X = x;
node.GlobalPosition = gp;
}
public static void SetGlobalY( this Node3D node, float y )
{
var gp = node.GlobalPosition;
gp.Y = y;
node.GlobalPosition = gp;
}
public static void SetGlobalZ( this Node3D node, float z )
{
var gp = node.GlobalPosition;
gp.Z = z;
node.GlobalPosition = gp;
}
public static Aabb? GetWorldBounds( this Node3D node )
{
return GetWorldBoundsFrom( node );
}
public static Aabb? GetWorldBoundsFrom( Node3D node )
{
Aabb? worldBounds = null;
Nodes.ForEach<VisualInstance3D>( node,
( vi )=>
{
var nBounds = vi.GetAabb();
worldBounds = worldBounds == null ? nBounds : ( ((Aabb)worldBounds).Merge( nBounds ) );
}
);
return worldBounds;
}
}
}

View File

@ -10,6 +10,103 @@ namespace Rokojori
{
public const float fps120Delta = 1/120f;
public static float Min( params float[] values )
{
var value = - float.MaxValue;
for ( int i = 0; i < values.Length; i++ )
{
value = Mathf.Min( values[ i ], value );
}
return value;
}
public static float Max( params float[] values )
{
var value = - float.MaxValue;
for ( int i = 0; i < values.Length; i++ )
{
value = Mathf.Max( values[ i ], value );
}
return value;
}
public static float Sample( float tl, float tr, float bl, float br, Vector2 uv )
{
return Mathf.Lerp( tl, tr, uv.X ) +
( bl - tl ) * uv.Y * ( 1.0f - uv.X ) +
( br - tr ) * uv.X * uv.Y;
}
public static float Sample(
float ba, float bb, float bc, float bd,
float ta, float tb, float tc, float td,
Vector3 uvw
)
{
var bottom = Sample( ba, bb, bc, bd, new Vector2( uvw.X, uvw.Y ) );
var top = Sample( ta, tb, tc, td, new Vector2( uvw.X, uvw.Y ) );
return Mathf.Lerp( bottom, top, uvw.Z );
}
public static float Fract( float value )
{
return value - Mathf.Floor( value );
}
public static float TimeLerpAmountExp( float t, float time )
{
return Mathf.Exp( -t * time );
}
public static float TimeLerp( float lastValue, float nextValue, float t, float time )
{
return Mathf.Lerp( nextValue, lastValue, TimeLerpAmountExp( t, time ) );
}
public static float AbsoluteDeltaAngle( float degreesA, float degreesB )
{
return Mathf.Abs( AngleDelta( degreesA, degreesB ) );
}
public static float Smoothstep ( float edge0, float edge1, float x )
{
x = MathX.Clamp01( ( x - edge0 ) / ( edge1 - edge0 ) );
return x * x * ( 3.0f - 2.0f * x );
}
public static float SnapRounded( float value, float snappingDistance )
{
return Mathf.Round( value / snappingDistance ) * snappingDistance;
}
public static float SnapCeiled( float value, float snappingDistance )
{
return Mathf.Ceil( value / snappingDistance ) * snappingDistance;
}
public static float SnapFloored( float value, float snappingDistance )
{
return Mathf.Floor( value / snappingDistance ) * snappingDistance;
}
public static float AngleDelta( float degreesA, float degreesB)
{
var angleDelta = degreesB - degreesA;
angleDelta = (angleDelta + 180f) % 360f - 180f;
return angleDelta;
}
static float CustomModulo( float a, float n )
{
return a - Mathf.Floor( a / n ) * n;
}
public static float Clamp01( float value )
{
return Mathf.Clamp( value, 0, 1 );
@ -225,10 +322,7 @@ 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 )
{

View File

@ -13,7 +13,7 @@ Replace: $1.DistanceTo(
Match: \.x
Replace: .X
Match: \.Y
Match: \.y
Replace: .Y
Match: \.z

View File

@ -0,0 +1,51 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Rokojori
{
public class NavigationMap2D
{
public Rid map;
public Rid region;
public Vector2[] GetPath( Vector2 start, Vector2 end )
{
return NavigationServer2D.MapGetPath( map, start, end, true );
}
public static async Task<NavigationMap2D> Create( Node node, List<Path2> regions )
{
var map = NavigationServer2D.MapCreate();
NavigationServer2D.MapSetActive( map, true );
var cellSize = NavigationServer2D.MapGetCellSize( map );
var region = NavigationServer2D.RegionCreate();
NavigationServer2D.RegionSetTransform( region, Transform2D.Identity );
NavigationServer2D.RegionSetMap( region, map );
var polygon = new NavigationPolygon();
// polygon.SetCe
for ( int i = 0; i < 1 && i < regions.Count; i++ )
{
regions[ i ].AddToNavigationPolygon( polygon );
}
NavigationServer2D.RegionSetNavigationPolygon( region, polygon );
await node.ToSignal( node.GetTree(), SceneTree.SignalName.PhysicsFrame );
var mapData = new NavigationMap2D();
mapData.map = map;
mapData.region = region;
return mapData;
}
}
}

View File

@ -1,7 +1,8 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
using System;
namespace Rokojori
{
@ -15,6 +16,47 @@ namespace Rokojori
public Rid rid;
public static bool HasCollisionMask( Node n )
{
return n is CsgShape3D || n is CollisionObject3D;
}
public static uint GetCollisionMask( Node n )
{
if ( n is CsgShape3D )
{
return ( n as CsgShape3D ).CollisionMask;
}
if ( n is CollisionObject3D )
{
return ( n as CollisionObject3D ).CollisionMask;
}
return 0;
}
public static bool HasCollisionLayer( Node n )
{
return n is CsgShape3D || n is CollisionObject3D;
}
public static uint GetCollisionLayer( Node n )
{
if ( n is CsgShape3D )
{
return ( n as CsgShape3D ).CollisionLayer;
}
if ( n is CollisionObject3D )
{
return ( n as CollisionObject3D ).CollisionLayer;
}
return 0;
}
public void Get( PhysicsRayQueryParameters3D ray, PhysicsDirectSpaceState3D physicsState )
{
var result = physicsState.IntersectRay( ray );
@ -40,5 +82,96 @@ namespace Rokojori
// RJLog.Log( "Has Collision:", HierarchyName.Of( collider ), ">> at position:", position, "with normal:", normal );
}
public static CollisionData FindCollision( World3D world, PhysicsRayQueryParameters3D rayParameters, Func<CollisionData,bool> predicate )
{
var physics = world.DirectSpaceState;
var excludes = new Array<Rid>();
var maxHits = 10000;
for ( int i = 0; i < maxHits; i++ )
{
rayParameters.Exclude = excludes;
var collisionData = new CollisionData();
collisionData.Get( rayParameters, physics );
if ( ! collisionData.hasCollision )
{
return null;
}
if ( predicate( collisionData ) )
{
return collisionData;
}
excludes.Add( collisionData.rid );
}
return null;
}
public static int MultiRayCast( World3D world, PhysicsRayQueryParameters3D rayParameters, List<CollisionData> collisionsOutput )
{
var physics = world.DirectSpaceState;
var excludes = new Array<Rid>();
var numCollisions = 0;
for ( int i = 0; i < collisionsOutput.Count; i++ )
{
rayParameters.Exclude = excludes;
var collisionData = collisionsOutput[ i ];
collisionData.Get( rayParameters, physics );
if ( ! collisionData.hasCollision )
{
return i;
}
excludes.Add( collisionData.rid );
numCollisions ++;
}
return numCollisions;
}
public static List<CollisionData> MultiRayCast( World3D world, PhysicsRayQueryParameters3D rayParameters, int maxHits = 100 )
{
var physics = world.DirectSpaceState;
var excludes = new Array<Rid>();
var collisions = new List<CollisionData>();
for ( int i = 0; i < maxHits; i++ )
{
rayParameters.Exclude = excludes;
var collisionData = new CollisionData();
collisionData.Get( rayParameters, physics );
if ( ! collisionData.hasCollision )
{
return collisions;
}
excludes.Add( collisionData.rid );
collisions.Add( collisionData );
}
return collisions;
}
}
}

View File

@ -0,0 +1,130 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ConnectionCircle:Node3D
#if TOOLS
, GizmoDrawer
#endif
{
[Export]
public bool updateAlways = false;
[Export]
public Node3D root;
[Export]
public float radius = 1;
float _radius = -1;
[Export( PropertyHint.Range, "2,64") ]
public int divisions = 16;
int _divisions = -1;
[Export]
public float editorGizmoSize = 1;
float _editorGizmoSize = 1;
public Pose GetLocalPose( int index )
{
var p = new Pose();
var state = index / (float)( divisions );
var radians = state * Mathf.Pi * 2f;
var angle = Mathf.RadToDeg( radians );
p.rotation = Quaternion.FromEuler( new Vector3( 0, -radians , 0 ) );
p.position = p.rotation * Vector3.Forward * radius;
// RJLog.Log( radians, angle, p.rotation, p.position );
return p;
}
public Pose GetGlobalPose( int index )
{
return GetLocalPose( index ).ToGlobal( this );
}
#if TOOLS
public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
gizmo.Clear();
var material = gizmoPlugin.GetMaterial( "main", gizmo );
for ( int i = 0; i < divisions; i++ )
{
var p = GetLocalPose( i );
var center = p.position;
var up = p.position + p.up * editorGizmoSize;
var forward = p.position + p.forward * editorGizmoSize;
var right = p.position + p.right * radius * 4f / divisions;
var left = p.position - p.right * radius * 4f / divisions;
/*gizmo.AddLines( new Vector3[]
{
center, forward
},
material
);*/
gizmo.AddLines( new Vector3[]
{
center, up,
center, forward,
right, left,
right, forward,
left, forward,
up, forward
},
material
);
}
}
public override void _Process( double delta )
{
var changed = false;
if ( editorGizmoSize != _editorGizmoSize ||
radius != _radius ||
divisions != _divisions
)
{
_editorGizmoSize = editorGizmoSize;
_radius = radius;
_divisions = divisions;
changed = true;
}
if ( changed )
{
UpdateGizmos();
}
}
#endif
}
}

View File

@ -0,0 +1,170 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ConnectionPin:Node3D
#if TOOLS
, GizmoDrawer
#endif
{
[Export]
public bool updateAlways = false;
[Export]
public Node3D root;
[Export]
public bool moveToMinX = false;
[Export]
public bool moveToMaxX = false;
[Export]
public bool moveToMinY = false;
[Export]
public bool moveToMaxY = false;
[Export]
public bool moveToMinZ = false;
[Export]
public bool moveToMaxZ = false;
[Export]
public float editorGizmoSize = 1;
float _editorGizmoSize = 1;
#if TOOLS
public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
gizmo.Clear();
var center = Vector3.Zero;
var up = Vector3.Up * editorGizmoSize;
var forward = Vector3.Forward * editorGizmoSize;
var right = Vector3.Right * editorGizmoSize;
var material = gizmoPlugin.GetMaterial( "main", gizmo );
gizmo.AddLines(
new Vector3[]
{
center, up,
center, -forward,
center, right,
center, -right,
right, -forward,
-right, -forward,
up, -forward,
up, -right,
up, right
},
material
);
}
public override void _Process( double delta )
{
var changed = false;
if ( editorGizmoSize != _editorGizmoSize )
{
_editorGizmoSize = editorGizmoSize;
changed = true;
}
if ( changed )
{
UpdateGizmos();
}
if ( ! ( moveToMinX || moveToMaxX || moveToMinY || moveToMaxY || moveToMinZ || moveToMaxZ ) )
{
return;
}
if ( root == null )
{
RJLog.Log( "No Root!" );
ClearFlags();
return;
}
var optionalBbounds = root.GetWorldBounds();
if ( optionalBbounds == null )
{
RJLog.Log( "No Bounds!" );
ClearFlags();
return;
}
var bounds = (Aabb) optionalBbounds;
if ( moveToMinX || moveToMaxX )
{
this.SetGlobalX( ( moveToMinX ? bounds.Position : bounds.End ).X );
}
if ( moveToMinY || moveToMaxY )
{
this.SetGlobalY( ( moveToMinY ? bounds.Position : bounds.End ).Y );
}
if ( moveToMinZ || moveToMaxZ )
{
this.SetGlobalZ( ( moveToMinZ ? bounds.Position : bounds.End ).Z );
}
ClearFlags();
}
void ClearFlags()
{
moveToMinX = false;
moveToMaxX = false;
moveToMinY = false;
moveToMaxY = false;
moveToMinZ = false;
moveToMaxZ = false;
}
#endif
public static readonly Quaternion YawFlipRotation = Quaternion.FromEuler( new Vector3( 0, 180, 0 ) / 180 * Mathf.Pi );
public static void Connect( Node3D target, Node3D targetPin, Node3D sourcePin )
{
var targetRotation = target.GetGlobalQuaternion();
var targetPinRotation = targetPin.GetGlobalQuaternion();
var pinToParent = targetPinRotation.GetDifference( targetRotation );
var forward = sourcePin.GlobalForward();
var up = sourcePin.GlobalUp();
var rotation = ConnectionPin.YawFlipRotation * pinToParent;
target.LookTowards( forward, up, rotation );
var offset = ( sourcePin.GlobalPosition - targetPin.GlobalPosition );
target.GlobalTranslate( offset );
}
}
}

View File

@ -0,0 +1,192 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ConnectionPinTester:Node3D
{
[Export]
public ConnectionPin fromPin;
[Export]
public Node3D toParent;
[Export]
public ConnectionPin toPin;
[Export]
public bool update = false;
[Export]
public bool updateAlways = false;
[Export]
public Vector3 eulerRotation;
[Export]
public bool diff_reverse = false;
[Export]
public bool mult_reverse = false;
[Export]
public Vector4 targetPin;
[Export]
public Vector4 currentPin;
[Export]
public Vector4 needed;
[Export]
public Vector4 oldApplying;
[Export]
public Vector4 applyingTargetApplied;
[Export]
public Vector4 pinApplyied;
[Export]
public Node3D applyingTarget;
[Export]
public Node3D forwardTarget;
[Export]
public Vector3 customRotationEulers;
[Export]
public bool rotateBefore;
public override void _Process( double delta )
{
Connect3();
}
void Connect3()
{
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
ConnectionPin.Connect( toParent, toPin, fromPin );
}
void Connect2()
{
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
var targetRotation = fromPin.GetGlobalQuaternion();
var currentPinRotation = toPin.GetGlobalQuaternion();
var parentRotation = applyingTarget.GetGlobalQuaternion();
var pinToParent = diff_reverse ?
Math3D.GetQuaternionDifference( currentPinRotation, parentRotation ) :
Math3D.GetQuaternionDifference( parentRotation, currentPinRotation );
var forward = fromPin.GlobalForward();
var up = fromPin.GlobalUp();
var customRotation = Quaternion.FromEuler( customRotationEulers / 180 * Mathf.Pi ) * pinToParent;
forwardTarget.GlobalPosition = applyingTarget.GlobalPosition + forward;
targetPin = targetRotation.AsVector4();
currentPin = currentPinRotation.AsVector4();
needed = pinToParent.AsVector4();
oldApplying = parentRotation.AsVector4();
applyingTargetApplied = Vector4.One * -1;
pinApplyied = Vector4.One * -1;
applyingTarget.LookTowards( forward, up, customRotation );
var offset = ( fromPin.GlobalPosition - toPin.GlobalPosition );
applyingTarget.GlobalTranslate( offset );
}
void Connect()
{
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
var eulerRads = eulerRotation / 180 * Mathf.Pi;
var targetRotation = fromPin.GetGlobalQuaternion();
var currentRotation = toPin.GetGlobalQuaternion();
//toPin.SetGlobalQuaternion( targetRotation );
var neededRotation = diff_reverse ?
Math3D.GetQuaternionDifference( currentRotation, targetRotation ) :
Math3D.GetQuaternionDifference( targetRotation, currentRotation );
var oldRotation = applyingTarget.GetGlobalQuaternion();
var resultRotation = mult_reverse ? neededRotation * currentRotation :
oldRotation * neededRotation;
var pinResultRotation = mult_reverse ? neededRotation * currentRotation :
currentRotation * neededRotation;
RJLog.Log(
"Target:", targetRotation, "\n",
"Current:", currentRotation, "\n",
"Needed:", neededRotation, "\n",
"OldRotation:", oldRotation, "\n",
"Applied:", resultRotation,"\n",
"Pin:", pinResultRotation
);
targetPin = targetRotation.AsVector4();
currentPin = currentRotation.AsVector4();
needed = neededRotation.AsVector4();
oldApplying = oldRotation.AsVector4();
applyingTargetApplied = resultRotation.AsVector4();
pinApplyied = pinResultRotation.AsVector4();
applyingTarget.SetGlobalQuaternion( resultRotation );
//var positionOffset = fromPin.GlobalPosition - toPin.GlobalPosition;
//toParent.GlobalPosition += positionOffset;
//toPin.SetGlobalQuaternion( currentRotation * neededRotation );
//Math3D.SetGlobalRotation( toPin, targetRotation );
}
}
}

View File

@ -0,0 +1,200 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Spline.svg") ]
public partial class Spline : Node3D
#if TOOLS
, GizmoDrawer
#endif
{
[Export]
public bool closed = false;
[Export]
public int editorResolution = 20;
[Export]
public bool updateAlways = false;
[Export]
public int xzPathBakeResolution = 50;
SplineCurve splineCurve;
Vector3 min;
Vector3 max;
public Box3 GetBounds()
{
GetCurve();
return Box3.Create( min, max );
}
public SplineCurve GetCurve()
{
if ( splineCurve == null )
{
var splinePoints = Nodes.GetDirectChildren<SplinePoint>( this );
splineCurve = new SplineCurveCreator().Create( splinePoints, closed );
min = splineCurve.MinPointPosition();
max = splineCurve.MaxPointPosition();
}
return splineCurve;
}
SplineCurve splineCurveXZ;
public SplineCurve GetCurveXZ()
{
if ( splineCurveXZ == null )
{
var splinePoints = Nodes.GetDirectChildren<SplinePoint>( this );
splineCurveXZ = new SplineCurveCreator().Create( splinePoints, closed ).CloneForXZ( 0 );
}
return splineCurveXZ;
}
Path2 xzPath;
Convex2Group convex2Group;
public Path2 GetXZPath2()
{
if ( xzPath == null )
{
xzPath = Path2.AsXZFrom( GetCurve(), closed, xzPathBakeResolution );
}
return xzPath;
}
public Convex2Group GetConvex2Group()
{
if ( convex2Group == null )
{
convex2Group = Convex2Group.FromPath( GetXZPath2() );
}
return convex2Group;
}
public void ClearCurveCache()
{
splineCurve = null;
splineCurveXZ = null;
xzPath = null;
convex2Group = null;
}
#if TOOLS
public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
ClearCurveCache();
var curve = GetCurve();
gizmo.Clear();
var linePoints = new List<Vector3>();
int resolution = editorResolution <= 0 ? 20 : editorResolution;
renderedResolution = editorResolution;
var lastPoint = ToLocal( curve.SampleAt( 0 ) );
for ( int i = 1; i < resolution; i++ )
{
var t = i / (float) (resolution - 1 );
var point = ToLocal( curve.SampleAt( t ) );
linePoints.Add( lastPoint );
linePoints.Add( point );
lastPoint = point;
}
for ( int i = 0; i < curve.points.Count; i++ )
{
var p = curve.points[ i ];
linePoints.Add( ToLocal( p.position ) );
linePoints.Add( ToLocal( p.tangentBefore.position ) );
linePoints.Add( ToLocal( p.position ) );
linePoints.Add( ToLocal( p.tangentNext.position ) );
}
var material = gizmoPlugin.GetMaterial( "main", gizmo );
gizmo.AddLines( linePoints.ToArray(), material, false );
}
List<Vector3> cachedPositions = new List<Vector3>();
int renderedResolution = -100;
public override void _Process( double delta )
{
var changed = updateAlways;
var index = 0;
if ( renderedResolution != editorResolution )
{
changed = true;
}
Nodes.ForEachDirectChild<SplinePoint>( this,
sp =>
{
if ( changed )
{
return;
}
if ( index >= cachedPositions.Count )
{
changed = true;
return;
}
if ( cachedPositions[ index ] != sp.GlobalPosition )
{
changed = true;
}
index ++;
}
);
if ( ! changed )
{
return;
}
cachedPositions = Nodes.MapDirectChildren<SplinePoint,Vector3>( this, sp => sp.GlobalPosition );
// RJLog.Log( "Updating gizmos" );
UpdateGizmos();
}
#endif
}
}

View File

@ -0,0 +1,114 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
public enum SplinePointTangentMode
{
Automatic,
Custom
}
[System.Serializable]
public class Tangent
{
[Export]
public Node3D node3D;
[Export( PropertyHint.Range, "0,2" )]
public float weight = 0.5f;
}
[Tool]
[GlobalClass]
public partial class SplinePoint : Node3D
#if TOOLS
, GizmoDrawerWithHandles
#endif
{
#if TOOLS
public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
gizmo.Clear();
var mesh = new SphereMesh();
var size = 0.1f;
mesh.Radius = size;
mesh.Height = size * 2f;
var transform = new Transform3D();
transform.Basis = Basis.Identity;
transform.Origin = Vector3.Zero;
var material = gizmoPlugin.GetMaterial( "main", gizmo );
gizmo.AddMesh( mesh, material, transform );
gizmo.AddHandles( new Vector3[]{ Vector3.Zero }, material, new int[]{} );
}
public string GetHandleName( EditorNode3DGizmo gizmo, int handleId, bool secondary )
{
return "";
}
Vector3 startDragPosition = Vector3.Zero;
public Variant GetHandleValue( EditorNode3DGizmo gizmo, int handleId, bool secondary )
{
startDragPosition = GlobalPosition;
return Variant.From( GlobalPosition );
}
public void SetHandle( EditorNode3DGizmo gizmo, int id, bool secondary, Camera3D camera, Vector2 point )
{
var xzPlane = new Plane( Vector3.Up, startDragPosition);
var cameraDirection = camera.ProjectLocalRayNormal( point );
var intersection = xzPlane.IntersectsRay( camera.GlobalPosition, cameraDirection );
if ( intersection == null )
{
return;
}
GlobalPosition = (Vector3) intersection;
}
public void CommitHandle( EditorNode3DGizmo gizmo, int id, bool secondary, Variant restore, bool cancel )
{
}
#endif
[Export]
public SplinePointTangentMode tangentMode;
[Export]
public Node3D tangentBefore;
[Export( PropertyHint.Range, "0,2" )]
public float tangentBeforeWeight = 1;
[Export]
public Node3D tangentNext;
[Export( PropertyHint.Range, "0,2" )]
public float tangentNextWeight = 1;
[Export( PropertyHint.Range, "0,1")]
public float overshootPrevention = 0.5f;
[Export( PropertyHint.Range, "0,3")]
public float tangentScale = 1f;
[Export( PropertyHint.Range, "0,1")]
public float symmetricTangentLength = 0f;
}
}

View File

@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public enum DiscardMode
{
DiscardOutside,
DiscardInside
}
public enum DiscardBooleanCombinator
{
Overwrite,
UseOperator
}
}

View File

@ -0,0 +1,43 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public enum DiscardNoiseOwnPositionMode
{
Ignore,
Add_Global,
Add_Local
}
[Tool]
[GlobalClass]
public partial class DiscardNoise:Discarder
{
[Export(PropertyHint.Range, "0,1")]
public float insideTreshold = 0.5f;
[Export]
public float noiseScale = 1;
[Export]
public Vector3 noiseOffset = Vector3.Zero;
[Export]
public ScattererOwnPositionMode ownPositionMode = ScattererOwnPositionMode.Ignore;
public override bool IsInside( Vector3 position )
{
var offset = noiseOffset + ScattererOwnPosition.ComputeOffset( ownPositionMode, this );
var value = Noise.Perlin( ( position - offset ) * noiseScale );
return value < insideTreshold;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class DiscardSphere:Discarder
{
[Export]
public float radius;
public override bool IsInside( Vector3 position )
{
var localPoint = ToLocal( position );
return localPoint.LengthSquared() < radius * radius;
}
}
}

View File

@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class DiscardSpline:Discarder
{
[Export]
public Spline spline;
[Export]
public float size;
[Export]
public bool xzOnly = false;
[Export]
public bool asFilledXZPath = false;
public override bool IsInside( Vector3 position )
{
if ( asFilledXZPath )
{
var bounds = spline.GetBounds().AsXZBox2();
bounds.GrowRelativeToSize( 1/3f );
var p2 = Math2D.XZ( position );
if ( bounds.ContainsPoint( p2 ) )
{
//var path = spline.GetXZPath2();
var cg = spline.GetConvex2Group();
var insideGroup = cg.ContainsPoint( p2 );
// var insidePath = insideGroup || path.PointInPath( p2, fastButUnreliableFilledXZPathChecks, false );
if ( insideGroup )
{
return true;
}
}
}
if ( size <= 0 )
{
return false;
}
var curve = xzOnly ? spline.GetCurveXZ() : spline.GetCurve();
var testPosition = position;
if ( xzOnly )
{
testPosition.Y = 0;
}
var distance = curve.GetDistanceTo( testPosition );
return distance < size;
}
}
}

View File

@ -0,0 +1,58 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
public partial class Discarder:Scatterer
{
[Export]
public DiscardMode mode;
[Export]
public DiscardBooleanCombinator combinator;
[Export]
public BooleanLogicBinaryOperator discordOperator;
public virtual bool IsInside( Vector3 position )
{
return true;
}
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
points.ForEach( p =>
{
var inside = IsInside( p.globalPosition );
var outside = ! inside;
var visible = DiscardMode.DiscardInside == mode && outside ||
DiscardMode.DiscardOutside == mode && inside;
if ( DiscardBooleanCombinator.Overwrite == combinator )
{
p.visible = visible;
}
else
{
var result = BooleanLogic.Binary( discordOperator, p.visible, visible );
p.visible = result;
}
}
);
return points;
}
}
}

View File

@ -0,0 +1,72 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public enum DiscardListMode
{
At_Least_Inside_One,
Only_Inside_One,
Must_Be_Inside_All
}
[Tool]
[GlobalClass]
public partial class DiscarderList:Discarder
{
[Export]
public DiscardListMode listMode;
[Export]
public bool childrenNeedToBeVisible = true;
[Export]
public bool childrenNeedToBeProcessing = false;
public override bool IsInside( Vector3 position )
{
var inside = 0;
var numNodes = 0;
Nodes.ForEachDirectChild<Discarder>( this,
d =>
{
if ( ! IsChildEnabled( d, childrenNeedToBeVisible, childrenNeedToBeProcessing ) )
{
return;
}
if ( d.IsInside( position ) )
{
inside ++;
}
numNodes++;
}
);
if ( DiscardListMode.At_Least_Inside_One == listMode )
{
return inside > 0;
}
else if ( DiscardListMode.Only_Inside_One == listMode )
{
return inside == 1;
}
else if ( DiscardListMode.Must_Be_Inside_All == listMode )
{
return numNodes == inside;
}
return false;
}
}
}

View File

@ -0,0 +1,111 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GenerateFence:GeneratorScatterer
{
[Export]
public Spline spline;
[Export]
public bool xzOnly = true;
[Export]
public float sampleDensity = 1;
[Export]
public GeneratorEntry segment;
[Export]
public float segmentLength;
[Export]
public GeneratorEntry pole;
[Export]
public float poleLength;
[Export]
public GeneratorEntry startPole;
[Export]
public float startPoleLength;
[Export]
public GeneratorEntry endPole;
[Export]
public float endPoleLength;
SplineCurve last;
LerpCurve3 equalSpacedCurve;
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
CreateWeights();
var curve = spline.GetCurve();
if ( last != curve )
{
last = curve;
equalSpacedCurve = LerpCurve3.SampledEqually( curve, sampleDensity );
}
var curveLength = equalSpacedCurve.ComputeLength( 0 );
var numPoints = Mathf.CeilToInt( curveLength * sampleDensity );
var id = 0;
for ( int i = 0; i < numPoints; i++ )
{
var t = i / (float) ( numPoints - 1 );
var position = equalSpacedCurve.SampleAt( t );
var rawDirection = equalSpacedCurve.GradientAt( t, 1f / 100f );
var direction = rawDirection;
var length = direction.Length();
if ( length != 0 )
{
direction /= length;
}
/*RJLog.Log( "i:", i, "t:", t,
"P:", position,
"L:", length,
"RD:", rawDirection,
"D:", direction
);*/
var point = CreatePoint( points, id, position.X, position.Y, position.Z );
point.rotation = Math3D.LookRotation( direction, Vector3.Up );
id = point.creatorID + 1;
}
return points;
}
ScatterPoint CreatePoint( List<ScatterPoint> points, int id, float x, float y, float z )
{
var p = new ScatterPoint( this, id );
p.position = new Vector3( x, y, z );
p.visible = ! setDiscarded;
p.seed = Noise.CreateSeed( p.position );
AssginSceneAndContainer( p );
points.Add( p );
return p;
}
}
}

View File

@ -0,0 +1,104 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GenerateInBox:GeneratorScatterer
{
[Export]
public Node3D cornerA;
[Export]
public Node3D cornerB;
[Export]
public bool xzOnly = true;
[Export]
public float density = 10;
[Export]
public bool snapToWorldGrid = false;
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
CreateWeights();
var minPosition = Math3D.MinPosition( cornerA, cornerB );
var maxPosition = Math3D.MaxPosition( cornerA, cornerB );
if ( snapToWorldGrid )
{
var snapping = Vector3.One / density;
minPosition = Math3D.SnapCeiled( minPosition, snapping );
maxPosition = Math3D.SnapFloored( maxPosition, snapping );
}
var pointsX = Mathf.CeilToInt( ( maxPosition.X - minPosition.X ) * density );
var pointsY = Mathf.CeilToInt( ( maxPosition.Y - minPosition.Y ) * density );
var pointsZ = Mathf.CeilToInt( ( maxPosition.Z - minPosition.Z ) * density );
var id = 0;
if ( xzOnly )
{
var y = ( cornerA.GlobalPosition.Y + cornerB.GlobalPosition.Y ) / 2f;
for ( int x = 0; x < pointsX; x++ )
{
var xPosition = MathX.Map( x, 0, pointsX - 1, minPosition.X, maxPosition.X );
for ( int z = 0; z < pointsZ; z++ )
{
var zPosition = MathX.Map( z, 0, pointsZ- 1, minPosition.Z, maxPosition.Z );
id = CreatePoint( points, id, xPosition, y, zPosition );
}
}
}
else
{
for ( int x = 0; x < pointsX; x++ )
{
var xPosition = MathX.Map( x, 0, pointsX - 1, minPosition.X, maxPosition.X );
for ( int y = 0; y < pointsY; y++ )
{
var yPosition = MathX.Map( y, 0, pointsY- 1, minPosition.Y, maxPosition.Y );
for ( int z = 0; z < pointsZ; z++ )
{
var zPosition = MathX.Map( z, 0, pointsZ- 1, minPosition.Z, maxPosition.Z );
id = CreatePoint( points, id, xPosition, yPosition, zPosition );
}
}
}
}
return points;
}
int CreatePoint( List<ScatterPoint> points, int id, float x, float y, float z )
{
var p = new ScatterPoint( this, id++ );
p.position = new Vector3( x, y, z );
p.visible = ! setDiscarded;
p.seed = Noise.CreateSeed( p.position );
AssginSceneAndContainer( p );
points.Add( p );
return id;
}
}
}

View File

@ -0,0 +1,91 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GenerateOnSpline:GeneratorScatterer
{
[Export]
public Spline spline;
[Export]
public bool xzOnly = true;
[Export]
public float density = 1;
SplineCurve last;
LerpCurve3 equalSpacedCurve;
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
CreateWeights();
var curve = spline.GetCurve();
if ( last != curve )
{
last = curve;
equalSpacedCurve = LerpCurve3.SampledEqually( curve, density );
}
var curveLength = equalSpacedCurve.ComputeLength( 0 );
var numPoints = Mathf.CeilToInt( curveLength * density );
var id = 0;
for ( int i = 0; i < numPoints; i++ )
{
var t = i / (float) ( numPoints - 1 );
var position = equalSpacedCurve.SampleAt( t );
var rawDirection = equalSpacedCurve.GradientAt( t, 1f / 100f );
var direction = rawDirection;
var length = direction.Length();
if ( length != 0 )
{
direction /= length;
}
/*RJLog.Log( "i:", i, "t:", t,
"P:", position,
"L:", length,
"RD:", rawDirection,
"D:", direction
);*/
var point = CreatePoint( points, id, position.X, position.Y, position.Z );
point.rotation = Math3D.LookRotation( direction, Vector3.Up );
id = point.creatorID + 1;
}
return points;
}
ScatterPoint CreatePoint( List<ScatterPoint> points, int id, float x, float y, float z )
{
var p = new ScatterPoint( this, id );
p.position = new Vector3( x, y, z );
p.visible = ! setDiscarded;
p.seed = Noise.CreateSeed( p.position );
AssginSceneAndContainer( p );
points.Add( p );
return p;
}
}
}

View File

@ -0,0 +1,57 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GeneratePinBoundary:GeneratorScatterer
{
[Export]
public Spline spline;
[Export]
public bool xzOnly = true;
[Export]
public float sampleDensity;
SplineCurve last;
LerpCurve3 equalSpacedCurve;
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
var curve = spline.GetCurve();
if ( last != curve )
{
last = curve;
equalSpacedCurve = LerpCurve3.SampledEqually( curve, sampleDensity );
}
return points;
}
int CreatePoint( List<ScatterPoint> points, int id, float x, float y, float z )
{
var p = new ScatterPoint( this, id++ );
p.position = new Vector3( x, y, z );
p.visible = ! setDiscarded;
p.seed = Noise.CreateSeed( p.position );
AssginSceneAndContainer( p );
points.Add( p );
return id;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GeneratorEntry:Node3D
{
[Export]
public PackedScene packedScene;
[Export]
public float probability = 1;
[Export]
public Node3D container;
}
}

View File

@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GeneratorScatterer:Scatterer
{
[Export]
public bool setDiscarded = true;
[Export]
public PackedScene packedScene;
[Export]
public Node3D container;
[Export]
public bool useGeneratorEntryFromChildren = true;
[Export]
public float childGeneratorNoiseScale = 1;
[Export]
public Vector3 childGeneratorNoiseOffset = Vector3.Zero;
protected List<GeneratorEntry> childGenerators = new List<GeneratorEntry>();
protected List<float> childGeneratorWeights = new List<float>();
public void CreateWeights()
{
if ( ! useGeneratorEntryFromChildren )
{
return;
}
childGenerators = Nodes.GetDirectChildren<GeneratorEntry>( this );
childGeneratorWeights = Lists.Map( childGenerators, c => c.probability );
var sum = 0f;
childGeneratorWeights.ForEach( w => sum += w );
childGeneratorWeights = Lists.Map( childGeneratorWeights, c => c /= sum );
}
public void AssginSceneAndContainer( ScatterPoint p )
{
if ( ! useGeneratorEntryFromChildren )
{
p.scene = packedScene;
p.parent = container;
}
else
{
var value = Noise.Perlin( ( p.position + childGeneratorNoiseOffset ) * childGeneratorNoiseScale );
var index = _FindElementIndexWithWeights( childGeneratorWeights, value );
var gs = childGenerators[ index ];
p.scene = gs.packedScene != null ? gs.packedScene : packedScene;
p.parent = gs.container != null ? gs.container : container;
}
}
int _FindElementIndexWithWeights( List<float> weights, float value )
{
var limit = 0f;
for ( int i = 0; i < weights.Count; i++ )
{
var before = limit;
limit += weights[ i ];
if ( before <= value && value < limit )
{
return i;
}
}
return weights.Count - 1;
}
}
}

View File

@ -0,0 +1,72 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ScatterList:Scatterer
{
[Export]
public bool childrenNeedToBeVisible = true;
[Export]
public bool childrenNeedToBeProcessing = false;
[Export]
public bool clearChildContainers = false;
public override void _Process( double delta )
{
if ( clearContainers )
{
clearContainers = false;
ClearContainers();
}
if ( clearChildContainers )
{
clearChildContainers = false;
Nodes.ForEachDirectChild<Scatterer>( this, s => s.ClearContainers() );
}
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
ScatterAndInstantiatePoints();
}
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
var output = points;
Nodes.ForEachDirectChild<Scatterer>( this,
( s )=>
{
if ( ! IsChildEnabled( s ) )
{
return;
}
output = s.Scatter( output );
}
);
return output;
}
bool IsChildEnabled( Scatterer s )
{
return IsChildEnabled( s, childrenNeedToBeVisible, childrenNeedToBeProcessing );
}
}
}

View File

@ -0,0 +1,90 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class ScatterPoint
{
protected Scatterer _creator;
public Scatterer creator => _creator;
protected int _creatorID;
public int creatorID => _creatorID;
public ScatterPoint( Scatterer creator, int id )
{
this._creator = creator;
this._creatorID = id;
}
public bool visible = false;
public Vector3 position = Vector3.Zero;
public bool useGlobalPosition = true;
public Quaternion rotation = Quaternion.Identity;
public bool useGlobalRotation = true;
public Vector3 scale = Vector3.One;
public PackedScene scene;
public Node3D parent;
public int seed;
public Vector3 globalPosition => useGlobalPosition || parent == null ?
position : parent.ToGlobal( position );
public bool CanBeReusedBy( ScatterPoint other )
{
return parent == other.parent && scene == other.scene;
}
public void UpdateInstantiated( Node3D node )
{
ApplyTransform( node );
}
public Node3D Instantiate()
{
if ( ! visible || scene == null || parent == null )
{
return null;
}
var node3D = scene.Instantiate<Node3D>();
parent.AddChild( node3D );
node3D.Owner = parent.Owner;
ApplyTransform( node3D );
return node3D;
}
void ApplyTransform( Node3D node3D )
{
if ( useGlobalPosition )
{
node3D.GlobalPosition = position;
}
else
{
node3D.Position = position;
}
if ( useGlobalRotation )
{
Math3D.SetGlobalRotationTo( node3D, rotation );
}
else
{
node3D.Quaternion = rotation;
}
node3D.Scale = scale;
}
}
}

View File

@ -0,0 +1,260 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Scatterer.svg") ]
public partial class Scatterer:Node3D
{
[Export]
public bool update = false;
[Export]
public bool updateAlways = false;
[Export]
public Node3D[] containersToClearNodes;
[Export]
public bool clearContainers = false;
[Export]
public bool removeDiscarded = false;
[Export]
public int READ_ONLY_createdPoints = 0;
[Export]
public int READ_ONLY_instantiatedPoints = 0;
[Export]
public int READ_ONLY_reusedPoints = 0;
[Export]
public int READ_ONLY_remappedPoints = 0;
public override void _Process( double delta )
{
if ( clearContainers )
{
clearContainers = false;
ClearContainers();
}
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
ScatterAndInstantiatePoints();
}
public void ClearContainers()
{
Arrays.ForEach( containersToClearNodes,
c =>
{
Nodes.RemoveAndDeleteChildren( c );
}
);
}
class InstantiatedScatterPoint
{
public string hash;
public ScatterPoint scatterPoint;
public Node3D output;
}
Dictionary<string,InstantiatedScatterPoint> _instantiatedPoints = new Dictionary<string, InstantiatedScatterPoint>();
Dictionary<Scatterer,int> _scattererIDs = new Dictionary<Scatterer, int>();
int GetScattererID( Scatterer s )
{
if ( ! _scattererIDs.ContainsKey( s ) )
{
_scattererIDs[ s ] = _scattererIDs.Count;
}
return _scattererIDs[ s ];
}
string GetScatterPointHash( ScatterPoint p )
{
var scatterID = GetScattererID( p.creator ) + ":" + p.creatorID;
return scatterID;
}
public void ScatterAndInstantiatePoints()
{
if ( _instantiatedPoints.Count == 0 )
{
ClearContainers();
}
var points = Scatter( new List<ScatterPoint>() );
READ_ONLY_createdPoints = points.Count;
READ_ONLY_instantiatedPoints = 0;
READ_ONLY_reusedPoints = 0;
READ_ONLY_remappedPoints = 0;
var usedPoints = new HashSet<string>();
points.RemoveAll( p => ! p.visible );
points.ForEach( p =>
{
var hash = GetScatterPointHash( p );
if ( ! CanReuse( hash, p ) )
{
return;
}
var existing = _instantiatedPoints[ hash ];
existing.scatterPoint = p;
existing.scatterPoint.UpdateInstantiated( existing.output );
usedPoints.Add( hash );
READ_ONLY_reusedPoints ++;
return;
}
);
var unused = new DictionaryList<PackedScene,InstantiatedScatterPoint>();
foreach ( var vk in _instantiatedPoints )
{
var ip = vk.Value;
if ( usedPoints.Contains( ip.hash ) )
{
continue;
}
unused.Add( ip.scatterPoint.scene, ip );
}
points.ForEach( p =>
{
var hash = GetScatterPointHash( p );
if ( usedPoints.Contains( hash ) )
{
return;
}
if ( unused.ContainsKey( p.scene ) )
{
var unusedPoint = unused[ p.scene ].Find( ip => ip.scatterPoint.CanBeReusedBy( p ) );
if ( unusedPoint != null )
{
unused.Remove( p.scene, unusedPoint );
unusedPoint.scatterPoint = p;
unusedPoint.scatterPoint.UpdateInstantiated( unusedPoint.output );
usedPoints.Add( hash );
READ_ONLY_remappedPoints ++;
return;
}
}
var instantiatedOutput = p.Instantiate();
if ( instantiatedOutput == null )
{
return;
}
var ip = new InstantiatedScatterPoint();
ip.hash = hash;
ip.output = instantiatedOutput;
ip.scatterPoint = p;
if ( _instantiatedPoints.ContainsKey( hash ) )
{
Nodes.RemoveAndDelete( _instantiatedPoints[ hash ].output );
}
_instantiatedPoints[ hash ] = ip;
usedPoints.Add( hash );
READ_ONLY_instantiatedPoints++;
}
);
Dictionaries.RemoveAll(
_instantiatedPoints, ( k, v ) =>
{
if ( usedPoints.Contains( k ) )
{
return false;
}
Nodes.RemoveAndDelete( v.output );
return true;
}
);
}
bool CanReuse( string hash, ScatterPoint sp )
{
if ( ! _instantiatedPoints.ContainsKey( hash ) )
{
return false;
}
var existing = _instantiatedPoints[ hash ];
var canBeReused = existing.scatterPoint.CanBeReusedBy( sp );
return canBeReused;
}
public List<ScatterPoint> Scatter( List<ScatterPoint> points )
{
var returnedPoints = _Scatter( points );
if ( removeDiscarded )
{
returnedPoints.RemoveAll( p => ! p.visible );
}
return returnedPoints;
}
protected bool IsChildEnabled( Scatterer s, bool childrenNeedToBeVisible, bool childrenNeedToBeProcessing )
{
var enabled = true;
if ( childrenNeedToBeVisible )
{
enabled = enabled && s.Visible;
}
if ( childrenNeedToBeProcessing )
{
enabled = enabled && ( s.ProcessMode != ProcessModeEnum.Disabled );
}
return enabled;
}
protected virtual List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
return points;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public enum ScattererOwnPositionMode
{
Ignore,
Add_Global,
Add_Local
}
public class ScattererOwnPosition
{
public static Vector3 ComputeOffset( ScattererOwnPositionMode mode, Node3D n )
{
var offset = Vector3.Zero;
if ( ScattererOwnPositionMode.Add_Global == mode )
{
offset += n.GlobalPosition;
}
if ( ScattererOwnPositionMode.Add_Local == mode )
{
offset += n.Position;
}
return offset;
}
}
}

View File

@ -0,0 +1,121 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ProjectOnColliders:Scatterer
{
[ExportGroup("Ray")]
[Export]
public Vector3 rayDirection = Vector3.Down;
[Export]
public float rayLength = 10;
[Export]
public Vector3 rayOffset = Vector3.Zero;
[ExportGroup("Normal")]
[Export(PropertyHint.Range, "0,1")]
public float rotationAlignment = 1;
[Export]
public bool discardSteepNormals = true;
[Export]
public Vector3 steepNormalDirection = Vector3.Down;
[Export(PropertyHint.Range, "0,180")]
public float steepNormalRemovalAngle = 0;
[ExportGroup("Collisions")]
[Export( PropertyHint.Layers3DPhysics)]
public uint collisionLayer = 0;
[Export]
public bool collideWithAreas = true;
[Export]
public bool collideWithBodies = true;
[Export]
public bool hitFromInside = true;
[Export]
public bool hitBackFaces = true;
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
var world = GetWorld3D();
var ray = new PhysicsRayQueryParameters3D();
var direction = rayDirection.Normalized();
var normalizedSteepNormalDirection = steepNormalDirection.Normalized();
var steepNormalRemovalTreshold = Mathf.Cos( Mathf.DegToRad( steepNormalRemovalAngle ) );
points.ForEach(
p =>
{
if ( ! p.visible )
{
return;
}
ray.From = p.globalPosition;
ray.To = ray.From + direction * rayLength;
var collisionData = CollisionData.FindCollision( world, ray,
( cd ) =>
{
if ( ! CollisionData.HasCollisionLayer( cd.collider ) )
{
return false;
}
var colliderCollisionLayer = CollisionData.GetCollisionLayer( cd.collider );
var collides = ( colliderCollisionLayer & collisionLayer ) != 0;
// RJLog.Log( "Collision With:", "collides", collides, "colliderLayer:", colliderCollisionLayer, "own", collisionLayer, "name:", cd.collider.Name );
return collides;
}
);
p.visible = collisionData != null;
if ( ! p.visible )
{
return;
}
if ( discardSteepNormals )
{
var dot = normalizedSteepNormalDirection.Dot( collisionData.normal );
if ( dot >= steepNormalRemovalTreshold )
{
p.visible = false;
return;
}
}
p.position = collisionData.position;
var alginedRotation =Math3D.AlignUp( p.rotation, collisionData.normal );
p.rotation = p.rotation.Slerp( alginedRotation, rotationAlignment );
}
);
return points;
}
}
}

View File

@ -0,0 +1,91 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class RandomizeTransform:Scatterer
{
[ExportGroup( "All" )]
[Export]
public float noiseScale = 1;
[Export]
public ScattererOwnPositionMode ownPositionMode = ScattererOwnPositionMode.Ignore;
[ExportGroup( "Position" )]
[Export]
public Vector3 positionOffset = Vector3.Zero;
[Export]
public float positionOffsetScale = 1;
[Export]
public float positionNoiseScale = 1;
[Export]
public Vector3 positionNoiseOffset = Vector3.Zero;
[ExportGroup( "Rotation" )]
[Export]
public Vector3 rotationOffset = Vector3.Zero;
[Export]
public float rotationOffsetScale = 1;
[Export]
public float rotationNoiseScale = 1;
[Export]
public Vector3 rotationNoiseOffset = Vector3.Zero;
[ExportGroup( "Scale" )]
[Export]
public float uniformScaleMultiplyMin = 1;
[Export]
public float uniformScaleMultiplyMax = 1;
[Export]
public Vector3 componentScaleMinMultiply = Vector3.One;
[Export]
public Vector3 componentScaleMaxMultiply = Vector3.One;
[Export]
public float scaleNoiseScale = 1;
[Export]
public Vector3 scaleNoiseOffset = Vector3.Zero;
protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
var output = points;
var ownOffset = ScattererOwnPosition.ComputeOffset( ownPositionMode, this );
points.ForEach(
p =>
{
var point = p.position;
var positionPerlin = ( point + positionNoiseOffset + ownOffset ) * ( noiseScale * positionNoiseScale );
var rotationPerlin = ( point + rotationNoiseOffset + ownOffset ) * ( noiseScale * rotationNoiseScale );
var scalePerlin = ( point + scaleNoiseOffset + ownOffset ) * ( noiseScale * scaleNoiseScale );
var positionRandom = Noise.PerlinPolar3( positionPerlin );
var rotationRandom = Noise.PerlinPolar3( rotationPerlin );
var scaleRandom = Noise.Perlin3( scalePerlin );
var uniformScaleRandom = Noise.Perlin( scalePerlin + new Vector3( 100002, -10002, 1000 ) );
p.position += positionOffset * positionRandom * positionOffsetScale;
p.rotation *= Quaternion.FromEuler( rotationOffset * rotationRandom * rotationOffsetScale );
p.scale *= Math3D.LerpComponents( componentScaleMinMultiply, componentScaleMaxMultiply, scaleRandom );
p.scale *= Mathf.Lerp( uniformScaleMultiplyMin, uniformScaleMultiplyMax, uniformScaleRandom );
}
);
return output;
}
}
}

View File

@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class GodotRandom:RandomEngine
{
static GodotRandom _instance;
public static GodotRandom Get()
{
if ( _instance != null )
{
return _instance;
}
_instance = new GodotRandom();
return _instance;
}
RandomNumberGenerator rng;
public GodotRandom()
{
rng = new RandomNumberGenerator();
rng.Randomize();
}
public override float Next()
{
return rng.Randf();
}
}
}

130
Runtime/Random/Noise.cs Normal file
View File

@ -0,0 +1,130 @@
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
public class Noise
{
public static int CreateSeed( Vector3 p )
{
var x = p.X;
var y = p.Y;
var z = p.Z;
x += 20422.4225f;
y += 12353.299336f;
z += 2139.4f;
x += 3254f;
x = x % 157.3245f;
y += 8994f;
y = y % 17.33636634f;
z += 3.4f;
z = z % 15.99925f;
var seed = 35891.323903f + x * 1000 + y * 11000 + z*20935;
seed = seed % 0.12492f;
seed = Mathf.Cos( seed * 2.235f ) + Mathf.Sin( z * 2102491054 ) + Mathf.Cos( y*48924.9042f);
var intSeed = Mathf.RoundToInt( seed * 20898977 );
intSeed = Mathf.RoundToInt( intSeed + x * 42.3f -z *235 );
intSeed = intSeed << 2;
seed += y % z;
seed += intSeed;
seed = seed % 0.0012f;
seed += y * 100203.3f;
seed -= z * 0122f;
seed += x * 13.35f;
seed = seed % ( y *32535 + z + 0.1221f );
seed -= x * 2f;
seed = seed % (x * 0.02f );
seed += 2005235;
seed = seed % 1157.5f;
return Mathf.RoundToInt( seed * 10000 );
}
public static float Random( Vector2 v )
{
var d = Math2D.Dot( v, new Vector2( 12.9898f, 78.233f ) );
return MathX.Fract( Mathf.Sin ( d ) * 43758.5453123f );
}
public static float Random( Vector3 v )
{
var d = Math3D.Dot( v, new Vector3( 12.9898f, 78.233f, 23.7219f ) );
return MathX.Fract( Mathf.Sin ( d ) * 43758.5453123f );
}
public static float Perlin( Vector2 position )
{
var index = position.Floor();
var lerp = Math2D.Fract( position );
var uv = Math2D.SmoothStep( Vector2.Zero, Vector2.One, lerp );
var a = Random( index );
var b = Random( index + new Vector2( 1.0f, 0.0f ) );
var c = Random( index + new Vector2( 0.0f, 1.0f ) );
var d = Random( index + new Vector2( 1.0f, 1.0f ) );
return MathX.Sample( a, b, c, d, uv );
}
public static float Perlin( Vector3 position )
{
var index = position.Floor();
var lerp = Math3D.Fract( position );
var uvw = Math3D.SmoothStep( Vector3.Zero, Vector3.One, lerp );
var ba = Random( index );
var bb = Random( index + new Vector3( 1.0f, 0.0f, 0.0f ) );
var bc = Random( index + new Vector3( 0.0f, 1.0f, 0.0f ) );
var bd = Random( index + new Vector3( 1.0f, 1.0f, 0.0f ) );
var ta = Random( index + new Vector3( 0.0f, 0.0f, 1.0f ) );
var tb = Random( index + new Vector3( 1.0f, 0.0f, 1.0f ) );
var tc = Random( index + new Vector3( 0.0f, 1.0f, 1.0f ) );
var td = Random( index + new Vector3( 1.0f, 1.0f, 1.0f ) );
return MathX.Sample( ba, bb, bc, bd, ta, tb, tc, td, uvw );
}
public static float PerlinPolar( Vector3 position )
{
return Perlin( position ) * 2 - 1;
}
static readonly Vector3 Perlin3OffsetY = new Vector3( 31789.23f, -2101.23f, 912990.21f );
static readonly Vector3 Perlin3OffsetZ = new Vector3( 91290.0340f, 12921, -99122.21424f );
public static Vector3 Perlin3( Vector3 position, Vector3? offsetY =null, Vector3? offsetZ = null )
{
var offY = ( Vector3 ) ( offsetY == null ? Perlin3OffsetY : offsetY );
var offZ = ( Vector3 ) ( offsetZ == null ? Perlin3OffsetZ : offsetZ );
var x = Perlin( position );
var y = Perlin( position + offY );
var z = Perlin( position + offZ );
return new Vector3( x, y, z );
}
public static Vector3 PerlinPolar3( Vector3 position, Vector3? offsetY =null, Vector3? offsetZ = null )
{
return Perlin3( position, offsetY, offsetZ ) * 2 - Vector3.One;
}
}
}

View File

@ -290,6 +290,24 @@ namespace Rokojori
return selection;
}
public int IndexFromUnnormalizedWeights( List<float> weights, float sumWeights = 0 )
{
if ( sumWeights <= 0 )
{
sumWeights = 0;
weights.ForEach( w => sumWeights += w );
}
return _FindElementIndexWithWeights( weights, Next() * sumWeights );
}
public int IndexFromNormalizedWeights( List<float> weights, float sumWeights = 0 )
{
return IndexFromUnnormalizedWeights( weights, 1 );
}
int _FindElementIndexWithWeights( List<float> weights, float value )
{
var limit = 0f;

View File

@ -139,3 +139,25 @@ vec4 fade( vec4 rgba, float fade )
{
return vec4( rgba.rgb, rgba.a * fade );
}
vec4 straightToPremultipliedColor( vec4 color )
{
return vec4( color.rgb * color.a, color.a );
}
vec4 premultipliedToStraightColor( vec4 color )
{
color.a = max( color.a, 0.000001f );
return vec4( color.rgb / color.a, color.a );
}
vec4 blendMode_alpha( vec4 top, vec4 bottom )
{
float alpha = top.a + bottom.a * ( 1.0 - top.a );
vec3 color = top.rgb * top.a + bottom.rgb * bottom.a * ( 1.0 - top.a );
return vec4( color, alpha );
}

View File

@ -0,0 +1,60 @@
float random( vec2 uv )
{
return fract( sin( dot( uv.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453123 );
}
float worley( vec2 uv, float columns, float rows )
{
vec2 index_uv = floor( vec2( uv.x * columns, uv.y * rows ) );
vec2 fract_uv = fract( vec2( uv.x * columns, uv.y * rows ) );
float minimum_dist = 1.0;
for ( int y= -1; y <= 1; y++ )
{
for ( int x= -1; x <= 1; x++ )
{
vec2 neighbor = vec2( float( x ), float( y ) );
vec2 point = random( index_uv + neighbor );
vec2 diff = neighbor + point - fract_uv;
float dist = length (diff );
minimum_dist = min( minimum_dist, dist );
}
}
return minimum_dist;
}
vec2 voronoi( vec2 uv, float columns, float rows )
{
vec2 index_uv = floor( vec2( uv.x * columns, uv.y * rows ) );
vec2 fract_uv = fract( vec2( uv.x * columns, uv.y * rows ) );
float minimum_dist = 1.0;
vec2 minimum_point;
for ( int y= -1; y <= 1; y++ )
{
for ( int x= -1; x <= 1; x++ )
{
vec2 neighbor = vec2( float( x ), float( y ) );
vec2 point = random( index_uv + neighbor );
vec2 diff = neighbor + point - fract_uv;
float dist = length( diff );
if ( dist < minimum_dist )
{
minimum_dist = dist;
minimum_point = point;
}
}
}
return minimum_point;
}

View File

@ -8,3 +8,19 @@ vec3 worldToLocal( vec3 _VERTEX, mat4 _MODEL_MATRIX )
{
return ( inverse( _MODEL_MATRIX ) * vec4( _VERTEX, 1.0 ) ).xyz;
}
vec2 tilingOffset( vec2 uv, vec4 tilingOffset )
{
uv *= tilingOffset.xy;
uv += tilingOffset.zw;
return uv;
}
vec2 tilingOffsetRepeat( vec2 uv, vec4 tilingOffset )
{
uv *= tilingOffset.xy;
uv += tilingOffset.zw;
return mod( uv, vec2(1,1) );
}

View File

@ -0,0 +1,54 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ColorPropertyName : Resource
{
[Export]
public string propertyName;
public void Set( Material material, Color value )
{
if ( material is ShaderMaterial )
{
var shaderMaterial = ( ShaderMaterial ) material;
shaderMaterial.SetShaderParameter( propertyName, value );
return;
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return;
}
propertyInfo.SetValue( material, value );
}
public Color Get( Material material )
{
if ( material is ShaderMaterial )
{
var m = ( ShaderMaterial ) material;
return (Color) m.GetShaderParameter( propertyName );
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return default( Color );
}
return (Color) propertyInfo.GetValue( material );
}
}
}

View File

@ -0,0 +1,54 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class FloatPropertyName : Resource
{
[Export]
public string propertyName;
public void Set( Material material, float value )
{
if ( material is ShaderMaterial )
{
var shaderMaterial = ( ShaderMaterial ) material;
shaderMaterial.SetShaderParameter( propertyName, value );
return;
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return;
}
propertyInfo.SetValue( material, value );
}
public float Get( Material material )
{
if ( material is ShaderMaterial )
{
var m = ( ShaderMaterial ) material;
return (float) m.GetShaderParameter( propertyName );
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return default( float );
}
return (float) propertyInfo.GetValue( material );
}
}
}

View File

@ -0,0 +1,53 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Vector2PropertyName : Resource
{
[Export]
public string propertyName;
public void Set( Material material, Vector2 value )
{
if ( material is ShaderMaterial )
{
var shaderMaterial = ( ShaderMaterial ) material;
shaderMaterial.SetShaderParameter( propertyName, value );
return;
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return;
}
propertyInfo.SetValue( material, value );
}
public Vector2 Get( Material material )
{
if ( material is ShaderMaterial )
{
var m = ( ShaderMaterial ) material;
return (Vector2) m.GetShaderParameter( propertyName );
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return default( Vector2 );
}
return (Vector2) propertyInfo.GetValue( material );
}
}
}

View File

@ -0,0 +1,54 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Vector3PropertyName : Resource
{
[Export]
public string propertyName;
public void Set( Material material, Vector3 value )
{
if ( material is ShaderMaterial )
{
var shaderMaterial = ( ShaderMaterial ) material;
shaderMaterial.SetShaderParameter( propertyName, value );
return;
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return;
}
propertyInfo.SetValue( material, value );
}
public Vector3 Get( Material material )
{
if ( material is ShaderMaterial )
{
var m = ( ShaderMaterial ) material;
return (Vector3) m.GetShaderParameter( propertyName );
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return default( Vector3 );
}
return (Vector3) propertyInfo.GetValue( material );
}
}
}

View File

@ -0,0 +1,54 @@
using Godot;
using System.Reflection;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Vector4PropertyName : Resource
{
[Export]
public string propertyName;
public void Set( Material material, Vector4 value )
{
if ( material is ShaderMaterial )
{
var shaderMaterial = ( ShaderMaterial ) material;
shaderMaterial.SetShaderParameter( propertyName, value );
return;
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return;
}
propertyInfo.SetValue( material, value );
}
public Vector4 Get( Material material )
{
if ( material is ShaderMaterial )
{
var m = ( ShaderMaterial ) material;
return (Vector4) m.GetShaderParameter( propertyName );
}
var propertyInfo = material.GetType().GetProperty( propertyName );
if ( propertyInfo == null )
{
return default( Vector4 );
}
return (Vector4) propertyInfo.GetValue( material );
}
}
}

View File

@ -0,0 +1,40 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
public class DictionaryList<K,V>:Dictionary<K,List<V>>
{
public void Add( K key, V value )
{
if ( ! ContainsKey( key ) )
{
this[ key ] = new List<V>();
}
this[ key ].Add( value );
}
public void Remove( K key, V value )
{
if ( ! ContainsKey( key ) )
{
return;
}
var list = this[ key ];
list.Remove( value );
if ( list.Count == 0 )
{
Remove( key );
}
}
}
}

View File

@ -8,6 +8,7 @@ using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TimeLineManager:RJTimeLineManager
{
@ -51,6 +52,38 @@ namespace Rokojori
return _idCounter;
}
public static float GetPosition( RJTimeLine timeLine )
{
var manager = Unique<TimeLineManager>.Get();
if ( manager == null )
{
return Time.GetTicksMsec() / 1000f;
}
var index = Arrays.IndexOf( manager.timeLines, timeLine );
index = Mathf.Max( index, 0 );
return (float) manager.GetPosition( index );
}
public static float GetPhase( RJTimeLine timeLine, float duration, float offset = 0 )
{
var time = GetPosition( timeLine ) + offset;
return ( time % duration ) / duration;
}
public static float GetRangePhase( RJTimeLine timeLine, float start, float end )
{
var time = GetPosition( timeLine );
var normalized = MathX.Normalize( time, start, end );
return normalized;
}
public override void _Process( double delta )
{
_runners.ForEach( r => r.UpdateTimeLine( delta, this ) );

View File

@ -7,6 +7,24 @@ namespace Rokojori
{
public class Arrays
{
public static int IndexOf<T>( T[] values, T other )
{
return Array.IndexOf( values, other );
}
public static int FindIndex<T>( T[] values, Func<T,bool> predicate )
{
for ( int i = 0; i < values.Length; i++ )
{
if ( predicate( values[ i ] ) )
{
return i;
}
}
return -1;
}
public static bool Contains <T>( T[] values, T other )
{
return Array.IndexOf( values, other ) != -1;

View File

@ -0,0 +1,35 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Rokojori
{
public enum BooleanLogicBinaryOperator
{
AND,
OR,
XOR,
NOR,
NAND,
XNOR
}
public class BooleanLogic
{
public static bool Binary( BooleanLogicBinaryOperator op, bool l, bool r )
{
switch ( op )
{
case BooleanLogicBinaryOperator.AND: return l && r;
case BooleanLogicBinaryOperator.OR: return l || r;
case BooleanLogicBinaryOperator.XOR: return l || r && ! ( l && r );
case BooleanLogicBinaryOperator.NOR: return ! ( l || r ) ;
case BooleanLogicBinaryOperator.NAND: return ! ( l && r ) ;
case BooleanLogicBinaryOperator.XNOR: return ( ! l && ! r ) || ( l && r ) ;
}
return false;
}
}
}

View File

@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Rokojori
{
public class Dictionaries
{
public static void RemoveAll<K,V>( Dictionary<K,V> dictionary, Func<K,V,bool> predicate )
{
var list = new List<K>();
foreach ( var kv in dictionary )
{
if ( predicate( kv.Key, kv.Value ) )
{
list.Add( kv.Key );
}
}
list.ForEach( k => dictionary.Remove( k ) );
}
}
}

View File

@ -43,7 +43,7 @@ namespace Rokojori
}
public static Type GetTypeByName( string name)
public static Type GetTypeByName( string name )
{
var assemblies = new List<Assembly>();
assemblies.AddRange( AppDomain.CurrentDomain.GetAssemblies() );
@ -166,7 +166,7 @@ namespace Rokojori
}
}
public static System.Reflection.FieldInfo GetFieldInfo( object instance, string memberName )
public static FieldInfo GetFieldInfo( object instance, string memberName )
{
var type = instance.GetType();
var fields = type.GetFields();

View File

@ -30,13 +30,12 @@ namespace Rokojori
{
var container = (UIStylePropertyContainer) control;
var left = UIStyle.Left( container );
var right = UIStyle.Right( container );
var top = UIStyle.Top( container );
var bottom = UIStyle.Bottom( container );
var offsetX = UINumber.Compute( control, left, 0 ) - UINumber.Compute( control, right, 0 );
var offsetY = UINumber.Compute( control, top, 0 ) - UINumber.Compute( control, bottom, 0 );
var offsetX = UINumber.Compute( control, UIStyleNumberProperty.Left, 0 ) -
UINumber.Compute( control, UIStyleNumberProperty.Right, 0 );
var offsetY = UINumber.Compute( control, UIStyleNumberProperty.Top, 0 ) -
UINumber.Compute( control, UIStyleNumberProperty.Bottom, 0 );
UILayouting.SetPosition( control, parentOffset + new Vector2( offset + offsetX, yOffset + offsetY ) );
}
@ -66,33 +65,66 @@ namespace Rokojori
var maxWidth = 0f;
var maxHeight = 0f;
var maxVerticalPlacementOffset = 0f;
var maxLineY = lines.Count > 0 ? lines[ lines.Count - 1 ].maxY : 0;
if ( UINumber.IsNullOrNone( UIStyle.Width( region ) ) )
region.contentSize.X = 0f;
region.contentSize.Y = maxLineY;
lines.ForEach( l => { region.contentSize.X = Mathf.Max( region.contentSize.X, l.maxX ); } );
if ( UINumber.IsNullOrNone( region, UIStyleNumberProperty.Width ) )
{
lines.ForEach( l => { maxWidth = Mathf.Max( maxWidth, l.maxX ); } );
maxWidth = region.contentSize.X;
}
else
{
maxWidth = UINumber.Compute( region, UIStyle.Width( region ) );
maxWidth = UINumber.Compute( region, UIStyleNumberProperty.Width );
}
if ( lines.Count > 0 )
if ( ! UINumber.IsNullOrNone( UIStyle.GetUINumberProperty( region, UIStyleNumberProperty.Height ) ) )
{
maxHeight = lines[ lines.Count - 1 ].maxY;
maxHeight = UINumber.Compute( region, UIStyleNumberProperty.Height );
maxVerticalPlacementOffset = maxHeight - maxLineY;
}
else if ( lines.Count > 0 )
{
maxHeight = maxLineY;
}
var margin = UINumber.Compute( region, UIStyle.Margin( region ), 0 );
var marginLeft = margin + UINumber.Compute( region, UIStyle.MarginLeft( region ), 0 );
var marginTop = margin + UINumber.Compute( region, UIStyle.MarginTop( region ), 0 );
var margin = UINumber.Compute( region, UIStyleNumberProperty.Margin, 0 );
var marginLeft = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginLeft, 0 );
var marginTop = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginTop, 0 );
var marginRight = margin + UINumber.Compute( region, UIStyle.MarginRight( region ), 0 );
var marginBottom = margin + UINumber.Compute( region, UIStyle.MarginBottom( region ), 0 );
var marginRight = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginRight, 0 );
var marginBottom = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginBottom, 0 );
var marginOffset = new Vector2( marginLeft, marginTop );
var verticalPlacementOffset = UINumber.Compute( region, UIStyleNumberProperty.VerticalPlacement, 0, maxVerticalPlacementOffset );
// if ( region.Name == "Audio" )
// {
// RJLog.Log( "Audio Placements",
// "maxHeight:", maxHeight,
// "maxLineY:", maxLineY,
// "maxVerticalPlacementOffset", maxVerticalPlacementOffset,
// "verticalPlacementOffset", verticalPlacementOffset
// );
// }
var marginOffset = new Vector2( marginLeft, marginTop + verticalPlacementOffset );
region.contentOffset = marginOffset;
PlaceControls( region, lines, marginOffset );
var alignmentWidth = maxWidth + marginLeft + marginRight;
var horizontalAlignment = UINumber.Compute( region, UIStyleNumberProperty.HorizontalAlignment, 0 );
region.contentOffset.X = Mathf.Lerp( 0, alignmentWidth - region.contentSize.X, horizontalAlignment / 100f );
var verticalMargins = marginTop + marginBottom;
var horizontalMargins = marginLeft + marginRight;
@ -124,12 +156,13 @@ namespace Rokojori
static List<Line> CreateLines( UIRegion region )
{
var x = 0f;
var width = UINumber.Compute( region, UIStyle.Width( region ), 100000000f );
var width = UINumber.Compute( region, UIStyleNumberProperty.Width, 100000000f );
var lines = new List<Line>();
var currentLine = new Line();
var elementSpacing = UINumber.Compute( region, UIStyle.ElementSpacing( region ), 0f );
var elementSpacing = UINumber.Compute( region, UIStyleNumberProperty.ElementSpacing, 0f );
var lineSpacing = UINumber.Compute( region, UIStyleNumberProperty.LineSpacing, 0f );
Nodes.ForEachDirectChild<Control>( region, c => UILayouting.UpdateChild( c ) );
@ -137,6 +170,10 @@ namespace Rokojori
Nodes.ForEachDirectChild<Control>( region,
child =>
{
if ( ! child.Visible )
{
return;
}
var styleContainer = child as UIStylePropertyContainer;
@ -150,12 +187,16 @@ namespace Rokojori
var nextEndX = x + cWidth + elementSpacing;
if ( nextEndX > width )
var lineWrap = UIStyle.LineWrap( styleContainer );
lineWrap = lineWrap == UILineWrap.___ ? UILineWrap.Wrap_On_Overflow : lineWrap;
if ( UILineWrap.Wrap_Never != lineWrap && ( UILineWrap.Wrap_Always == lineWrap || nextEndX > width ) )
{
lines.Add( currentLine );
currentLine = new Line();
x = 0;
currentLine.y = lines[ lines.Count - 1 ].maxY;
currentLine.y = lines[ lines.Count - 1 ].maxY + lineSpacing;
nextEndX = cWidth + elementSpacing;
}
@ -175,7 +216,7 @@ namespace Rokojori
if ( lines.Count > 0 )
{
currentLine.y = lines[ lines.Count - 1 ].maxY;
currentLine.y = lines[ lines.Count - 1 ].maxY + lineSpacing;
}
lines.Add( currentLine );
@ -188,17 +229,17 @@ namespace Rokojori
static void AdjustLines( UIRegion region, List<Line> lines )
{
var verticalAlignment = UINumber.Compute( region, region.verticalAlignment, 0.5f, 1 );
var verticalAlignment = UINumber.Compute( region, UIStyleNumberProperty.VerticalAlignment, 0.5f, 1 );
lines.ForEach( line => AdjustVerticalAlignment( region, line, verticalAlignment ) );
if ( UINumber.IsNullOrNone( UIStyle.Width( region ) ) )
if ( UINumber.IsNullOrNone( region, UIStyleNumberProperty.Width ) )
{
return;
}
var horizontalAlignment = UINumber.Compute( region, region.horizontalAlignment, 0.5f, 1 );
var maxWidth = UINumber.Compute( region, UIStyle.Width( region ), 0 );
var horizontalAlignment = UINumber.Compute( region, UIStyleNumberProperty.HorizontalAlignment, 0.5f, 1 );
var maxWidth = UINumber.Compute( region, UIStyleNumberProperty.Width, 0 );
lines.ForEach( line => AdjustHorizontalAlignment( region, line, horizontalAlignment, maxWidth ) );

View File

@ -6,6 +6,7 @@ namespace Rokojori
{
public enum UILayout
{
___,
Flow_Left_Top
}
}

View File

@ -6,14 +6,44 @@ namespace Rokojori
{
public class UILayouting
{
public static Vector2 GetContentSize( Control control )
{
if ( control is UIRegion )
{
return ( (UIRegion) control ).contentSize;
}
return control.Size;
}
public static Vector2 GetContentOffset( Control control )
{
if ( control is UIRegion )
{
return ( (UIRegion) control ).contentOffset;
}
return control.Size;
}
public static void UpdateChild( Control control )
{
if ( ! control.Visible )
{
return;
}
if ( control is UIRegion )
{
var childUIRegion = (UIRegion) control;
childUIRegion.Layout();
}
else if ( control is UIImage )
else if ( control is UIImage || control is UIBorderImage )
{
var tw = 0;
var th = 0;
if ( control is UIImage )
{
var uiImage = (UIImage) control;
@ -23,26 +53,102 @@ namespace Rokojori
return;
}
var tw = uiImage.Texture.GetWidth();
var th = uiImage.Texture.GetHeight();
tw = uiImage.Texture.GetWidth();
th = uiImage.Texture.GetHeight();
}
else if ( control is UIBorderImage )
{
var uiBorderImage = (UIBorderImage) control;
var w = UINumber.Compute( control, uiImage.width, tw, tw / 100f );
var h = UINumber.Compute( control, uiImage.height, th, th / 100f );
if ( uiBorderImage.Texture == null )
{
uiBorderImage.Size = new Vector2( 0, 0 );
return;
}
uiImage.Size = new Vector2( w, h );
tw = uiBorderImage.Texture.GetWidth();
th = uiBorderImage.Texture.GetHeight();
}
var container = (UIStylePropertyContainer) control;
var w = UINumber.Compute( control, UIStyleNumberProperty.Width, tw, tw / 100f );
var h = UINumber.Compute( control, UIStyleNumberProperty.Height, th, th / 100f );
control.Size = new Vector2( w, h );
if ( control is UIImage )
{
var uiImage = (UIImage) control;
if ( uiImage.Material != null )
{
var ui = Unique<UI>.Get();
if ( ui == null )
{
ui = NodesWalker.Get().GetInParents( control, n => n is UI ) as UI;
}
if ( ui == null )
{
RJLog.Log( "No UI Found" );
return;
}
//RJLog.Log( "Setting Size", ui.settings.sizePropertyName.propertyName, HierarchyName.Of( uiImage ) );
ui.settings.sizePropertyName.Set( uiImage.Material, uiImage.Size );
UIShaderProperties.UpdateProperties( uiImage, uiImage.Material );
return;
}
}
}
else if ( control is UIText )
{
var text = (UIText) control;
var container = (UIStylePropertyContainer) control;
text.uiTextLabelSettings.FontSize =
UINumber.ComputeInt( control, UIStyleNumberProperty.FontSize, UINumber.em(), UINumber.em() / 100f );
text.uiTextLabelSettings.FontColor =
UIColor.Compute( control, UIStyleColorProperty.FontColor, Colors.White );
text.uiTextLabelSettings.OutlineSize =
UINumber.ComputeInt( control, UIStyleNumberProperty.FontOutlineSize, 0 );
text.uiTextLabelSettings.OutlineColor =
UIColor.Compute( control, UIStyleColorProperty.FontOutlineColor, Colors.Transparent );
text.uiTextLabelSettings.ShadowSize =
UINumber.ComputeInt( control, UIStyleNumberProperty.FontShadowSize, 0 );
text.uiTextLabelSettings.ShadowColor =
UIColor.Compute( control, UIStyleColorProperty.FontShadowColor, Colors.Black );
text.uiTextLabelSettings.ShadowOffset = new Vector2(
UINumber.Compute( control, UIStyleNumberProperty.FontShadowOffsetX, 0 ),
UINumber.Compute( control, UIStyleNumberProperty.FontShadowOffsetY, 0 )
);
control.UpdateMinimumSize();
control.Size = control.GetMinimumSize();
}
else
{
control.UpdateMinimumSize();
control.Size = control.GetMinimumSize();
// control.UpdateMinimumSize();
// control.Size = control.GetMinimumSize();
}
UILayouting.UpdatePivot( control );
}
public static void SetPositionInParentAnchor( UIStylePropertyContainer region )
public static void SetPositionInParentAnchor( UIStylePropertyContainer container )
{
var control = (Control) region;
var control = (Control) container;
var p = NodesWalker.Get().Parent( control ) as Control;
var pWidth = p == null ? UI.GetWindowWidth( control ) : UILayouting.GetWidth( p );
@ -52,31 +158,37 @@ namespace Rokojori
var y = p.Position.Y;
if ( ! UINumber.IsNullOrNone( UIStyle.Left( region ) ))
if ( ! UINumber.IsNullOrNone( container, UIStyleNumberProperty.Left ) )
{
var left = UINumber.Compute( control, UIStyle.Left( region ), 0 );
var left = UINumber.Compute( control, UIStyleNumberProperty.Left, 0 );
x = left;
}
else if ( ! UINumber.IsNullOrNone( UIStyle.Right( region ) ) )
else if ( ! UINumber.IsNullOrNone( container, UIStyleNumberProperty.Right ) )
{
var right = UINumber.Compute( control, UIStyle.Right( region ), 0 );
var right = UINumber.Compute( control, UIStyleNumberProperty.Right, 0 );
x = ( pWidth - UILayouting.GetWidth( control ) ) - right;
}
if ( ! UINumber.IsNullOrNone( UIStyle.Top( region ) ))
if ( ! UINumber.IsNullOrNone( container, UIStyleNumberProperty.Top ))
{
var top = UINumber.Compute( control, UIStyle.Top( region ), 0 );
var top = UINumber.Compute( control, UIStyleNumberProperty.Top, 0 );
y = top;
}
else if ( ! UINumber.IsNullOrNone( UIStyle.Bottom( region ) ) )
else if ( ! UINumber.IsNullOrNone( container, UIStyleNumberProperty.Bottom ) )
{
var bottom = UINumber.Compute( control, UIStyle.Bottom( region ), 0 );
var bottom = UINumber.Compute( control, UIStyleNumberProperty.Bottom, 0 );
y = ( pHeight - UILayouting.GetHeight( control ) ) - bottom;
}
// var margin = UINumber.Compute( control, UIStyle.Margin( container ), 0 );
// var marginLeft = margin + UINumber.Compute( control, UIStyle.MarginLeft( container ), 0 );
// var marginTop = margin + UINumber.Compute( control, UIStyle.MarginRight( container ), 0 );
// var marginRight = margin + UINumber.Compute( control, UIStyle.MarginRight( container ), 0 );
// var marginBottom = margin + UINumber.Compute( control, UIStyle.MarginBottom( container ), 0 );
// UILayouting.SetPosition( control, new Vector2( x - ( marginLeft + marginRight ), y - ( marginTop + marginBottom ) ) );
UILayouting.SetPosition( control, new Vector2( x, y ) );
}
public static void UpdatePivot( Control c )
@ -88,18 +200,18 @@ namespace Rokojori
var container = c as UIStylePropertyContainer;
var pivotX = UINumber.Compute( c, UIStyle.PivotX( container ), 0.5f * c.Size.X, c.Size.X );
var pivotY = UINumber.Compute( c, UIStyle.PivotY( container ), 0.5f * c.Size.Y, c.Size.Y );
var pivotX = UINumber.Compute( c, UIStyleNumberProperty.PivotX, 0.5f * c.Size.X, c.Size.X );
var pivotY = UINumber.Compute( c, UIStyleNumberProperty.PivotY, 0.5f * c.Size.Y, c.Size.Y );
c.PivotOffset = new Vector2( pivotX, pivotY );
c.Rotation = UINumber.Compute( c, UIStyle.Rotation( container ), 0 );
c.Rotation = UINumber.Compute( c, UIStyleNumberProperty.Rotation, 0 );
var scale = UINumber.Compute( c, UIStyle.Scale( container ), 1, 1 );
var scale = UINumber.Compute( c, UIStyleNumberProperty.Scale, 1, 1 );
c.Scale = new Vector2(
UINumber.Compute( c, UIStyle.ScaleX( container ), 1, 1 ) ,
UINumber.Compute( c, UIStyle.ScaleY( container ), 1, 1 )
UINumber.Compute( c, UIStyleNumberProperty.ScaleX, 1, 1 ) ,
UINumber.Compute( c, UIStyleNumberProperty.ScaleY, 1, 1 )
) * scale;
}
@ -110,9 +222,9 @@ namespace Rokojori
{
var container = c as UIStylePropertyContainer;
var margin = UINumber.Compute( c, UIStyle.Margin( container ), 0 );
var marginLeft = margin + UINumber.Compute( c, UIStyle.MarginLeft( container ), 0 );
var marginTop = margin + UINumber.Compute( c, UIStyle.MarginTop( container ), 0 );
var margin = UINumber.Compute( c, UIStyleNumberProperty.Margin, 0 );
var marginLeft = margin + UINumber.Compute( c, UIStyleNumberProperty.MarginLeft, 0 );
var marginTop = margin + UINumber.Compute( c, UIStyleNumberProperty.MarginTop, 0 );
position.X += marginLeft;
position.Y += marginTop;
@ -128,9 +240,9 @@ namespace Rokojori
{
var container = c as UIStylePropertyContainer;
var margin = UINumber.Compute( c, UIStyle.Margin( container ), 0 );
var marginLeft = margin + UINumber.Compute( c, UIStyle.MarginLeft( container ), 0 );
var marginRight = margin + UINumber.Compute( c, UIStyle.MarginRight( container ), 0 );
var margin = UINumber.Compute( c, UIStyleNumberProperty.Margin, 0 );
var marginLeft = margin + UINumber.Compute( c, UIStyleNumberProperty.MarginLeft, 0 );
var marginRight = margin + UINumber.Compute( c, UIStyleNumberProperty.MarginRight, 0 );
return c.Size.X + marginLeft + marginRight;
@ -145,9 +257,9 @@ namespace Rokojori
{
var container = c as UIStylePropertyContainer;
var margin = UINumber.Compute( c, UIStyle.Margin( container ), 0 );
var marginTop = margin + UINumber.Compute( c, UIStyle.MarginTop( container ), 0 );
var marginBottom = margin + UINumber.Compute( c, UIStyle.MarginBottom( container ), 0 );
var margin = UINumber.Compute( c, UIStyleNumberProperty.Margin, 0 );
var marginTop = margin + UINumber.Compute( c, UIStyleNumberProperty.MarginTop, 0 );
var marginBottom = margin + UINumber.Compute( c, UIStyleNumberProperty.MarginBottom, 0 );
return c.Size.Y + marginTop + marginBottom;

View File

@ -0,0 +1,178 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class UIBorderImage:NinePatchRect, UIStylePropertyContainer
{
[Export]
public UIStyle parentStyle;
[ExportGroup("Size & Margins")]
[Export]
public UINumber width;
[Export]
public UINumber height;
[Export]
public UINumber margin;
[Export]
public UINumber marginLeft;
[Export]
public UINumber marginTop;
[Export]
public UINumber marginRight;
[Export]
public UINumber marginBottom;
[ExportGroup( "Position" )]
[Export]
public UIPosition position;
[Export]
public UILineWrap lineWrap;
[Export]
public UINumber left;
[Export]
public UINumber top;
[Export]
public UINumber right;
[Export]
public UINumber bottom;
[ExportGroup("Rotation & Scale")]
[Export]
public UINumber pivotX;
[Export]
public UINumber pivotY;
[Export]
public UINumber rotation;
[Export]
public UINumber scale;
[Export]
public UINumber scaleX;
[Export]
public UINumber scaleY;
public List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions()
{
return null;
}
public List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions()
{
return null;
}
[ExportGroup("Transitions")]
[Export]
public TransitionSettingsAll transitionSettings;
public TransitionSettingsAll GetTransitionSettingsAll()
{
return transitionSettings;
}
[Export]
public UINumberTransition[] numberTransitions;
public UINumberTransition[] GetNumberTransitions()
{
return numberTransitions;
}
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> activeNumberTransitions = new List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>>();
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> GetActiveUINumberTransitions()
{
return activeNumberTransitions;
}
[Export]
public UIColorTransition[] colorTransitions;
public UIColorTransition[] GetColorTransitions()
{
return colorTransitions;
}
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> activeColorTransitions = new List<ActiveStyleTransition<UIColor,UIStyleColorProperty>>();
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> GetActiveUIColorTransitions()
{
return activeColorTransitions;
}
public UIStyle GetUIStyleParent()
{
return parentStyle;
}
public UIPosition GetUIPosition()
{
return position;
}
public UILineWrap GetUILineWrap()
{
return lineWrap;
}
public UILayout GetUILayout()
{
return UILayout.___;
}
public ShaderUIColor[] GetShaderUIColors()
{
return null;
}
public ShaderUINumber[] GetShaderUINumbers()
{
return null;
}
public UIColor GetUIStyleColorProperty( UIStyleColorProperty property )
{
return null;
}
public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property )
{
switch ( property )
{
case UIStyleNumberProperty.Width: return width;
case UIStyleNumberProperty.Height: return height;
case UIStyleNumberProperty.Margin: return margin;
case UIStyleNumberProperty.MarginLeft: return marginLeft;
case UIStyleNumberProperty.MarginRight: return marginRight;
case UIStyleNumberProperty.MarginTop: return marginTop;
case UIStyleNumberProperty.MarginBottom: return marginBottom;
case UIStyleNumberProperty.Left: return left;
case UIStyleNumberProperty.Right: return right;
case UIStyleNumberProperty.Top: return top;
case UIStyleNumberProperty.Bottom: return bottom;
case UIStyleNumberProperty.PivotX: return pivotX;
case UIStyleNumberProperty.PivotY: return pivotY;
case UIStyleNumberProperty.Rotation: return rotation;
case UIStyleNumberProperty.Scale: return scale;
case UIStyleNumberProperty.ScaleX: return scaleX;
case UIStyleNumberProperty.ScaleY: return scaleY;
}
return null;
}
}
}

View File

@ -1,6 +1,7 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
@ -11,7 +12,7 @@ namespace Rokojori
[Export]
public UIStyle parentStyle;
[ExportCategory("Size & Margins")]
[ExportGroup("Size & Margins")]
[Export]
public UINumber width;
@ -32,10 +33,12 @@ namespace Rokojori
public UINumber marginBottom;
[ExportCategory("Position")]
[ExportGroup( "Position" )]
[Export]
public UIPosition position;
[Export]
public UILineWrap lineWrap;
[Export]
public UINumber left;
[Export]
public UINumber top;
@ -45,7 +48,7 @@ namespace Rokojori
public UINumber bottom;
[ExportCategory("Rotation & Scale")]
[ExportGroup( "Rotation & Scale" )]
[Export]
public UINumber pivotX;
[Export]
@ -62,6 +65,58 @@ namespace Rokojori
[Export]
public UINumber scaleY;
[ExportGroup( "Shader Properties" )]
[Export]
public ShaderUIColor[] colorProperties;
public List<ActiveStyleTransition<UIColor,ColorPropertyName>> activeShaderColorTransitions = new List<ActiveStyleTransition<UIColor,ColorPropertyName>>();
public List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions()
{
return activeShaderColorTransitions;
}
[Export]
public ShaderUINumber[] numberProperties;
public List<ActiveStyleTransition<UINumber,FloatPropertyName>> activeShaderNumberTransitions = new List<ActiveStyleTransition<UINumber,FloatPropertyName>>();
public List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions()
{
return activeShaderNumberTransitions;
}
[ExportGroup("Transitions")]
[Export]
public TransitionSettingsAll transitionSettings;
public TransitionSettingsAll GetTransitionSettingsAll()
{
return transitionSettings;
}
[Export]
public UINumberTransition[] numberTransitions;
public UINumberTransition[] GetNumberTransitions()
{
return numberTransitions;
}
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> activeNumberTransitions = new List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>>();
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> GetActiveUINumberTransitions()
{
return activeNumberTransitions;
}
[Export]
public UIColorTransition[] colorTransitions;
public UIColorTransition[] GetColorTransitions()
{
return colorTransitions;
}
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> activeColorTransitions = new List<ActiveStyleTransition<UIColor,UIStyleColorProperty>>();
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> GetActiveUIColorTransitions()
{
return activeColorTransitions;
}
public UIStyle GetUIStyleParent()
{
@ -73,6 +128,27 @@ namespace Rokojori
return position;
}
public UILineWrap GetUILineWrap()
{
return lineWrap;
}
public UILayout GetUILayout()
{
return UILayout.___;
}
public ShaderUIColor[] GetShaderUIColors()
{
return colorProperties;
}
public ShaderUINumber[] GetShaderUINumbers()
{
return numberProperties;
}
public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property )
{
switch ( property )
@ -104,5 +180,10 @@ namespace Rokojori
return null;
}
public UIColor GetUIStyleColorProperty( UIStyleColorProperty property )
{
return null;
}
}
}

View File

@ -1,5 +1,6 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
@ -10,7 +11,7 @@ namespace Rokojori
[Export]
public UIStyle parentStyle;
[ExportCategory( "Layout" )]
[ExportGroup( "Layout" )]
[Export]
public UILayout layout;
@ -18,6 +19,8 @@ namespace Rokojori
public UINumber horizontalAlignment;
[Export]
public UINumber verticalAlignment;
[Export]
public UINumber verticalPlacement;
[Export]
public UINumber elementSpacing;
@ -25,7 +28,7 @@ namespace Rokojori
public UINumber lineSpacing;
[ExportCategory( "Size & Margins" )]
[ExportGroup( "Size & Margins" )]
[Export]
public UINumber width;
[Export]
@ -44,7 +47,22 @@ namespace Rokojori
[Export]
public UINumber marginBottom;
[ExportCategory( "Font" )]
[ExportGroup( "Position" )]
[Export]
public UIPosition position;
[Export]
public UILineWrap lineWrap;
[Export]
public UINumber left;
[Export]
public UINumber top;
[Export]
public UINumber right;
[Export]
public UINumber bottom;
[ExportGroup( "Font" )]
[Export]
public Font font;
[Export]
@ -62,20 +80,55 @@ namespace Rokojori
[Export]
public UIColor shadowColor;
[Export]
public UINumber shadowOffsetX;
[Export]
public UINumber shadowOffsetY;
public List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions()
{
return null;
}
public List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions()
{
return null;
}
[ExportCategory( "Position" )]
[ExportGroup("Transitions")]
[Export]
public UIPosition position;
[Export]
public UINumber left;
[Export]
public UINumber top;
[Export]
public UINumber right;
[Export]
public UINumber bottom;
public TransitionSettingsAll transitionSettings;
public TransitionSettingsAll GetTransitionSettingsAll()
{
return transitionSettings;
}
[Export]
public UINumberTransition[] numberTransitions;
public UINumberTransition[] GetNumberTransitions()
{
return numberTransitions;
}
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> activeNumberTransitions = new List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>>();
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> GetActiveUINumberTransitions()
{
return activeNumberTransitions;
}
[Export]
public UIColorTransition[] colorTransitions;
public UIColorTransition[] GetColorTransitions()
{
return colorTransitions;
}
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> activeColorTransitions = new List<ActiveStyleTransition<UIColor,UIStyleColorProperty>>();
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> GetActiveUIColorTransitions()
{
return activeColorTransitions;
}
public UIStyle GetUIStyleParent()
{
@ -87,6 +140,26 @@ namespace Rokojori
return position;
}
public UILineWrap GetUILineWrap()
{
return lineWrap;
}
public UILayout GetUILayout()
{
return layout;
}
public ShaderUIColor[] GetShaderUIColors()
{
return null;
}
public ShaderUINumber[] GetShaderUINumbers()
{
return null;
}
public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property )
{
switch ( property )
@ -99,7 +172,9 @@ namespace Rokojori
case UIStyleNumberProperty.Width: return width;
case UIStyleNumberProperty.Height: return height;
case UIStyleNumberProperty.HorizontalAlignment: return horizontalAlignment;
case UIStyleNumberProperty.VerticalAlignment: return verticalAlignment;
case UIStyleNumberProperty.VerticalPlacement: return verticalPlacement;
case UIStyleNumberProperty.ElementSpacing: return elementSpacing;
case UIStyleNumberProperty.LineSpacing: return lineSpacing;
@ -109,20 +184,48 @@ namespace Rokojori
case UIStyleNumberProperty.MarginRight: return marginRight;
case UIStyleNumberProperty.MarginTop: return marginTop;
case UIStyleNumberProperty.MarginBottom: return marginBottom;
case UIStyleNumberProperty.FontSize: return fontSize;
case UIStyleNumberProperty.FontOutlineSize: return outlineSize;
case UIStyleNumberProperty.FontShadowSize: return shadowSize;
case UIStyleNumberProperty.FontShadowOffsetX: return shadowOffsetX;
case UIStyleNumberProperty.FontShadowOffsetY: return shadowOffsetY;
}
return null;
}
public UIColor GetUIStyleColorProperty( UIStyleColorProperty property )
{
switch ( property )
{
case UIStyleColorProperty.FontColor: return fontColor;
case UIStyleColorProperty.FontOutlineColor: return outlineColor;
case UIStyleColorProperty.FontShadowColor: return shadowColor;
}
return null;
}
public void Layout()
{
var layout = UIStyle.Layout( this );
switch ( layout )
{
case UILayout.Flow_Left_Top: UIFlowLayout.Apply( this ); break;
case UILayout.___:
case UILayout.Flow_Left_Top:
{
UIFlowLayout.Apply( this );
}
break;
}
}
public Vector2 contentSize = Vector2.Zero;
public Vector2 contentOffset = Vector2.Zero;
}
}

View File

@ -1,6 +1,7 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
@ -11,10 +12,30 @@ namespace Rokojori
[Export]
public UIStyle parentStyle;
[ExportGroup( "Font" )]
[Export]
public Font font;
[Export]
public UINumber fontSize;
[Export]
public UIColor fontColor;
[ExportCategory("Size & Margins")]
[Export]
public UINumber outlineSize;
[Export]
public UIColor outlineColor;
[Export]
public UINumber shadowSize;
[Export]
public UIColor shadowColor;
[Export]
public UINumber shadowOffsetX;
[Export]
public UINumber shadowOffsetY;
[ExportGroup("Size & Margins")]
[Export]
public UINumber width;
@ -35,10 +56,12 @@ namespace Rokojori
public UINumber marginBottom;
[ExportCategory("Position")]
[ExportGroup( "Position" )]
[Export]
public UIPosition position;
[Export]
public UILineWrap lineWrap;
[Export]
public UINumber left;
[Export]
public UINumber top;
@ -48,7 +71,7 @@ namespace Rokojori
public UINumber bottom;
[ExportCategory("Rotation & Scale")]
[ExportGroup("Rotation & Scale")]
[Export]
public UINumber pivotX;
[Export]
@ -65,6 +88,72 @@ namespace Rokojori
[Export]
public UINumber scaleY;
public List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions()
{
return null;
}
public List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions()
{
return null;
}
[ExportGroup("Transitions")]
[Export]
public TransitionSettingsAll transitionSettings;
public TransitionSettingsAll GetTransitionSettingsAll()
{
return transitionSettings;
}
[Export]
public UINumberTransition[] numberTransitions;
public UINumberTransition[] GetNumberTransitions()
{
return numberTransitions;
}
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> activeNumberTransitions = new List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>>();
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> GetActiveUINumberTransitions()
{
return activeNumberTransitions;
}
[Export]
public UIColorTransition[] colorTransitions;
public UIColorTransition[] GetColorTransitions()
{
return colorTransitions;
}
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> activeColorTransitions = new List<ActiveStyleTransition<UIColor,UIStyleColorProperty>>();
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> GetActiveUIColorTransitions()
{
return activeColorTransitions;
}
LabelSettings _labelSettings;
public LabelSettings uiTextLabelSettings
{
get
{
if ( _labelSettings == null )
{
_labelSettings = new LabelSettings();
}
LabelSettings = _labelSettings;
return _labelSettings;
}
set
{
_labelSettings = value.Duplicate() as LabelSettings;
LabelSettings = _labelSettings;
}
}
public UIStyle GetUIStyleParent()
{
return parentStyle;
@ -75,6 +164,28 @@ namespace Rokojori
return position;
}
public UILineWrap GetUILineWrap()
{
return lineWrap;
}
public UILayout GetUILayout()
{
return UILayout.___;
}
public ShaderUIColor[] GetShaderUIColors()
{
return null;
}
public ShaderUINumber[] GetShaderUINumbers()
{
return null;
}
public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property )
{
switch ( property )
@ -102,6 +213,24 @@ namespace Rokojori
case UIStyleNumberProperty.Scale: return scale;
case UIStyleNumberProperty.ScaleX: return scaleX;
case UIStyleNumberProperty.ScaleY: return scaleY;
case UIStyleNumberProperty.FontSize: return fontSize;
case UIStyleNumberProperty.FontOutlineSize: return outlineSize;
case UIStyleNumberProperty.FontShadowSize: return shadowSize;
case UIStyleNumberProperty.FontShadowOffsetX: return shadowOffsetX;
case UIStyleNumberProperty.FontShadowOffsetY: return shadowOffsetY;
}
return null;
}
public UIColor GetUIStyleColorProperty( UIStyleColorProperty property )
{
switch ( property )
{
case UIStyleColorProperty.FontColor: return fontColor;
case UIStyleColorProperty.FontOutlineColor: return outlineColor;
case UIStyleColorProperty.FontShadowColor: return shadowColor;
}
return null;

View File

@ -0,0 +1,45 @@
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class OnSliderValueChange : Node
{
[Export]
public HSlider slider;
HSlider _connectedSlider = null;
[Export]
public RJAction onChange;
public void Update()
{
if ( _connectedSlider == slider )
{
return;
}
if ( _connectedSlider != null )
{
_connectedSlider.Changed -= OnChanged;
}
_connectedSlider = slider;
if ( _connectedSlider != null )
{
_connectedSlider.Changed += OnChanged;
}
}
void OnChanged()
{
Actions.Trigger( onChange );
}
}
}

View File

@ -0,0 +1,7 @@
[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://v423srfwpna8"]
[ext_resource type="Script" path="res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_4guhd"]
[resource]
script = ExtResource("1_4guhd")
propertyName = "borderRadius"

View File

@ -0,0 +1,7 @@
[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://dbg2rgj5s7uqn"]
[ext_resource type="Script" path="res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_2hnh6"]
[resource]
script = ExtResource("1_2hnh6")
propertyName = "offset"

View File

@ -0,0 +1,7 @@
[gd_resource type="Resource" script_class="FloatPropertyName" load_steps=2 format=3 uid="uid://dngbeoiix72sf"]
[ext_resource type="Script" path="res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Properties/FloatPropertyName.cs" id="1_0sf2s"]
[resource]
script = ExtResource("1_0sf2s")
propertyName = "strokeSize"

View File

@ -0,0 +1,24 @@
using Godot;
using Rokojori;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ShaderUIColor : Resource
{
[Export]
public ColorPropertyName colorPropertyName;
[Export]
public UIColor color;
public void UpdateMaterial( UIStylePropertyContainer container, Material material )
{
var colorValue = UIColor.Compute( container as Control, color, Colors.White );
colorPropertyName.Set( material, colorValue );
}
}
}

View File

@ -0,0 +1,46 @@
using Godot;
using Rokojori;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ShaderUINumber : Resource
{
[Export]
public FloatPropertyName floatPropertyName;
[Export]
public UINumber number;
[Export]
public TransitionSettings transitionSettings;
public void UpdateMaterial( UIStylePropertyContainer container, Material material )
{
var control = container as Control;
var numberValue = UINumber.Compute( container as Control, number, 0f );
var allSettings = UIStyle.GetTransitionSettingsAll( container );
var usesTransition = transitionSettings != null || allSettings != null && allSettings.transitionAllProperties;
if ( ! usesTransition )
{
floatPropertyName.Set( material, numberValue );
return;
}
/*
var transitionValue = ActiveStyleTransition<UINumber,FloatPropertyName>.ProcessTransition<float>(
container, numberValue, container.GetActiveShaderUINumberTransitions(),
number, floatPropertyName,
()=>{ return UIStyle.GetTransitionSettings( container ); }
);
*/
}
}
}

View File

@ -0,0 +1,42 @@
using Godot;
using Rokojori;
namespace Rokojori
{
public partial class UIShaderProperties
{
public static void UpdateProperties( UIStylePropertyContainer container, Material material )
{
var numbers = container.GetShaderUINumbers();
if ( numbers != null )
{
foreach ( var n in numbers )
{
if ( n == null )
{
continue;
}
n.UpdateMaterial( container, material );
}
}
var colors = container.GetShaderUIColors();
if ( colors != null )
{
foreach ( var c in colors )
{
if ( c == null )
{
continue;
}
c.UpdateMaterial( container, material );
}
}
}
}
}

View File

@ -0,0 +1,7 @@
[gd_resource type="Resource" script_class="Vector2PropertyName" load_steps=2 format=3 uid="uid://bhy8b3gopkq4m"]
[ext_resource type="Script" path="res://Scripts/Rokojori/Rokojori-Action-Library/Runtime/Shading/Properties/Vector2PropertyName.cs" id="1_t5csl"]
[resource]
script = ExtResource("1_t5csl")
propertyName = "size"

View File

@ -4,12 +4,129 @@ using Rokojori;
namespace Rokojori
{
public enum UIColorAnimationBlend
{
Multiply,
Add,
Replace
}
[Tool]
[GlobalClass]
public partial class UIColor : Resource
{
[Export]
public Color color;
public Color color = Colors.White;
[Export]
public bool isAnimated = false;
[Export]
public Gradient animationGradient;
[Export]
public ColorBlendModeType blendMode = ColorBlendModeType.Multiply;
[Export]
public float animationDuration = 1;
[Export]
public float animationOffset = 0;
[Export]
public RJTimeLine timeLine;
public static Color Compute( Control control, UIStyleColorProperty property, Color defaultColor )
{
var container = control as UIStylePropertyContainer;
var transition = UIStyle.GetTransition( container, property );
var uiColor = UIStyle.GetUIColorProperty( control as UIStylePropertyContainer, property );
var computedColor = Compute( control, uiColor, defaultColor );
var allSettings = UIStyle.GetTransitionSettingsAll( container );
var usesTransition = transition != null || allSettings != null && allSettings.transitionAllProperties;
if ( ! usesTransition )
{
return computedColor;
}
var activeNumberTransitions = container.GetActiveUIColorTransitions();
var propertyTransition = activeNumberTransitions.Find( t => t != null && t.propertyType == property );
if ( propertyTransition == null )
{
propertyTransition = new ActiveStyleTransition<UIColor, UIStyleColorProperty>();
propertyTransition.propertyType = property;
propertyTransition.value = uiColor;
propertyTransition.transitioning = false;
activeNumberTransitions.Add( propertyTransition );
return computedColor;
}
else
{
if ( propertyTransition.value != uiColor && ! propertyTransition.transitioning && UIStyle.GetTransitionSettings( container, property ) != null)
{
var transitionSettings = UIStyle.GetTransitionSettings( container, property );
propertyTransition.timeLine = transitionSettings.timeLine;
propertyTransition.start = TimeLineManager.GetPosition( transitionSettings.timeLine );
propertyTransition.end = propertyTransition.start + transitionSettings.duration;
propertyTransition.transitioning = true;
propertyTransition.curve = transitionSettings.curve;
}
}
if ( propertyTransition.value == uiColor )
{
propertyTransition.transitioning = false;
return computedColor;
}
var computedTransitionValue = Compute( control, propertyTransition.value, defaultColor );
var transitionPhase = TimeLineManager.GetRangePhase( propertyTransition.timeLine, propertyTransition.start, propertyTransition.end );
if ( transitionPhase >= 1 )
{
activeNumberTransitions.Remove( propertyTransition );
}
var amount = MathX.Clamp01( transitionPhase );
var curveAmount = amount;
if ( propertyTransition.curve != null )
{
curveAmount = propertyTransition.curve.Sample( curveAmount );
}
return ColorX.Lerp( computedTransitionValue, computedColor, curveAmount );
}
public static Color Compute( Control control, UIColor color, Color defaultColor )
{
if ( color == null )
{
return defaultColor;
}
if ( ! color.isAnimated || color.animationGradient == null )
{
return color.color;
}
var phase = TimeLineManager.GetPhase( color.timeLine, color.animationDuration, color.animationOffset );
var gradientColor = color.animationGradient.Sample( phase );
return ColorBlendMode.Blend( color.blendMode, color.color, gradientColor );
}
}
}

View File

@ -0,0 +1,15 @@
using Godot;
using Rokojori;
namespace Rokojori
{
public enum UILineWrap
{
___,
Wrap_On_Overflow,
Wrap_Always,
Wrap_Never
}
}

View File

@ -15,15 +15,36 @@ namespace Rokojori
[Export]
public string unit = "";
[ExportGroup("Animation")]
[Export]
public bool isAnimated;
[Export]
public Curve animationCurve;
[Export]
public float animationDuration;
[Export]
public float animationOffset;
[Export]
public RJTimeLine timeLine;
public bool IsNone => unit == "none";
public float Compute( Control control, float alternative )
public float ComputeRaw( Control control, float alternative )
{
return UINumber.Compute( control, this, alternative );
}
public static bool IsNullOrNone( UIStylePropertyContainer container, UIStyleNumberProperty property )
{
return IsNullOrNone( UIStyle.GetUINumberProperty( container, property ) );
}
public static bool IsNullOrNone( UINumber number )
{
if ( number == null )
@ -34,6 +55,154 @@ namespace Rokojori
return number.IsNone;
}
public static int ComputeInt( Control control, UINumber number, float alternative = 0, float relative = 100 )
{
return Mathf.RoundToInt( Compute( control, number, alternative, relative ) );
}
public static int ComputeInt( Control control, UIStyleNumberProperty property, float alternative = 0, float relative = 100 )
{
return Mathf.RoundToInt( Compute( control, property, alternative, relative ) );
}
public bool Equals( UINumber other )
{
if ( other == this )
{
return true;
}
if ( other == null )
{
return false;
}
if ( other.value != value )
{
return false;
}
if ( other.unit != unit )
{
return false;
}
if ( other.isAnimated != isAnimated )
{
return false;
}
if ( other.animationCurve != animationCurve )
{
return false;
}
if ( other.animationDuration != animationDuration )
{
return false;
}
if ( other.animationOffset != animationOffset )
{
return false;
}
if ( other.timeLine != timeLine )
{
return false;
}
return true;
}
public void CopyForm( UINumber other )
{
if ( other == this )
{
return;
}
value = other.value;
unit = other.unit;
isAnimated = other.isAnimated;
animationCurve = other.animationCurve;
animationDuration = other.animationDuration;
animationOffset = other.animationOffset;
timeLine = other.timeLine;
}
public static float Compute( Control control, UIStyleNumberProperty property, float alternative = 0, float relative = 100 )
{
var container = control as UIStylePropertyContainer;
var transition = UIStyle.GetTransition( container, property );
var number = UIStyle.GetUINumberProperty( control as UIStylePropertyContainer, property );
var computedValue = Compute( control, number, alternative, relative );
var allSettings = UIStyle.GetTransitionSettingsAll( container );
var usesTransition = transition != null || allSettings != null && allSettings.transitionAllProperties;
if ( ! usesTransition )
{
return computedValue;
}
var activeNumberTransitions = container.GetActiveUINumberTransitions();
var propertyTransition = activeNumberTransitions.Find( t => t != null && t.propertyType == property );
if ( propertyTransition == null )
{
propertyTransition = new ActiveStyleTransition<UINumber, UIStyleNumberProperty>();
propertyTransition.propertyType = property;
propertyTransition.value = number;
propertyTransition.transitioning = false;
activeNumberTransitions.Add( propertyTransition );
return computedValue;
}
else
{
if ( propertyTransition.value != number && ! propertyTransition.transitioning && UIStyle.GetTransitionSettings( container, property ) != null )
{
var transitionSettings = UIStyle.GetTransitionSettings( container, property );
propertyTransition.timeLine = transitionSettings.timeLine;
propertyTransition.start = TimeLineManager.GetPosition( transitionSettings.timeLine );
propertyTransition.end = propertyTransition.start + transitionSettings.duration;
propertyTransition.transitioning = true;
propertyTransition.curve = transitionSettings.curve;
}
}
if ( propertyTransition.value == number )
{
propertyTransition.transitioning = false;
return computedValue;
}
var computedTransitionValue = Compute( control, propertyTransition.value, alternative, relative );
var transitionPhase = TimeLineManager.GetRangePhase( propertyTransition.timeLine, propertyTransition.start, propertyTransition.end );
if ( transitionPhase >= 1 )
{
activeNumberTransitions.Remove( propertyTransition );
}
var amount = MathX.Clamp01( transitionPhase );
var curveAmount = amount;
if ( propertyTransition.curve != null )
{
curveAmount = propertyTransition.curve.Sample( curveAmount );
}
return Mathf.Lerp( computedTransitionValue, computedValue, curveAmount );
}
public static float Compute( Control control, UINumber number, float alternative = 0, float relative = 100 )
{
@ -52,12 +221,27 @@ namespace Rokojori
static UI _ui;
static float em()
public static float em()
{
return _ui == null ? 12 : _ui.X_computedFontSizePixels;
}
public static float Compute( Control control, UINumber number, float width, float height, float relative )
{
var value = ComputeWithoutAnimation( control, number, width, height, relative );
if ( ! number.isAnimated || number.animationCurve == null )
{
return value;
}
var phase = TimeLineManager.GetPhase( number.timeLine, number.animationDuration, number.animationOffset );
return number.animationCurve.Sample( phase ) * value;
}
static float ComputeWithoutAnimation( Control control, UINumber number, float width, float height, float relative )
{
if ( number == null )
{
@ -92,13 +276,37 @@ namespace Rokojori
return number.value / 100f * ( parent == null ? width : parent.Size.X );
}
case "cw":
{
var parent = control.GetParent<Control>();
return number.value / 100f * ( parent == null ? width : UILayouting.GetContentSize( parent ).X );
}
case "ch":
{
var parent = control.GetParent<Control>();
return number.value / 100f * ( parent == null ? height : UILayouting.GetContentSize( parent ).Y );
}
case "cx":
{
var parent = control.GetParent<Control>();
return number.value / 100f * ( parent == null ? 0 : UILayouting.GetContentOffset( parent ).X );
}
case "cy":
{
var parent = control.GetParent<Control>();
return number.value / 100f * ( parent == null ? 0 : UILayouting.GetContentOffset( parent ).Y );
}
case "ph":
{
var parent = control.GetParent<Control>();
return number.value / 100f * ( parent == null ? height : parent.Size.Y );
}
case "px": case "":
case "":
{
return number.value;
}
@ -111,12 +319,21 @@ namespace Rokojori
var expression = new Expression();
var expressionText = number.unit == null ? "" : RegexUtility.Replace( number.unit, "%", " * relative " );
var parseResult = expression.Parse( expressionText,
new string[]
{
"em","vw", "vh", "pw", "ph", "px", "relative", "value" }
"em",
"vw", "vh",
"pw", "ph",
"cw","ch",
"cx","cy",
"relative",
"value"
}
);
if ( Error.Ok != parseResult )
@ -128,14 +345,26 @@ namespace Rokojori
var inputs = new Godot.Collections.Array();
inputs.Add( em() );
inputs.Add( width / 100f );
inputs.Add( height / 100f );
// vw, vh
inputs.Add( width / 100f ); inputs.Add( height / 100f );
// pw, ph
inputs.Add( ( parentControl == null ? width : parentControl.Size.X ) / 100f );
inputs.Add( ( parentControl == null ? height : parentControl.Size.Y ) / 100f );
inputs.Add( 1 );
// cw, ch
inputs.Add( ( parentControl == null ? width : UILayouting.GetContentSize( parentControl ).X ) / 100f );
inputs.Add( ( parentControl == null ? width : UILayouting.GetContentSize( parentControl ).Y ) / 100f );
// cx, cy
inputs.Add( ( parentControl == null ? 0 : UILayouting.GetContentOffset( parentControl ).X ) / 100f );
inputs.Add( ( parentControl == null ? 0 : UILayouting.GetContentOffset( parentControl ).Y ) / 100f );
// "relative"
inputs.Add( relative / 100f );
// value
if ( number.unit.IndexOf( "value" ) == -1 )
{
inputs.Add( 0 );

View File

@ -1,6 +1,7 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
@ -12,7 +13,7 @@ namespace Rokojori
[Export]
public UIStyle parentStyle;
[ExportCategory( "Layout" )]
[ExportGroup( "Layout" )]
[Export]
public UILayout layout;
@ -20,6 +21,8 @@ namespace Rokojori
public UINumber horizontalAlignment;
[Export]
public UINumber verticalAlignment;
[Export]
public UINumber verticalPlacement;
[Export]
public UINumber elementSpacing;
@ -27,7 +30,7 @@ namespace Rokojori
public UINumber lineSpacing;
[ExportCategory( "Size & Margins" )]
[ExportGroup( "Size & Margins" )]
[Export]
public UINumber width;
[Export]
@ -46,7 +49,7 @@ namespace Rokojori
[Export]
public UINumber marginBottom;
[ExportCategory( "Font" )]
[ExportGroup( "Font" )]
[Export]
public Font font;
[Export]
@ -64,10 +67,17 @@ namespace Rokojori
[Export]
public UIColor shadowColor;
[ExportCategory( "Position" )]
[Export]
public UINumber shadowOffsetX;
[Export]
public UINumber shadowOffsetY;
[ExportGroup( "Position" )]
[Export]
public UIPosition position;
[Export]
public UILineWrap lineWrap;
[Export]
public UINumber left;
[Export]
public UINumber top;
@ -76,6 +86,73 @@ namespace Rokojori
[Export]
public UINumber bottom;
[ExportGroup("Rotation & Scale")]
[Export]
public UINumber pivotX;
[Export]
public UINumber pivotY;
[Export]
public UINumber rotation;
[Export]
public UINumber scale;
[Export]
public UINumber scaleX;
[Export]
public UINumber scaleY;
[ExportGroup( "Shader Properties" )]
[Export]
public ShaderUIColor[] colorProperties;
[Export]
public ShaderUINumber[] numberProperties;
public List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions()
{
return null;
}
public List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions()
{
return null;
}
[ExportGroup("Transitions")]
[Export]
public TransitionSettingsAll transitionSettings;
public TransitionSettingsAll GetTransitionSettingsAll()
{
return transitionSettings;
}
[Export]
public UINumberTransition[] numberTransitions;
public UINumberTransition[] GetNumberTransitions()
{
return numberTransitions;
}
public List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> GetActiveUINumberTransitions()
{
return null;
}
[Export]
public UIColorTransition[] colorTransitions;
public UIColorTransition[] GetColorTransitions()
{
return colorTransitions;
}
public List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> GetActiveUIColorTransitions()
{
return null;;
}
public UIStyle GetUIStyleParent()
{
return parentStyle;
@ -86,6 +163,26 @@ namespace Rokojori
return position;
}
public UILineWrap GetUILineWrap()
{
return lineWrap;
}
public UILayout GetUILayout()
{
return layout;
}
public ShaderUIColor[] GetShaderUIColors()
{
return colorProperties;
}
public ShaderUINumber[] GetShaderUINumbers()
{
return numberProperties;
}
public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property )
{
switch ( property )
@ -95,6 +192,9 @@ namespace Rokojori
case UIStyleNumberProperty.Top: return top;
case UIStyleNumberProperty.Bottom: return bottom;
case UIStyleNumberProperty.HorizontalAlignment: return horizontalAlignment;
case UIStyleNumberProperty.VerticalAlignment: return verticalAlignment;
case UIStyleNumberProperty.VerticalPlacement: return verticalPlacement;
case UIStyleNumberProperty.ElementSpacing: return elementSpacing;
case UIStyleNumberProperty.LineSpacing: return lineSpacing;
@ -102,12 +202,40 @@ namespace Rokojori
case UIStyleNumberProperty.Width: return width;
case UIStyleNumberProperty.Height: return height;
case UIStyleNumberProperty.Margin: return margin;
case UIStyleNumberProperty.MarginLeft: return marginLeft;
case UIStyleNumberProperty.MarginRight: return marginRight;
case UIStyleNumberProperty.MarginTop: return marginTop;
case UIStyleNumberProperty.MarginBottom: return marginBottom;
case UIStyleNumberProperty.FontSize: return fontSize;
case UIStyleNumberProperty.FontOutlineSize: return outlineSize;
case UIStyleNumberProperty.FontShadowSize: return shadowSize;
case UIStyleNumberProperty.FontShadowOffsetX: return shadowOffsetX;
case UIStyleNumberProperty.FontShadowOffsetY: return shadowOffsetY;
case UIStyleNumberProperty.PivotX: return pivotX;
case UIStyleNumberProperty.PivotY: return pivotY;
case UIStyleNumberProperty.Rotation: return rotation;
case UIStyleNumberProperty.Scale: return scale;
case UIStyleNumberProperty.ScaleX: return scaleX;
case UIStyleNumberProperty.ScaleY: return scaleY;
}
return null;
}
public UIColor GetUIStyleColorProperty( UIStyleColorProperty property )
{
switch ( property )
{
case UIStyleColorProperty.FontColor: return fontColor;
case UIStyleColorProperty.FontOutlineColor: return outlineColor;
case UIStyleColorProperty.FontShadowColor: return shadowColor;
}
return null;
@ -132,8 +260,367 @@ namespace Rokojori
return GetReferenceableNumberProperty( style, property );
}
public static UIColor GetReferenceableColorProperty( UIStylePropertyContainer container, UIStyleColorProperty property )
{
if ( container == null )
{
return null;
}
var ownProperty = container.GetUIStyleColorProperty( property );
if ( ownProperty != null )
{
return ownProperty;
}
var style = container.GetUIStyleParent();
return GetReferenceableColorProperty( style, property );
}
public static UINumberTransition GetTransition( UIStylePropertyContainer container, UIStyleNumberProperty property )
{
switch ( property )
{
case UIStyleNumberProperty.FontSize:
case UIStyleNumberProperty.FontOutlineSize:
case UIStyleNumberProperty.FontShadowSize:
case UIStyleNumberProperty.FontShadowOffsetX:
case UIStyleNumberProperty.FontShadowOffsetY:
{
return _GetTransition( true, container, property );
}
}
return _GetTransition( false, container, property );
}
static UINumberTransition _GetTransition( bool inheritable, UIStylePropertyContainer container, UIStyleNumberProperty property )
{
var transitions = container.GetNumberTransitions();
var index = transitions == null ? -1 : Arrays.FindIndex( transitions, t => t != null && t.property == property );
if ( index != -1 )
{
return transitions[ index ];
}
var styleParent = container.GetUIStyleParent();
if ( styleParent == null )
{
return null;
}
var styleParentTransition = _GetTransition( false, styleParent, property );
if ( styleParentTransition != null )
{
return styleParentTransition;
}
if ( ! ( container is Control ) || ! inheritable )
{
return null;
}
var control = container as Control;
UINumberTransition parentTransition = null;
NodesWalker.Get().GetInParents( control,
it =>
{
if ( ! ( it is UIStylePropertyContainer ) )
{
return false;
}
var container = (UIStylePropertyContainer) it;
parentTransition = _GetTransition( true, container, property );
return parentTransition != null;
}
);
return parentTransition;
}
public static TransitionSettingsAll GetTransitionSettingsAll( UIStylePropertyContainer container )
{
var settings = container.GetTransitionSettingsAll();
if ( settings != null )
{
return settings;
}
var styleParent = container.GetUIStyleParent();
if ( styleParent == null )
{
return null;
}
return GetTransitionSettingsAll( styleParent );
}
/*public static TransitionSettings GetTransitionSettings( UIStylePropertyContainer container, FloatPropertyName floatProperty )
{
var transition = GetTransition( container, floatProperty );
if ( transition != null && transition.settings != null )
{
return transition.settings;
}
var containerSettings = container.GetTransitionSettingsAll();
if ( containerSettings != null )
{
return containerSettings;
}
var styleParent = container.GetUIStyleParent();
if ( styleParent == null )
{
return null;
}
return GetTransitionSettings( styleParent, floatProperty );
}
*/
public static TransitionSettings GetTransitionSettings( UIStylePropertyContainer container, UIStyleColorProperty colorProperty )
{
var transition = GetTransition( container, colorProperty );
if ( transition != null && transition.settings != null )
{
return transition.settings;
}
var containerSettings = container.GetTransitionSettingsAll();
if ( containerSettings != null )
{
return containerSettings;
}
var styleParent = container.GetUIStyleParent();
if ( styleParent == null )
{
return null;
}
return GetTransitionSettings( styleParent, colorProperty );
}
public static TransitionSettings GetTransitionSettings( UIStylePropertyContainer container, UIStyleNumberProperty numberProperty )
{
var transition = GetTransition( container, numberProperty );
if ( transition != null && transition.settings != null )
{
return transition.settings;
}
var containerSettings = container.GetTransitionSettingsAll();
if ( containerSettings != null )
{
return containerSettings;
}
var styleParent = container.GetUIStyleParent();
if ( styleParent == null )
{
return null;
}
return GetTransitionSettings( styleParent, numberProperty );
}
public static UIColorTransition GetTransition( UIStylePropertyContainer container, UIStyleColorProperty property )
{
switch ( property )
{
case UIStyleColorProperty.FontColor:
case UIStyleColorProperty.FontOutlineColor:
case UIStyleColorProperty.FontShadowColor:
{
return _GetTransition( true, container, property );
}
}
return _GetTransition( false, container, property );
}
static UIColorTransition _GetTransition( bool inheritable, UIStylePropertyContainer container, UIStyleColorProperty property )
{
var transitions = container.GetColorTransitions();
var index = transitions == null ? -1 : Arrays.FindIndex( transitions, t => t != null && t.property == property );
if ( index != -1 )
{
return transitions[ index ];
}
var styleParent = container.GetUIStyleParent();
if ( styleParent == null )
{
return null;
}
var styleParentTransition = _GetTransition( false, styleParent, property );
if ( styleParentTransition != null )
{
return styleParentTransition;
}
if ( ! ( container is Control ) || ! inheritable )
{
return null;
}
var control = container as Control;
UIColorTransition parentTransition = null;
NodesWalker.Get().GetInParents( control,
it =>
{
if ( ! ( it is UIStylePropertyContainer ) )
{
return false;
}
var container = (UIStylePropertyContainer) it;
parentTransition = _GetTransition( true, container, property );
return parentTransition != null;
}
);
return parentTransition;
}
public static UINumber GetInheritableNumberProperty( UIStylePropertyContainer container, UIStyleNumberProperty property )
{
if ( container == null )
{
return null;
}
var ownProperty = container.GetUIStyleNumberProperty( property );
if ( ownProperty != null )
{
return ownProperty;
}
var parentStyle = container.GetUIStyleParent();
var parentStyleProperty = GetReferenceableNumberProperty( parentStyle, property );
if ( parentStyleProperty != null )
{
return parentStyleProperty;
}
if ( ! ( container is Control ) )
{
return null;
}
var control = container as Control;
UINumber parentNumber = null;
NodesWalker.Get().GetInParents( control,
it =>
{
if ( ! ( it is UIStylePropertyContainer ) )
{
return false;
}
var container = (UIStylePropertyContainer) it;
parentNumber = GetReferenceableNumberProperty( container, property );
return parentNumber != null;
}
);
return parentNumber;
}
public static UIColor GetInheritableColorProperty( UIStylePropertyContainer container, UIStyleColorProperty property )
{
if ( container == null )
{
return null;
}
var ownProperty = container.GetUIStyleColorProperty( property );
if ( ownProperty != null )
{
return ownProperty;
}
var parentStyle = container.GetUIStyleParent();
var parentStyleProperty = GetReferenceableColorProperty( parentStyle, property );
if ( parentStyleProperty != null )
{
return parentStyleProperty;
}
if ( ! ( container is Control ) )
{
return null;
}
var control = container as Control;
UIColor parentColor = null;
NodesWalker.Get().GetInParents( control,
it =>
{
if ( ! ( it is UIStylePropertyContainer ) )
{
return false;
}
var container = (UIStylePropertyContainer) it;
parentColor = GetReferenceableColorProperty( container, property );
return parentColor != null;
}
);
return parentColor;
}
public static UIPosition Position( UIStylePropertyContainer container )
{
if ( container == null )
{
return UIPosition.___;
}
var ownProperty = container.GetUIPosition();
if ( ownProperty != UIPosition.___ )
@ -141,102 +628,94 @@ namespace Rokojori
return ownProperty;
}
return container.GetUIPosition();
var parent = container.GetUIStyleParent();
if ( parent == null )
{
return UIPosition.___;
}
public static UINumber Width( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Width );
return parent.GetUIPosition();
}
public static UINumber Height( UIStylePropertyContainer container )
public static UILayout Layout( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Height );
if ( container == null )
{
return UILayout.___;
}
public static UINumber Left( UIStylePropertyContainer container )
var ownProperty = container.GetUILayout();
if ( ownProperty != UILayout.___ )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Left );
return ownProperty;
}
public static UINumber Right( UIStylePropertyContainer container )
var parent = container.GetUIStyleParent();
if ( parent == null )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Right );
return UILayout.___;
}
public static UINumber Top( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Top );
return parent.GetUILayout();
}
public static UINumber Bottom( UIStylePropertyContainer container )
public static UILineWrap LineWrap( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Bottom );
if ( container == null )
{
return UILineWrap.___;
}
public static UINumber Margin( UIStylePropertyContainer container )
var ownProperty = container.GetUILineWrap();
if ( ownProperty != UILineWrap.___ )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Margin );
return ownProperty;
}
public static UINumber MarginLeft( UIStylePropertyContainer container )
var parent = container.GetUIStyleParent();
if ( parent == null )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginLeft );
return UILineWrap.___;
}
public static UINumber MarginRight( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginRight );
return parent.GetUILineWrap();
}
public static UINumber MarginTop( UIStylePropertyContainer container )
public static UINumber GetUINumberProperty( UIStylePropertyContainer container, UIStyleNumberProperty property )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginTop );
switch ( property )
{
case UIStyleNumberProperty.FontSize:
case UIStyleNumberProperty.FontOutlineSize:
case UIStyleNumberProperty.FontShadowSize:
case UIStyleNumberProperty.FontShadowOffsetX:
case UIStyleNumberProperty.FontShadowOffsetY:
{
return GetInheritableNumberProperty( container, property );
}
}
public static UINumber MarginBottom( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginBottom );
return GetReferenceableNumberProperty( container, property );
}
public static UINumber PivotX( UIStylePropertyContainer container )
public static UIColor GetUIColorProperty( UIStylePropertyContainer container, UIStyleColorProperty property )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.PivotX );
switch ( property )
{
case UIStyleColorProperty.FontColor:
case UIStyleColorProperty.FontOutlineColor:
case UIStyleColorProperty.FontShadowColor:
{
return GetInheritableColorProperty( container, property );
}
}
public static UINumber PivotY( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.PivotY );
}
public static UINumber Rotation( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Rotation );
}
public static UINumber Scale( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Scale );
}
public static UINumber ScaleX( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.ScaleX );
}
public static UINumber ScaleY( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.ScaleY );
}
public static UINumber ElementSpacing( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.ElementSpacing );
}
public static UINumber LineSpacing( UIStylePropertyContainer container )
{
return GetReferenceableNumberProperty( container, UIStyleNumberProperty.LineSpacing );
return GetReferenceableColorProperty( container, property );
}
}

View File

@ -16,6 +16,9 @@ namespace Rokojori
ElementSpacing,
LineSpacing,
HorizontalAlignment,
VerticalAlignment,
VerticalPlacement,
Margin,
MarginLeft,
@ -34,7 +37,9 @@ namespace Rokojori
FontSize,
FontOutlineSize,
FontShadowSize
FontShadowSize,
FontShadowOffsetX,
FontShadowOffsetY
}
public enum UIStyleColorProperty

View File

@ -1,6 +1,7 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
namespace Rokojori
{
@ -9,6 +10,23 @@ namespace Rokojori
UIStyle GetUIStyleParent();
UIPosition GetUIPosition();
UILayout GetUILayout();
UILineWrap GetUILineWrap();
UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property );
UIColor GetUIStyleColorProperty( UIStyleColorProperty property );
ShaderUIColor[] GetShaderUIColors();
List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions();
ShaderUINumber[] GetShaderUINumbers();
List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions();
TransitionSettingsAll GetTransitionSettingsAll();
UINumberTransition[] GetNumberTransitions();
List<ActiveStyleTransition<UINumber,UIStyleNumberProperty>> GetActiveUINumberTransitions();
UIColorTransition[] GetColorTransitions();
List<ActiveStyleTransition<UIColor,UIStyleColorProperty>> GetActiveUIColorTransitions();
}
}

View File

@ -8,7 +8,7 @@ namespace Rokojori
{
public static bool CanPosition( Control control )
{
if ( control is UIRegion || control is UIImage || control is UIText )
if ( control is UIRegion || control is UIImage || control is UIBorderImage || control is UIText )
{
return true;
}

View File

@ -0,0 +1,82 @@
using Godot;
using Rokojori;
using System.Collections.Generic;
using System;
namespace Rokojori
{
[Tool]
public class ActiveStyleTransition<V,P>
{
public V value;
public P propertyType;
public RJTimeLine timeLine;
public float start;
public float end;
public Curve curve;
public bool transitioning;
public static O ProcessTransition<O>(
UIStylePropertyContainer container, O computedValue,
List<ActiveStyleTransition<V,P>> activeTransitions, V activeValue, P property,
Func<TransitionSettingsAll> getTransitionSettings,
Func<V,O> computeValue,
Func<O,O,float,O> lerpValue
)
{
var propertyTransition = activeTransitions.Find( t => t != null && EqualityComparer<P>.Default.Equals( t.propertyType, property ) );
if ( propertyTransition == null )
{
propertyTransition = new ActiveStyleTransition<V, P>();
propertyTransition.propertyType = property;
propertyTransition.value = activeValue;
propertyTransition.transitioning = false;
activeTransitions.Add( propertyTransition );
return computedValue;
}
else
{
if ( ! EqualityComparer<V>.Default.Equals( propertyTransition.value, activeValue ) &&
! propertyTransition.transitioning && getTransitionSettings() != null )
{
var transitionSettings = getTransitionSettings();
propertyTransition.timeLine = transitionSettings.timeLine;
propertyTransition.start = TimeLineManager.GetPosition( transitionSettings.timeLine );
propertyTransition.end = propertyTransition.start + transitionSettings.duration;
propertyTransition.transitioning = true;
propertyTransition.curve = transitionSettings.curve;
}
}
if ( EqualityComparer<V>.Default.Equals( propertyTransition.value, activeValue ) )
{
propertyTransition.transitioning = false;
return computedValue;
}
var computedTransitionValue = computeValue( propertyTransition.value );
var transitionPhase = TimeLineManager.GetRangePhase( propertyTransition.timeLine, propertyTransition.start, propertyTransition.end );
if ( transitionPhase >= 1 )
{
activeTransitions.Remove( propertyTransition );
}
var amount = MathX.Clamp01( transitionPhase );
var curveAmount = amount;
if ( propertyTransition.curve != null )
{
curveAmount = propertyTransition.curve.Sample( curveAmount );
}
return lerpValue( computedTransitionValue, computedValue, curveAmount );
}
}
}

View File

@ -0,0 +1,23 @@
using Godot;
using Rokojori;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TransitionSettings:Resource
{
[Export]
public float duration;
[Export]
public Curve curve;
[Export]
public RJTimeLine timeLine;
}
}

View File

@ -0,0 +1,15 @@
using Godot;
using Rokojori;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TransitionSettingsAll:TransitionSettings
{
[Export]
public bool transitionAllProperties;
}
}

View File

@ -0,0 +1,19 @@
using Godot;
using Rokojori;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class UIColorTransition : Resource
{
[Export]
public UIStyleColorProperty property;
[Export]
public TransitionSettings settings;
}
}

View File

@ -0,0 +1,18 @@
using Godot;
using Rokojori;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class UINumberTransition : Resource
{
[Export]
public UIStyleNumberProperty property;
[Export]
public TransitionSettings settings;
}
}

Some files were not shown because too many files have changed in this diff Show More