Files
UnrealEngine/Engine/Plugins/Runtime/MeshModelingToolset/Source/ModelingComponents/Private/SplineUtil.cpp
2025-05-18 13:04:45 +08:00

177 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SplineUtil.h"
#include "Components/SplineComponent.h"
#include "PrimitiveDrawingUtils.h" // FPrimitiveDrawInterface
#include "SceneView.h"
#include "Styling/StyleColors.h"
#include "ToolContextInterfaces.h" // IToolsContextRenderAPI
UE::Geometry::SplineUtil::FDrawSplineSettings::FDrawSplineSettings()
: RegularColor(FStyleColors::White.GetSpecifiedColor().ToFColor(true))
, SelectedColor(FStyleColors::AccentOrange.GetSpecifiedColor().ToFColor(true))
{
}
// Mostly copied from the editor-only FSplineComponentVisualizer
void UE::Geometry::SplineUtil::DrawSpline(const USplineComponent& SplineComp, IToolsContextRenderAPI& RenderAPI, const FDrawSplineSettings& Settings)
{
const FSceneView* View = RenderAPI.GetSceneView();
auto GetDashSize = [View](const FVector& Start, const FVector& End, float Scale) -> double
{
const double StartW = View->WorldToScreen(Start).W;
const double EndW = View->WorldToScreen(End).W;
const double WLimit = 10.0f;
if (StartW > WLimit || EndW > WLimit)
{
return FMath::Max(StartW, EndW) * Scale;
}
return 0;
};
FPrimitiveDrawInterface* PDI = RenderAPI.GetPrimitiveDrawInterface();
const FInterpCurveVector& SplineInfo = SplineComp.GetSplinePointsPosition();
const float GrabHandleSize = 10.0f;
const bool bShouldVisualizeScale = Settings.ScaleVisualizationWidth > 0;
const float DefaultScale = Settings.ScaleVisualizationWidth;
FVector OldKeyPos(0);
FVector OldKeyRightVector(0);
FVector OldKeyScale(0);
const int32 NumPoints = SplineInfo.Points.Num();
const int32 NumSegments = SplineInfo.bIsLooped ? NumPoints : NumPoints - 1;
for (int32 KeyIdx = 0; KeyIdx < NumSegments + 1; KeyIdx++)
{
const FVector NewKeyPos = SplineComp.GetLocationAtSplinePoint(KeyIdx, ESplineCoordinateSpace::World);
const FVector NewKeyRightVector = SplineComp.GetRightVectorAtSplinePoint(KeyIdx, ESplineCoordinateSpace::World);
const FVector NewKeyUpVector = SplineComp.GetUpVectorAtSplinePoint(KeyIdx, ESplineCoordinateSpace::World);
const FVector NewKeyScale = SplineComp.GetScaleAtSplinePoint(KeyIdx) * DefaultScale;
const FColor KeyColor = (Settings.SelectedKeys && Settings.SelectedKeys->Contains(KeyIdx)) ? Settings.SelectedColor
: Settings.RegularColor;
// Draw the keypoint and up/right vectors
if (KeyIdx < NumPoints)
{
if (bShouldVisualizeScale)
{
PDI->DrawLine(NewKeyPos, NewKeyPos - NewKeyRightVector * NewKeyScale.Y, KeyColor, SDPG_Foreground);
PDI->DrawLine(NewKeyPos, NewKeyPos + NewKeyRightVector * NewKeyScale.Y, KeyColor, SDPG_Foreground);
PDI->DrawLine(NewKeyPos, NewKeyPos + NewKeyUpVector * NewKeyScale.Z, KeyColor, SDPG_Foreground);
const int32 ArcPoints = 20;
FVector OldArcPos = NewKeyPos + NewKeyRightVector * NewKeyScale.Y;
for (int32 ArcIndex = 1; ArcIndex <= ArcPoints; ArcIndex++)
{
float Sin;
float Cos;
FMath::SinCos(&Sin, &Cos, ArcIndex * PI / ArcPoints);
const FVector NewArcPos = NewKeyPos + Cos * NewKeyRightVector * NewKeyScale.Y + Sin * NewKeyUpVector * NewKeyScale.Z;
PDI->DrawLine(OldArcPos, NewArcPos, KeyColor, SDPG_Foreground);
OldArcPos = NewArcPos;
}
}
PDI->DrawPoint(NewKeyPos, KeyColor, GrabHandleSize, SDPG_Foreground);
}
// If not the first keypoint, draw a line to the previous keypoint.
if (KeyIdx > 0)
{
const FColor LineColor = Settings.RegularColor;
// For constant interpolation - don't draw ticks - just draw dotted line.
if (SplineInfo.Points[KeyIdx - 1].InterpMode == CIM_Constant)
{
const double DashSize = GetDashSize(OldKeyPos, NewKeyPos, 0.03f);
if (DashSize > 0.0f)
{
DrawDashedLine(PDI, OldKeyPos, NewKeyPos, LineColor, DashSize, SDPG_World);
}
}
else
{
// Determine the colors to use
const bool bKeyIdxLooped = (SplineInfo.bIsLooped && KeyIdx == NumPoints);
const int32 BeginIdx = bKeyIdxLooped ? 0 : KeyIdx;
const int32 EndIdx = KeyIdx - 1;
const bool bBeginSelected = Settings.SelectedKeys && Settings.SelectedKeys->Contains(BeginIdx);
const bool bEndSelected = Settings.SelectedKeys && Settings.SelectedKeys->Contains(BeginIdx);
const FColor BeginColor = (bBeginSelected) ? Settings.SelectedColor : Settings.RegularColor;
const FColor EndColor = (bEndSelected) ? Settings.SelectedColor : Settings.RegularColor;
// Find position on first keyframe.
FVector OldPos = OldKeyPos;
FVector OldRightVector = OldKeyRightVector;
FVector OldScale = OldKeyScale;
// Then draw a line for each substep.
constexpr int32 NumSteps = 20;
constexpr float PartialGradientProportion = 0.75f;
constexpr int32 PartialNumSteps = (int32)(NumSteps * PartialGradientProportion);
const float SegmentLineThickness = 0;
for (int32 StepIdx = 1; StepIdx <= NumSteps; StepIdx++)
{
const float StepRatio = StepIdx / static_cast<float>(NumSteps);
const float Key = EndIdx + StepRatio;
const FVector NewPos = SplineComp.GetLocationAtSplineInputKey(Key, ESplineCoordinateSpace::World);
const FVector NewRightVector = SplineComp.GetRightVectorAtSplineInputKey(Key, ESplineCoordinateSpace::World);
const FVector NewScale = SplineComp.GetScaleAtSplineInputKey(Key) * DefaultScale;
// creates a gradient that starts partway through the selection
FColor StepColor;
if (bBeginSelected == bEndSelected)
{
StepColor = BeginColor;
}
else if (bBeginSelected && StepIdx > (NumSteps - PartialNumSteps))
{
const float LerpRatio = (1.0f - StepRatio) / PartialGradientProportion;
StepColor = FMath::Lerp(BeginColor.ReinterpretAsLinear(), EndColor.ReinterpretAsLinear(), LerpRatio).ToFColor(false);
}
else if (bEndSelected && StepIdx <= PartialNumSteps)
{
const float LerpRatio = 1.0f - (StepRatio / PartialGradientProportion);
StepColor = FMath::Lerp(BeginColor.ReinterpretAsLinear(), EndColor.ReinterpretAsLinear(), LerpRatio).ToFColor(false);
}
else
{
StepColor = Settings.RegularColor; // unselected
}
PDI->DrawLine(OldPos, NewPos, StepColor, SDPG_Foreground, SegmentLineThickness);
if (bShouldVisualizeScale)
{
PDI->DrawLine(OldPos - OldRightVector * OldScale.Y, NewPos - NewRightVector * NewScale.Y, LineColor, SDPG_Foreground);
PDI->DrawLine(OldPos + OldRightVector * OldScale.Y, NewPos + NewRightVector * NewScale.Y, LineColor, SDPG_Foreground);
constexpr bool bVisualizeSplineInterpolatedVectors = false;
if (bVisualizeSplineInterpolatedVectors)
{
const FVector NewUpVector = SplineComp.GetUpVectorAtSplineInputKey(Key, ESplineCoordinateSpace::World);
PDI->DrawLine(NewPos, NewPos + NewUpVector * Settings.ScaleVisualizationWidth * 0.5f, LineColor, SDPG_Foreground);
PDI->DrawLine(NewPos, NewPos + NewRightVector * Settings.ScaleVisualizationWidth * 0.5f, LineColor, SDPG_Foreground);
}
}
OldPos = NewPos;
OldRightVector = NewRightVector;
OldScale = NewScale;
}
}
}
OldKeyPos = NewKeyPos;
OldKeyRightVector = NewKeyRightVector;
OldKeyScale = NewKeyScale;
}
}