
One of the base functionalities that I wanted to add to the CageSpline actor is generating a bridge.
Currently I’m using a data asset to for bridge construction. The bridge portion is constructed from all of the meshes in the UCageBridgeSettings data asset. For each mesh, a HISM Comp (Hierarchical Instanced Static Mesh Component) is created. As the points are gathered, a new instance of the mesh is added the corresponding HISM Comp. For calculation optimization purposes, all operations are done in C++. Each HISM Comp is tagged so it can be cleaned up when a different data asset/mesh is selected.
This is concept-in-progress, which works as it currently stands. The goal is to have the bridge automatically generate based on spline points from external actors. I also would like to have roads, land objects, forestry, etc. to be considered.
UCageBridgeSettings
UCLASS(Blueprintable, BlueprintType)
class CAGETOOLS_API UCageBridgeSettings : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly)
float MovingSpeedThreshold{0.0f};
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr MainSupportColumn = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr MainSupportColumnFooter = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr ArchMesh = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr ArcSupportBeamsMesh = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr ArcCenterFrameMesh = nullptr;
public:
UCageBridgeSettings();
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};
GenerateBridge .h
UFUNCTION(BlueprintCallable) void GenerateBridge();
GenerateBridge .cpp
void ACageSpline::GenerateBridge()
{
if(!BridgeData || !bAutoGenerateBridges) return;
FName ComponentTag = "BridgeArch";
CleanUpComponents(ComponentTag);
CleanUpComponents("ArchPieces");
CleanUpComponents("ArchSpline");
CleanUpComponents("ArchColumnPieces");
CleanUpComponents("ArchColumnSpline");
CleanUpComponents("MainColumnPieces");
CleanUpComponents("MainColumnFooterPieces");
// Create HISM for Arch
UHierarchicalInstancedStaticMeshComponent* HISMArch = NewObject
(this, UHierarchicalInstancedStaticMeshComponent::StaticClass());
HISMArch->AttachToComponent(Spline, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
HISMArch->SetStaticMesh(BridgeData->ArchMesh);
HISMArch->ComponentTags.Add("ArchPieces");
HISMArch->RegisterComponent();
// Create HISM for columns
UHierarchicalInstancedStaticMeshComponent* HISMArchColumnPieces = NewObject
(this, UHierarchicalInstancedStaticMeshComponent::StaticClass());
HISMArchColumnPieces->AttachToComponent(Spline, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
HISMArchColumnPieces->SetStaticMesh(BridgeData->ArcSupportBeamsMesh);
HISMArchColumnPieces->ComponentTags.Add("ArchColumnPieces");
HISMArchColumnPieces->RegisterComponent();
// Create HISM for main columns
UHierarchicalInstancedStaticMeshComponent* HISMMainColumn = NewObject
(this, UHierarchicalInstancedStaticMeshComponent::StaticClass());
HISMMainColumn->AttachToComponent(Spline, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
HISMMainColumn->SetStaticMesh(BridgeData->MainSupportColumn);
HISMMainColumn->ComponentTags.Add("MainColumnPieces");
HISMMainColumn->RegisterComponent();
// Create HISM for main column footers
UHierarchicalInstancedStaticMeshComponent* HISMMainColumnFooter = NewObject
(this, UHierarchicalInstancedStaticMeshComponent::StaticClass());
HISMMainColumnFooter->AttachToComponent(Spline, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
HISMMainColumnFooter->SetStaticMesh(BridgeData->MainSupportColumnFooter);
HISMMainColumnFooter->ComponentTags.Add("MainColumnFooterPieces");
HISMMainColumnFooter->RegisterComponent();
// int32 Amount = 4;
// float BridgeDataDistance = 12000.f;
// float BridgeArchGroundZHit = 6000.f;
// float BridgeArchOffsetDistance = 0.f;
float ArchMeshSize = 0.f;
if(BridgeData->ArchMesh) ArchMeshSize = UCageUtilities::GetMeshDimensions(BridgeData->ArchMesh, false).X;
float ArcSupportBeamsMeshSize = 0.f;
if(BridgeData->ArcSupportBeamsMesh) ArcSupportBeamsMeshSize = UCageUtilities::GetMeshDimensions(BridgeData->ArcSupportBeamsMesh, false).Z;
float MainSupportColumnMeshSize = 0.f;
if(BridgeData->MainSupportColumn) MainSupportColumnMeshSize = UCageUtilities::GetMeshDimensions(BridgeData->MainSupportColumn, false).Z;
float MainSupportColumnFooterMeshSize = 0.f;
if(BridgeData->MainSupportColumnFooter) MainSupportColumnFooterMeshSize = UCageUtilities::GetMeshDimensions(BridgeData->MainSupportColumnFooter, false).Z;
ESplineCoordinateSpace::Type BridgeArchCoordinateSpace = ESplineCoordinateSpace::Type::Local;
ESplineCoordinateSpace::Type BridgeArchCoordinateSpaceWorld = ESplineCoordinateSpace::Type::World;
int32 PointIndex = 2;
// TEMP: Should ultimately get the specific position at desired distance (arch columns for instance)
FVector MainSplineBasePosition = Spline->GetLocationAtDistanceAlongSpline(0, BridgeArchCoordinateSpace);
// Bridge Arch Section
for(int32 i = 0; i < BridgeArchAmount; i++)
{
float CurrentBridgeDistance = BridgeDataDistance * i;
// Create new spline component
USplineComponent* ArchSC = NewObject(this);
ArchSC->AttachToComponent(Spline, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
ArchSC->ComponentTags.Add("ArchSpline");
ArchSC->ClearSplinePoints(true);
for(int32 j = 0; j < PointIndex + 1; j++)
{
FVector PointPosition;
float AdjustedDistance = 0.f;
if(j == 0)
{
PointPosition = Spline->GetLocationAtDistanceAlongSpline(CurrentBridgeDistance, BridgeArchCoordinateSpace);
PointPosition.Z = PointPosition.Z - BridgeArchGroundZHit;
}
else if(j == 1)
{
AdjustedDistance = CurrentBridgeDistance + (BridgeDataDistance /2.f);
PointPosition = Spline->GetLocationAtDistanceAlongSpline(AdjustedDistance, BridgeArchCoordinateSpace);
PointPosition.Z = PointPosition.Z + BridgeArchOffsetDistance;
}
else if(j == 2)
{
AdjustedDistance = CurrentBridgeDistance + BridgeDataDistance;
PointPosition = Spline->GetLocationAtDistanceAlongSpline(AdjustedDistance, BridgeArchCoordinateSpace);
PointPosition.Z = PointPosition.Z - BridgeArchGroundZHit;
}
else
{
break;
}
ArchSC->AddSplinePoint(PointPosition, BridgeArchCoordinateSpace, false);
// ArchSC->AddSplinePointAtIndex(PointPosition ,PointIndex, BridgeArchCoordinateSpace, true);
}
ArchSC->UpdateSpline();
ArchSC->RegisterComponent();
float ArchSCLength = ArchSC->GetSplineLength();
// Add Meshes to arch
float ArchSpread = ArchSCLength / ArchMeshSize;
for(int32 k = 0; k < FMath::TruncToInt(ArchSpread); k++)
{
FTransform InstanceTransform;
FVector ArchMeshLocation = ArchSC->GetLocationAtDistanceAlongSpline(k * ArchMeshSize, BridgeArchCoordinateSpace);
FRotator ArchMeshRotation = ArchSC->GetRotationAtDistanceAlongSpline(k * ArchMeshSize, BridgeArchCoordinateSpace);
InstanceTransform.SetRotation(ArchMeshRotation.Quaternion());
InstanceTransform.SetLocation(ArchMeshLocation);
HISMArch->AddInstance(InstanceTransform);
}
// Add vertical support columns splines
TArray ArchColumnArray;
int32 ArchColumnsAmount = FMath::TruncToInt(ArchSCLength / VerticalSupportColumnsSpacing);
for(int32 l = 0; l < ArchColumnsAmount; l++)
{
if(!BridgeData->ArcSupportBeamsMesh) break;
// Create new spline component
USplineComponent* ArchColumnSpline = NewObject(this);
ArchColumnSpline->AttachToComponent(Spline, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
ArchColumnSpline->ComponentTags.Add("ArchColumnSpline");
ArchColumnSpline->ClearSplinePoints(true);
FVector ArchPoint;
FVector BridgePoint;
// Adjusted computation for spline point distance
float DistanceAlongSpline = l * (ArchSCLength / ArchColumnsAmount);
ArchPoint = ArchSC->GetLocationAtDistanceAlongSpline(DistanceAlongSpline, BridgeArchCoordinateSpace);
BridgePoint = FVector(ArchPoint.X, ArchPoint.Y, MainSplineBasePosition.Z);
ArchColumnSpline->AddSplinePoint(ArchPoint, BridgeArchCoordinateSpace, false);
ArchColumnSpline->AddSplinePoint(BridgePoint, BridgeArchCoordinateSpace, false);
ArchColumnSpline->UpdateSpline();
ArchColumnSpline->RegisterComponent();
ArchColumnArray.Add(ArchColumnSpline);
}
for(USplineComponent* CurrentArchColumn : ArchColumnArray)
{
// Add HISM column pieces
float ArchColumnSplineLength = CurrentArchColumn->GetSplineLength();
int32 ArchColumnDistributeAmount = FMath::TruncToInt(ArchColumnSplineLength / ArcSupportBeamsMeshSize);
for(int32 m = 0; m < ArchColumnDistributeAmount; m++)
{
float DistanceAlongColumnSpline = m * ArcSupportBeamsMeshSize;//(ArchColumnSplineLength / ArchColumnDistributeAmount);
FTransform CurrentColumnPieceTransform;
FVector CurrentColumnPieceLocation = CurrentArchColumn->GetLocationAtDistanceAlongSpline(DistanceAlongColumnSpline, BridgeArchCoordinateSpace);
CurrentColumnPieceTransform.SetLocation(CurrentColumnPieceLocation);
HISMArchColumnPieces->AddInstance(CurrentColumnPieceTransform);
}
// if(!CurrentArchColumn) break;
}
}
// Add Main vertical support column
TArray GroundHits;
TArray ColumnSupportTransforms;
TArray SurfaceHitTransforms;
ColumnSupportTransforms = UCageUtilities::CalculateVerticalDistributionFromSplineLocation(Spline,
ESplineCoordinateSpace::Type::World,
ESplineCoordinateSpace::Type::World,
MainSplineBasePosition,
BridgeDataDistance,
MainSupportColumnMeshSize,
BridgeArchOffsetDistance,
0.f, //float SurfaceOffset,
false, //bool bTraceComplex,
"Landscape",
GroundHits, //TArray& SurfaceHits,
SurfaceHitTransforms //TArray& SurfaceTransforms
);
for(auto MCT : ColumnSupportTransforms)
{
HISMMainColumn->AddInstance(MCT, true);
}
// HISMMainColumn->AddInstances(ColumnSupportTransforms, true);
// Add Main vertical support column footer
// MainSupportColumnFooterMeshSize
for(auto CFT : SurfaceHitTransforms)
{
// FTransform HitTransform;
// HitTransform.SetLocation(Hit.Location);
HISMMainColumnFooter->AddInstance(CFT, true);
}
}
Here are some images of the current progress: To be continued…

