From ef45d31599a73dc3c748084224ebc7dc194d51fc Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 27 Jun 2025 07:12:53 +0200 Subject: [PATCH] Tree Update --- Runtime/Godot/NodeState.cs | 6 +- Runtime/Godot/Nodes.cs | 47 +- Runtime/Graphs/Trees/CustomTreeWalker.cs | 28 + Runtime/Graphs/Trees/CustomTreeWalker.cs.uid | 1 + Runtime/Graphs/Trees/TreeWalker.cs | 117 ++- .../CharacterController.cs | 22 +- Runtime/LOD/MultiMeshGenerator.cs | 2 +- Runtime/LOD/NTree/OcTree/OcTree.cs | 65 ++ Runtime/LOD/NTree/OcTree/OcTreeCell.cs | 47 ++ Runtime/Math/Geometry/Box3.cs | 33 + Runtime/Math/Geometry/Capsule3.cs | 7 + Runtime/Math/Geometry/Line3.cs | 8 + Runtime/Math/Geometry/Overlap3D.cs | 44 ++ Runtime/Math/Geometry/Overlap3D.cs.uid | 1 + Runtime/Math/Geometry/Pose.cs | 9 + Runtime/Math/Math3D.cs | 18 + Runtime/Math/MathX.cs | 30 +- Runtime/Math/Range.cs | 21 +- Runtime/Procedural/Assets/Tree/TreeBranch.cs | 71 ++ .../Procedural/Assets/Tree/TreeBranch.cs.uid | 1 + .../Procedural/Assets/Tree/TreeGenerator.cs | 737 ++++++++++++++++++ .../Assets/Tree/TreeGenerator.cs.uid | 1 + .../Baking/MultiBaker/MultiBaker.cs | 2 +- Runtime/Procedural/Baking/TextureMerger.cs | 2 +- Runtime/Procedural/Mesh/MeshGeometry.cs | 96 ++- .../QuadBillboardMeshGenerator.cs | 2 +- .../Procedural/Parametric/Spline/Spline.cs | 12 +- .../Parametric/Spline/SplinePoint.cs | 7 +- Runtime/Procedural/Parametric/Tube/Tube.cs | 78 +- .../Parametric/Tube/TubeGeometry.cs | 146 ++++ .../Parametric/Tube/TubeGeometry.cs.uid | 1 + .../Parametric/Tube/TubeGeometrySettings.cs | 63 ++ .../Tube/TubeGeometrySettings.cs.uid | 1 + Runtime/Random/RandomEngine.cs | 12 +- .../Reallusion/CCImportFile/CCMaterialInfo.cs | 4 +- .../CCImportSettings/CCImportConfiguration.cs | 17 +- .../CCImportSettings/CCMaterialSettings.cs | 7 + Runtime/Reallusion/CCImporter.cs | 30 + .../Shaders/Hair/CCHairBase.gdshaderinc | 5 +- .../Shaders/Hair/Variants/CCHair.gdshader | 4 +- .../TemporalSmearSimple.glsl | 4 +- Runtime/Shading/Library/Wind.gdshaderinc | 50 +- Runtime/Text/RegexUtility.cs | 91 +++ 43 files changed, 1881 insertions(+), 69 deletions(-) create mode 100644 Runtime/Graphs/Trees/CustomTreeWalker.cs create mode 100644 Runtime/Graphs/Trees/CustomTreeWalker.cs.uid create mode 100644 Runtime/Math/Geometry/Overlap3D.cs create mode 100644 Runtime/Math/Geometry/Overlap3D.cs.uid create mode 100644 Runtime/Procedural/Assets/Tree/TreeBranch.cs create mode 100644 Runtime/Procedural/Assets/Tree/TreeBranch.cs.uid create mode 100644 Runtime/Procedural/Assets/Tree/TreeGenerator.cs create mode 100644 Runtime/Procedural/Assets/Tree/TreeGenerator.cs.uid create mode 100644 Runtime/Procedural/Parametric/Tube/TubeGeometry.cs create mode 100644 Runtime/Procedural/Parametric/Tube/TubeGeometry.cs.uid create mode 100644 Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs create mode 100644 Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs.uid diff --git a/Runtime/Godot/NodeState.cs b/Runtime/Godot/NodeState.cs index ac2a487..63119f8 100644 --- a/Runtime/Godot/NodeState.cs +++ b/Runtime/Godot/NodeState.cs @@ -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 ); } diff --git a/Runtime/Godot/Nodes.cs b/Runtime/Godot/Nodes.cs index d53a525..d16e273 100644 --- a/Runtime/Godot/Nodes.cs +++ b/Runtime/Godot/Nodes.cs @@ -9,6 +9,12 @@ namespace Rokojori { public static class Nodes { + + public static NodePath GetNodePath( this Node node ) + { + return node.GetPath(); + } + public static T Get( this Node node ) where T:Node { return GetAnyChild( node ); @@ -161,7 +167,7 @@ namespace Rokojori return list; } - public static List AllIn( Node root, Func filter = null, bool includeRoot = true) where T:class + public static List GetAll( this Node root, Func filter = null, bool includeRoot = true) where T:class { var list = new List(); @@ -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( Node parent ) where T:Node + public static T GetDirectChild( this Node parent ) where T:Node { if ( parent == null ) { @@ -506,7 +524,7 @@ namespace Rokojori return null; } - public static List GetDirectChildren( Node parent ) where T:Node + public static List GetDirectChildren( this Node parent ) where T:Node { if ( parent == null ) { @@ -537,10 +555,6 @@ namespace Rokojori return list; } - public static void OnAllDirectChildren( this Node parent, System.Action action ) where T:Node - { - ForEachDirectChild( parent, action ); - } public static int NumDirectChildrenOf( this Node parent ) where T:Node { @@ -566,6 +580,23 @@ namespace Rokojori return typeIndex; } + public static T GetLastChild( 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( this Node parent, int index ) where T:Node { if ( parent == null ) @@ -596,7 +627,7 @@ namespace Rokojori } - public static void ForEachDirectChild( Node parent, System.Action action ) where T:Node + public static void ForEachDirectChild( this Node parent, System.Action action ) where T:Node { if ( parent == null || action == null ) { diff --git a/Runtime/Graphs/Trees/CustomTreeWalker.cs b/Runtime/Graphs/Trees/CustomTreeWalker.cs new file mode 100644 index 0000000..fe22d10 --- /dev/null +++ b/Runtime/Graphs/Trees/CustomTreeWalker.cs @@ -0,0 +1,28 @@ +using System.Collections; +using System.Collections.Generic; +using System; + +namespace Rokojori +{ + public class CustomTreeWalker:TreeWalker where N:class + { + Func _getParent; + Func _childAt; + Func _numChildren; + + + public CustomTreeWalker( Func getParent, Func childAt, Func 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 ); } + } +} \ No newline at end of file diff --git a/Runtime/Graphs/Trees/CustomTreeWalker.cs.uid b/Runtime/Graphs/Trees/CustomTreeWalker.cs.uid new file mode 100644 index 0000000..d6543fa --- /dev/null +++ b/Runtime/Graphs/Trees/CustomTreeWalker.cs.uid @@ -0,0 +1 @@ +uid://bat8vplx8obqc diff --git a/Runtime/Graphs/Trees/TreeWalker.cs b/Runtime/Graphs/Trees/TreeWalker.cs index 5e66a57..b81bc9a 100644 --- a/Runtime/Graphs/Trees/TreeWalker.cs +++ b/Runtime/Graphs/Trees/TreeWalker.cs @@ -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 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 selector ) + { + return FindChild( node, true, selector ); + } + + public N FindAnyChild( N node, Predicate selector ) + { + return FindChild( node, false, selector ); + } + + public N FindChild( N node, bool directChildrenOnly, Predicate 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 getValue ) + { + return GetChildWithLowestValue( node, true, getValue ); + } + + public N GetAnyChildWithLowestValue( N node, Func getValue ) + { + return GetChildWithLowestValue( node, false, getValue ); + } + + public N GetChildWithLowestValue( N node, bool directChildrenOnly, Func 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 getValue ) + { + return GetChildWithHighestValue( node, true, getValue ); + } + + public N GetAnyChildWithHighestValue( N node, Func getValue ) + { + return GetChildWithHighestValue( node, false, getValue ); + } + + public N GetChildWithHighestValue( N node, bool directChildrenOnly, Func 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 ) { diff --git a/Runtime/Interactions/CharacterController/CharacterController.cs b/Runtime/Interactions/CharacterController/CharacterController.cs index 1d0737b..91c0371 100644 --- a/Runtime/Interactions/CharacterController/CharacterController.cs +++ b/Runtime/Interactions/CharacterController/CharacterController.cs @@ -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 ); } diff --git a/Runtime/LOD/MultiMeshGenerator.cs b/Runtime/LOD/MultiMeshGenerator.cs index 99bddb8..feffadb 100644 --- a/Runtime/LOD/MultiMeshGenerator.cs +++ b/Runtime/LOD/MultiMeshGenerator.cs @@ -50,7 +50,7 @@ namespace Rokojori this.LogInfo( "Grid Created", grid ); - var instances = Nodes.AllIn( source ); + var instances = Nodes.GetAll( source ); this.LogInfo( "Grabbed instances", instances.Count ); diff --git a/Runtime/LOD/NTree/OcTree/OcTree.cs b/Runtime/LOD/NTree/OcTree/OcTree.cs index a8643c9..4292239 100644 --- a/Runtime/LOD/NTree/OcTree/OcTree.cs +++ b/Runtime/LOD/NTree/OcTree/OcTree.cs @@ -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> combinePoints ) @@ -74,6 +85,40 @@ namespace Rokojori return result; } + public List GetInsideBox( Box3 box, List list = null ) + { + list = list == null ? new List() : list; + + GetInsideBoxWithDuplicates( box, list ); + + var set = new HashSet( [ .. list ] ); + return set.ToList(); + } + + public List GetInsideBoxWithDuplicates( Box3 box, List list = null ) + { + list = list == null ? new List() : 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; diff --git a/Runtime/LOD/NTree/OcTree/OcTreeCell.cs b/Runtime/LOD/NTree/OcTree/OcTreeCell.cs index 7d27467..879e5a0 100644 --- a/Runtime/LOD/NTree/OcTree/OcTreeCell.cs +++ b/Runtime/LOD/NTree/OcTree/OcTreeCell.cs @@ -49,6 +49,27 @@ namespace Rokojori public int rootCellIndex => _rootCellIndex; + public void GetInsideBox( Box3 box, List 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 Create( OcTree tree, Vector3 min, Vector3 max, int rootIndex ) { var cell = new OcTreeCell(); @@ -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(); + } + + values.Add( data ); + return; + } + + Nest(); + + cells.ForEach( c => c.BoxInsert( data, box ) ); + + return; + } + + public bool Insert( T data ) { if ( isLeaf ) diff --git a/Runtime/Math/Geometry/Box3.cs b/Runtime/Math/Geometry/Box3.cs index 7c4e9f0..9a67ca1 100644 --- a/Runtime/Math/Geometry/Box3.cs +++ b/Runtime/Math/Geometry/Box3.cs @@ -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 ) ) diff --git a/Runtime/Math/Geometry/Capsule3.cs b/Runtime/Math/Geometry/Capsule3.cs index b8a177e..a084042 100644 --- a/Runtime/Math/Geometry/Capsule3.cs +++ b/Runtime/Math/Geometry/Capsule3.cs @@ -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 ) ); + } + } } diff --git a/Runtime/Math/Geometry/Line3.cs b/Runtime/Math/Geometry/Line3.cs index 77bcf3a..461d3d3 100644 --- a/Runtime/Math/Geometry/Line3.cs +++ b/Runtime/Math/Geometry/Line3.cs @@ -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 ); diff --git a/Runtime/Math/Geometry/Overlap3D.cs b/Runtime/Math/Geometry/Overlap3D.cs new file mode 100644 index 0000000..68f7628 --- /dev/null +++ b/Runtime/Math/Geometry/Overlap3D.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Runtime/Math/Geometry/Overlap3D.cs.uid b/Runtime/Math/Geometry/Overlap3D.cs.uid new file mode 100644 index 0000000..c1447d3 --- /dev/null +++ b/Runtime/Math/Geometry/Overlap3D.cs.uid @@ -0,0 +1 @@ +uid://b8oy166pmufgv diff --git a/Runtime/Math/Geometry/Pose.cs b/Runtime/Math/Geometry/Pose.cs index b5baf27..ab17fd4 100644 --- a/Runtime/Math/Geometry/Pose.cs +++ b/Runtime/Math/Geometry/Pose.cs @@ -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 ); diff --git a/Runtime/Math/Math3D.cs b/Runtime/Math/Math3D.cs index d86cefb..022afbc 100644 --- a/Runtime/Math/Math3D.cs +++ b/Runtime/Math/Math3D.cs @@ -264,6 +264,7 @@ namespace Rokojori return center / points.Length; } + public static Vector3 Center( List 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 ); diff --git a/Runtime/Math/MathX.cs b/Runtime/Math/MathX.cs index 092e974..7822c5a 100644 --- a/Runtime/Math/MathX.cs +++ b/Runtime/Math/MathX.cs @@ -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 factorialLookUp = new Dictionary(); 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(); diff --git a/Runtime/Math/Range.cs b/Runtime/Math/Range.cs index 87dbdcb..54b8c2e 100644 --- a/Runtime/Math/Range.cs +++ b/Runtime/Math/Range.cs @@ -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; + + } + } diff --git a/Runtime/Procedural/Assets/Tree/TreeBranch.cs b/Runtime/Procedural/Assets/Tree/TreeBranch.cs new file mode 100644 index 0000000..2ac2a08 --- /dev/null +++ b/Runtime/Procedural/Assets/Tree/TreeBranch.cs @@ -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 ); + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Assets/Tree/TreeBranch.cs.uid b/Runtime/Procedural/Assets/Tree/TreeBranch.cs.uid new file mode 100644 index 0000000..9f54a2b --- /dev/null +++ b/Runtime/Procedural/Assets/Tree/TreeBranch.cs.uid @@ -0,0 +1 @@ +uid://qfl4okpj3plh diff --git a/Runtime/Procedural/Assets/Tree/TreeGenerator.cs b/Runtime/Procedural/Assets/Tree/TreeGenerator.cs new file mode 100644 index 0000000..0069b43 --- /dev/null +++ b/Runtime/Procedural/Assets/Tree/TreeGenerator.cs @@ -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 _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(); + meshInstances.ForEach( m => m.SelfDestroy() ); + + if ( useDebugSingleMeshGeneration ) + { + this.GetDirectChildren().ForEach( m => m.SelfDestroy() ); + } + else + { + this.GetAll().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( "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( "Leaves Mesh @" + b.index + "." + i ); + leavesInstance.Mesh = leafGeometry.GenerateMesh(); + leavesInstance.SetSurfaceOverrideMaterial( 0, leavesMaterial ); + } + else + { + leavesMG.Add( leafGeometry ); + } + + + + } + + + } + + + + + if ( ! useDebugSingleMeshGeneration ) + { + var leavesInstance = this.CreateChild( "Leaves Mesh" ); + leavesInstance.Mesh = leavesMG.GenerateMesh(); + leavesInstance.SetSurfaceOverrideMaterial( 0, leavesMaterial ); + + var meshInstance3D = this.CreateChild( "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 ocTree; + CustomTreeWalker walker; + + async Task GenerateBranches() + { + if ( walker == null ) + { + walker = new CustomTreeWalker( + 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( GlobalPosition, rootHeight * 3 * maxLevel, 10 ); + + structure = this.CreateChild( "Tree Structure" ); + + random = LCG.WithSeed( seed ); + + branches = _branches.ToArray(); + + var nextParents = new List(); + + 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(); + + 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(); + + if ( lastPoint == null ) + { + this.LogInfo( "has no lastPoint:", p.index ); + continue; + } + + var childSpline = child.GetSpline( this ); + var firstPoint = childSpline.GetChild(); + var lastPoint2 = childSpline.GetLastChild(); + + 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(); + 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(); + 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( "Branch - " + treeBranch.level + " - " + childIndex); + + + treeBranch.spline = spline.GetNodePath(); + + if ( parentBranch == null ) + { + var root = spline.CreateChild(); + var end = spline.CreateChild(); + + 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(); + var endPoint = spline.CreateChild(); + + 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; + } + + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Assets/Tree/TreeGenerator.cs.uid b/Runtime/Procedural/Assets/Tree/TreeGenerator.cs.uid new file mode 100644 index 0000000..d79e867 --- /dev/null +++ b/Runtime/Procedural/Assets/Tree/TreeGenerator.cs.uid @@ -0,0 +1 @@ +uid://dpy0wfloqjafd diff --git a/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs b/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs index 289ac23..c019893 100644 --- a/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs +++ b/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs @@ -340,7 +340,7 @@ namespace Rokojori public List GetAllViewports() { - return Nodes.AllIn( X_views, null, false ); + return Nodes.GetAll( X_views, null, false ); } public void CleanUp() diff --git a/Runtime/Procedural/Baking/TextureMerger.cs b/Runtime/Procedural/Baking/TextureMerger.cs index 4a6d3e5..2d4beb0 100644 --- a/Runtime/Procedural/Baking/TextureMerger.cs +++ b/Runtime/Procedural/Baking/TextureMerger.cs @@ -157,7 +157,7 @@ namespace Rokojori { if ( sourceViewportsContainer != null ) { - sourceViewports = Nodes.AllIn( sourceViewportsContainer, null, false ).ToArray(); + sourceViewports = Nodes.GetAll( sourceViewportsContainer, null, false ).ToArray(); } var vpTextures = Arrays.Map( sourceViewports, diff --git a/Runtime/Procedural/Mesh/MeshGeometry.cs b/Runtime/Procedural/Mesh/MeshGeometry.cs index c6c98fa..933347e 100644 --- a/Runtime/Procedural/Mesh/MeshGeometry.cs +++ b/Runtime/Procedural/Mesh/MeshGeometry.cs @@ -531,6 +531,61 @@ namespace Rokojori return u * ( segments + 1 ) + v; } + public static MeshGeometry CreateCapUVFunction( Func uvFunc, int uSegments, bool start = false ) + { + var positions = new List(); + var uvs = new List(); + var normals = new List(); + + 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 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 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 lods = null, LODLerpingData lerpingData = null ) diff --git a/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs index 9bb7070..43e1a88 100644 --- a/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs +++ b/Runtime/Procedural/Parametric/QuadBillboards/QuadBillboardMeshGenerator.cs @@ -64,7 +64,7 @@ namespace Rokojori var list = new List(); - this.OnAllDirectChildren( + this.ForEachDirectChild( ( p )=> { list = p.Process( list ); diff --git a/Runtime/Procedural/Parametric/Spline/Spline.cs b/Runtime/Procedural/Parametric/Spline/Spline.cs index 2d64717..746722e 100644 --- a/Runtime/Procedural/Parametric/Spline/Spline.cs +++ b/Runtime/Procedural/Parametric/Spline/Spline.cs @@ -42,7 +42,13 @@ namespace Rokojori [Export] public int xzPathBakeResolution = 50; - + + + public void SetEditorPointSize( float size ) + { + this.ForEachDirectChild( 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( 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; diff --git a/Runtime/Procedural/Parametric/Spline/SplinePoint.cs b/Runtime/Procedural/Parametric/Spline/SplinePoint.cs index 4bb1eb9..31339ba 100644 --- a/Runtime/Procedural/Parametric/Spline/SplinePoint.cs +++ b/Runtime/Procedural/Parametric/Spline/SplinePoint.cs @@ -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; diff --git a/Runtime/Procedural/Parametric/Tube/Tube.cs b/Runtime/Procedural/Parametric/Tube/Tube.cs index 38d3b0d..f8d0632 100644 --- a/Runtime/Procedural/Parametric/Tube/Tube.cs +++ b/Runtime/Procedural/Parametric/Tube/Tube.cs @@ -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(); + } + 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; + */ } } diff --git a/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs b/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs new file mode 100644 index 0000000..5b5a5ed --- /dev/null +++ b/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs @@ -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(); + + if ( shapes != null && shapes.Length > 0 ) + { + shapesList.AddRange( shapes ); + Lists.Sort( shapesList, s => s.tubePosition ); + + shapesList.ForEach( s => s.ClearCache() ); + + } + + System.Func 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; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs.uid b/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs.uid new file mode 100644 index 0000000..af44588 --- /dev/null +++ b/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs.uid @@ -0,0 +1 @@ +uid://ccc2vjq7kmqiu diff --git a/Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs b/Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs new file mode 100644 index 0000000..2357e54 --- /dev/null +++ b/Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs @@ -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; + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs.uid b/Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs.uid new file mode 100644 index 0000000..3702a5a --- /dev/null +++ b/Runtime/Procedural/Parametric/Tube/TubeGeometrySettings.cs.uid @@ -0,0 +1 @@ +uid://c7ud5wnn6t4iw diff --git a/Runtime/Random/RandomEngine.cs b/Runtime/Random/RandomEngine.cs index a2deca0..014b1ae 100644 --- a/Runtime/Random/RandomEngine.cs +++ b/Runtime/Random/RandomEngine.cs @@ -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(); diff --git a/Runtime/Reallusion/CCImportFile/CCMaterialInfo.cs b/Runtime/Reallusion/CCImportFile/CCMaterialInfo.cs index 52e9ea5..3b7de03 100644 --- a/Runtime/Reallusion/CCImportFile/CCMaterialInfo.cs +++ b/Runtime/Reallusion/CCImportFile/CCMaterialInfo.cs @@ -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 ) => diff --git a/Runtime/Reallusion/CCImportSettings/CCImportConfiguration.cs b/Runtime/Reallusion/CCImportSettings/CCImportConfiguration.cs index 2bafb83..e649701 100644 --- a/Runtime/Reallusion/CCImportSettings/CCImportConfiguration.cs +++ b/Runtime/Reallusion/CCImportSettings/CCImportConfiguration.cs @@ -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; diff --git a/Runtime/Reallusion/CCImportSettings/CCMaterialSettings.cs b/Runtime/Reallusion/CCImportSettings/CCMaterialSettings.cs index 882ae96..922a373 100644 --- a/Runtime/Reallusion/CCImportSettings/CCMaterialSettings.cs +++ b/Runtime/Reallusion/CCImportSettings/CCMaterialSettings.cs @@ -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; diff --git a/Runtime/Reallusion/CCImporter.cs b/Runtime/Reallusion/CCImporter.cs index f5ab1b2..833c0e8 100644 --- a/Runtime/Reallusion/CCImporter.cs +++ b/Runtime/Reallusion/CCImporter.cs @@ -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 ) diff --git a/Runtime/Reallusion/Shaders/Hair/CCHairBase.gdshaderinc b/Runtime/Reallusion/Shaders/Hair/CCHairBase.gdshaderinc index a3d4242..3ba2fe4 100644 --- a/Runtime/Reallusion/Shaders/Hair/CCHairBase.gdshaderinc +++ b/Runtime/Reallusion/Shaders/Hair/CCHairBase.gdshaderinc @@ -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( diff --git a/Runtime/Reallusion/Shaders/Hair/Variants/CCHair.gdshader b/Runtime/Reallusion/Shaders/Hair/Variants/CCHair.gdshader index 4590e12..f88b008 100644 --- a/Runtime/Reallusion/Shaders/Hair/Variants/CCHair.gdshader +++ b/Runtime/Reallusion/Shaders/Hair/Variants/CCHair.gdshader @@ -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" \ No newline at end of file diff --git a/Runtime/Rendering/Compositor/CompositorEffects/TemporalSmearSimple/TemporalSmearSimple.glsl b/Runtime/Rendering/Compositor/CompositorEffects/TemporalSmearSimple/TemporalSmearSimple.glsl index 2ffacb4..96ed5fb 100644 --- a/Runtime/Rendering/Compositor/CompositorEffects/TemporalSmearSimple/TemporalSmearSimple.glsl +++ b/Runtime/Rendering/Compositor/CompositorEffects/TemporalSmearSimple/TemporalSmearSimple.glsl @@ -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 ); diff --git a/Runtime/Shading/Library/Wind.gdshaderinc b/Runtime/Shading/Library/Wind.gdshaderinc index 5e35ad3..3410302 100644 --- a/Runtime/Shading/Library/Wind.gdshaderinc +++ b/Runtime/Shading/Library/Wind.gdshaderinc @@ -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); } \ No newline at end of file diff --git a/Runtime/Text/RegexUtility.cs b/Runtime/Text/RegexUtility.cs index 2c01d79..2ff4b06 100644 --- a/Runtime/Text/RegexUtility.cs +++ b/Runtime/Text/RegexUtility.cs @@ -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 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(); + + 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 + "" );