using System.Collections; using System.Collections.Generic; using Godot; using System; namespace Rokojori { [Tool] [GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Spline.svg") ] public partial class LeafMesh:Node3D { [Export] public MeshInstance3D output; [Export] public int seed; [Export] public bool update; [Export] public bool updateAlways; [Export] public float height; [Export] public Curve centerStrandShape; [Export] public float centerStrandShapeTopOffset; [Export] public float centerStrandShapeBottomOffset; [Export] public int numStrands; [Export] public Curve strandHeightPositions; [Export] public Curve strandShape; [Export] public Curve strandSize; [Export] public Curve strandWidth; [Export] public Curve strandLength; [Export] public Curve strandAngles; [Export] public int strandResolution = 20; [Export] public float positionJitterMax; [Export] public Vector2 positionJitterScale; [Export] public Vector2 positionJitterOffset; public override void _Process( double delta ) { if ( ! ( update || updateAlways ) ) { return; } update = false; try { Create(); } catch ( System.Exception e ) { RJLog.Error( e ); updateAlways = false; } } void Create() { var paths = new List(); for ( int i = 0; i < numStrands; i++ ) { var part = CreateStrandPart( i ); var t = i / (float) ( numStrands - 1 ); var ht = strandHeightPositions.Sample( t ); var y = ht * height; var turns = strandAngles.Sample( t ); var rotation = Mathf.DegToRad( turns * 360f ); var trsf = new Transform2D( rotation, new Vector2( 0, y ) ); part.ApplyTransform( trsf ); paths.Add( part ); var mirrored = part.Clone(); mirrored.MirrorX(); paths.Add( mirrored ); } var centerPathPoints = new List(); for ( int i = 0; i < strandResolution; i++ ) { var t = i / (float)( strandResolution - 1 ); var y = strandHeightPositions.Sample( t ); var x = centerStrandShape.Sample( t ); var size = ( height + centerStrandShapeTopOffset + centerStrandShapeBottomOffset ); var p = new Vector2( x, y * size - centerStrandShapeBottomOffset ); centerPathPoints.Add( p ); } for ( int i = 0; i < strandResolution; i++ ) { var index = ( strandResolution - 1 ) - i; var p = centerPathPoints[ index ]; p.X = -p.X; centerPathPoints.Add( p ); } var centerPath = new Path2( centerPathPoints ); paths.Add( centerPath ); var random = new LCG(); random.SetSeed( seed ); paths.ForEach( p => p.PositionJitter( positionJitterMax, positionJitterScale, positionJitterOffset, random ) ); var shape = Shape2.UnionAll( paths ); // RJLog.Log( shape.ToSVGPath() ); var fillGeometry = shape.CreateFillMeshGeometry(); var mesh = fillGeometry.GenerateMesh(); // RJLog.Log( "First Path:", shape.paths[ 0 ].points, shape.paths, "mesh", fillGeometry.indices.Count ); output.Mesh = mesh; } Path2 CreateStrandPart( int index ) { var points = new List(); var u = index / (float)( numStrands - 1 ); var s = strandSize.Sample( u ); var l = strandLength.Sample( u ) * s; var w = strandWidth.Sample( u ) * s; for ( int i = 0; i < strandResolution; i++ ) { var t = i / (float)( strandResolution - 1 ); var p = new Vector2( t * l, strandShape.Sample( t ) * w ); points.Add( p ); } for ( int i = 0; i < strandResolution; i++ ) { var reverseIndex = ( strandResolution - 1 ) - i; var lowerPoint = points[ reverseIndex ]; lowerPoint.Y = -lowerPoint.Y; points.Add( lowerPoint ); } var path = new Path2( points ); return path; } } }