Runtime water body (River) creation, C++

Hey @TimeWinder2 ,

I ran into the same issue. After changing the Transform on the object at runtime, the water suddenly appeared. Then I added the “UpdateAll” method call and it loaded the water correctly at runtime. Hope this helps.

    UE::Geometry::FDynamicMesh3 WaterMeshRef;
    UE::Geometry::FDynamicMesh3 DilatedMeshRef;

WaterBody = World->SpawnActor<ABellwetherWaterBody>(ABellwetherWaterBody::StaticClass(), GetActorLocation(), GetActorRotation());
    WaterBody->InitializeBody();
    WaterBody->SetActorScale3D(FVector(5.0f, 5.0f, 5.0f));
    WaterBody->SetActorLocation(FVector(0.0f, 0.0f, 0.0f));
    
    WaterBody->MarkComponentsRenderStateDirty();
    WaterBody->UpdateComponentTransforms();

    UGerstnerWaterWaves* Waves = NewObject<UGerstnerWaterWaves>(WaterBody, UGerstnerWaterWaves::StaticClass(), TEXT("WaterWaves"));
    UGerstnerWaterWaveGeneratorSimple* WaveGenerator = NewObject<UGerstnerWaterWaveGeneratorSimple>(Waves, UGerstnerWaterWaveGeneratorSimple::StaticClass(), TEXT("GerstnerWaterWaveGeneratorSimple"));

    Waves->GerstnerWaveGenerator = WaveGenerator;
    WaterBody->SetWaterWaves(Waves);

 UWaterBodyComponent* WaterBodyComponent = WaterBody->GetWaterBodyComponent();
    WaterBodyComponent->SetMobility(EComponentMobility::Movable);
    UWaterBodyLakeComponent* LakeComponent = Cast<UWaterBodyLakeComponent>(WaterBodyComponent);
    LakeComponent->SetMobility(EComponentMobility::Movable);
    WaterBodyManager->AddWaterBodyComponent(WaterBodyComponent);
    WaterBodyComponent->ShouldGenerateWaterMeshTile();
    WaterBodyComponent->UpdateWaterZones();
    AWaterZone* WaterZone = WaterBodyComponent->GetWaterZone();
    WaterBodyComponent->SetWaterZoneOverride(WaterZone);
    WaterBodyComponent->WaterMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Water/Materials/WaterSurface/Water_Material_Lake.Water_Material_Lake"));

