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…