Aollero/river preset generator (#5650)

* First steps on river generation

* Region of interest base sckeleton

* Vegetation ROIs back logic implementation

* Region of interest for vegetation integrated into widget

* Some more improvements in River generation

* ROI selection clicking on preiew heightmap

* ROIs visual preview selection and support for many Rois

* Persistent widget state

* Widget bugs fixed

* Soil tab

* Landscape smooth tool and widget init bug fixed

* Weather tab finished and some river generation progress

* Flatening tiles that contains rivers

* Widget updates

* Missing references

* Deleted unnecessary assets

* Some progresses on Rivers but not fully working

* Terrain ROIs Widget adaptations

* First steps on Terrain ROIs

* Format fixed
This commit is contained in:
adrian-ollero 2022-08-30 17:14:07 +02:00 committed by GitHub
parent 5daeb4d63d
commit 9bb8f41f3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 714 additions and 48 deletions

View File

@ -130,6 +130,7 @@ void ACarlaGameModeBase::InitGame(
UGameplayStatics::GetActorOfClass(GetWorld(), AWeather::StaticClass());
if (WeatherActor != nullptr) {
UE_LOG(LogCarla, Log, TEXT("Existing weather actor. Doing nothing then!"));
Episode->Weather = static_cast<AWeather*>(WeatherActor);
}
else if (WeatherClass != nullptr) {
Episode->Weather = World->SpawnActor<AWeather>(WeatherClass);

View File

@ -0,0 +1,26 @@
{
"isPersistentState": true,
"mapName": "",
"workingPath": "/Game/MyMaps",
"activeTabName": "",
"terrainGeneralSize": 4,
"terrainGeneralSlope": 0.5,
"terrainGeneralHeight": 3,
"terrainGeneralMinHeight": 0,
"terrainGeneralMaxHeight": 1,
"terrainGeneralInvert": 0,
"terrainOverallSeed": 700,
"terrainOverallScale": 26,
"terrainOverallSlope": 2.5,
"terrainOverallHeight": 1,
"terrainOverallMinHeight": 0,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0,
"terrainDetailedSeed": 4300,
"terrainDetailedScale": 10,
"terrainDetailedSlope": 1,
"terrainDetailedHeight": 2,
"terrainDetailedMinHeight": 0,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0
}

View File

@ -0,0 +1,26 @@
{
"isPersistentState": true,
"mapName": "",
"workingPath": "/Game/MyMaps",
"activeTabName": "",
"terrainGeneralSize": 4,
"terrainGeneralSlope": 0.5,
"terrainGeneralHeight": 3,
"terrainGeneralMinHeight": 0,
"terrainGeneralMaxHeight": 1,
"terrainGeneralInvert": 0,
"terrainOverallSeed": 700,
"terrainOverallScale": 26,
"terrainOverallSlope": 2.5,
"terrainOverallHeight": 1,
"terrainOverallMinHeight": 0,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0,
"terrainDetailedSeed": 4300,
"terrainDetailedScale": 10,
"terrainDetailedSlope": 1,
"terrainDetailedHeight": 2,
"terrainDetailedMinHeight": 0,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0
}

View File

@ -60,7 +60,9 @@ public class CarlaTools : ModuleRules
"Foliage",
"FoliageEdit",
"Carla",
"PhysXVehicles"
"PhysXVehicles",
"Json",
"JsonUtilities"
// ... add private dependencies that you statically link with here ...
}
);

View File

@ -9,6 +9,7 @@
#include "ActorFactories/ActorFactory.h"
#include "AssetRegistryModule.h"
#include "Carla/MapGen/LargeMapManager.h"
#include "Carla/Weather/Weather.h"
#include "Components/SplineComponent.h"
#include "Editor/FoliageEdit/Public/FoliageEdMode.h"
#include "EditorLevelLibrary.h"
@ -19,6 +20,8 @@
#include "Kismet/KismetMathLibrary.h"
#include "Landscape.h"
#include "LandscapeProxy.h"
#include "Misc/FileHelper.h"
#include "Misc/CString.h"
#include "ProceduralFoliageComponent.h"
#include "ProceduralFoliageVolume.h"
#include "Runtime/Engine/Classes/Engine/ObjectLibrary.h"
@ -31,6 +34,11 @@
#include "UObject/UObjectGlobals.h"
#include "UObject/ObjectMacros.h"
#include "Dom/JsonObject.h"
#include "JsonObjectConverter.h"
#include "Serialization/JsonSerializer.h"
#include "Serialization/JsonReader.h"
#define CUR_CLASS_FUNC (FString(__FUNCTION__))
#define CUR_LINE (FString::FromInt(__LINE__))
#define CUR_CLASS_FUNC_LINE (CUR_CLASS_FUNC + "::" + CUR_LINE)
@ -219,9 +227,14 @@ AActor* UMapGeneratorWidget::GenerateWater(TSubclassOf<class AActor> RiverClass)
UWorld* World = GetWorld();
float ActorZCoord = GetLandscapeSurfaceHeight(World, 0, 0, false);
FVector Location(20000, 20000, ActorZCoord); // Auxiliar values for x and y coords
FRotator Rotation(0,0,0);
float XCoord = 2000;
float YCoord = 2000;
float ZRot = 50;
float ActorZCoord = GetLandscapeSurfaceHeight(World, XCoord, YCoord, false);
FVector Location(XCoord, YCoord, ActorZCoord+5); // Auxiliar values for x and y coords
FRotator Rotation(0, ZRot, 0);
FActorSpawnParameters SpawnInfo;
@ -245,6 +258,68 @@ AActor* UMapGeneratorWidget::GenerateWater(TSubclassOf<class AActor> RiverClass)
return RiverActor;
}
bool UMapGeneratorWidget::GenerateWaterFromWorld(UWorld* RiversWorld, TSubclassOf<class AActor> RiverClass)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Starting Generating Rivers from world %s"),
*CUR_CLASS_FUNC_LINE, *RiversWorld->GetMapName());
TArray<AActor*> RiversActors;
UGameplayStatics::GetAllActorsOfClass(RiversWorld, RiverClass, RiversActors);
if(RiversActors.Num() <= 0)
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: No Rivers Found in %s"),
*CUR_CLASS_FUNC_LINE, *RiversWorld->GetMapName());
return false;
}
float RiverSurfaceDisplacement = 100.0f;
for(AActor* RiverActor : RiversActors)
{
USplineComponent* RiverSpline = dynamic_cast<USplineComponent*>(RiverActor->GetComponentByClass(USplineComponent::StaticClass()));
for(int i = 0; i < RiverSpline->GetNumberOfSplinePoints(); i++)
{
FVector SplinePosition = RiverSpline->GetWorldLocationAtSplinePoint(i);
SplinePosition.Z = GetLandscapeSurfaceHeight(RiversWorld, SplinePosition.X, SplinePosition.Y, false) + RiverSurfaceDisplacement;
RiverSpline->SetWorldLocationAtSplinePoint(i, SplinePosition);
}
}
return true;
}
UWorld* UMapGeneratorWidget::DuplicateWorld(FString BaseWorldPath, FString TargetWorldPath, const FString NewWorldName)
{
UWorld* DuplicateWorld;
UWorld* BaseWorld = LoadObject<UWorld>(nullptr, *BaseWorldPath);
if(BaseWorld == nullptr)
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: No World Found in %s"),
*CUR_CLASS_FUNC_LINE, *BaseWorldPath);
return nullptr;
}
const FString PackageName = TargetWorldPath + "/" + NewWorldName;
UPackage* WorldPackage = CreatePackage(*PackageName);
FObjectDuplicationParameters Parameters(BaseWorld, WorldPackage);
Parameters.DestName = FName(*NewWorldName);
Parameters.DestClass = BaseWorld->GetClass();
Parameters.DuplicateMode = EDuplicateMode::World;
Parameters.PortFlags = PPF_Duplicate;
DuplicateWorld = CastChecked<UWorld>(StaticDuplicateObjectEx(Parameters));
const FString PackageFileName = FPackageName::LongPackageNameToFilename(
PackageName,
FPackageName::GetMapPackageExtension());
UPackage::SavePackage(WorldPackage, DuplicateWorld, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
*PackageFileName, GError, nullptr, true, true, SAVE_NoError);
return DuplicateWorld;
}
AActor* UMapGeneratorWidget::AddWeatherToExistingMap(TSubclassOf<class AActor> WeatherActorClass,
const FMapGeneratorMetaInfo& MetaInfo, const FString SelectedWeather)
{
@ -268,12 +343,112 @@ AActor* UMapGeneratorWidget::AddWeatherToExistingMap(TSubclassOf<class AActor> W
const FString WorldToLoadPath = MapCompletePath + "." + MetaInfo.MapName;
UWorld* World = LoadObject<UWorld>(nullptr, *WorldToLoadPath);
AActor* WeatherActor = World->SpawnActor<AActor>(WeatherActorClass);
AActor* WeatherActor = UGameplayStatics::GetActorOfClass(World, AWeather::StaticClass());
if(WeatherActor == nullptr)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Creating a new weather actor to world"),
*CUR_CLASS_FUNC_LINE);
WeatherActor = World->SpawnActor<AActor>(WeatherActorClass);
}
return WeatherActor;
}
TMap<FRoiTile, FVegetationROI> UMapGeneratorWidget::CreateVegetationRoisMap(TArray<FVegetationROI> VegetationRoisArray)
{
TMap<FRoiTile, FVegetationROI> ResultMap;
for(FVegetationROI VegetationRoi : VegetationRoisArray)
{
for(FRoiTile VegetationRoiTile : VegetationRoi.TilesList)
{
ResultMap.Add(VegetationRoiTile, VegetationRoi);
}
}
return ResultMap;
}
TMap<FRoiTile, FTerrainROI> UMapGeneratorWidget::CreateTerrainRoisMap(TArray<FTerrainROI> TerrainRoisArray)
{
TMap<FRoiTile, FTerrainROI> ResultMap;
for(FTerrainROI TerrainRoi : TerrainRoisArray)
{
for(FRoiTile TerrainRoiTile : TerrainRoi.TilesList)
{
ResultMap.Add(TerrainRoiTile, TerrainRoi);
}
}
return ResultMap;
}
bool UMapGeneratorWidget::DeleteAllVegetationInMap(const FString Path, const FString MapName)
{
TArray<FAssetData> AssetsData;
const FString TilesPath = Path;
bool success = LoadWorlds(AssetsData, TilesPath);
if(!success || AssetsData.Num() <= 0)
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("No Tiles found in %s. Vegetation cooking Aborted!"), *TilesPath);
return false;
}
// Cook vegetation for each of the maps
for(FAssetData AssetData : AssetsData)
{
UWorld* World = GetWorldFromAssetData(AssetData);
TArray<AActor*> FoliageVolumeActors;
UGameplayStatics::GetAllActorsOfClass(World, AProceduralFoliageVolume::StaticClass(), FoliageVolumeActors);
for(AActor* FoliageActor : FoliageVolumeActors)
{
FoliageActor->Destroy();
}
SaveWorld(World);
}
return true;
}
bool UMapGeneratorWidget::GenerateWidgetStateFileFromStruct(FMapGeneratorWidgetState WidgetState, const FString JsonPath)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Creating Widget State JSON"),
*CUR_CLASS_FUNC_LINE);
TSharedRef<FJsonObject> OutJsonObject = MakeShareable(new FJsonObject());
FJsonObjectConverter::UStructToJsonObject(FMapGeneratorWidgetState::StaticStruct(), &WidgetState, OutJsonObject, 0, 0);
FString OutputJsonString;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&OutputJsonString);
FJsonSerializer::Serialize(OutJsonObject, JsonWriter);
FFileHelper::SaveStringToFile(OutputJsonString, *JsonPath);
return true;
}
FMapGeneratorWidgetState UMapGeneratorWidget::LoadWidgetStateStructFromFile(const FString JsonPath)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Creating Widget State Struct from JSON"),
*CUR_CLASS_FUNC_LINE);
FMapGeneratorWidgetState WidgetState;
FString File;
FFileHelper::LoadFileToString(File, *JsonPath);
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(*File);
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
bool bDeserializeSuccess = FJsonSerializer::Deserialize(JsonReader, JsonObject, FJsonSerializer::EFlags::None);
if (bDeserializeSuccess && JsonObject.IsValid())
{
FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), FMapGeneratorWidgetState::StaticStruct(), &WidgetState, 1, 0);
}
return WidgetState;
}
bool UMapGeneratorWidget::LoadWorlds(TArray<FAssetData>& WorldAssetsData, const FString& BaseMapPath, bool bRecursive)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Loading Worlds from %s"),
@ -442,8 +617,26 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
UE_LOG(LogCarlaToolsMapGenerator, Warning, TEXT("%s: Heightmap detected with dimensions %dx%d"),
*CUR_CLASS_FUNC_LINE, HeightRT->SizeX, HeightRT->SizeY);
TArray<uint16> HeightData;
// TODO: UTexture2D and GetMipData
UpdateTileRT(i, MetaInfo.SizeY-j-1);
FMapGeneratorTileMetaInfo TileMetaInfo;
TileMetaInfo.IndexX = i;
TileMetaInfo.IndexY = MetaInfo.SizeY-j-1;
TileMetaInfo.MapMetaInfo = MetaInfo;
// River Management
if(FMath::RandRange(0.0f, 100.0f) < MetaInfo.RiverChanceFactor)
{
TileMetaInfo.ContainsRiver = true;
}
else
{
TileMetaInfo.ContainsRiver = false;
}
// Update and get heightmap from texture
UpdateTileRT(TileMetaInfo);
FTextureRenderTargetResource* RenderTargetResource = HeightRT->GameThread_GetRenderTargetResource();
FIntRect Rect = FIntRect(0, 0, HeightRT->SizeX, HeightRT->SizeY);
TArray<FLinearColor> HeightmapColor;
@ -457,6 +650,106 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
HeightData.Add((uint16)(LinearColor.R * 255 * 255 + LinearColor.G * 255));
}
// Terrain ROI
FRoiTile ThisTileIndex(i, j);
if(FRegionOfInterest::IsTileInRegionsSet(ThisTileIndex, MetaInfo.TerrainRoisMap))
{
FTerrainROI TileRegion = MetaInfo.TerrainRoisMap[ThisTileIndex];
// Update ROI RT with ROI material
UpdateTileRoiRT(TileMetaInfo, TileRegion.RoiMaterialInstance);
UTextureRenderTarget2D* RoiHeightRT = TileRegion.RoiHeightmapRenderTarget;
TArray<uint16> RoiHeightData;
FTextureRenderTargetResource* RoiRenderTargetResource = RoiHeightRT->GameThread_GetRenderTargetResource();
FIntRect RoiRect = FIntRect(0, 0, RoiHeightRT->SizeX, RoiHeightRT->SizeY);
TArray<FLinearColor> RoiHeightmapColor;
RoiHeightmapColor.Reserve(RoiRect.Width() * RoiRect.Height());
RoiRenderTargetResource->ReadLinearColorPixels(RoiHeightmapColor, FReadSurfaceDataFlags(RCM_MinMax, CubeFace_MAX), RoiRect);
RoiHeightData.Reserve(RoiHeightmapColor.Num());
for(FLinearColor RoiLinearColor : RoiHeightmapColor)
{
RoiHeightData.Add((uint16)(RoiLinearColor.R * 255 * 255 + RoiLinearColor.G * 255));
}
int FlateningMargin = 30; // Not flatened
int FlateningFalloff = 100; // Transition from actual and flat value
int TileSize = 1009; // Should be calculated by sqrt(HeightData.Num())
for(int X = FlateningMargin; X < (TileSize - FlateningMargin); X++)
{
for(int Y = FlateningMargin; Y < (TileSize - FlateningMargin); Y++)
{
float TransitionFactor = 1.0f;
if(X < (FlateningMargin + FlateningFalloff))
{
TransitionFactor *= (X - FlateningMargin) / (float) FlateningFalloff;
}
if(Y < (FlateningMargin + FlateningFalloff))
{
TransitionFactor *= (Y - FlateningMargin) / (float) FlateningFalloff;
}
if(X > (TileSize - FlateningMargin - FlateningFalloff))
{
TransitionFactor *= 1 - ((X - (TileSize - FlateningMargin - FlateningFalloff)) / (float) FlateningFalloff);
}
if(Y > (TileSize - FlateningMargin - FlateningFalloff))
{
TransitionFactor *= 1 - ((Y - (TileSize - FlateningMargin - FlateningFalloff)) / (float) FlateningFalloff);
}
HeightData[(X * TileSize) + Y] = (RoiHeightData[(X * TileSize) + Y]) * TransitionFactor + HeightData[(X * TileSize) + Y] * (1-TransitionFactor);
}
}
}
// Flatening if contains river
// TODO: Move this if to a function
// TODO: Check and fix flatening algorithm
if(TileMetaInfo.ContainsRiver)
{
int FlateningMargin = 30; // Not flatened
int FlateningFalloff = 100; // Transition from actual and flat value
int TileSize = 1009; // Should be calculated by sqrt(HeightData.Num())
for(int X = FlateningMargin; X < (TileSize - FlateningMargin); X++)
{
for(int Y = FlateningMargin; Y < (TileSize - FlateningMargin); Y++)
{
float TransitionFactor = 1.0f;
if(X < (FlateningMargin + FlateningFalloff))
{
TransitionFactor *= (X - FlateningMargin) / (float) FlateningFalloff;
}
if(Y < (FlateningMargin + FlateningFalloff))
{
TransitionFactor *= (Y - FlateningMargin) / (float) FlateningFalloff;
}
if(X > (TileSize - FlateningMargin - FlateningFalloff))
{
TransitionFactor *= 1 - ((X - (TileSize - FlateningMargin - FlateningFalloff)) / (float) FlateningFalloff);
}
if(Y > (TileSize - FlateningMargin - FlateningFalloff))
{
TransitionFactor *= 1 - ((Y - (TileSize - FlateningMargin - FlateningFalloff)) / (float) FlateningFalloff);
}
// HeightData[(X * TileSize) + Y] = (HeightData[(X * TileSize) + Y] * MetaInfo.RiverFlateningFactor) * TransitionFactor + HeightData[(X * TileSize) + Y] * (1-TransitionFactor);
}
}
DuplicateWorld("/CarlaTools/MapGenerator/Rivers/RiverPresets/River01/RiverPreset01.RiverPreset01",
MetaInfo.DestinationPath + "/Rivers", MapName + "_River");
}
// Smooth process
TArray<uint16> SmoothedData;
SmoothHeightmap(HeightData, SmoothedData);
HeightData = SmoothedData;
FVector LandscapeScaleVector(100.0f, 100.0f, 100.0f);
Landscape->CreateLandscapeInfo();
Landscape->SetActorTransform(FTransform(FQuat::Identity, FVector(), LandscapeScaleVector));
@ -492,6 +785,8 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
*CUR_CLASS_FUNC_LINE, *ErrorUnloadingStr.ToString());
return false;
}
// TODO: Instantiate water if needed
}
}
@ -546,8 +841,24 @@ bool UMapGeneratorWidget::CookVegetationToTiles(const FMapGeneratorMetaInfo& Met
return false;
}
// ROI checks
int TileIndexX, TileIndexY;
ExtractCoordinatedFromMapName(World->GetMapName(), TileIndexX, TileIndexY);
FRoiTile ThisTileIndex(TileIndexX, TileIndexY);
TArray<UProceduralFoliageSpawner*> FoliageSpawnersToCook;
if(FRegionOfInterest::IsTileInRegionsSet(ThisTileIndex, MetaInfo.VegetationRoisMap))
{
FVegetationROI TileRegion = MetaInfo.VegetationRoisMap[ThisTileIndex];
FoliageSpawnersToCook = TileRegion.GetFoliageSpawners();
}
else
{
FoliageSpawnersToCook = MetaInfo.FoliageSpawners;
}
// Cook vegetation to world
bool bVegetationSuccess = CookVegetationToWorld(World, MetaInfo.FoliageSpawners);
bool bVegetationSuccess = CookVegetationToWorld(World, FoliageSpawnersToCook);
if(!bVegetationSuccess){
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: Error Cooking Vegetation in %s"),
*CUR_CLASS_FUNC_LINE, *MapNameToLoad);
@ -629,39 +940,73 @@ float UMapGeneratorWidget::GetLandscapeSurfaceHeight(UWorld* World, float x, flo
{
if(World)
{
FVector RayStartingPoint(x, y, 999999);
FVector RayEndPoint(x, y, -999999);
// Raytrace
FHitResult HitResult;
World->LineTraceSingleByObjectType(
OUT HitResult,
RayStartingPoint,
RayEndPoint,
FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldStatic),
FCollisionQueryParams());
// Draw debug line.
if (bDrawDebugLines)
{
FColor LineColor;
if (HitResult.GetActor()) LineColor = FColor::Red;
else LineColor = FColor::Green;
DrawDebugLine(
World,
RayStartingPoint,
RayEndPoint,
LineColor,
true,
5.f,
0.f,
10.f);
}
// Return Z Location.
if (HitResult.GetActor()) return HitResult.ImpactPoint.Z;
ALandscape* Landscape = (ALandscape*) UGameplayStatics::GetActorOfClass(
World,
ALandscape::StaticClass());
FVector Location(x, y, 0);
TOptional<float> Height = Landscape->GetHeightAtLocation(Location);
// TODO: Change function return type to TOptional<float>
return Height.IsSet() ? Height.GetValue() : 0.0f;
}
return 0.0f;
}
void UMapGeneratorWidget::ExtractCoordinatedFromMapName(const FString MapName, int& X, int& Y)
{
FString Name, Coordinates;
MapName.Split(TEXT("_Tile_"), &Name, &Coordinates);
FString XStr, YStr;
Coordinates.Split(TEXT("_"), &XStr, &YStr);
X = FCString::Atoi(*XStr);
Y = FCString::Atoi(*YStr);
}
void UMapGeneratorWidget::SmoothHeightmap(TArray<uint16> HeightData, TArray<uint16>& OutHeightData)
{
TArray<uint16> SmoothedData(HeightData);
// Prepare Gaussian Kernel
int KernelSize = 5;
int KernelWeight = 273;
float Kernel[] = {1, 4, 7, 4, 1,
4, 16, 26, 16, 4,
7, 26, 41, 26, 7,
4, 16, 26, 16, 4,
1, 4, 7, 4, 1};
TArray<float> SmoothKernel;
for(int i = 0; i < KernelSize*KernelSize; i++)
{
SmoothKernel.Add(Kernel[i] / KernelWeight);
}
// Apply kernel to height data
int TileMargin = 2;
int TileSize = 1009; // Should be calculated by sqrt(HeightData.Num())
for(int X = TileMargin; X < (TileSize - TileMargin); X++)
{
for(int Y = TileMargin; Y < (TileSize - TileMargin); Y++)
{
int Value = 0;
for(int i = -2; i <= 2; i++)
{
for(int j = -2; j <=2; j++)
{
float KernelValue = SmoothKernel[(i+2)*2 + (j+2)];
int HeightValue = HeightData[ (X+i) * TileSize + (Y+j) ];
Value += (int) ( KernelValue * HeightValue );
}
}
SmoothedData[(X * TileSize) + Y] = Value;
}
}
OutHeightData = SmoothedData;
}

