Tree Update
This commit is contained in:
parent
26d24f9fe6
commit
ef45d31599
|
@ -5,7 +5,7 @@ using System;
|
|||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class NodeState
|
||||
public static class NodeState
|
||||
{
|
||||
public static void Configure( Node n, bool processEnabled, bool inputEnabled, bool physicsEnabled, bool signalsEnabled,
|
||||
Node.ProcessModeEnum processMode, bool visible )
|
||||
|
@ -50,7 +50,7 @@ namespace Rokojori
|
|||
Arrays.ForEach( nodes, n => Set( n, enabled ) );
|
||||
}
|
||||
|
||||
public static void Enable( Node n )
|
||||
public static void Enable( this Node n )
|
||||
{
|
||||
Set( n, true );
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ namespace Rokojori
|
|||
Set( n, true );
|
||||
}
|
||||
|
||||
public static void Disable( Node n )
|
||||
public static void Disable( this Node n )
|
||||
{
|
||||
Set( n, false );
|
||||
}
|
||||
|
|
|
@ -9,6 +9,12 @@ namespace Rokojori
|
|||
{
|
||||
public static class Nodes
|
||||
{
|
||||
|
||||
public static NodePath GetNodePath( this Node node )
|
||||
{
|
||||
return node.GetPath();
|
||||
}
|
||||
|
||||
public static T Get<T>( this Node node ) where T:Node
|
||||
{
|
||||
return GetAnyChild<T>( node );
|
||||
|
@ -161,7 +167,7 @@ namespace Rokojori
|
|||
return list;
|
||||
}
|
||||
|
||||
public static List<T> AllIn<T>( Node root, Func<T,bool> filter = null, bool includeRoot = true) where T:class
|
||||
public static List<T> GetAll<T>( this Node root, Func<T,bool> filter = null, bool includeRoot = true) where T:class
|
||||
{
|
||||
var list = new List<T>();
|
||||
|
||||
|
@ -481,10 +487,22 @@ namespace Rokojori
|
|||
}
|
||||
}
|
||||
|
||||
public static void SelfDestroy( this Node node )
|
||||
{
|
||||
var parent = node.GetParent();
|
||||
|
||||
if ( parent != null )
|
||||
{
|
||||
parent.RemoveChild( node );
|
||||
}
|
||||
|
||||
node.QueueFree();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static T GetDirectChild<T>( Node parent ) where T:Node
|
||||
public static T GetDirectChild<T>( this Node parent ) where T:Node
|
||||
{
|
||||
if ( parent == null )
|
||||
{
|
||||
|
@ -506,7 +524,7 @@ namespace Rokojori
|
|||
return null;
|
||||
}
|
||||
|
||||
public static List<T> GetDirectChildren<T>( Node parent ) where T:Node
|
||||
public static List<T> GetDirectChildren<T>( this Node parent ) where T:Node
|
||||
{
|
||||
if ( parent == null )
|
||||
{
|
||||
|
@ -537,10 +555,6 @@ namespace Rokojori
|
|||
return list;
|
||||
}
|
||||
|
||||
public static void OnAllDirectChildren<T>( this Node parent, System.Action<T> action ) where T:Node
|
||||
{
|
||||
ForEachDirectChild<T>( parent, action );
|
||||
}
|
||||
|
||||
public static int NumDirectChildrenOf<T>( this Node parent ) where T:Node
|
||||
{
|
||||
|
@ -566,6 +580,23 @@ namespace Rokojori
|
|||
return typeIndex;
|
||||
}
|
||||
|
||||
public static T GetLastChild<T>( this Node parent ) where T:Node
|
||||
{
|
||||
var numChildren = parent.GetChildCount();
|
||||
|
||||
for ( int i = numChildren - 1; i >= 0 ; i-- )
|
||||
{
|
||||
var child = parent.GetChild( i );
|
||||
|
||||
if ( child is T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T GetNthDirectChild<T>( this Node parent, int index ) where T:Node
|
||||
{
|
||||
if ( parent == null )
|
||||
|
@ -596,7 +627,7 @@ namespace Rokojori
|
|||
}
|
||||
|
||||
|
||||
public static void ForEachDirectChild<T>( Node parent, System.Action<T> action ) where T:Node
|
||||
public static void ForEachDirectChild<T>( this Node parent, System.Action<T> action ) where T:Node
|
||||
{
|
||||
if ( parent == null || action == null )
|
||||
{
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CustomTreeWalker<N>:TreeWalker<N> where N:class
|
||||
{
|
||||
Func<N,N> _getParent;
|
||||
Func<N,int,N> _childAt;
|
||||
Func<N,int> _numChildren;
|
||||
|
||||
|
||||
public CustomTreeWalker( Func<N,N> getParent, Func<N,int,N> childAt, Func<N,int> numChildren )
|
||||
{
|
||||
_getParent = getParent;
|
||||
_childAt = childAt;
|
||||
_numChildren = numChildren;
|
||||
|
||||
}
|
||||
|
||||
public override N Parent( N node ){ return _getParent( node ); }
|
||||
|
||||
public override N ChildAt( N node, int index ){ return _childAt( node, index ); }
|
||||
|
||||
public override int NumChildren( N node ){ return _numChildren( node ); }
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://bat8vplx8obqc
|
|
@ -177,7 +177,122 @@ namespace Rokojori
|
|||
var num = NumChildren( node );
|
||||
return num <= 0 ? null : ChildAt( node, num - 1 );
|
||||
}
|
||||
|
||||
|
||||
public void ForEachChild( N node, bool directChildrenOnly, Action<N> action )
|
||||
{
|
||||
if ( directChildrenOnly )
|
||||
{
|
||||
var numChildren = NumChildren( node );
|
||||
|
||||
for ( int i = 0; i < numChildren; i++ )
|
||||
{
|
||||
var child = ChildAt( node, i );
|
||||
action( child );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterate( node, action, true );
|
||||
}
|
||||
}
|
||||
|
||||
public N FindDirectChild( N node, Predicate<N> selector )
|
||||
{
|
||||
return FindChild( node, true, selector );
|
||||
}
|
||||
|
||||
public N FindAnyChild( N node, Predicate<N> selector )
|
||||
{
|
||||
return FindChild( node, false, selector );
|
||||
}
|
||||
|
||||
public N FindChild( N node, bool directChildrenOnly, Predicate<N> selector )
|
||||
{
|
||||
if ( directChildrenOnly )
|
||||
{
|
||||
var numChildren = NumChildren( node );
|
||||
|
||||
for ( int i = 0; i < numChildren; i++ )
|
||||
{
|
||||
var child = ChildAt( node, i );
|
||||
|
||||
if ( selector( child ) )
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Find( node, selector, true );
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public N GetDirectChildWithLowestValue( N node, Func<N, float> getValue )
|
||||
{
|
||||
return GetChildWithLowestValue( node, true, getValue );
|
||||
}
|
||||
|
||||
public N GetAnyChildWithLowestValue( N node, Func<N, float> getValue )
|
||||
{
|
||||
return GetChildWithLowestValue( node, false, getValue );
|
||||
}
|
||||
|
||||
public N GetChildWithLowestValue( N node, bool directChildrenOnly, Func<N, float> getValue )
|
||||
{
|
||||
var lowestValue = float.MaxValue;
|
||||
N childWithLowestValue = null;
|
||||
|
||||
ForEachChild( node, directChildrenOnly,
|
||||
( child )=>
|
||||
{
|
||||
var value = getValue( child );
|
||||
|
||||
if ( value < lowestValue )
|
||||
{
|
||||
lowestValue = value;
|
||||
childWithLowestValue = child;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return childWithLowestValue;
|
||||
}
|
||||
|
||||
public N GetDirectChildWithHighestValue( N node, Func<N, float> getValue )
|
||||
{
|
||||
return GetChildWithHighestValue( node, true, getValue );
|
||||
}
|
||||
|
||||
public N GetAnyChildWithHighestValue( N node, Func<N, float> getValue )
|
||||
{
|
||||
return GetChildWithHighestValue( node, false, getValue );
|
||||
}
|
||||
|
||||
public N GetChildWithHighestValue( N node, bool directChildrenOnly, Func<N, float> getValue )
|
||||
{
|
||||
var highestValue = -float.MaxValue;
|
||||
N childWithHighestValue = null;
|
||||
|
||||
ForEachChild( node, directChildrenOnly,
|
||||
( child )=>
|
||||
{
|
||||
var value = getValue( child );
|
||||
|
||||
if ( value > highestValue )
|
||||
{
|
||||
highestValue = value;
|
||||
childWithHighestValue = child;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return childWithHighestValue;
|
||||
}
|
||||
|
||||
|
||||
public N NextNode( N node )
|
||||
{
|
||||
|
|
|
@ -26,14 +26,21 @@ namespace Rokojori
|
|||
|
||||
[Export]
|
||||
public Node3D graphics;
|
||||
|
||||
|
||||
[Export]
|
||||
public Smoothing rotationSmoothing;
|
||||
|
||||
[Export]
|
||||
public Smoothing positionSmoothing;
|
||||
|
||||
|
||||
|
||||
[Export]
|
||||
public Node3D groundedTransform;
|
||||
|
||||
[Export]
|
||||
public float lastGroundedHeight = 0;
|
||||
|
||||
|
||||
public float delta = 0;
|
||||
|
||||
|
||||
|
@ -54,6 +61,17 @@ namespace Rokojori
|
|||
|
||||
graphics.GlobalPosition = Smoothing.Apply( positionSmoothing, body.GlobalPosition, this.delta );
|
||||
graphics.SetGlobalQuaternion( Smoothing.Apply( rotationSmoothing, body.GetGlobalQuaternion(), this.delta ) );
|
||||
|
||||
if ( body.IsOnFloor() )
|
||||
{
|
||||
lastGroundedHeight = graphics.GlobalPosition.Y;
|
||||
}
|
||||
|
||||
var grounedPosition = graphics.GlobalPosition;
|
||||
grounedPosition.Y = lastGroundedHeight;
|
||||
groundedTransform.GlobalPosition = grounedPosition;
|
||||
groundedTransform.SetGlobalQuaternion( graphics.GetGlobalQuaternion() );
|
||||
|
||||
// Pose.CopyTo( body, graphics );
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Rokojori
|
|||
|
||||
this.LogInfo( "Grid Created", grid );
|
||||
|
||||
var instances = Nodes.AllIn<MeshInstance3D>( source );
|
||||
var instances = Nodes.GetAll<MeshInstance3D>( source );
|
||||
|
||||
this.LogInfo( "Grabbed instances", instances.Count );
|
||||
|
||||
|
|
|
@ -31,6 +31,17 @@ namespace Rokojori
|
|||
|
||||
CreateRootCells();
|
||||
|
||||
}
|
||||
|
||||
public OcTree( Vector3 center, float rootCellSize, int maxDepth )
|
||||
{
|
||||
this._min = center - Vector3.One * rootCellSize / 2f;
|
||||
this._max = center + Vector3.One * rootCellSize / 2f;
|
||||
this._rootCellSize = rootCellSize;
|
||||
this._maxDepth = maxDepth;
|
||||
|
||||
CreateRootCells();
|
||||
|
||||
}
|
||||
|
||||
public void SetCombiner( Func<List<T>,List<T>> combinePoints )
|
||||
|
@ -74,6 +85,40 @@ namespace Rokojori
|
|||
return result;
|
||||
}
|
||||
|
||||
public List<T> GetInsideBox( Box3 box, List<T> list = null )
|
||||
{
|
||||
list = list == null ? new List<T>() : list;
|
||||
|
||||
GetInsideBoxWithDuplicates( box, list );
|
||||
|
||||
var set = new HashSet<T>( [ .. list ] );
|
||||
return set.ToList<T>();
|
||||
}
|
||||
|
||||
public List<T> GetInsideBoxWithDuplicates( Box3 box, List<T> list = null )
|
||||
{
|
||||
list = list == null ? new List<T>() : list;
|
||||
var rootIndexMin = PositionToRootIndex( box.min );
|
||||
var rootIndexMax = PositionToRootIndex( box.max );
|
||||
|
||||
for ( int x = rootIndexMin.X; x <= rootIndexMax.X; x++ )
|
||||
{
|
||||
for ( int y = rootIndexMin.Y; y <= rootIndexMax.Y; y++ )
|
||||
{
|
||||
for ( int z = rootIndexMin.Z; z <= rootIndexMax.Z; z++ )
|
||||
{
|
||||
var rootIndex = new Vector3I( x, y, z );
|
||||
var rootCell = GetRootCellByRootIndex( rootIndex );
|
||||
|
||||
rootCell.GetInsideBox( box, list );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
public bool Insert( T data )
|
||||
{
|
||||
var rootCell = GetRootCell( _getPosition( data ) );
|
||||
|
@ -91,6 +136,26 @@ namespace Rokojori
|
|||
return rootCell.Insert( data );
|
||||
}
|
||||
|
||||
public void BoxInsert( T data, Box3 box )
|
||||
{
|
||||
var rootIndexMin = PositionToRootIndex( box.min );
|
||||
var rootIndexMax = PositionToRootIndex( box.max );
|
||||
|
||||
for ( int x = rootIndexMin.X; x <= rootIndexMax.X; x++ )
|
||||
{
|
||||
for ( int y = rootIndexMin.Y; y <= rootIndexMax.Y; y++ )
|
||||
{
|
||||
for ( int z = rootIndexMin.Z; z <= rootIndexMax.Z; z++ )
|
||||
{
|
||||
var rootIndex = new Vector3I( x, y, z );
|
||||
var rootCell = GetRootCellByRootIndex( rootIndex );
|
||||
|
||||
rootCell.BoxInsert( data, box );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetLevelForSize( float size )
|
||||
{
|
||||
var level = 0;
|
||||
|
|
|
@ -49,6 +49,27 @@ namespace Rokojori
|
|||
public int rootCellIndex => _rootCellIndex;
|
||||
|
||||
|
||||
public void GetInsideBox( Box3 box, List<T> list )
|
||||
{
|
||||
if ( ! _box.Overlaps( box ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( _values != null )
|
||||
{
|
||||
list.AddRange( _values );
|
||||
}
|
||||
|
||||
if ( _cells == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cells.ForEach( c => c.GetInsideBox( box, list ) );
|
||||
|
||||
}
|
||||
|
||||
public static OcTreeCell<T> Create( OcTree<T> tree, Vector3 min, Vector3 max, int rootIndex )
|
||||
{
|
||||
var cell = new OcTreeCell<T>();
|
||||
|
@ -83,6 +104,32 @@ namespace Rokojori
|
|||
return _center + halfSize * polarUVW;
|
||||
}
|
||||
|
||||
public void BoxInsert( T data, Box3 box )
|
||||
{
|
||||
if ( ! _box.Overlaps( box ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isLeaf )
|
||||
{
|
||||
if ( _values == null )
|
||||
{
|
||||
_values = new List<T>();
|
||||
}
|
||||
|
||||
values.Add( data );
|
||||
return;
|
||||
}
|
||||
|
||||
Nest();
|
||||
|
||||
cells.ForEach( c => c.BoxInsert( data, box ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
public bool Insert( T data )
|
||||
{
|
||||
if ( isLeaf )
|
||||
|
|
|
@ -100,6 +100,39 @@ namespace Rokojori
|
|||
return point;
|
||||
}
|
||||
|
||||
public bool Overlaps( Box3 other )
|
||||
{
|
||||
return Overlap3D.Has( this, other );
|
||||
}
|
||||
|
||||
public float DistanceTo( Sphere a )
|
||||
{
|
||||
return Mathf.Sqrt( SquareDistanceTo( a ) );
|
||||
}
|
||||
|
||||
public float SquareDistanceTo( Sphere a )
|
||||
{
|
||||
var squareDistance = 0.0f;
|
||||
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
var dimension = center[ i ] ;
|
||||
|
||||
if ( dimension < min[ i ] )
|
||||
{
|
||||
var dimensionDistance = dimension - min[ i ];
|
||||
squareDistance += dimensionDistance * dimensionDistance;
|
||||
}
|
||||
else if ( dimension > max[ i ] )
|
||||
{
|
||||
var distance = dimension - max[ i ];
|
||||
squareDistance += distance * distance;
|
||||
}
|
||||
}
|
||||
|
||||
return squareDistance;
|
||||
}
|
||||
|
||||
public bool ContainsPoint( Vector3 point )
|
||||
{
|
||||
if ( ! Range.Contains( min.X, max.X, point.X ) )
|
||||
|
|
|
@ -38,6 +38,13 @@ namespace Rokojori
|
|||
return Mathf.Max( 0, lineDistance - radius );
|
||||
}
|
||||
|
||||
public float DistanceTo( Capsule3 other )
|
||||
{
|
||||
var lineDistance = centerLine.DistanceToLine( other.centerLine );
|
||||
|
||||
return Mathf.Max( 0, lineDistance - ( radius + other.radius ) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,14 @@ namespace Rokojori
|
|||
this.end = end;
|
||||
}
|
||||
|
||||
public Box3 GetBox( float expansion = 0 )
|
||||
{
|
||||
var min = start.Min( end ) - Vector3.One * expansion;
|
||||
var max = start.Max( end ) + Vector3.One * expansion;
|
||||
|
||||
return Box3.Create( min, max );
|
||||
}
|
||||
|
||||
public override Vector3 PositionAt( float t )
|
||||
{
|
||||
return start.Lerp( end, t );
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Overlap3D
|
||||
{
|
||||
public static bool Has( Box3 a, Box3 b )
|
||||
{
|
||||
if ( ! Range.Overlap( a.min.Y, a.max.Y, b.min.Y, b.max.Y ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! Range.Overlap( a.min.X, a.max.X, b.min.X, b.max.X ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! Range.Overlap( a.min.Z, a.max.Z, b.min.Z, b.max.Z ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Has( Sphere a, Sphere b )
|
||||
{
|
||||
return a.IntersectsSphere( b );
|
||||
}
|
||||
|
||||
public static bool Has( Box3 a, Sphere b )
|
||||
{
|
||||
return a.SquareDistanceTo( b ) <= 0;
|
||||
}
|
||||
|
||||
public static bool Has( Sphere a, Box3 b )
|
||||
{
|
||||
return b.SquareDistanceTo( a ) <= 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://b8oy166pmufgv
|
|
@ -18,6 +18,15 @@ namespace Rokojori
|
|||
}
|
||||
}
|
||||
|
||||
public Pose Clone()
|
||||
{
|
||||
var p =new Pose();
|
||||
p._rotation = _rotation;
|
||||
p._position = _position;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public void ApplyTwist( float twist )
|
||||
{
|
||||
rotation = rotation * Math3D.RotateZ( twist );
|
||||
|
|
|
@ -264,6 +264,7 @@ namespace Rokojori
|
|||
return center / points.Length;
|
||||
}
|
||||
|
||||
|
||||
public static Vector3 Center<N>( List<N> points ) where N:Node3D
|
||||
{
|
||||
var center = Vector3.Zero;
|
||||
|
@ -600,6 +601,11 @@ namespace Rokojori
|
|||
return new Vector4( q.X, q.Y, q.Z, q.W );
|
||||
}
|
||||
|
||||
public static Quaternion FromEulerDegrees( Vector3 eulerYXZ )
|
||||
{
|
||||
return Quaternion.FromEuler( eulerYXZ * MathX.DegreesToRadians );
|
||||
}
|
||||
|
||||
public static Quaternion RotateX( float radians )
|
||||
{
|
||||
if ( radians == 0 ) { return Quaternion.Identity; }
|
||||
|
@ -938,6 +944,18 @@ namespace Rokojori
|
|||
return amount <= 0.5 ? a : b;
|
||||
}
|
||||
|
||||
public static Box3 ToBox3( this Aabb aabb )
|
||||
{
|
||||
return (Box3) aabb;
|
||||
}
|
||||
|
||||
public static Box3 GetWorldBox( this Node3D node, bool onlyVisible = true )
|
||||
{
|
||||
var aabb = GetWorldBounds( node, onlyVisible );
|
||||
|
||||
return aabb == null ? null : ( (Aabb)aabb).ToBox3();
|
||||
}
|
||||
|
||||
public static Aabb? GetWorldBounds( this Node3D node, bool onlyVisible = true )
|
||||
{
|
||||
return GetWorldBoundsFrom( node, onlyVisible );
|
||||
|
|
|
@ -10,6 +10,9 @@ namespace Rokojori
|
|||
{
|
||||
public const float fps120Delta = 1/120f;
|
||||
|
||||
public const float DegreesToRadians = Mathf.Pi / 180f;
|
||||
public const float RadiansToDegrees = 180f / Mathf.Pi;
|
||||
|
||||
static Dictionary<int,int> factorialLookUp = new Dictionary<int, int>();
|
||||
|
||||
public static int Factorial( int i )
|
||||
|
@ -198,8 +201,7 @@ namespace Rokojori
|
|||
}
|
||||
|
||||
|
||||
public const float DegreesToRadians = Mathf.Pi / 180f;
|
||||
public const float RadiansToDegrees = 180f / Mathf.Pi;
|
||||
|
||||
|
||||
public static float AngleDelta( float degreesA, float degreesB)
|
||||
{
|
||||
|
@ -426,6 +428,30 @@ namespace Rokojori
|
|||
return value;
|
||||
}
|
||||
|
||||
public static Curve CurveFromPoints( params float[] points )
|
||||
{
|
||||
var curve = new Curve();
|
||||
|
||||
for ( int i = 0; i < points.Length; i++ )
|
||||
{
|
||||
var t = (float) i / (float) ( points.Length - 1f );
|
||||
curve.AddPoint( new Vector2( t, points[ i ] ) );
|
||||
|
||||
if ( i != 0 )
|
||||
{
|
||||
curve.SetPointLeftMode( i, Godot.Curve.TangentMode.Linear );
|
||||
}
|
||||
|
||||
if ( i != points.Length - 1 )
|
||||
{
|
||||
curve.SetPointRightMode( i, Godot.Curve.TangentMode.Linear );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
public static Curve Curve( float y0, float y1, float minValue = 0, float maxValue = 1 )
|
||||
{
|
||||
var curve = new Curve();
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Rokojori
|
|||
return true;
|
||||
}
|
||||
|
||||
if ( Contains( min ) || Contains( max ) )
|
||||
if ( Contains( other.min ) || Contains( other.max ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -95,6 +95,25 @@ namespace Rokojori
|
|||
return min <= value && value < max;
|
||||
}
|
||||
|
||||
public static bool Overlap( float minA, float maxA, float minB, float maxB )
|
||||
{
|
||||
if ( maxB < minA ) { return false; }
|
||||
if ( minB > maxA ) { return false; }
|
||||
|
||||
if ( Contains( minB, maxB, minA ) || Contains( minB, maxB, maxA ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( Contains( minA, maxA, minB ) || Contains( minA, maxA, maxB ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class TreeBranch:Resource
|
||||
{
|
||||
[Export]
|
||||
public NodePath spline;
|
||||
|
||||
[Export]
|
||||
public Vector3 start;
|
||||
|
||||
[Export]
|
||||
public Vector3 end;
|
||||
|
||||
[Export]
|
||||
public int startSeed;
|
||||
|
||||
[Export]
|
||||
public Curve curve;
|
||||
|
||||
|
||||
public Spline GetSpline( Node node )
|
||||
{
|
||||
return node.GetNode( spline ) as Spline;
|
||||
}
|
||||
|
||||
[Export]
|
||||
public int index = -1;
|
||||
|
||||
[Export]
|
||||
public int parent = -1;
|
||||
|
||||
[Export]
|
||||
public int level = -1;
|
||||
|
||||
[Export]
|
||||
public int[] children = [];
|
||||
|
||||
[Export]
|
||||
public float radius = 0;
|
||||
|
||||
[Export]
|
||||
public float radius2 = 0;
|
||||
|
||||
[Export]
|
||||
public float radius3 = 0;
|
||||
|
||||
[Export]
|
||||
public int numRadi = 1;
|
||||
|
||||
[Export]
|
||||
public float height = 0;
|
||||
|
||||
[Export]
|
||||
public float branchPosition = 0;
|
||||
|
||||
public Line3 GetLine3()
|
||||
{
|
||||
return new Line3( start, end );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://qfl4okpj3plh
|
|
@ -0,0 +1,737 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Scatterer.svg") ]
|
||||
public partial class TreeGenerator:Node3D
|
||||
{
|
||||
[Export]
|
||||
public float rootRadius;
|
||||
|
||||
[Export]
|
||||
public float rootHeight;
|
||||
|
||||
[Export]
|
||||
public Curve branchPosition;
|
||||
|
||||
[Export]
|
||||
public Curve levelToChildBranches;
|
||||
|
||||
[Export]
|
||||
public Curve levelToSubdivisionNoise;
|
||||
|
||||
[Export]
|
||||
public Curve subdivisionNoiseRange;
|
||||
|
||||
[Export]
|
||||
public int maxLevel = 4;
|
||||
|
||||
|
||||
|
||||
|
||||
[Export]
|
||||
public Curve radiusScale;
|
||||
|
||||
[Export]
|
||||
public Curve heightScale;
|
||||
|
||||
[Export]
|
||||
public Curve siblingRotationVariance;
|
||||
|
||||
[Export]
|
||||
public Curve rotationPerLevel;
|
||||
|
||||
[Export]
|
||||
public Curve rotationVariation;
|
||||
|
||||
[Export]
|
||||
public int radialSegments = 8;
|
||||
|
||||
[Export]
|
||||
public int minRadialSegements = 3;
|
||||
|
||||
[Export]
|
||||
public int maxRadialSegments = 64;
|
||||
|
||||
[Export]
|
||||
public Curve radialSegementsMultiplier;
|
||||
|
||||
[Export]
|
||||
public bool avoidClusters = true;
|
||||
|
||||
[Export]
|
||||
public float minClusterDistance = 1;
|
||||
|
||||
[Export]
|
||||
public float noise = 0.1f;
|
||||
|
||||
[Export]
|
||||
public TubeGeometrySettings branchSettings;
|
||||
|
||||
[Export]
|
||||
public TubeShape[] shapes;
|
||||
|
||||
[Export]
|
||||
public Material branchMaterial;
|
||||
|
||||
[Export]
|
||||
public Material leavesMaterial;
|
||||
|
||||
[Export]
|
||||
public int leavesRows = 4;
|
||||
|
||||
[Export]
|
||||
public int leavesColumns = 4;
|
||||
|
||||
[Export]
|
||||
public bool hideStructureWhenMeshWasBuild = true;
|
||||
|
||||
[Export]
|
||||
public int seed = 1998;
|
||||
|
||||
[Export]
|
||||
public bool randomizeSeed = false;
|
||||
|
||||
[ExportToolButton( "Generate Branches Structure" )]
|
||||
public Callable GenerateBranchesButton => Callable.From(
|
||||
( )=>
|
||||
{
|
||||
GenerateBranches();
|
||||
}
|
||||
);
|
||||
|
||||
[ExportToolButton( "Generate Branches Mesh" )]
|
||||
public Callable GenerateMeshButton => Callable.From(
|
||||
( )=>
|
||||
{
|
||||
GenerateMesh();
|
||||
}
|
||||
);
|
||||
|
||||
[ExportToolButton( "Generate Branches And Mesh" )]
|
||||
public Callable GenerateBranchesMeshButton => Callable.From(
|
||||
( )=>
|
||||
{
|
||||
GenerateBranchesAndMesh();
|
||||
}
|
||||
);
|
||||
|
||||
[Export]
|
||||
bool _generating = false;
|
||||
|
||||
[Export]
|
||||
public TreeBranch[] branches = [];
|
||||
|
||||
List<TreeBranch> _branches = [];
|
||||
|
||||
SeedableRandomEngine random;
|
||||
|
||||
|
||||
[Export]
|
||||
public Node deselecter;
|
||||
|
||||
async Task GenerateBranchesAndMesh()
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( useDebugSingleMeshGeneration )
|
||||
{
|
||||
EditorInterface.Singleton.GetSelection().Clear();
|
||||
EditorInterface.Singleton.GetSelection().AddNode( deselecter );
|
||||
await this.RequestNextFrame();
|
||||
}
|
||||
|
||||
await GenerateBranches();
|
||||
await GenerateMesh();
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
this.LogError( e );
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[Export]
|
||||
public bool useDebugSingleMeshGeneration = false;
|
||||
|
||||
async Task GenerateMesh()
|
||||
{
|
||||
var time = Async.StartTimer();
|
||||
|
||||
var mg = new MeshGeometry();
|
||||
|
||||
var meshInstances = this.GetDirectChildren<MeshInstance3D>();
|
||||
meshInstances.ForEach( m => m.SelfDestroy() );
|
||||
|
||||
if ( useDebugSingleMeshGeneration )
|
||||
{
|
||||
this.GetDirectChildren<MeshInstance3D>().ForEach( m => m.SelfDestroy() );
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GetAll<MeshInstance3D>().ForEach( m => m.SelfDestroy() );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < _branches.Count; i++ )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, this, processTimePerFrame );
|
||||
var branchMG = CreateBranchMesh( _branches[ i ] );
|
||||
|
||||
if ( useDebugSingleMeshGeneration )
|
||||
{
|
||||
var meshInstance3D = this.CreateChild<MeshInstance3D>( "Branch Mesh @" + _branches[ i ].index );
|
||||
meshInstance3D.Mesh = branchMG.GenerateMesh();
|
||||
meshInstance3D.SetSurfaceOverrideMaterial( 0, branchMaterial );
|
||||
|
||||
if ( i % 5 == 0 )
|
||||
{
|
||||
await this.RequestNextFrame();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mg.Add( branchMG );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var leavesMG = new MeshGeometry();
|
||||
|
||||
for ( int bb =0; bb < _branches.Count; bb++ )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, this, processTimePerFrame );
|
||||
var b = _branches[ bb ];
|
||||
|
||||
if ( b.level < leavesLevel )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var numLeaves = ( b.height / leafSize ) * random.Sample( leavesDensity );
|
||||
var curve = b.GetSpline( this ).GetCurve();
|
||||
|
||||
|
||||
|
||||
for ( int i = 0; i < numLeaves; i++ )
|
||||
{
|
||||
var t = i / ( numLeaves - 1f );
|
||||
var leafGeometry = new MeshGeometry();
|
||||
|
||||
var index = random.IntegerExclusive( leavesRows * leavesColumns );
|
||||
var indexX = index % leavesColumns;
|
||||
var indexY = index / leavesColumns;
|
||||
|
||||
var uvStart = new Vector2( indexX, indexY ) / new Vector2( leavesColumns, leavesRows );
|
||||
var uvEnd = uvStart + Vector2.One / new Vector2( leavesColumns, leavesRows );
|
||||
|
||||
|
||||
var p = curve.PoseAt( t );
|
||||
var rx = random.Range( 0, 360 );
|
||||
var ry = random.Range( 0, 360 );
|
||||
var rz = random.Range( 0, 360 );
|
||||
|
||||
var leafSizeCurrent = leafSize * random.Sample( leafSizeScaleVariance, 1, 1 );
|
||||
|
||||
leafGeometry.AddQuad( Math3D.FromEulerDegrees( new Vector3( rx, ry, rz ) ),
|
||||
leafSizeCurrent, uvStart, uvEnd, p.position );
|
||||
|
||||
leafGeometry.BlendNormalsOverY( Vector3.Up, leavesNormalBlending, leavesStartBlend, leavesEndBlend, leavesBlendCurve );
|
||||
|
||||
if ( useDebugSingleMeshGeneration )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, processTimePerFrame );
|
||||
var leavesInstance = this.CreateChild<MeshInstance3D>( "Leaves Mesh @" + b.index + "." + i );
|
||||
leavesInstance.Mesh = leafGeometry.GenerateMesh();
|
||||
leavesInstance.SetSurfaceOverrideMaterial( 0, leavesMaterial );
|
||||
}
|
||||
else
|
||||
{
|
||||
leavesMG.Add( leafGeometry );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if ( ! useDebugSingleMeshGeneration )
|
||||
{
|
||||
var leavesInstance = this.CreateChild<MeshInstance3D>( "Leaves Mesh" );
|
||||
leavesInstance.Mesh = leavesMG.GenerateMesh();
|
||||
leavesInstance.SetSurfaceOverrideMaterial( 0, leavesMaterial );
|
||||
|
||||
var meshInstance3D = this.CreateChild<MeshInstance3D>( "Tree Mesh");
|
||||
meshInstance3D.Mesh = mg.GenerateMesh();
|
||||
meshInstance3D.SetSurfaceOverrideMaterial( 0, branchMaterial );
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ( hideStructureWhenMeshWasBuild )
|
||||
{
|
||||
structure.Disable();
|
||||
}
|
||||
|
||||
numTriangles = mg.numTriangles;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[Export]
|
||||
public int leavesLevel = 3;
|
||||
|
||||
[Export]
|
||||
public Vector3 leavesEulerRotationPre =Vector3.Zero;
|
||||
|
||||
[Export]
|
||||
public Vector3 leavesEulerRotationPost =Vector3.Zero;
|
||||
|
||||
[Export]
|
||||
public float leafSize = 1;
|
||||
|
||||
[Export]
|
||||
public Curve leafSizeScaleVariance;
|
||||
|
||||
[Export]
|
||||
public Curve leavesDensity;
|
||||
|
||||
[Export]
|
||||
public float leavesNormalBlending = 0.5f;
|
||||
|
||||
[Export]
|
||||
public Curve leavesBlendCurve;
|
||||
|
||||
[Export]
|
||||
public float leavesStartBlend = 4;
|
||||
|
||||
[Export]
|
||||
public float leavesEndBlend = 8;
|
||||
|
||||
[Export]
|
||||
public int numTriangles = 0;
|
||||
|
||||
[Export]
|
||||
public float processTimePerFrame = 1f/80f;
|
||||
|
||||
[Export]
|
||||
public float smallestRadius = 0.02f;
|
||||
|
||||
float SmallestChildRadius( TreeBranch tb )
|
||||
{
|
||||
var radius = smallestRadius;
|
||||
|
||||
if ( tb.children.Length > 0 )
|
||||
{
|
||||
radius = _branches[ tb.children[ 0 ] ].radius;
|
||||
}
|
||||
|
||||
for ( int i = 1; i < tb.children.Length; i++ )
|
||||
{
|
||||
var childIndex = tb.children[ i ];
|
||||
var childTB = _branches[ childIndex ];
|
||||
radius = Mathf.Min( radius, childTB.radius );
|
||||
}
|
||||
|
||||
return radius;
|
||||
}
|
||||
|
||||
MeshGeometry CreateBranchMesh( TreeBranch tb )
|
||||
{
|
||||
var tubeGeometry = new TubeGeometry();
|
||||
tubeGeometry.curve = tb.GetSpline( this ).GetCurve();
|
||||
var normalizedLevel = tb.level / (float)( maxLevel );
|
||||
var levelSegments = radialSegementsMultiplier.Sample( normalizedLevel );
|
||||
|
||||
branchSettings.radialSegments = Mathf.RoundToInt( radialSegments * levelSegments );
|
||||
tubeGeometry.settings = branchSettings;
|
||||
tubeGeometry.shapes = shapes;
|
||||
branchSettings.radius = 1.0f;
|
||||
|
||||
if ( tb.numRadi == 3 )
|
||||
{
|
||||
|
||||
tb.curve = MathX.CurveFromPoints( tb.radius, tb.radius2, tb.radius3 );
|
||||
branchSettings.radiusSizeCurve = tb.curve;
|
||||
}
|
||||
else
|
||||
{
|
||||
branchSettings.radiusSizeCurve = MathX.Curve( tb.radius, SmallestChildRadius( tb ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
var mesh = tubeGeometry.CreateMesh();
|
||||
// mesh.SmoothNormals();
|
||||
|
||||
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[Export]
|
||||
public Node3D structure;
|
||||
|
||||
|
||||
OcTree<TreeBranch> ocTree;
|
||||
CustomTreeWalker<TreeBranch> walker;
|
||||
|
||||
async Task GenerateBranches()
|
||||
{
|
||||
if ( walker == null )
|
||||
{
|
||||
walker = new CustomTreeWalker<TreeBranch>(
|
||||
tb => _branches[ tb.parent ],
|
||||
( tb, childIndex ) => _branches[ childIndex ],
|
||||
tb => tb.children.Count()
|
||||
);
|
||||
}
|
||||
|
||||
if ( randomizeSeed )
|
||||
{
|
||||
seed = GodotRandom.Get().IntegerInclusive( 0, 10000 );
|
||||
}
|
||||
|
||||
branches = [];
|
||||
_branches.Clear();
|
||||
this.DestroyChildren();
|
||||
|
||||
|
||||
ocTree = new OcTree<TreeBranch>( GlobalPosition, rootHeight * 3 * maxLevel, 10 );
|
||||
|
||||
structure = this.CreateChild<Node3D>( "Tree Structure" );
|
||||
|
||||
random = LCG.WithSeed( seed );
|
||||
|
||||
branches = _branches.ToArray();
|
||||
|
||||
var nextParents = new List<TreeBranch>();
|
||||
|
||||
var root = GenerateBranch( -1, 0, 0 );
|
||||
nextParents.Add( root );
|
||||
|
||||
var time = Async.StartTimer();
|
||||
|
||||
maxChecksFull = 0;
|
||||
|
||||
for ( int i = 0; i < maxLevel; i++ )
|
||||
{
|
||||
|
||||
var parents = nextParents;
|
||||
nextParents = new List<TreeBranch>();
|
||||
|
||||
var normalizedLevel = ( ( (float) i ) / (float) ( maxLevel - 1f ) );
|
||||
|
||||
// this.LogInfo( "Level", i, normalizedLevel, "Parents", parents.Count );
|
||||
|
||||
for ( int j =0 ; j < parents.Count; j++ )
|
||||
{
|
||||
var p = parents[ j ];
|
||||
random.SetSeed( p.startSeed + 1234 );
|
||||
var numKids = Mathf.RoundToInt( levelToChildBranches.Sample( normalizedLevel ) );
|
||||
|
||||
// this.LogInfo( i, "Parent", j, "Index:", p.index, "Kids:", numKids );
|
||||
|
||||
for ( int k = 0; k < numKids; k++ )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, this, processTimePerFrame );
|
||||
nextParents.Add( GenerateBranch( p.index, normalizedLevel, numKids ) );
|
||||
}
|
||||
|
||||
// this.LogInfo( "Num Next", nextParents.Count );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( int i = 0; i < _branches.Count; i++ )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, processTimePerFrame );
|
||||
|
||||
var p = _branches[ i ];
|
||||
|
||||
if ( p.children.Length == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var child = walker.GetDirectChildWithHighestValue( p, tb => tb.branchPosition );
|
||||
|
||||
if ( child == null || child.parent != p.index )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var spline = p.GetSpline( this );
|
||||
var lastPoint = spline.GetLastChild<SplinePoint>();
|
||||
|
||||
if ( lastPoint == null )
|
||||
{
|
||||
this.LogInfo( "has no lastPoint:", p.index );
|
||||
continue;
|
||||
}
|
||||
|
||||
var childSpline = child.GetSpline( this );
|
||||
var firstPoint = childSpline.GetChild<SplinePoint>();
|
||||
var lastPoint2 = childSpline.GetLastChild<SplinePoint>();
|
||||
|
||||
p.radius2 = SmallestChildRadius( p );
|
||||
p.radius3 = SmallestChildRadius( child ) * 0.5f;
|
||||
p.numRadi = 3;
|
||||
|
||||
|
||||
lastPoint.GlobalPosition = firstPoint.GlobalPosition;
|
||||
lastPoint.SetGlobalQuaternion( firstPoint.GetGlobalQuaternion() );
|
||||
|
||||
var additional = spline.CreateChild<SplinePoint>();
|
||||
additional.GlobalPosition = lastPoint2.GlobalPosition;
|
||||
additional.SetGlobalQuaternion( lastPoint2.GetGlobalQuaternion() );
|
||||
|
||||
spline.ClearCurveCache();
|
||||
spline.AutoOrientate();
|
||||
spline.GetCurve();
|
||||
}
|
||||
|
||||
if ( levelToSubdivisionNoise != null )
|
||||
{
|
||||
for ( int bb = 0; bb < _branches.Count; bb++ )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, processTimePerFrame );
|
||||
|
||||
var b = _branches[ bb ];
|
||||
|
||||
var normalizedLevel = b.level / maxLevel;
|
||||
var subdivisions = Mathf.Ceil( levelToSubdivisionNoise.Sample( normalizedLevel ) );
|
||||
var spline = b.GetSpline( this );
|
||||
var curve = spline.GetCurve();
|
||||
var numPoints = curve.points.Count + subdivisions;
|
||||
|
||||
spline.DestroyChildren();
|
||||
spline.ClearCurveCache();
|
||||
|
||||
if ( useDebugSingleMeshGeneration && bb % 5 == 0 )
|
||||
{
|
||||
await this.RequestNextFrame();
|
||||
}
|
||||
|
||||
for ( int i = 0; i < numPoints; i++ )
|
||||
{
|
||||
time = await Async.WaitIfExceeded( time, processTimePerFrame );
|
||||
|
||||
var t = i / (numPoints - 1f );
|
||||
var t2 = 1f - t;
|
||||
var minT = Mathf.Min( t * 2f, t2 * 2f );
|
||||
var p = curve.PoseAt( t );
|
||||
var rr = random.Sample( subdivisionNoiseRange );
|
||||
p.position += random.InSphere( rr * minT * b.height / (float)subdivisions );
|
||||
var sp = spline.CreateChild<SplinePoint>();
|
||||
sp.editorSplinePointSize = 0.001f;
|
||||
|
||||
p.Set( sp );
|
||||
|
||||
}
|
||||
|
||||
|
||||
var ss = b.GetSpline( this );
|
||||
ss.AutoOrientate();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
branches = _branches.ToArray();
|
||||
|
||||
if ( hideStructureWhenMeshWasBuild )
|
||||
{
|
||||
structure.Disable();
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Export]
|
||||
int maxChecksFull = 0;
|
||||
|
||||
TreeBranch GenerateBranch( int parent, float normalizedLevel, int numSiblings )
|
||||
{
|
||||
|
||||
TreeBranch parentBranch = null;
|
||||
var childIndex = 0;
|
||||
|
||||
if ( parent != -1 )
|
||||
{
|
||||
parentBranch = _branches[ parent ];
|
||||
childIndex = parentBranch.children.Length;
|
||||
}
|
||||
|
||||
var treeBranch = new TreeBranch();
|
||||
treeBranch.parent = parent;
|
||||
treeBranch.level = parentBranch == null ? 0 : parentBranch.level + 1;
|
||||
treeBranch.index = _branches.Count;
|
||||
treeBranch.startSeed = seed * 100000 + treeBranch.level * 1000 + childIndex;
|
||||
|
||||
|
||||
|
||||
random.SetSeed( treeBranch.startSeed );
|
||||
|
||||
if ( parentBranch == null )
|
||||
{
|
||||
treeBranch.radius = rootRadius;
|
||||
treeBranch.height = rootHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
treeBranch.radius = parentBranch.radius * random.Sample( radiusScale );
|
||||
treeBranch.height = parentBranch.height * random.Sample( heightScale );
|
||||
|
||||
parentBranch.children = Arrays.Add( parentBranch.children, treeBranch.index );
|
||||
}
|
||||
|
||||
treeBranch.branchPosition = random.SelectCurveWeights( branchPosition, 100 );
|
||||
|
||||
var spline = structure.CreateChild<Spline>( "Branch - " + treeBranch.level + " - " + childIndex);
|
||||
|
||||
|
||||
treeBranch.spline = spline.GetNodePath();
|
||||
|
||||
if ( parentBranch == null )
|
||||
{
|
||||
var root = spline.CreateChild<SplinePoint>();
|
||||
var end = spline.CreateChild<SplinePoint>();
|
||||
|
||||
root.editorSplinePointSize = 0.001f;
|
||||
|
||||
end.Position = new Vector3( 0, rootHeight, 0 ) + random.InSphere( noise );
|
||||
|
||||
|
||||
spline.autoOrientationUp = Vector3.Back;
|
||||
}
|
||||
else
|
||||
{
|
||||
var parentSpline = parentBranch.GetSpline( this );
|
||||
var parentCurve = parentSpline.GetCurve();
|
||||
|
||||
|
||||
var rootPose = parentCurve.PoseAt( treeBranch.branchPosition );
|
||||
|
||||
var siblingRotation = 360f * ( childIndex / (float)numSiblings );
|
||||
siblingRotation += random.Sample( siblingRotationVariance, 0, 0 );
|
||||
|
||||
rootPose.ApplyTwist( siblingRotation );
|
||||
var rootRotationCenter = rotationPerLevel.Sample( normalizedLevel );
|
||||
var rootRotationRange = rotationVariation == null ? 0 : random.Sample( rotationVariation );
|
||||
rootPose.RotateAround( Math3D.RotateXDegrees( random.Range( rootRotationCenter - rootRotationRange, rootRotationCenter + rootRotationRange ) ), rootPose.position );
|
||||
|
||||
|
||||
var endPose = rootPose.Clone();
|
||||
endPose.position = rootPose.position + rootPose.forward * treeBranch.height + random.InSphere( noise );;
|
||||
|
||||
var rootPoint = spline.CreateChild<SplinePoint>();
|
||||
var endPoint = spline.CreateChild<SplinePoint>();
|
||||
|
||||
rootPose.Set( rootPoint );
|
||||
endPose.Set( endPoint );
|
||||
|
||||
var checkForOverlaps = true;
|
||||
var maxChecks = 30;
|
||||
var checks = 0;
|
||||
|
||||
while ( avoidClusters && checkForOverlaps && checks < maxChecks )
|
||||
{
|
||||
var line = new Line3( rootPoint.GlobalPosition, endPoint.GlobalPosition );
|
||||
|
||||
var testBox = line.GetBox( treeBranch.radius );
|
||||
|
||||
var overlapping = ocTree.GetInsideBox( testBox );
|
||||
|
||||
overlapping = overlapping.Filter( o => o != parentBranch );
|
||||
|
||||
var conflictElement = overlapping.Find(
|
||||
( tb )=>
|
||||
{
|
||||
var tbLine = tb.GetLine3();
|
||||
|
||||
if ( line.DistanceToLine( tbLine ) < treeBranch.radius * minClusterDistance )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
if ( conflictElement == null )
|
||||
{
|
||||
checkForOverlaps = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
treeBranch.branchPosition = random.SelectCurveWeights( branchPosition, 100 );
|
||||
|
||||
rootPose = parentCurve.PoseAt( treeBranch.branchPosition );
|
||||
|
||||
rootPose.ApplyTwist( random.Range( 0, 360 ) );
|
||||
rootRotationCenter = rotationPerLevel.Sample( normalizedLevel );
|
||||
rootRotationRange = rotationVariation == null ? 0 : random.Sample( rotationVariation );
|
||||
rootPose.RotateAround( Math3D.RotateXDegrees( random.Range( rootRotationCenter - rootRotationRange, rootRotationCenter + rootRotationRange ) ), rootPose.position );
|
||||
|
||||
endPose = rootPose.Clone();
|
||||
endPose.position = rootPose.position + rootPose.forward * treeBranch.height + random.InSphere( noise );
|
||||
|
||||
rootPose.Set( rootPoint );
|
||||
endPose.Set( endPoint );
|
||||
}
|
||||
|
||||
checks ++;
|
||||
|
||||
}
|
||||
|
||||
if ( checks == maxChecks )
|
||||
{
|
||||
maxChecksFull ++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
spline.AutoOrientate();
|
||||
spline.SetEditorPointSize( 0.1f / 3f );
|
||||
|
||||
var curve = spline.GetCurve();
|
||||
treeBranch.start = curve.PositionAt( 0 );
|
||||
treeBranch.end = curve.PositionAt( 1 );
|
||||
|
||||
|
||||
var box = spline.GetBounds();
|
||||
|
||||
ocTree.BoxInsert( treeBranch, box );
|
||||
|
||||
_branches.Add( treeBranch );
|
||||
|
||||
return treeBranch;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://dpy0wfloqjafd
|
|
@ -340,7 +340,7 @@ namespace Rokojori
|
|||
|
||||
public List<SubViewport> GetAllViewports()
|
||||
{
|
||||
return Nodes.AllIn<SubViewport>( X_views, null, false );
|
||||
return Nodes.GetAll<SubViewport>( X_views, null, false );
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
|
|
|
@ -157,7 +157,7 @@ namespace Rokojori
|
|||
{
|
||||
if ( sourceViewportsContainer != null )
|
||||
{
|
||||
sourceViewports = Nodes.AllIn<SubViewport>( sourceViewportsContainer, null, false ).ToArray();
|
||||
sourceViewports = Nodes.GetAll<SubViewport>( sourceViewportsContainer, null, false ).ToArray();
|
||||
}
|
||||
|
||||
var vpTextures = Arrays.Map( sourceViewports,
|
||||
|
|
|
@ -531,6 +531,61 @@ namespace Rokojori
|
|||
return u * ( segments + 1 ) + v;
|
||||
}
|
||||
|
||||
public static MeshGeometry CreateCapUVFunction( Func<Vector2,Pose> uvFunc, int uSegments, bool start = false )
|
||||
{
|
||||
var positions = new List<Vector3>();
|
||||
var uvs = new List<Vector2>();
|
||||
var normals = new List<Vector3>();
|
||||
|
||||
var v = start ? 0f : 1f;
|
||||
|
||||
for ( int i = 0; i <= uSegments; i++ )
|
||||
{
|
||||
var u = i / (float)( uSegments );
|
||||
|
||||
var lookUpUV = new Vector2( u, v );
|
||||
var p = uvFunc( lookUpUV );
|
||||
positions.Add( p.position );
|
||||
normals.Add( ( start ? -1f : 1f ) * p.forward );
|
||||
|
||||
var angle = u * Mathf.Pi * 2.0f;
|
||||
var uvCoord = Math2D.Circle( angle ) * 0.5f + Vector2.One * 0.5f;
|
||||
uvs.Add( uvCoord );
|
||||
}
|
||||
|
||||
var mg = new MeshGeometry();
|
||||
|
||||
var centerPosition = Math3D.ComputeAverage( positions );
|
||||
var centerNormal = Math3D.ComputeAverage( normals ).Normalized();
|
||||
var centerUV = new Vector2( 0.5f, 0.5f );
|
||||
|
||||
|
||||
for ( int i = 0; i < positions.Count; i++ )
|
||||
{
|
||||
var j = ( i + 1 ) % ( positions.Count );
|
||||
|
||||
if ( start )
|
||||
{
|
||||
mg.AddTriangle(
|
||||
positions[ j ], positions[i ], centerPosition,
|
||||
normals[ j ], normals[ i ], centerNormal,
|
||||
uvs[ j], uvs[ i ], centerUV
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
mg.AddTriangle(
|
||||
positions[ i ], positions[ j ], centerPosition,
|
||||
normals[ i ], normals[ j ], centerNormal,
|
||||
uvs[ i ], uvs[ j ], centerUV
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return mg;
|
||||
}
|
||||
|
||||
public static MeshGeometry CreateFromUVFunction( Func<Vector2,Pose> uv, int uSegments, int vSegments, bool fullUVQuads = false )
|
||||
{
|
||||
|
@ -1176,9 +1231,14 @@ namespace Rokojori
|
|||
mg.ApplyUVTransform( uvTransform );
|
||||
|
||||
Add( mg );
|
||||
}
|
||||
}
|
||||
|
||||
public void AddQuad( Quaternion rotation, float size, Vector2 uv00, Vector2 uv11 )
|
||||
{
|
||||
AddQuad( rotation, size, uv00, uv11, Vector3.Zero );
|
||||
}
|
||||
|
||||
public void AddQuad( Quaternion rotation, float size, Vector2 uv00, Vector2 uv11, Vector3 offset )
|
||||
{
|
||||
var l = size * 0.5f;
|
||||
|
||||
|
@ -1198,7 +1258,7 @@ namespace Rokojori
|
|||
var uv01 = new Vector2( uv00.X, uv11.Y );
|
||||
|
||||
AddQuad(
|
||||
points[ 0 ], points[ 1 ], points[ 2 ], points[ 3 ],
|
||||
points[ 0 ] + offset, points[ 1 ] + offset, points[ 2 ] + offset, points[ 3 ] + offset,
|
||||
normal, normal, normal, normal,
|
||||
uv10, uv00, uv01, uv11
|
||||
);
|
||||
|
@ -1385,7 +1445,35 @@ namespace Rokojori
|
|||
|
||||
}
|
||||
|
||||
|
||||
public void SmoothNormals()
|
||||
{
|
||||
for ( int i = 0; i < normals.Count; i +=3 )
|
||||
{
|
||||
normals[ i ] = Vector3.Zero;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < indices.Count; i +=3 )
|
||||
{
|
||||
int i0 = indices[ i ];
|
||||
int i1 = indices[ i + 1 ];
|
||||
int i2 = indices[ i + 2 ];
|
||||
|
||||
var v0 = vertices[ i0 ];
|
||||
var v1 = vertices[ i1 ];
|
||||
var v2 = vertices[ i2 ];
|
||||
|
||||
var faceNormal = ( v1 - v0 ).Cross( v2 - v0 ).Normalized();
|
||||
|
||||
normals[ i0 ] += faceNormal;
|
||||
normals[ i1 ] += faceNormal;
|
||||
normals[ i2 ] += faceNormal;
|
||||
}
|
||||
|
||||
for (int i = 0; i < normals.Count; i++ )
|
||||
{
|
||||
normals[ i ] = normals[ i ].Normalized();
|
||||
}
|
||||
}
|
||||
public static ArrayMesh GenerateLODMesh( List<MeshGeometry>
|
||||
mgs, Mesh.PrimitiveType type = Mesh.PrimitiveType.Triangles, ArrayMesh arrayMesh = null,
|
||||
bool generateTangents = true, LODLerpingData lerpingData = null )
|
||||
|
@ -1408,6 +1496,8 @@ namespace Rokojori
|
|||
return GenerateLODMesh( mgs, Mesh.PrimitiveType.Triangles, null, generateTangents, lerpingData );
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ArrayMesh GenerateMesh(
|
||||
Mesh.PrimitiveType type = Mesh.PrimitiveType.Triangles, ArrayMesh arrayMesh = null,
|
||||
bool generateTangents = true, List<MeshGeometry> lods = null, LODLerpingData lerpingData = null )
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace Rokojori
|
|||
|
||||
var list = new List<QuadBillboardData>();
|
||||
|
||||
this.OnAllDirectChildren<QuadBillboardDataProcessor>(
|
||||
this.ForEachDirectChild<QuadBillboardDataProcessor>(
|
||||
( p )=>
|
||||
{
|
||||
list = p.Process( list );
|
||||
|
|
|
@ -42,7 +42,13 @@ namespace Rokojori
|
|||
|
||||
[Export]
|
||||
public int xzPathBakeResolution = 50;
|
||||
|
||||
|
||||
|
||||
public void SetEditorPointSize( float size )
|
||||
{
|
||||
this.ForEachDirectChild<SplinePoint>( p => p.editorSplinePointSize = size );
|
||||
}
|
||||
|
||||
SplineCurve splineCurve;
|
||||
Vector3 min;
|
||||
Vector3 max;
|
||||
|
@ -228,7 +234,7 @@ namespace Rokojori
|
|||
}
|
||||
#endif
|
||||
|
||||
void AutoOrientate()
|
||||
public void AutoOrientate()
|
||||
{
|
||||
var list = Nodes.GetDirectChildren<SplinePoint>( this );
|
||||
|
||||
|
@ -253,7 +259,7 @@ namespace Rokojori
|
|||
{
|
||||
var first = list[ 0 ];
|
||||
var firstDirection = ( first.GlobalPosition - point.GlobalPosition ).Normalized();
|
||||
point.LookTowards( firstDirection, up);
|
||||
point.LookTowards( firstDirection, up );
|
||||
}
|
||||
|
||||
continue;
|
||||
|
|
|
@ -31,13 +31,18 @@ namespace Rokojori
|
|||
|
||||
#if TOOLS
|
||||
|
||||
|
||||
[ExportGroup("Editor")]
|
||||
[Export]
|
||||
public float editorSplinePointSize = 0.1f;
|
||||
|
||||
public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
|
||||
{
|
||||
gizmo.Clear();
|
||||
|
||||
|
||||
var mesh = new SphereMesh();
|
||||
var size = 0.1f;
|
||||
var size = editorSplinePointSize;
|
||||
mesh.Radius = size;
|
||||
mesh.Height = size * 2f;
|
||||
|
||||
|
|
|
@ -30,42 +30,45 @@ namespace Rokojori
|
|||
[Export]
|
||||
public Spline spline;
|
||||
|
||||
[Export]
|
||||
public TubeSegmentMode segmentMode = TubeSegmentMode.Fixed_Division;
|
||||
// [Export]
|
||||
// public TubeSegmentMode segmentMode = TubeSegmentMode.Fixed_Division;
|
||||
|
||||
[Export]
|
||||
public bool useFullUVQuads = false;
|
||||
// [Export]
|
||||
// public bool useFullUVQuads = false;
|
||||
|
||||
[Export]
|
||||
public int fixedSplineSegmentDivisions = 20;
|
||||
// [Export]
|
||||
// public int fixedSplineSegmentDivisions = 20;
|
||||
|
||||
[Export]
|
||||
public float splineSegmentLength = 2;
|
||||
// [Export]
|
||||
// public float splineSegmentLength = 2;
|
||||
|
||||
|
||||
[Export]
|
||||
public bool undistortSplineSegments = true;
|
||||
// [Export]
|
||||
// public bool undistortSplineSegments = true;
|
||||
|
||||
// [Export]
|
||||
// public Curve twistCurve;
|
||||
|
||||
// [Export]
|
||||
// public float radius;
|
||||
|
||||
// [Export]
|
||||
// public int radialSegments = 8;
|
||||
|
||||
// [Export]
|
||||
// public Curve radiusSizeCurve;
|
||||
|
||||
// [Export]
|
||||
// public Curve radiusWidthCurve;
|
||||
|
||||
// [Export]
|
||||
// public Curve radiusHeightCurve;
|
||||
|
||||
// [Export]
|
||||
// public bool scaleRadiusByPathTransforms = true;
|
||||
|
||||
[Export]
|
||||
public Curve twistCurve;
|
||||
|
||||
[Export]
|
||||
public float radius;
|
||||
|
||||
[Export]
|
||||
public int radialSegments = 8;
|
||||
|
||||
[Export]
|
||||
public Curve radiusSizeCurve;
|
||||
|
||||
[Export]
|
||||
public Curve radiusWidthCurve;
|
||||
|
||||
[Export]
|
||||
public Curve radiusHeightCurve;
|
||||
|
||||
[Export]
|
||||
public bool scaleRadiusByPathTransforms = true;
|
||||
public TubeGeometrySettings settings;
|
||||
|
||||
[Export]
|
||||
public TubeShape[] shapes = new TubeShape[ 0 ];
|
||||
|
@ -140,6 +143,12 @@ namespace Rokojori
|
|||
|
||||
|
||||
var mg = CreateMesh();
|
||||
|
||||
if ( output == null )
|
||||
{
|
||||
output = this.CreateChild<MeshInstance3D>();
|
||||
}
|
||||
|
||||
output.Mesh = mg.GenerateMesh();
|
||||
|
||||
UpdateGizmos();
|
||||
|
@ -151,6 +160,16 @@ namespace Rokojori
|
|||
MeshGeometry CreateMesh()
|
||||
{
|
||||
var curve = spline.GetCurve();
|
||||
|
||||
var tubeGeometry = new TubeGeometry();
|
||||
tubeGeometry.curve = curve;
|
||||
tubeGeometry.settings = settings;
|
||||
tubeGeometry.shapes = shapes;
|
||||
|
||||
return tubeGeometry.CreateMesh();
|
||||
|
||||
|
||||
/*
|
||||
int splineSegments = fixedSplineSegmentDivisions;
|
||||
|
||||
if ( TubeSegmentMode.Fixed_Division != segmentMode )
|
||||
|
@ -259,6 +278,7 @@ namespace Rokojori
|
|||
);
|
||||
|
||||
return mg;
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
|
||||
using Godot;
|
||||
using Rokojori;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
||||
public class TubeGeometry
|
||||
{
|
||||
public SplineCurve curve;
|
||||
public TubeShape[] shapes = [];
|
||||
|
||||
public TubeGeometrySettings settings;
|
||||
|
||||
public MeshGeometry CreateMesh()
|
||||
{
|
||||
int splineSegments = settings.fixedSplineSegmentDivisions;
|
||||
|
||||
if ( TubeSegmentMode.Fixed_Division != settings.segmentMode )
|
||||
{
|
||||
splineSegments = Mathf.CeilToInt( curve.ComputeLength( curve.points.Count * 3 ) / settings.splineSegmentLength );
|
||||
|
||||
if ( TubeSegmentMode.Maximum_Of_Both == settings.segmentMode )
|
||||
{
|
||||
splineSegments = Mathf.Max( splineSegments, settings.fixedSplineSegmentDivisions );
|
||||
}
|
||||
if ( TubeSegmentMode.Minimum_Of_Both == settings.segmentMode )
|
||||
{
|
||||
splineSegments = Mathf.Min( splineSegments, settings.fixedSplineSegmentDivisions );
|
||||
}
|
||||
}
|
||||
|
||||
var shapesList = new List<TubeShape>();
|
||||
|
||||
if ( shapes != null && shapes.Length > 0 )
|
||||
{
|
||||
shapesList.AddRange( shapes );
|
||||
Lists.Sort( shapesList, s => s.tubePosition );
|
||||
|
||||
shapesList.ForEach( s => s.ClearCache() );
|
||||
|
||||
}
|
||||
|
||||
System.Func<Vector2,Pose> uvFunction = ( Vector2 uv ) =>
|
||||
{
|
||||
var t = settings.undistortSplineSegments ? curve.ComputeTforNormalizedCurveLength( uv.Y, splineSegments ) : uv.Y;
|
||||
var index = curve.NormalizedToPointIndex( t );
|
||||
var pose = curve.PoseAt( t );
|
||||
// RJLog.Log( "Pose at t:", t, "rot:", pose.rotation, "pos", pose.position );
|
||||
var radiusSize = settings.radius * ( settings.radiusSizeCurve == null ? 1 : settings.radiusSizeCurve.Sample( t ) );
|
||||
var sizeX = radiusSize;
|
||||
var sizeY = radiusSize;
|
||||
var twistOffset = 0f;
|
||||
|
||||
if ( settings.radiusWidthCurve != null )
|
||||
{
|
||||
sizeX *= settings.radiusWidthCurve.Sample( t );
|
||||
}
|
||||
|
||||
if ( settings.radiusHeightCurve != null )
|
||||
{
|
||||
sizeY *= settings.radiusHeightCurve.Sample( t );
|
||||
}
|
||||
|
||||
if ( settings.scaleRadiusByPathTransforms )
|
||||
{
|
||||
|
||||
var splineScale = curve.SmoothStepScaleByPointIndex( index );
|
||||
|
||||
if ( settings.scaleRadiusByPathTransforms )
|
||||
{
|
||||
sizeX *= splineScale.X;
|
||||
sizeY *= splineScale.Y;
|
||||
}
|
||||
}
|
||||
|
||||
if ( settings.twistCurve != null )
|
||||
{
|
||||
twistOffset = Mathf.DegToRad( settings.twistCurve.Sample( t ) );
|
||||
}
|
||||
|
||||
twistOffset += Mathf.DegToRad( curve.SmoothStepTwistByPointIndex( index ) * 360 );
|
||||
|
||||
var twistRotation = Math3D.RotateZ( twistOffset ).Normalized();
|
||||
|
||||
var scale = new Vector3( sizeX, sizeY, 0 );
|
||||
|
||||
if ( shapesList.Count > 0 )
|
||||
{
|
||||
if ( shapesList.Count == 1 )
|
||||
{
|
||||
return shapesList[ 0 ].GetPose( settings.undistortSplineSegments, splineSegments, settings.radialSegments, pose, uv, scale, twistRotation );
|
||||
}
|
||||
|
||||
var lerpResult = Lists.LerpIndex( shapesList, uv.Y, s => s.tubePosition );
|
||||
|
||||
|
||||
var closestShape = shapesList[ lerpResult.closestIndex ];
|
||||
var secondShape = shapesList[ lerpResult.secondIndex ];
|
||||
|
||||
var closestPose = closestShape.GetPose( settings.undistortSplineSegments, splineSegments, settings.radialSegments, pose, uv, scale, twistRotation );
|
||||
var secondPose = secondShape.GetPose( settings.undistortSplineSegments, splineSegments, settings.radialSegments, pose, uv, scale, twistRotation );
|
||||
|
||||
var smoothLerp = Mathf.SmoothStep( 0f, 1f, lerpResult.lerpAmount );
|
||||
return Pose.Lerp( closestPose, secondPose, smoothLerp );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var angle = uv.X * 2f * Mathf.Pi * ( sizeX / sizeY );
|
||||
var circlePose = Pose.Create( Vector3.Zero, pose.rotation * Math3D.RotateZ( angle ) );
|
||||
|
||||
|
||||
circlePose.rotation = circlePose.rotation.Normalized();
|
||||
var circleStart = Vector3.Up * radiusSize * scale;
|
||||
|
||||
|
||||
|
||||
var positionOnCircle = circlePose.Apply( circleStart );
|
||||
|
||||
return Pose.Create( positionOnCircle + pose.position, circlePose.rotation );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var mg = MeshGeometry.CreateFromUVFunction( uvFunction,
|
||||
settings.radialSegments, splineSegments, settings.useFullUVQuads
|
||||
);
|
||||
|
||||
if ( TubeGeometrySettings.CapType.Flat == settings.startCapType )
|
||||
{
|
||||
var startCapMG = MeshGeometry.CreateCapUVFunction( uvFunction, settings.radialSegments, true );
|
||||
mg.Add( startCapMG );
|
||||
}
|
||||
|
||||
if ( TubeGeometrySettings.CapType.Flat == settings.endCapType )
|
||||
{
|
||||
var endCapMG = MeshGeometry.CreateCapUVFunction( uvFunction, settings.radialSegments, false );
|
||||
mg.Add( endCapMG );
|
||||
}
|
||||
|
||||
return mg;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://ccc2vjq7kmqiu
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
using Godot;
|
||||
using Rokojori;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class TubeGeometrySettings:Resource
|
||||
{
|
||||
[Export]
|
||||
public TubeSegmentMode segmentMode = TubeSegmentMode.Fixed_Division;
|
||||
[Export]
|
||||
public bool useFullUVQuads = false;
|
||||
[Export]
|
||||
public int fixedSplineSegmentDivisions = 20;
|
||||
[Export]
|
||||
public float splineSegmentLength = 2;
|
||||
[Export]
|
||||
public bool undistortSplineSegments = true;
|
||||
[Export]
|
||||
public Curve twistCurve;
|
||||
[Export]
|
||||
public float radius;
|
||||
[Export]
|
||||
public int radialSegments = 8;
|
||||
[Export]
|
||||
public Curve radiusSizeCurve;
|
||||
[Export]
|
||||
public Curve radiusWidthCurve;
|
||||
[Export]
|
||||
public Curve radiusHeightCurve;
|
||||
[Export]
|
||||
public bool scaleRadiusByPathTransforms = true;
|
||||
|
||||
public enum CapType
|
||||
{
|
||||
None,
|
||||
Flat,
|
||||
Half_Sphere
|
||||
}
|
||||
|
||||
[Export]
|
||||
public CapType startCapType;
|
||||
|
||||
[Export]
|
||||
public Vector2 startCapUVMin =Vector2.Zero;
|
||||
|
||||
[Export]
|
||||
public Vector2 startCapUVMax =Vector2.One;
|
||||
|
||||
[Export]
|
||||
public CapType endCapType;
|
||||
|
||||
[Export]
|
||||
public Vector2 endCapUVMin =Vector2.Zero;
|
||||
|
||||
[Export]
|
||||
public Vector2 endCapUVMax =Vector2.One;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://c7ud5wnn6t4iw
|
|
@ -31,8 +31,13 @@ namespace Rokojori
|
|||
return this.Next()*( b - a ) + a;
|
||||
}
|
||||
|
||||
public float Sample( Curve curve )
|
||||
public float Sample( Curve curve, float min = 0, float max = 0 )
|
||||
{
|
||||
if ( curve == null )
|
||||
{
|
||||
return Range( min, max );
|
||||
}
|
||||
|
||||
return curve.Sample( Next() );
|
||||
}
|
||||
|
||||
|
@ -365,6 +370,11 @@ namespace Rokojori
|
|||
return _FindElementIndexWithWeights( weights, Next() * sumWeights );
|
||||
}
|
||||
|
||||
public float SelectCurveWeights( Curve curve, int numSteps = 100 )
|
||||
{
|
||||
return IndexFromCurveWeights( curve, numSteps ) / (float) numSteps;
|
||||
}
|
||||
|
||||
public int IndexFromCurveWeights( Curve curve, int numIndices )
|
||||
{
|
||||
var weights = new List<float>();
|
||||
|
|
|
@ -514,7 +514,9 @@ namespace Rokojori.Reallusion
|
|||
// mat.specular.AssignFor( m, 0.5f );
|
||||
mat.opacity.AssignFor( m, opacity.GetOr( 1 ) * opacityScale );
|
||||
mat.blendAmount.AssignFor( m, 0f );
|
||||
|
||||
|
||||
mat.roughnessOffset.AssignFor( m, settings.configuration.roughnessOffset );
|
||||
mat.metallicOffset.AssignFor( m, settings.configuration.metallicOffset );
|
||||
|
||||
textures.value.ForEach(
|
||||
( t ) =>
|
||||
|
|
|
@ -14,8 +14,7 @@ namespace Rokojori.Reallusion
|
|||
public partial class CCImportConfiguration:Resource
|
||||
{
|
||||
|
||||
[Export]
|
||||
public bool setRenderPrioritiesForHair = true;
|
||||
|
||||
|
||||
[Export]
|
||||
public float gammaForAlbedoColor = 1.0f / 2.2f;
|
||||
|
@ -49,12 +48,26 @@ namespace Rokojori.Reallusion
|
|||
[Export]
|
||||
public HairShaderMode hairShaderMode = HairShaderMode.Scissor_AlphaBack_AlphaFront;
|
||||
|
||||
[ExportGroup( "Hair/Render Order")]
|
||||
[Export]
|
||||
public bool setRenderPrioritiesForHair = true;
|
||||
|
||||
[Export]
|
||||
public CCMaterialTarget[] preRenderPriorityTargets = [];
|
||||
|
||||
[ExportGroup( "Hair")]
|
||||
[Export(PropertyHint.Range,"0,1")]
|
||||
public float naturalColors = 0.5f;
|
||||
|
||||
[Export(PropertyHint.Range,"0,1")]
|
||||
public float maximumHighlightAmount = 0.5f;
|
||||
|
||||
[Export(PropertyHint.Range,"-1,1")]
|
||||
public float roughnessOffset = -0.1f;
|
||||
|
||||
[Export(PropertyHint.Range,"-1,1")]
|
||||
public float metallicOffset = 0.0f;
|
||||
|
||||
[Export]
|
||||
public float hairOpacityGamma = 1.2f;
|
||||
|
||||
|
|
|
@ -26,6 +26,13 @@ namespace Rokojori.Reallusion
|
|||
[Export]
|
||||
public CCMaterialType materialType;
|
||||
|
||||
|
||||
[Export]
|
||||
public int materialRenderPriority = 0;
|
||||
|
||||
[Export]
|
||||
public bool assignRenderPriority = false;
|
||||
|
||||
[Export]
|
||||
public int materialRenderPriorityIndexBack;
|
||||
|
||||
|
|
|
@ -130,6 +130,31 @@ namespace Rokojori.Reallusion
|
|||
{
|
||||
var renderIndex = 0;
|
||||
|
||||
for ( int i = 0; i < importSettings.configuration.preRenderPriorityTargets.Length; i++ )
|
||||
{
|
||||
var target = importSettings.configuration.preRenderPriorityTargets[ i ];
|
||||
var targetName = target.meshName;
|
||||
var targetIndex = target.materialSurfaceIndex;
|
||||
|
||||
var mesh = importSettings.meshes.Find( m => m.meshName == targetName );
|
||||
|
||||
if ( mesh == null || targetIndex < 0 || targetIndex > mesh.materials.Length )
|
||||
{
|
||||
this.LogInfo( "Mesh not found:",
|
||||
targetName, ":", targetIndex
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var material = mesh.materials[ targetIndex ];
|
||||
|
||||
material.assignRenderPriority = true;
|
||||
material.materialRenderPriority = renderIndex;
|
||||
|
||||
renderIndex++;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < importSettings.meshes.Length; i++ )
|
||||
{
|
||||
var ms = importSettings.meshes[ i ];
|
||||
|
@ -180,6 +205,11 @@ namespace Rokojori.Reallusion
|
|||
materialGenerator.CreateMaterial( m.materials[ i ], settings );
|
||||
|
||||
|
||||
if ( settings.assignRenderPriority )
|
||||
{
|
||||
material.RenderPriority = settings.materialRenderPriority;
|
||||
}
|
||||
|
||||
var overwriteMaterial = settings.configuration.GetOverwriteMaterial( m.name, i );
|
||||
|
||||
if ( overwriteMaterial != null )
|
||||
|
|
|
@ -11,6 +11,7 @@ uniform sampler2D texture_albedo : source_color, filter_linear_mipmap_anisotropi
|
|||
uniform vec3 hslOffset = vec3( 0, 0, 0 );
|
||||
uniform sampler2D texture_blend : source_color,hint_default_white, filter_linear_mipmap_anisotropic;
|
||||
uniform float blendAmount:hint_range(0, 1) =1;
|
||||
// uniform float grow:hint_range(-0.01, 0.01, 0.0001) = 00;
|
||||
|
||||
uniform float naturalColors: hint_range(0, 1) = 0.5;
|
||||
|
||||
|
@ -100,7 +101,7 @@ group_uniforms;
|
|||
|
||||
group_uniforms Roughness_Metallness;
|
||||
uniform float roughness : hint_range(0.0, 1.0) = 1.0;
|
||||
uniform float roughnessOffset: hint_range(-1.0, 1.0) = -0.1;
|
||||
uniform float roughnessOffset: hint_range(-1.0, 1.0) = 0;
|
||||
uniform sampler2D texture_roughness : hint_roughness_r, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
|
||||
uniform float metallic : hint_range(0.0, 1.0, 0.01) = 1.0;
|
||||
|
@ -211,6 +212,8 @@ void vertex()
|
|||
cameraDistance, highlightFadeOutStart, highlightFadeOutEnd, highlightFadeOutPower, highlightFadeOutAmount
|
||||
) * maximumHighlightAmount;
|
||||
|
||||
// VERTEX += NORMAL * grow;
|
||||
|
||||
#ifdef USE_SHADOW
|
||||
|
||||
float farAmount = computeCameraFadeout(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
shader_type spatial;
|
||||
render_mode blend_mix, depth_draw_always, cull_disabled, diffuse_burley, specular_schlick_ggx, depth_prepass_alpha;
|
||||
render_mode blend_mix, depth_draw_opaque, cull_disabled, diffuse_burley, specular_schlick_ggx, depth_prepass_alpha;
|
||||
|
||||
#define USE_ALPHA_DISCARD
|
||||
//#define USE_ALPHA_DISCARD
|
||||
#include "res://addons/rokojori_action_library/Runtime/Reallusion/Shaders/Hair/CCHairBase.gdshaderinc"
|
|
@ -24,9 +24,7 @@ void main()
|
|||
ivec2 currentPosition = ivec2( gl_GlobalInvocationID.xy );
|
||||
|
||||
vec4 currentPixel = ensureValidVec4( imageLoad( currentImage, currentPosition ) );
|
||||
vec4 lastPixel = ensureValidVec4( imageLoad( lastProcessedImage, currentPosition ) );
|
||||
|
||||
|
||||
vec4 lastPixel = ensureValidVec4( imageLoad( lastProcessedImage, currentPosition ) );
|
||||
|
||||
vec4 nextPixel = currentPixel + params.smearing * ( lastPixel - currentPixel );
|
||||
vec4 combinedPixel = mix( currentPixel, nextPixel, params.amount );
|
||||
|
|
|
@ -3,10 +3,51 @@
|
|||
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc"
|
||||
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc"
|
||||
|
||||
|
||||
/*
|
||||
uniform bool windEnabled = false;
|
||||
uniform float windOcclusionAmount = 0;
|
||||
uniform float windStrength = 0;
|
||||
uniform vec2 windSpeed = vec2(1,1);
|
||||
uniform float windScale = 0.1;
|
||||
uniform sampler2D windNoise;
|
||||
uniform vec2 windNoiseAngleOffset;
|
||||
uniform vec2 windNoiseStrengthOffset;
|
||||
uniform float windStart = 0;
|
||||
uniform float windEnd = 1;
|
||||
uniform float windWeightCurve:hint_range(0,1) = 0.5f;
|
||||
uniform float windHeightCompensation :hint_range(0,1) = 0.5f;
|
||||
uniform float windNormalBending :hint_range(0,1) = 0.1f;
|
||||
varying float vertexWindAO;
|
||||
|
||||
applyWind(
|
||||
TIME,
|
||||
MODEL_MATRIX,
|
||||
VERTEX,
|
||||
NORMAL,
|
||||
windAO,
|
||||
windOcclusionAmount,
|
||||
windStrength,
|
||||
windSpeed,
|
||||
windScale,
|
||||
windNoise,
|
||||
windNoiseAngleOffset,
|
||||
windNoiseStrengthOffset,
|
||||
windStart,
|
||||
windEnd,
|
||||
windWeightCurve,
|
||||
windHeightCompensation,
|
||||
windNormalBending
|
||||
);
|
||||
|
||||
*/
|
||||
void applyWind(
|
||||
float _TIME,
|
||||
mat4 _MODEL_MATRIX,
|
||||
inout vec3 _VERTEX,
|
||||
inout vec3 _NORMAL,
|
||||
inout float _windAO,
|
||||
float _windOcclusionAmount,
|
||||
float _windStrength,
|
||||
vec2 _windSpeed,
|
||||
float _windScale,
|
||||
|
@ -16,18 +57,23 @@ void applyWind(
|
|||
float _windStart,
|
||||
float _windEnd,
|
||||
float _windWeightCurve,
|
||||
float _windHeightCompensation
|
||||
float _windHeightCompensation,
|
||||
float _windNormalBending
|
||||
)
|
||||
{
|
||||
float _windAmount = normalizeToRange01( _VERTEX.y, _windStart, _windEnd );
|
||||
float rawWindAmount = _windAmount;
|
||||
float originalHeight = _VERTEX.y;
|
||||
_windAmount = mix( _windAmount, _windAmount * _windAmount, _windWeightCurve );
|
||||
vec3 worldVertex = localToWorld( _VERTEX, _MODEL_MATRIX );
|
||||
vec2 _windUV = _TIME * _windSpeed + worldVertex.xz * _windScale;
|
||||
float angle = texture( _windNoise, _windUV + _windNoiseAngleOffset).r * PI * 2.0;
|
||||
float strength = texture( _windNoise, _windUV + _windNoiseStrengthOffset ).r * _windStrength;
|
||||
float generalStrength = texture( _windNoise, _windUV + _windNoiseStrengthOffset ).r;
|
||||
float strength = generalStrength * _windStrength;
|
||||
vec2 circle = onCircle( angle ) * strength;
|
||||
_VERTEX = worldToLocal( worldVertex + vec3( circle.x, 0, circle.y ) * _windAmount, _MODEL_MATRIX );
|
||||
float minY = min( _VERTEX.y, 0 );
|
||||
_VERTEX.y = mix( _VERTEX.y, max( minY, _VERTEX.y - strength * _windAmount), _windHeightCompensation * 4.0f );
|
||||
_NORMAL = normalize( mix( _NORMAL, vec3( circle.x, -1, circle.y ), generalStrength * _windNormalBending ) );
|
||||
_windAO = mix( 1, 0, generalStrength * _windOcclusionAmount);
|
||||
}
|
|
@ -10,8 +10,99 @@ using System.Linq;
|
|||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class MatchRange
|
||||
{
|
||||
public int start;
|
||||
public int length;
|
||||
|
||||
public MatchRange( int s, int l )
|
||||
{
|
||||
start = s;
|
||||
length = l;
|
||||
}
|
||||
|
||||
public string GetString( string source)
|
||||
{
|
||||
return source.Substring( start, length );
|
||||
}
|
||||
}
|
||||
|
||||
public class MatchResult
|
||||
{
|
||||
public string source;
|
||||
public bool matched = false;
|
||||
|
||||
public MatchRange match;
|
||||
|
||||
public List<MatchRange> groups;
|
||||
|
||||
public string this[ int index ]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( ! matched )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( index == 0 )
|
||||
{
|
||||
return match.GetString( source );
|
||||
}
|
||||
|
||||
if ( groups != null && index > 0 && index <= groups.Count )
|
||||
{
|
||||
return groups[ index - 1 ] == null ? null : groups[ index - 1 ].GetString( source );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class RegexUtility
|
||||
{
|
||||
public static MatchResult Exec( this string source, string pattern, RegexOptions options = RegexOptions.None )
|
||||
{
|
||||
var regex = new Regex( pattern );
|
||||
var match = regex.Match( source );
|
||||
|
||||
var result = new MatchResult();
|
||||
result.source = source;
|
||||
result.matched = match.Success;
|
||||
|
||||
if ( match.Success )
|
||||
{
|
||||
result.match = new MatchRange( match.Index, match.Length );
|
||||
|
||||
result.groups = new List<MatchRange>();
|
||||
|
||||
for ( int i = 1; i < match.Groups.Count; i++ )
|
||||
{
|
||||
var group = match.Groups[i];
|
||||
|
||||
if ( group.Success )
|
||||
{
|
||||
result.groups.Add( new MatchRange( group.Index, group.Length ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
result.groups.Add( null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool Matches( this string source, string regexPattern, RegexOptions options = RegexOptions.None )
|
||||
{
|
||||
var match = new Regex( regexPattern, options ).Match( source );
|
||||
|
||||
return match != null && match.Success;
|
||||
}
|
||||
|
||||
public static Regex MakeSticky( Regex regex )
|
||||
{
|
||||
var source = MakeSourceSticky( regex + "" );
|
||||
|
|
Loading…
Reference in New Issue