void USplineComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { if (PropertyChangedEvent.Property != nullptr) { const FName PropertyName(PropertyChangedEvent.Property->GetFName()); if (PropertyName == GET_MEMBER_NAME_CHECKED(USplineComponent, ReparamStepsPerSegment) || PropertyName == GET_MEMBER_NAME_CHECKED(USplineComponent, bStationaryEndpoints)) { UpdateSpline(); } if (PropertyName == GET_MEMBER_NAME_CHECKED(USplineComponent, bClosedLoop)) { if (bClosedLoop) { // Spline is guaranteed to be non-looping when we get here (due to PreEditChange). // Now just force the loop endpoint to be added if bClosedLoop == true. AddLoopEndpoint(); } UpdateSpline(); } } Super::PostEditChangeProperty(PropertyChangedEvent); }
void USplineComponent::ClearSplinePoints() { SplineInfo.Points.Reset(); SplineRotInfo.Points.Reset(); SplineScaleInfo.Points.Reset(); UpdateSpline(); }
void USplineComponent::Serialize(FArchive& Ar) { Super::Serialize(Ar); // Support old resources which don't have the rotation and scale splines present const int32 ArchiveUE4Version = Ar.UE4Ver(); if (ArchiveUE4Version < VER_UE4_INTERPCURVE_SUPPORTS_LOOPING) { int32 NumPoints = SplineInfo.Points.Num(); // The start point is no longer cloned as the endpoint when the spline is looped, so remove the extra endpoint if present const bool bHasExtraEndpoint = bClosedLoop && (SplineInfo.Points[0].OutVal == SplineInfo.Points[NumPoints - 1].OutVal); if (bHasExtraEndpoint) { SplineInfo.Points.RemoveAt(NumPoints - 1, 1, false); NumPoints--; } // Fill the other two splines with some defaults SplineRotInfo.Points.Reset(NumPoints); SplineScaleInfo.Points.Reset(NumPoints); for (int32 Count = 0; Count < NumPoints; Count++) { SplineRotInfo.Points.Emplace(0.0f, FQuat::Identity, FQuat::Identity, FQuat::Identity, CIM_CurveAuto); SplineScaleInfo.Points.Emplace(0.0f, FVector(1.0f), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); } USplineComponent* Archetype = CastChecked<USplineComponent>(GetArchetype()); bSplineHasBeenEdited = (SplineInfo.Points != Archetype->SplineInfo.Points); UpdateSpline(); } }
void USplineComponent::SetSplinePointType(int32 PointIndex, ESplinePointType::Type Type) { EInterpCurveMode InterpMode = CIM_Constant; switch (Type) { case ESplinePointType::Curve: InterpMode = CIM_CurveAuto; break; case ESplinePointType::CurveClamped: InterpMode = CIM_CurveAutoClamped; break; case ESplinePointType::Linear: InterpMode = CIM_Linear; break; } const int32 NumPoints = SplineInfo.Points.Num(); if ((PointIndex >= 0) && (PointIndex < NumPoints)) { SplineInfo.Points[PointIndex].InterpMode = InterpMode; if (IsClosedLoop()) { // In a closed loop, the first and last points are tied, so update one with the other if (PointIndex == 0) { SplineInfo.Points[NumPoints - 1].InterpMode = InterpMode; } else if (PointIndex == NumPoints - 1) { SplineInfo.Points[0].InterpMode = InterpMode; } } UpdateSpline(); } }
void USplineComponent::ApplyComponentInstanceData(FSplineInstanceData* SplineInstanceData) { if (SplineInstanceData) { if (bAllowSplineEditingPerInstance) { SplineInfo = SplineInstanceData->SplineInfo; // If the construction script changed bClosedLoop, amend the applied points accordingly if (SplineInstanceData->bClosedLoop != bClosedLoop) { if (bClosedLoop) { AddLoopEndpoint(); } else { RemoveLoopEndpoint(); } } UpdateSpline(); } } }
USplineComponent::USplineComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bAllowSplineEditingPerInstance_DEPRECATED(true) , ReparamStepsPerSegment(10) , Duration(1.0f) , bStationaryEndpoints(false) , bSplineHasBeenEdited(false) , bClosedLoop(false) , DefaultUpVector(FVector::UpVector) #if WITH_EDITORONLY_DATA , EditorUnselectedSplineSegmentColor(FLinearColor(1.0f, 1.0f, 1.0f)) , EditorSelectedSplineSegmentColor(FLinearColor(1.0f, 0.0f, 0.0f)) , bShouldVisualizeScale(false) , ScaleVisualizationWidth(30.0f) #endif { SplineInfo.Points.Reset(10); SplineRotInfo.Points.Reset(10); SplineScaleInfo.Points.Reset(10); SplineInfo.Points.Emplace(0.0f, FVector(0, 0, 0), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); SplineRotInfo.Points.Emplace(0.0f, FQuat::Identity, FQuat::Identity, FQuat::Identity, CIM_CurveAuto); SplineScaleInfo.Points.Emplace(0.0f, FVector(1.0f), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); SplineInfo.Points.Emplace(1.0f, FVector(100, 0, 0), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); SplineRotInfo.Points.Emplace(1.0f, FQuat::Identity, FQuat::Identity, FQuat::Identity, CIM_CurveAuto); SplineScaleInfo.Points.Emplace(1.0f, FVector(1.0f), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); UpdateSpline(); }
void USplineComponent::SetSplinePointType(int32 PointIndex, ESplinePointType::Type Type) { if ((PointIndex >= 0) && (PointIndex < SplineInfo.Points.Num())) { SplineInfo.Points[PointIndex].InterpMode = ConvertSplinePointTypeToInterpCurveMode(Type); UpdateSpline(); } }
void USplineComponent::AddSplinePoint(const FVector& Position, ESplineCoordinateSpace::Type CoordinateSpace) { const FVector TransformedPosition = (CoordinateSpace == ESplineCoordinateSpace::World) ? ComponentToWorld.InverseTransformPosition(Position) : Position; const float InKey = static_cast<float>(SplineInfo.Points.Num()); SplineInfo.Points.Emplace(InKey, TransformedPosition, FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); SplineRotInfo.Points.Emplace(InKey, FQuat::Identity, FQuat::Identity, FQuat::Identity, CIM_CurveAuto); SplineScaleInfo.Points.Emplace(InKey, FVector(1.0f), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); UpdateSpline(); }
void USplineComponent::SetDefaultUpVector(const FVector& UpVector, ESplineCoordinateSpace::Type CoordinateSpace) { if (CoordinateSpace == ESplineCoordinateSpace::World) { DefaultUpVector = ComponentToWorld.InverseTransformVector(UpVector); } else { DefaultUpVector = UpVector; } UpdateSpline(); }
void USplineComponent::SetLocationAtSplinePoint(int32 PointIndex, const FVector& InLocation, ESplineCoordinateSpace::Type CoordinateSpace) { const int32 NumPoints = SplineInfo.Points.Num(); if ((PointIndex >= 0) && (PointIndex < NumPoints)) { const FVector TransformedLocation = (CoordinateSpace == ESplineCoordinateSpace::World) ? ComponentToWorld.InverseTransformPosition(InLocation) : InLocation; SplineInfo.Points[PointIndex].OutVal = TransformedLocation; UpdateSpline(); } }
void USplineComponent::ApplyComponentInstanceData(FSplineInstanceData* SplineInstanceData, const bool bPostUCS) { check(SplineInstanceData); if (SplineInstanceData->bSplineHasBeenEdited) { SplineInfo = SplineInstanceData->SplineInfo; SplineRotInfo = SplineInstanceData->SplineRotInfo; SplineScaleInfo = SplineInstanceData->SplineScaleInfo; } bSplineHasBeenEdited = SplineInstanceData->bSplineHasBeenEdited; UpdateSpline(); }
void USplineComponent::AddSplineLocalPoint(const FVector& Position) { // If it's a closed loop, remove the endpoint before adding a new point const bool bWasLoop = IsClosedLoop(); SetClosedLoop(false); float InputKey = static_cast<float>(SplineInfo.Points.Num()); const int32 PointIndex = SplineInfo.AddPoint(InputKey, Position); SplineInfo.Points[PointIndex].InterpMode = CIM_CurveAuto; // Then re-close the spline if required SetClosedLoop(bWasLoop); UpdateSpline(); }
void USplineComponent::SetTangentAtSplinePoint(int32 PointIndex, const FVector& InTangent, ESplineCoordinateSpace::Type CoordinateSpace) { const int32 NumPoints = SplineInfo.Points.Num(); if ((PointIndex >= 0) && (PointIndex < NumPoints)) { const FVector TransformedTangent = (CoordinateSpace == ESplineCoordinateSpace::World) ? ComponentToWorld.InverseTransformVector(InTangent) : InTangent; SplineInfo.Points[PointIndex].LeaveTangent = TransformedTangent; SplineInfo.Points[PointIndex].ArriveTangent = TransformedTangent; SplineInfo.Points[PointIndex].InterpMode = CIM_CurveUser; UpdateSpline(); } }
void USplineComponent::SetSplineLocalPoints(const TArray<FVector>& Points) { const bool bWasLoop = IsClosedLoop(); SetClosedLoop(false); SplineInfo.Points.Reset(bWasLoop ? Points.Num() + 1 : Points.Num()); float InputKey = 0.0f; for (const auto& Point : Points) { int32 PointIndex = SplineInfo.AddPoint(InputKey, Point); SplineInfo.Points[PointIndex].InterpMode = CIM_CurveAuto; InputKey += 1.0f; } SetClosedLoop(bWasLoop); UpdateSpline(); }
void USplineComponent::AddSplinePointAtIndex(const FVector& Position, int32 Index, ESplineCoordinateSpace::Type CoordinateSpace) { const FVector TransformedPosition = (CoordinateSpace == ESplineCoordinateSpace::World) ? ComponentToWorld.InverseTransformPosition(Position) : Position; const float InKey = static_cast<float>(Index); if (((Index >= 0) && (Index < SplineInfo.Points.Num())) && (Index < SplineRotInfo.Points.Num()) && (Index < SplineScaleInfo.Points.Num())) { SplineInfo.Points.Insert(FInterpCurvePoint<FVector>(InKey, TransformedPosition, FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto), Index); SplineRotInfo.Points.Insert(FInterpCurvePoint<FQuat>(InKey, FQuat::Identity, FQuat::Identity, FQuat::Identity, CIM_CurveAuto), Index); SplineScaleInfo.Points.Insert(FInterpCurvePoint<FVector>(InKey, FVector(1.0f), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto), Index); } UpdateSpline(); }
void USplineComponent::SetSplineWorldPoints(const TArray<FVector>& Points) { // If it's a closed loop, mark it as not closed before setting a new array of points const bool bWasLoop = IsClosedLoop(); SetClosedLoop(false); SplineInfo.Points.Reset(bWasLoop ? Points.Num() + 1 : Points.Num()); float InputKey = 0.0f; for (const auto& Point : Points) { int32 PointIndex = SplineInfo.AddPoint(InputKey, ComponentToWorld.InverseTransformPosition(Point)); SplineInfo.Points[PointIndex].InterpMode = CIM_CurveAuto; InputKey += 1.0f; } SetClosedLoop(bWasLoop); UpdateSpline(); }
void USplineComponent::RemoveSplinePoint(const int32 Index) { int32 Count = 1; if ((Index >= 0) && (SplineInfo.Points.Num() >= 0) && (SplineRotInfo.Points.Num() >= 0) && (SplineScaleInfo.Points.Num() >= 0) && (Index + Count <= SplineInfo.Points.Num()) && (Index + Count <= SplineRotInfo.Points.Num()) && (Index + Count <= SplineScaleInfo.Points.Num()) ) { SplineInfo.Points.RemoveAt(Index, Count, false); SplineRotInfo.Points.RemoveAt(Index, Count, false); SplineScaleInfo.Points.RemoveAt(Index, Count, false); } UpdateSpline(); }
void USplineComponent::SetSplinePoints(const TArray<FVector>& Points, ESplineCoordinateSpace::Type CoordinateSpace) { const int32 NumPoints = Points.Num(); SplineInfo.Points.Reset(NumPoints); SplineRotInfo.Points.Reset(NumPoints); SplineScaleInfo.Points.Reset(NumPoints); float InputKey = 0.0f; for (const auto& Point : Points) { const FVector TransformedPoint = (CoordinateSpace == ESplineCoordinateSpace::World) ? ComponentToWorld.InverseTransformPosition(Point) : Point; SplineInfo.Points.Emplace(InputKey, TransformedPoint, FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); SplineRotInfo.Points.Emplace(InputKey, FQuat::Identity, FQuat::Identity, FQuat::Identity, CIM_CurveAuto); SplineScaleInfo.Points.Emplace(InputKey, FVector(1.0f), FVector::ZeroVector, FVector::ZeroVector, CIM_CurveAuto); InputKey += 1.0f; } UpdateSpline(); }
void USplineComponent::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) { if (PropertyChangedEvent.Property != nullptr) { static const FName ReparamStepsPerSegmentName = GET_MEMBER_NAME_CHECKED(USplineComponent, ReparamStepsPerSegment); static const FName StationaryEndpointsName = GET_MEMBER_NAME_CHECKED(USplineComponent, bStationaryEndpoints); static const FName DefaultUpVectorName = GET_MEMBER_NAME_CHECKED(USplineComponent, DefaultUpVector); static const FName ClosedLoopName = GET_MEMBER_NAME_CHECKED(USplineComponent, bClosedLoop); static const FName SplineInfoName = GET_MEMBER_NAME_CHECKED(USplineComponent, SplineInfo); static const FName SplineHasBeenEditedName = GET_MEMBER_NAME_CHECKED(USplineComponent, bSplineHasBeenEdited); const FName PropertyName(PropertyChangedEvent.Property->GetFName()); if (PropertyName == ReparamStepsPerSegmentName || PropertyName == StationaryEndpointsName || PropertyName == DefaultUpVectorName || PropertyName == ClosedLoopName) { UpdateSpline(); } } Super::PostEditChangeChainProperty(PropertyChangedEvent); }
USplineComponent::USplineComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bAllowSplineEditingPerInstance(true) , ReparamStepsPerSegment(10) , Duration(1.0f) , bStationaryEndpoints(false) , bClosedLoop(false) #if WITH_EDITORONLY_DATA , EditorUnselectedSplineSegmentColor(FLinearColor(1.0f, 1.0f, 1.0f)) , EditorSelectedSplineSegmentColor(FLinearColor(1.0f, 0.0f, 0.0f)) #endif { SplineInfo.Points.Reset(10); // Add 2 keys by default int32 PointIndex = SplineInfo.AddPoint(0.f, FVector(0,0,0)); SplineInfo.Points[PointIndex].InterpMode = CIM_CurveAuto; PointIndex = SplineInfo.AddPoint(1.f, FVector(100,0,0)); SplineInfo.Points[PointIndex].InterpMode = CIM_CurveAuto; UpdateSpline(); }
void USplineComponent::SetWorldLocationAtSplinePoint(int32 PointIndex, const FVector& InLocation) { const int32 NumPoints = SplineInfo.Points.Num(); if ((PointIndex >= 0) && (PointIndex < NumPoints)) { SplineInfo.Points[PointIndex].OutVal = ComponentToWorld.InverseTransformPosition(InLocation); if (IsClosedLoop()) { // In a closed loop, the first and last points are tied, so update one with the other if (PointIndex == 0) { SplineInfo.Points[NumPoints - 1].OutVal = InLocation; } else if (PointIndex == NumPoints - 1) { SplineInfo.Points[0].OutVal = InLocation; } } UpdateSpline(); } }
void USplineComponent::SetClosedLoop(bool bInClosedLoop) { UpdateLoopEndpoint(bInClosedLoop); UpdateSpline(); }
void USplineComponent::PostEditImport() { Super::PostEditImport(); UpdateSpline(); }
void USplineComponent::PostLoad() { Super::PostLoad(); UpdateSpline(); }
void USplineComponent::SetClosedLoop(bool bInClosedLoop) { bClosedLoop = bInClosedLoop; UpdateSpline(); }