rj-action-library/Runtime/Procedural/Assets/Leaf/LeafMesh.cs

192 lines
4.1 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/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<Path2>();
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<Vector2>();
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<Vector2>();
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;
}
}
}