using Godot; using Rokojori; using System.Collections.Generic; namespace Rokojori { [Tool] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Spline.svg") ] public partial class SplineMesh : Action { [Export] public Spline spline; [Export] public float splineRadius = 0.5f; [Export] public float resolutionU = 0.1f; [Export] public int maxSegmentsU = 64; [Export] public float resolutionV = 0.5f; [Export] public int maxSegmentsV = 1024; [Export] public MeshInstance3D output; [Export] public bool undistortSplineSegments = false; protected override void _OnTrigger() { CreateMesh(); } public void CreateMesh() { var curve = spline.GetCurve(); var length = curve.ComputeLength( 100 ); var numPoints = Mathf.Clamp( length / resolutionU, 2, maxSegmentsU ); var uSegments = (int) ( Mathf.Clamp( splineRadius * 2f * 3.14f / resolutionV, 3, maxSegmentsV ) ); var vSegments = (int) ( numPoints - 1 ); var uvFunction = ( Vector2 uv ) => { var t = undistortSplineSegments ? curve.ComputeTforNormalizedCurveLength( uv.Y, vSegments ) : uv.Y; var index = curve.NormalizedToPointIndex( t ); var pose = curve.PoseAt( t ); var radiusSize = splineRadius; var angle = uv.X * 2f * Mathf.Pi; var circlePose = Pose.Create( Vector3.Zero, pose.rotation * Math3D.RotateZ( angle ) ); circlePose.rotation = circlePose.rotation.Normalized(); var circleStart = Vector3.Up * radiusSize; var positionOnCircle = circlePose.Apply( circleStart ); return Pose.Create( positionOnCircle + pose.position, circlePose.rotation ); }; var mg = MeshGeometry.CreateFromUVFunction( uvFunction, uSegments, vSegments ); mg.Add( MeshGeometry.CreateCapUVFunction( uvFunction, uSegments, true ) ); mg.Add( MeshGeometry.CreateCapUVFunction( uvFunction, uSegments, false ) ); if ( output == null ) { output = this.CreateChild( "Spline MeshInstance"); } output.Mesh = mg.GenerateMesh(); } } }