USplineMeshComponent Incorrect World Location and Mesh Distortion

I’ve been trying and failing to setup a spline mesh component. It is both rotated and distorted. The mesh I’m using is simply a vertically oriented cylinder, and that’s what I’m expecting when I initially run the game. Instead, this is how it looks:


^Start point and tangent: <0, 0, 0> <0, 0, 0>
End point and tangent: <0, 0, -21><0, 0, 0>

^Start tan<0, 0, 1>
End: <0, 0, -1>

Here’s How I setup the USplineMeshComponent (RelativeRotation defaults to FRotator::ZeroRotator):

void CreateSplineMesh(const FString pathToMesh, FRotator RelativeRotation)
{
	USplineMeshComponent* SplineMeshComponent = NewObject<USplineMeshComponent>(this);

	// load static mesh
	UStaticMesh* LoadedMesh = LoadObject<UStaticMesh>(nullptr, *pathToMesh);
	if (!LoadedMesh)
	{
		UE_LOG(LogTemp, Warning, TEXT("Failed to load mesh at path: %s"), *pathToMesh);
		return;
	}
	SplineMeshComponent->SetStaticMesh(LoadedMesh);
	SplineMeshComponent->SetRelativeRotation(RelativeRotation);
	SplineMeshComponent->Mobility = EComponentMobility::Movable;
	SplineMeshComponent->SetupAttachment(RootComponent);
	SplineMeshComponent->RegisterComponent();
	SplineMeshComponents.Add(SplineMeshComponent);
}

And I add it to a TArray of USplineMeshComponents so I can iterate through them all on tick. In this iteration I set the start and end points and tangents according to the location and rotation on some sphere colliders. For simplicity, this version just augments the first USplineMeshComponent:

void UpdateSplineMeshes()
{
	QBodySegment Segment = BodySegments[0];
	
	USplineMeshComponent* SplineMesh = SplineMeshComponents[Segment.SplineMeshIndex];
	UPrimitiveComponent* Joint1 = ColliderComponents[Segment.Joint1Index];
	UPrimitiveComponent* Joint2 = ColliderComponents[Segment.Joint2Index];

	SplineMesh->SetStartAndEnd(
		Joint1->GetComponentLocation() + Joint1->GetComponentRotation().RotateVector(Segment.LocalOffset),
		FVector(0.f, 0.f, 0.f) /*Joint1->GetComponentRotation().Vector()*/,
		Joint2->GetComponentLocation(),
		FVector(0.f, 0.f, 0.f) /*Joint2->GetComponentRotation().Vector()*/
	);
}

I commented out my original tangent functions because they weren’t working, but by default their rotation is <0, 0, 0> by default, so it shouldn’t make much of a difference. Start and end points are points at <0, 0, 0> and <0, 0, -21>, so I’ve also tried tangents of <0, 0, 1> and <0, 0, -1> and other combinations of positive and negative unit vectors along the z axis. Changing the tangent certainly changes how screwed up the mesh is, but I still have no idea why the USplineMeshComponent is rotated by default and is so distorted.

Changed the SplineMeshUpdate function to instead track some USplineComponents which are properly tracking the sphere collision components. Printing the location of each spline and corresponding spline mesh show that they are the same, however clearly that is not true for the picture below:


They are nowhere close to eachother and one spline mesh is going in a completely different direction.

Here are my spline and spline mesh update functions:

void AQVessel::UpdateBodySegments()
{
	for (const QBodySegment Segment : BodySegments)
	{
		USplineComponent* Spline = SplineComponents[Segment.SplineIndex];
		UPrimitiveComponent* Joint1 = ColliderComponents[Segment.Joint1Index];
		Spline->SetLocationAtSplinePoint(0, Joint1->GetComponentLocation() + Joint1->GetComponentRotation().RotateVector(Segment.LocalOffset), ESplineCoordinateSpace::World);
		Spline->SetLocationAtSplinePoint(1, ColliderComponents[Segment.Joint2Index]->GetComponentLocation(), ESplineCoordinateSpace::World);
		Spline->SetLocationAtSplinePoint(2, ColliderComponents[Segment.Joint3Index]->GetComponentLocation(), ESplineCoordinateSpace::World);

		FVector Location = Spline->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::World);
		GEngine->AddOnScreenDebugMessage(-1, GetWorld()->DeltaTimeSeconds, FColor::Cyan,
			FString::Printf(TEXT("Spline: X=%.2f Y=%.2f Z=%.2f"),
				Location.X, Location.Y, Location.Z));
	}
}

void AQVessel::UpdateSplineMeshes()
{
	for (const QBodySegment Segment : BodySegments)
	{
		USplineMeshComponent* SplineMesh = SplineMeshComponents[Segment.SplineMeshIndex];
		USplineComponent* Spline = SplineComponents[Segment.SplineIndex];
		UPrimitiveComponent* Joint1 = ColliderComponents[Segment.Joint1Index];
		UPrimitiveComponent* Joint2 = ColliderComponents[Segment.Joint2Index];

		SplineMesh->SetStartAndEnd(
			Spline->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::World)/*Joint1->GetComponentLocation() + Joint1->GetComponentRotation().RotateVector(Segment.LocalOffset)*/,
			Spline->GetTangentAtSplinePoint(0, ESplineCoordinateSpace::World) /*Joint1->GetComponentRotation().Vector()*/,
			Spline->GetLocationAtSplinePoint(1, ESplineCoordinateSpace::World)/*Joint2->GetComponentLocation()*/,
			Spline->GetTangentAtSplinePoint(1, ESplineCoordinateSpace::World) /*Joint2->GetComponentRotation().Vector()*/
		);

		FVector Location = SplineMesh->GetStartPosition();
		GEngine->AddOnScreenDebugMessage(-1, GetWorld()->DeltaTimeSeconds, FColor::Cyan,
			FString::Printf(TEXT("SplineMesh: X=%.2f Y=%.2f Z=%.2f"),
			Location.X, Location.Y, Location.Z));
	}
}