#pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "LandscapeAuthoringLibrary.generated.h" class ALandscape; class ULandscapeLayerInfoObject; class UMaterialInterface; // One paint layer to import with the heightmap: the layer info asset the material expects and an 8-bit // greyscale file (PNG or raw) with the same resolution as the heightmap. USTRUCT(BlueprintType) struct FLandscapeAuthoringWeightmap { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Landscape") TObjectPtr LayerInfo = nullptr; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Landscape") FString File; }; // What the editor's "New Landscape > Import" button does, callable from an authoring script. The engine exposes // no landscape creation to Python, so this is the project's first editor tool. Mirrors // FLandscapeEditorDetailCustomization_NewLandscape::OnCreateButtonClicked without the regions path. UCLASS() class SALTYEDITOR_API ULandscapeAuthoringLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: // Creates a landscape in World from a 16-bit heightmap file, centred on Location, with the given per-quad // scale (cm; Z is the engine's height scale where 100 spans -256 m to +256 m). The component layout is chosen // from the file's resolution; a file that does not fit a layout exactly is expanded, not resampled. In a // world-partitioned level the landscape is split into streaming proxies of GridSize x GridSize components. // Returns null and logs why on failure. UFUNCTION(BlueprintCallable, Category = "Salty|Authoring") static ALandscape* CreateLandscapeFromHeightmap(UWorld* World, const FString& HeightmapFile, const TArray& Weightmaps, UMaterialInterface* Material, FVector Location, FVector Scale, int32 WorldPartitionGridSize = 4); // Creates a ULandscapeLayerInfoObject asset carrying LayerName, which is the name the landscape material // blends by and the name a weightmap is imported against. // // The second thing the engine exposes no Python path to, and for the same reason as the first. There is no // LayerInfo factory in the bindings, and ULandscapeLayerInfoObject::LayerName is VisibleAnywhere - so the // obvious workaround, duplicating an existing layer info, produces an asset that silently keeps the name it // was copied from and paints that substance wherever the new layer should be. Returns null and logs why on // failure; an existing asset at the path is returned rather than replaced, so this is safe to rerun. UFUNCTION(BlueprintCallable, Category = "Salty|Authoring") static ULandscapeLayerInfoObject* CreateLayerInfo(const FString& PackagePath, const FString& AssetName, FName LayerName); };