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) TObjectPtrMainSupportColumn = 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…