View File

@ -11,6 +11,7 @@
#include "EditorUtilityWidget.h"
#include "Engine/TextureRenderTarget2D.h"
#include "ProceduralFoliageSpawner.h"
#include "RegionOfInterest.h"
#include "UnrealString.h"
#include "MapGeneratorWidget.generated.h"
@ -40,6 +41,21 @@ struct CARLATOOLS_API FMapGeneratorMetaInfo
UPROPERTY(BlueprintReadWrite)
UTextureRenderTarget2D* GlobalHeightmap;
// UPROPERTY(BlueprintReadWrite)
// UTextureRenderTarget2D* PROVISIONALROIHEIGHTMAP;
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FTerrainROI> TerrainRoisMap;
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FVegetationROI> VegetationRoisMap;
UPROPERTY(BlueprintReadWrite)
float RiverChanceFactor;
UPROPERTY(BlueprintReadWrite)
float RiverFlateningFactor;
};
/// Struct used as container of basic tile information
@ -59,6 +75,92 @@ struct CARLATOOLS_API FMapGeneratorTileMetaInfo
UPROPERTY(BlueprintReadWrite)
int IndexY;
UPROPERTY(BlueprintReadWrite)
bool ContainsRiver;
UPROPERTY(BlueprintReadWrite)
FString RiverPreset;
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FMapGeneratorWidgetState
{
GENERATED_USTRUCT_BODY();
// General Fields
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
bool IsPersistentState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
FString MapName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
FString WorkingPath;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
FString ActiveTabName;
// Terrain
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainGeneralSize;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainGeneralSlope;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainGeneralHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainGeneralMinHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainGeneralMaxHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainGeneralInvert;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallSeed;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallScale;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallSlope;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallMinHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallMaxHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainOverallInvert;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedSeed;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedScale;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedSlope;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedMinHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedMaxHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary")
float TerrainDetailedInvert;
};
/// Class UMapGeneratorWidget extends the functionality of UEditorUtilityWidget
@ -74,9 +176,15 @@ public:
UFUNCTION(BlueprintImplementableEvent)
void AssignLandscapeMaterial(ALandscape* Landscape);
UFUNCTION(BlueprintImplementableEvent)
void InstantiateRiverSublevel(UWorld* World, const FMapGeneratorTileMetaInfo TileMetaInfo);
/// PROVISIONAL
UFUNCTION(BlueprintImplementableEvent)
void UpdateTileRT(int OffsetX, int OffsetY);
void UpdateTileRT(const FMapGeneratorTileMetaInfo& TileMetaInfo);
UFUNCTION(BlueprintImplementableEvent)
void UpdateTileRoiRT(const FMapGeneratorTileMetaInfo& TileMetaInfo, UMaterialInstanceDynamic* RoiMeterialInstance);
/// Function called by Widget Blueprint which generates all tiles of map
/// @a mapName, and saves them in @a destinationPath
@ -118,12 +226,34 @@ public:
UFUNCTION(Category="MapGenerator", BlueprintCallable)
AActor* GenerateWater(TSubclassOf<class AActor> RiverClass);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
bool GenerateWaterFromWorld(UWorld* RiversWorld, TSubclassOf<class AActor> RiverClass);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
UWorld* DuplicateWorld(const FString BaseWorldPath, const FString TargetWorldPath, const FString NewWorldName);
/// Adds weather actor of type @a WeatherActorClass and sets the @a SelectedWeather
/// to the map specified in @a MetaInfo
/// to the map specified in @a MetaInfo. Ifthe actor already exists on the map
/// then it is returned so only one weather actor is spawned in each map
UFUNCTION(Category="MapGenerator", BlueprintCallable)
AActor* AddWeatherToExistingMap(TSubclassOf<class AActor> WeatherActorClass,
const FMapGeneratorMetaInfo& MetaInfo, const FString SelectedWeather);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
TMap<FRoiTile, FVegetationROI> CreateVegetationRoisMap(TArray<FVegetationROI> VegetationRoisArray);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
TMap<FRoiTile, FTerrainROI> CreateTerrainRoisMap(TArray<FTerrainROI> TerrainRoisArray);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
bool DeleteAllVegetationInMap(const FString Path, const FString MapName);
UFUNCTION(Category="MapGenerator|JsonLibrary", BlueprintCallable)
bool GenerateWidgetStateFileFromStruct(FMapGeneratorWidgetState WidgetState, const FString JsonPath);
UFUNCTION(Category="MapGenerator|JsonLibrary", BlueprintCallable)
FMapGeneratorWidgetState LoadWidgetStateStructFromFile(const FString JsonPath);
private:
/// Loads a bunch of world objects located in @a BaseMapPath and
/// returns them in @a WorldAssetsData.
@ -136,6 +266,9 @@ private:
UFUNCTION()
bool SaveWorld(UWorld* WorldToBeSaved);
// UFUNCTION()
// bool SaveWorldPackage
/// Takes the name of the map from @a MetaInfo and created the main map
/// including all the actors needed by large map system
UFUNCTION()
@ -174,4 +307,10 @@ private:
/// @a x and @a y.
UFUNCTION()
float GetLandscapeSurfaceHeight(UWorld* World, float x, float y, bool bDrawDebugLines);
UFUNCTION()
void ExtractCoordinatedFromMapName(const FString MapName, int& X, int& Y);
UFUNCTION()
void SmoothHeightmap(TArray<uint16> HeightData, TArray<uint16>& OutHeightData);
};

