using Godot; using Rokojori; using System.Collections.Generic; namespace Rokojori { public enum TubeSegmentMode { Fixed_Division, Depending_On_Length, Maximum_Of_Both, Minimum_Of_Both } public enum ShapeOrientationMode { Auto, Original, Reverse } [Tool] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Spline.svg") ] public partial class Tube : Node3D { [Export] public MeshInstance3D output; [Export] public Spline spline; [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; [Export] public TubeShape[] shapes; [Export] public bool update; [Export] public bool updateAlways; #if TOOLS public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo ) { /* ClearCurveCache(); var curve = GetCurve(); gizmo.Clear(); var linePoints = new List(); int resolution = editorResolution <= 0 ? 20 : editorResolution; renderedResolution = editorResolution; var lastPoint = ToLocal( curve.SampleAt( 0 ) ); for ( int i = 1; i < resolution; i++ ) { var t = i / (float) (resolution - 1 ); var point = ToLocal( curve.SampleAt( t ) ); linePoints.Add( lastPoint ); linePoints.Add( point ); lastPoint = point; } for ( int i = 0; i < curve.points.Count; i++ ) { var p = curve.points[ i ]; linePoints.Add( ToLocal( p.position ) ); linePoints.Add( ToLocal( p.tangentBefore.position ) ); linePoints.Add( ToLocal( p.position ) ); linePoints.Add( ToLocal( p.tangentNext.position ) ); } var material = gizmoPlugin.GetMaterial( "main", gizmo ); gizmo.AddLines( linePoints.ToArray(), material, false ); */ } public override void _Process( double delta ) { if ( ! ( updateAlways || update ) ) { return; } if ( spline == null ) { return; } update = false; var mg = CreateMesh(); output.Mesh = mg.GenerateMesh(); UpdateGizmos(); } #endif MeshGeometry CreateMesh() { var curve = spline.GetCurve(); int splineSegments = fixedSplineSegmentDivisions; if ( TubeSegmentMode.Fixed_Division != segmentMode ) { splineSegments = Mathf.CeilToInt( curve.ComputeLength( curve.points.Count * 3 ) / splineSegmentLength ); if ( TubeSegmentMode.Maximum_Of_Both == segmentMode ) { splineSegments = Mathf.Max( splineSegments, fixedSplineSegmentDivisions ); } if ( TubeSegmentMode.Minimum_Of_Both == segmentMode ) { splineSegments = Mathf.Min( splineSegments, 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() ); } var mg = MeshGeometry.CreateFromUVFunction( ( Vector2 uv ) => { var t = undistortSplineSegments ? curve.ComputeTforNormalizedCurveLength( uv.Y, splineSegments ) : uv.Y; var index = curve.NormalizedToPointIndex( t ); var pose = curve.PoseAt( t ); var radiusSize = radius * ( radiusSizeCurve == null ? 1 : radiusSizeCurve.Sample( t ) ); var sizeX = radiusSize; var sizeY = radiusSize; var twistOffset = 0f; if ( radiusWidthCurve != null ) { sizeX *= radiusWidthCurve.Sample( t ); } if ( radiusHeightCurve != null ) { sizeY *= radiusHeightCurve.Sample( t ); } if ( scaleRadiusByPathTransforms ) { var splineScale = curve.SmoothStepScaleByPointIndex( index ); if ( scaleRadiusByPathTransforms ) { sizeX *= splineScale.X; sizeY *= splineScale.Y; } } if ( twistCurve != null ) { twistOffset = Mathf.DegToRad( 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( undistortSplineSegments, splineSegments, 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( undistortSplineSegments, splineSegments, radialSegments, pose, uv, scale, twistRotation ); var secondPose = secondShape.GetPose( undistortSplineSegments, splineSegments, 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 ) ); var circleStart = Vector3.Up * radiusSize * scale; var positionOnCircle = circlePose.Apply( circleStart ); return Pose.Create( positionOnCircle + pose.position, circlePose.rotation ); } }, radialSegments, splineSegments, useFullUVQuads ); return mg; } } }