void USplineComponent::UpdateSpline() { const int32 NumPoints = SplineInfo.Points.Num(); check(!bClosedLoop || NumPoints == 0 || (NumPoints >= 2 && SplineInfo.Points[0].OutVal == SplineInfo.Points[NumPoints - 1].OutVal)); // Automatically set the tangents on any CurveAuto keys SplineInfo.AutoSetTangents(0.0f, bStationaryEndpoints); // Nothing else to do if less than 2 points if (NumPoints < 2) { return; } // Adjust auto tangents for first and last keys to take into account the looping if (bClosedLoop) { auto& FirstPoint = SplineInfo.Points[0]; auto& LastPoint = SplineInfo.Points[NumPoints - 1]; const auto& SecondPoint = SplineInfo.Points[1]; const auto& PenultimatePoint = SplineInfo.Points[NumPoints - 2]; if (FirstPoint.InterpMode == CIM_CurveAuto || FirstPoint.InterpMode == CIM_CurveAutoClamped) { FVector Tangent; ComputeCurveTangent( PenultimatePoint.InVal - LastPoint.InVal, PenultimatePoint.OutVal, FirstPoint.InVal, FirstPoint.OutVal, SecondPoint.InVal, SecondPoint.OutVal, 0.0f, FirstPoint.InterpMode == CIM_CurveAutoClamped, Tangent); FirstPoint.LeaveTangent = Tangent; FirstPoint.ArriveTangent = Tangent; LastPoint.LeaveTangent = Tangent; LastPoint.ArriveTangent = Tangent; } } const int32 NumSegments = NumPoints - 1; // Start by clearing it SplineReparamTable.Points.Reset(NumSegments * ReparamStepsPerSegment + 1); float AccumulatedLength = 0.0f; for (int32 SegmentIndex = 0; SegmentIndex < NumSegments; ++SegmentIndex) { for (int32 Step = 0; Step < ReparamStepsPerSegment; ++Step) { const float Param = static_cast<float>(Step) / ReparamStepsPerSegment; const float SegmentLength = (Step == 0) ? 0.0f : GetSegmentLength(SegmentIndex, Param); SplineReparamTable.AddPoint(SegmentLength + AccumulatedLength, SegmentIndex + Param); } AccumulatedLength += GetSegmentLength(SegmentIndex, 1.0f); } SplineReparamTable.AddPoint(AccumulatedLength, static_cast<float>(NumSegments)); }
void FRichCurve::AutoSetTangents(float Tension) { // Iterate over all points in this InterpCurve for(int32 KeyIndex=0; KeyIndex<Keys.Num(); KeyIndex++) { FRichCurveKey& Key = Keys[KeyIndex]; float ArriveTangent = Key.ArriveTangent; float LeaveTangent = Key.LeaveTangent; if(KeyIndex == 0) { if(KeyIndex < Keys.Num()-1) // Start point { // If first section is not a curve, or is a curve and first point has manual tangent setting. if( Key.TangentMode == RCTM_Auto ) { LeaveTangent = 0.0f; } } } else { if(KeyIndex < Keys.Num()-1) // Inner point { FRichCurveKey& PrevKey = Keys[KeyIndex-1]; if( Key.InterpMode == RCIM_Cubic && (Key.TangentMode == RCTM_Auto )) { FRichCurveKey& NextKey = Keys[KeyIndex+1]; ComputeCurveTangent( Keys[ KeyIndex - 1 ].Time, // Previous time Keys[ KeyIndex - 1 ].Value, // Previous point Keys[ KeyIndex ].Time, // Current time Keys[ KeyIndex ].Value, // Current point Keys[ KeyIndex + 1 ].Time, // Next time Keys[ KeyIndex + 1 ].Value, // Next point Tension, // Tension false, // Want clamping? ArriveTangent ); // Out // In 'auto' mode, arrive and leave tangents are always the same LeaveTangent = ArriveTangent; } else if( PrevKey.InterpMode == RCIM_Constant || Key.InterpMode == RCIM_Constant ) { if(Keys[ KeyIndex - 1 ].InterpMode != RCIM_Cubic) { ArriveTangent = 0.0f; } LeaveTangent = 0.0f; } } else // End point { // If last section is not a curve, or is a curve and final point has manual tangent setting. if( Key.InterpMode == RCIM_Cubic && Key.TangentMode == RCTM_Auto) { ArriveTangent = 0.0f; } } } Key.ArriveTangent = ArriveTangent; Key.LeaveTangent = LeaveTangent; } }