View File

@ -3,7 +3,14 @@
#pragma once
#include "CoreMinimal.h"
#include "Containers/Array.h"
#include "Containers/EnumAsByte.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "ProceduralFoliageSpawner.h"
#include "Templates/UnrealTypeTraits.h"
#include "UObject/NoExportTypes.h"
#include "RegionOfInterest.generated.h"
@ -11,17 +18,137 @@ UENUM(BlueprintType)
enum ERegionOfInterestType
{
NONE,
TERRAIN_REGION,
WATERBODIES_REGION,
TERRAIN_REGION,
WATERBODIES_REGION, // Not Supported yet
VEGETATION_REGION
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FRoiTile
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
int X;
UPROPERTY(BlueprintReadWrite)
int Y;
public:
FRoiTile() : X(-1), Y(-1)
{};
FRoiTile(int X, int Y)
{
this->X = X;
this->Y = Y;
};
FRoiTile(const FRoiTile& Other)
: FRoiTile(Other.X, Other.Y)
{}
bool operator==(const FRoiTile& Other) const
{
return Equals(Other);
}
bool Equals(const FRoiTile& Other) const
{
return (this->X == Other.X) && (this->Y == Other.Y);
}
};
FORCEINLINE uint32 GetTypeHash(const FRoiTile& Thing)
{
uint32 Hash = FCrc::MemCrc32(&Thing, sizeof(FRoiTile));
return Hash;
}
/**
*
*/
UCLASS()
class CARLATOOLS_API URegionOfInterest : public UObject
USTRUCT(BlueprintType)
struct CARLATOOLS_API FRegionOfInterest
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
TArray<FRoiTile> TilesList;
UPROPERTY(BlueprintReadWrite)
TEnumAsByte<ERegionOfInterestType> RegionType = ERegionOfInterestType::NONE;
FRegionOfInterest()
{
TilesList.Empty();
}
void AddTile(int X, int Y)
{
FRoiTile Tile(X,Y);
TilesList.Add(Tile);
}
TEnumAsByte<ERegionOfInterestType> GetRegionType()
{
return this->RegionType;
}
template <typename R>
static FORCEINLINE bool IsTileInRegionsSet(FRoiTile RoiTile, TMap<FRoiTile, R> RoisMap)
{
static_assert(TIsDerivedFrom<R, FRegionOfInterest>::IsDerived,
"ROIs Map Value type is not an URegionOfInterest derived type.");
return RoisMap.Contains(RoiTile);
}
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FVegetationROI : public FRegionOfInterest
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
TArray<UProceduralFoliageSpawner*> FoliageSpawners;
FVegetationROI() : FRegionOfInterest()
{
this->FoliageSpawners.Empty();
}
void AddFoliageSpawner(UProceduralFoliageSpawner* Spawner)
{
FoliageSpawners.Add(Spawner);
}
void AddFoliageSpawners(TArray<UProceduralFoliageSpawner*> Spawners)
{
for(UProceduralFoliageSpawner* Spawner : Spawners)
{
AddFoliageSpawner(Spawner);
}
}
TArray<UProceduralFoliageSpawner*> GetFoliageSpawners()
{
return this->FoliageSpawners;
}
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FTerrainROI : public FRegionOfInterest
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
UMaterialInstanceDynamic* RoiMaterialInstance;
UPROPERTY(BlueprintReadWrite)
UTextureRenderTarget2D* RoiHeightmapRenderTarget;
FTerrainROI() : FRegionOfInterest(), RoiMaterialInstance()
{}
// TODO: IsEdge() funtion to avoid transition between tiles that belongs to the same ROI
};