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; } } }