rj-action-library/Runtime/Procedural/Parametric/Tube/TubeGeometry.cs

146 lines
4.8 KiB
C#
Raw Normal View History

2025-06-27 05:12:53 +00:00
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<TubeShape>();
if ( shapes != null && shapes.Length > 0 )
{
shapesList.AddRange( shapes );
Lists.Sort( shapesList, s => s.tubePosition );
shapesList.ForEach( s => s.ClearCache() );
}
System.Func<Vector2,Pose> 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;
}
}
}