FWaterBodyHeightmapSettings* HeightmapSettings = new FWaterBodyHeightmapSettings();
    HeightmapSettings->BlendMode = EWaterBrushBlendType::AlphaBlend;

    FWaterCurveSettings* WaterCurveSettings = new FWaterCurveSettings();
    WaterCurveSettings->ChannelDepth = 500.0f;
    WaterCurveSettings->bUseCurveChannel = true;
    WaterCurveSettings->ChannelEdgeOffset = 0.0f;
    WaterCurveSettings->CurveRampWidth = 2000.0f;

    WaterCurveSettings->ElevationCurveAsset = LoadObject<UCurveFloat>(nullptr, TEXT("/Water/Curves/FloatCurve.FloatCurve"));

    if (!WaterCurveSettings->ElevationCurveAsset)
    {
        UE_LOG(LogTemp, Warning, TEXT("Failed to load ElevationCurveAsset! Check the path."));
    }
    WaterBodyComponent->CurveSettings = *WaterCurveSettings;
    WaterBodyComponent->WaterHeightmapSettings = *HeightmapSettings;
    WaterBodyComponent->WaterHLODMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Water/Materials/HLOD/HLODWater.HLODWater"));
    
    UDynamicMeshComponent* WaterMeshComp = NewObject<UDynamicMeshComponent>(this);
    WaterMeshComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    WaterMeshComp->RegisterComponent();
    WaterMeshComp->SetCastShadow(false);
    WaterMeshComp->GetDynamicMesh()->EditMesh([&](FDynamicMesh3& MeshRef)
    {
        MeshRef.EnableAttributes();
        MeshRef.Attributes()->EnablePrimaryColors();
        MeshRef.Attributes()->EnableTangents();
    }, EDynamicMeshChangeType::GeneralEdit);
    UE_LOG(LogTemp, Log, TEXT("Assigning material to mesh component."));
    WaterMeshComp->SetMaterial(0, WaterMaterial);
    if (UBodySetup* BodySetup = WaterMeshComp->GetBodySetup())
    {
        BodySetup->InvalidatePhysicsData();
        BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
        BodySetup->CreatePhysicsMeshes();
        WaterMeshComp->RecreatePhysicsState();
    }
    WaterMeshComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    WaterMeshComp->SetCollisionResponseToAllChannels(ECR_Block);
    WaterMeshComp->SetCollisionObjectType(ECC_WorldStatic);
    WaterMeshComp->EnableComplexAsSimpleCollision();
    WaterMeshComp->UpdateComponentToWorld();
    WaterMeshComp->MarkRenderStateDirty();
    WaterMeshComp->MarkRenderDynamicDataDirty();
    WaterMeshComp->SetMobility(EComponentMobility::Movable);

    UDynamicMeshComponent* DilatedWaterMeshComp = NewObject<UDynamicMeshComponent>(this);
    DilatedWaterMeshComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    DilatedWaterMeshComp->RegisterComponent();
    DilatedWaterMeshComp->SetCastShadow(false); // clearly disable shadows on water mesh
    DilatedWaterMeshComp->GetDynamicMesh()->EditMesh([&](FDynamicMesh3& MeshRef)
    {
        MeshRef.EnableAttributes();
        MeshRef.Attributes()->EnablePrimaryColors();
        MeshRef.Attributes()->EnableTangents();
    }, EDynamicMeshChangeType::GeneralEdit);
    UE_LOG(LogTemp, Log, TEXT("Assigning material to mesh component."));
    DilatedWaterMeshComp->SetMaterial(0, WaterMaterial);
    if (UBodySetup* BodySetup = DilatedWaterMeshComp->GetBodySetup())
    {
        BodySetup->InvalidatePhysicsData();
        BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
        BodySetup->CreatePhysicsMeshes();
        DilatedWaterMeshComp->RecreatePhysicsState();
    }
    DilatedWaterMeshComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    DilatedWaterMeshComp->SetCollisionResponseToAllChannels(ECR_Block);
    DilatedWaterMeshComp->SetCollisionObjectType(ECC_WorldStatic);
    DilatedWaterMeshComp->EnableComplexAsSimpleCollision();
    DilatedWaterMeshComp->UpdateComponentToWorld();
    DilatedWaterMeshComp->MarkRenderStateDirty();
    DilatedWaterMeshComp->MarkRenderDynamicDataDirty();
    DilatedWaterMeshComp->SetMobility(EComponentMobility::Movable);

    // Extract Water Mesh
    WaterMeshComp->GetDynamicMesh()->ProcessMesh([&](const FDynamicMesh3& Mesh)
    {
        WaterMeshRef = Mesh; // Copy actual mesh data
    });

    // Extract Dilated Mesh (if available)
    if (DilatedWaterMeshComp)
    {
        DilatedWaterMeshComp->GetDynamicMesh()->ProcessMesh([&](const FDynamicMesh3& Mesh)
        {
            DilatedMeshRef = Mesh; // Copy actual mesh data
        });
    }

    WaterBodyComponent->SetWaterBodyStaticMeshEnabled(true);
    FOnWaterBodyChangedParams OnWaterBodyChangedParams;
    OnWaterBodyChangedParams.bShapeOrPositionChanged = true;
    OnWaterBodyChangedParams.bUserTriggered = true;
    OnWaterBodyChangedParams.bWeightmapSettingsChanged = true;
    WaterBodyComponent->UpdateAll(OnWaterBodyChangedParams);
    WaterBodyComponent->GenerateWaterBodyMesh(WaterMeshRef, DilatedWaterMeshComp ? &DilatedMeshRef : nullptr);

    WaterSubsystem->MarkAllWaterZonesForRebuild(
        EWaterZoneRebuildFlags::All,
        nullptr
    ); 

    // delete FalloffSettings;
    delete HeightmapSettings;
    delete WaterCurveSettings;
1 Like