Aollero/terrain rois (#5834)

* Terrain rois defined within the user widget

* Improvementes in ROIs blending with original heightmap

* Rois Tiling fixed

* Support for multiple ROIs (overlapping bug)

* Minor improvementes in river placement

* Support for Spreaded and specific actors ROIs

* Soil type Rois supported in widget

* Soil type assignment to main map container actor

* Soil type queried from soil type actor soil list and tag

* Cleaning code

* Basic Spreaded Actors feature

* Single selection for specific actors placement

* Misc Persistent state for specific location actors implemented

* Spreaded Actors feature completed

* Spreaded Actors Tagged, offset and delete option code

* Specific Location Actor basic implementation

* Specific actor placement feauture completed

* Terrain Editor Bugs Fixed

* Missing icon

* Smoothing terrain boundaries

* Sewing between terrain tiles

* Terrain overlapping ROIs not allowed

* Overlapping message bug fixed

* Preset c++ funtions

* Improvements in the terrain generation

* Presets creation

* Terrain preset feature finished

* Missing references fixed

* Format errors fixed

* Last format fixes

* Error in static member

* Changing tabs for spaces

* Identation to spaces

* Duplicated include deleted

Co-authored-by: bernat <bernatx@gmail.com>
This commit is contained in:
adrian-ollero 2022-10-17 16:17:22 +02:00 committed by GitHub
parent 5e4e82ab2e
commit 6117e548ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1104 additions and 67 deletions

View File

@ -239,6 +239,11 @@ float ALargeMapManager::GetTileSize()
return TileSide;
}
FVector ALargeMapManager::GetTile0Offset()
{
return Tile0Offset;
}
void ALargeMapManager::SetLayerStreamingDistance(float Distance)
{
LayerStreamingDistance = Distance;

View File

@ -156,6 +156,8 @@ public:
float GetTileSize();
FVector GetTile0Offset();
void SetLayerStreamingDistance(float Distance);
void SetActorStreamingDistance(float Distance);

View File

@ -0,0 +1,94 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB). This work is licensed under the terms of the MIT license. For a copy, see <https://opensource.org/licenses/MIT>.
#include "MapGen/SoilTypeManager.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
ASoilTypeManager::ASoilTypeManager()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
}
// Called when the game starts or when spawned
void ASoilTypeManager::BeginPlay()
{
Super::BeginPlay();
}
void ASoilTypeManager::Tick(float DeltaTime)
{
#if WITH_EDITOR // Only for debugging purposes. Requires to activate tick in contructor
if((int)DeltaTime % 2000 == 0)
{
ALargeMapManager* LargeMapManager = (ALargeMapManager*) UGameplayStatics::GetActorOfClass(GetWorld(), ALargeMapManager::StaticClass());
AActor* Car = UGameplayStatics::GetActorOfClass(GetWorld(), CarClass);
if(Car != nullptr)
{
FVector CarPos = Car->GetActorLocation();
FVector GlobalCarPos = LargeMapManager->LocalToGlobalLocation(CarPos);
FIntVector TileVector = LargeMapManager->GetTileVectorID(GlobalCarPos);
uint64 TileIndex = LargeMapManager->GetTileID(GlobalCarPos);
FString TypeStr = GetTerrainPropertiesAtGlobalLocation(GlobalCarPos).ToString();
UE_LOG(LogCarla, Log, TEXT("Current Tile Index %d ----> (%d, %d, %d) with position L[%f, %f, %f] G[%f, %f, %f] Terrain Type: %s"),
TileIndex, TileVector.X, TileVector.Y, TileVector.Z, CarPos.X, CarPos.Y, CarPos.Z, GlobalCarPos.X, GlobalCarPos.Y, GlobalCarPos.Z,
*TypeStr);
}
}
#endif
}
FSoilTerramechanicsProperties ASoilTypeManager::GetGeneralTerrainProperties()
{
return GeneralTerrainProperties;
}
FSoilTerramechanicsProperties ASoilTypeManager::GetTerrainPropertiesAtGlobalLocation(FVector VehicleLocation)
{
// Get Indexes from location
FIntVector TileVectorID = LargeMapManager->GetTileVectorID(VehicleLocation);
// Query the map, if not in map, return general
if(TilesTerrainProperties.Contains(TileVectorID))
return TilesTerrainProperties[TileVectorID]; // Tile properties
else
return GeneralTerrainProperties; // General properties
}
FSoilTerramechanicsProperties ASoilTypeManager::GetTerrainPropertiesAtLocalLocation(FVector VehicleLocation)
{
FVector GlobalVehiclePosition = LargeMapManager->LocalToGlobalLocation(VehicleLocation);
return GetTerrainPropertiesAtGlobalLocation(GlobalVehiclePosition);
}
void ASoilTypeManager::SetGeneralTerrainProperties(FSoilTerramechanicsProperties TerrainProperties)
{
const FString TerrainPropertiesStr = TerrainProperties.ToString();
UE_LOG(LogCarla, Log, TEXT("Setting General Terrain Settings %s"), *TerrainPropertiesStr);
GeneralTerrainProperties = TerrainProperties;
}
void ASoilTypeManager::AddTerrainPropertiesToTile(int TileX, int TileY, FSoilTerramechanicsProperties TerrainProperties)
{
// Compute ID from X,Y coords
check(LargeMapManager != nullptr)
FIntVector TileVectorID(TileX, TileY, 0);
// Add to map
if(TerrainProperties.TerrainType == ESoilTerramechanicsType::NONE_SOIL)
TilesTerrainProperties.Add(TileVectorID, GeneralTerrainProperties);
else
TilesTerrainProperties.Add(TileVectorID, TerrainProperties);
}
void ASoilTypeManager::ClearTerrainPropertiesMap()
{
TilesTerrainProperties.Empty(TilesTerrainProperties.Num());
}

View File

@ -0,0 +1,88 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB). This work is licensed under the terms of the MIT license. For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Carla/MapGen/LargeMapManager.h"
#include "SoilTypeManager.generated.h"
UENUM(BlueprintType)
enum ESoilTerramechanicsType
{
NONE_SOIL = 0,
DESERT = 1,
FOREST = 2
};
USTRUCT(BlueprintType)
struct CARLA_API FSoilTerramechanicsProperties
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TEnumAsByte<ESoilTerramechanicsType> TerrainType;
const FString ToString() const
{
switch(TerrainType)
{
case ESoilTerramechanicsType::NONE_SOIL:
return "None";
case ESoilTerramechanicsType::DESERT:
return "Desert";
case ESoilTerramechanicsType::FOREST:
return "Forest";
}
return "";
};
};
UCLASS()
class CARLA_API ASoilTypeManager : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere)
FSoilTerramechanicsProperties GeneralTerrainProperties;
UPROPERTY(EditAnywhere)
TMap<FIntVector, FSoilTerramechanicsProperties> TilesTerrainProperties;
UPROPERTY(EditAnywhere)
ALargeMapManager* LargeMapManager;
public:
// Sets default values for this actor's properties
ASoilTypeManager();
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> CarClass;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
UFUNCTION(Category="MapGen|Soil Manager")
FSoilTerramechanicsProperties GetGeneralTerrainProperties();
UFUNCTION(Category="MapGen|Soil Manager")
FSoilTerramechanicsProperties GetTerrainPropertiesAtGlobalLocation(FVector VehicleLocation);
UFUNCTION(Category="MapGen|Soil Manager")
FSoilTerramechanicsProperties GetTerrainPropertiesAtLocalLocation(FVector VehicleLocation);
UFUNCTION(Category="MapGen|Soil Manager")
void SetGeneralTerrainProperties(FSoilTerramechanicsProperties TerrainProperties);
UFUNCTION(Category="MapGen|Soil Manager")
void AddTerrainPropertiesToTile(int TileX, int TileY, FSoilTerramechanicsProperties TerrainProperties);
UFUNCTION(Category="MapGen|Soil Manager")
void ClearTerrainPropertiesMap();
virtual void Tick(float DeltaSeconds) override;
};

View File

@ -0,0 +1,8 @@
{
"isPersistentState": false,
"inTileCoordinates":
{
"x": -1,
"y": -1
}
}

View File

@ -4,23 +4,23 @@
"workingPath": "/Game/MyMaps",
"activeTabName": "",
"terrainGeneralSize": 4,
"terrainGeneralSlope": 0.5,
"terrainGeneralHeight": 3,
"terrainGeneralSlope": 1.3600000143051147,
"terrainGeneralHeight": 0.80000001192092896,
"terrainGeneralMinHeight": 0,
"terrainGeneralMaxHeight": 1,
"terrainGeneralInvert": 0,
"terrainOverallSeed": 700,
"terrainOverallScale": 26,
"terrainOverallSlope": 2.5,
"terrainOverallHeight": 1,
"terrainGeneralMaxHeight": 0.57999998331069946,
"terrainGeneralInvert": 0.44999998807907104,
"terrainOverallSeed": 8917.5087890625,
"terrainOverallScale": 58,
"terrainOverallSlope": 7.9000000953674316,
"terrainOverallHeight": 2.7000000476837158,
"terrainOverallMinHeight": 0,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0,
"terrainDetailedSeed": 4300,
"terrainDetailedScale": 10,
"terrainDetailedSlope": 1,
"terrainDetailedHeight": 2,
"terrainOverallInvert": 0.20000000298023224,
"terrainDetailedSeed": 1948.3016357421875,
"terrainDetailedScale": 21,
"terrainDetailedSlope": 0.30000001192092896,
"terrainDetailedHeight": 2.5,
"terrainDetailedMinHeight": 0,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0
"terrainDetailedInvert": 0.25
}

View File

@ -0,0 +1,30 @@
{
"presetName": "Canyons",
"widgetState":
{
"isPersistentState": false,
"mapName": "",
"workingPath": "",
"activeTabName": "",
"terrainGeneralSize": 0,
"terrainGeneralSlope": 1.3600000143051147,
"terrainGeneralHeight": 0.80000001192092896,
"terrainGeneralMinHeight": 0,
"terrainGeneralMaxHeight": 0.57999998331069946,
"terrainGeneralInvert": 0.44999998807907104,
"terrainOverallSeed": 2000,
"terrainOverallScale": 58,
"terrainOverallSlope": 7.9000000953674316,
"terrainOverallHeight": 2.7000000476837158,
"terrainOverallMinHeight": 0,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0.20000000298023224,
"terrainDetailedSeed": 6000,
"terrainDetailedScale": 21,
"terrainDetailedSlope": 0.30000001192092896,
"terrainDetailedHeight": 2.5,
"terrainDetailedMinHeight": 0,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0.25
}
}

View File

@ -0,0 +1,30 @@
{
"presetName": "Dunes",
"widgetState":
{
"isPersistentState": false,
"mapName": "",
"workingPath": "",
"activeTabName": "",
"terrainGeneralSize": 0,
"terrainGeneralSlope": 0.34000000357627869,
"terrainGeneralHeight": 2.0999999046325684,
"terrainGeneralMinHeight": 0,
"terrainGeneralMaxHeight": 1,
"terrainGeneralInvert": 0.40000000596046448,
"terrainOverallSeed": 2000,
"terrainOverallScale": 38,
"terrainOverallSlope": 10,
"terrainOverallHeight": 2.5,
"terrainOverallMinHeight": 0.75,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0.070000000298023224,
"terrainDetailedSeed": 6000,
"terrainDetailedScale": 89,
"terrainDetailedSlope": 1.7000000476837158,
"terrainDetailedHeight": 0.40000000596046448,
"terrainDetailedMinHeight": 0.014999999664723873,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0
}
}

View File

@ -0,0 +1,30 @@
{
"presetName": "Flatlands",
"widgetState":
{
"isPersistentState": false,
"mapName": "",
"workingPath": "",
"activeTabName": "",
"terrainGeneralSize": 0,
"terrainGeneralSlope": 0.54000002145767212,
"terrainGeneralHeight": 0.05000000074505806,
"terrainGeneralMinHeight": 0.20000000298023224,
"terrainGeneralMaxHeight": 1,
"terrainGeneralInvert": 0.55000001192092896,
"terrainOverallSeed": 2000,
"terrainOverallScale": 22,
"terrainOverallSlope": 10,
"terrainOverallHeight": 6.6999998092651367,
"terrainOverallMinHeight": 0.75,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0.6600000262260437,
"terrainDetailedSeed": 6000,
"terrainDetailedScale": 21,
"terrainDetailedSlope": 10,
"terrainDetailedHeight": 2.5999999046325684,
"terrainDetailedMinHeight": 0.014999999664723873,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0
}
}

View File

@ -0,0 +1,30 @@
{
"presetName": "Valley",
"widgetState":
{
"isPersistentState": false,
"mapName": "",
"workingPath": "",
"activeTabName": "",
"terrainGeneralSize": 0,
"terrainGeneralSlope": 0.54000002145767212,
"terrainGeneralHeight": 0.10000000149011612,
"terrainGeneralMinHeight": 0.20000000298023224,
"terrainGeneralMaxHeight": 1,
"terrainGeneralInvert": 0.55000001192092896,
"terrainOverallSeed": 900.00018310546875,
"terrainOverallScale": 22,
"terrainOverallSlope": 10,
"terrainOverallHeight": 6.6999998092651367,
"terrainOverallMinHeight": 1,
"terrainOverallMaxHeight": 1,
"terrainOverallInvert": 0.64999997615814209,
"terrainDetailedSeed": 89,
"terrainDetailedScale": 21,
"terrainDetailedSlope": 5.4000000953674316,
"terrainDetailedHeight": 2.5999999046325684,
"terrainDetailedMinHeight": 0.014999999664723873,
"terrainDetailedMaxHeight": 1,
"terrainDetailedInvert": 0.25
}
}

View File

@ -9,6 +9,7 @@
#include "ActorFactories/ActorFactory.h"
#include "AssetRegistryModule.h"
#include "Carla/MapGen/LargeMapManager.h"
#include "Carla/MapGen/SoilTypeManager.h"
#include "Carla/Weather/Weather.h"
#include "Components/SplineComponent.h"
#include "Editor/FoliageEdit/Public/FoliageEdMode.h"
@ -43,6 +44,11 @@
#define CUR_LINE (FString::FromInt(__LINE__))
#define CUR_CLASS_FUNC_LINE (CUR_CLASS_FUNC + "::" + CUR_LINE)
#define TAG_SPREADED FName("Spreaded Actor")
#define TAG_SPECIFIC_LOCATION FName("Specific Location Actor")
#define TILESIZE 1009
#undef CreateDirectory
#undef CopyFile
@ -90,6 +96,119 @@ void UMapGeneratorWidget::CookVegetation(const FMapGeneratorMetaInfo& MetaInfo)
}
}
void UMapGeneratorWidget::CookSoilTypeToMaps(const FMapGeneratorMetaInfo& MetaInfo)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Starting Cooking Soil Type to Tiles in %s %s"),
*CUR_CLASS_FUNC_LINE, *MetaInfo.DestinationPath, *MetaInfo.MapName);
// Check if map is valid
const FString MapCompletePath = MetaInfo.DestinationPath + "/" + MetaInfo.MapName;
const FString MapPackageFileName = FPackageName::LongPackageNameToFilename(
MapCompletePath,
FPackageName::GetMapPackageExtension());
if(!FPaths::FileExists(*MapPackageFileName))
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: Soil Terramechanics cannot be applied to a non existing map"),
*CUR_CLASS_FUNC_LINE);
return;
}
// Instantiate Weather Actor in main map
const FString WorldToLoadPath = MapCompletePath + "." + MetaInfo.MapName;
UWorld* World = LoadObject<UWorld>(nullptr, *WorldToLoadPath);
ASoilTypeManager* SoilTypeManagerActor = (ASoilTypeManager*) UGameplayStatics::GetActorOfClass(World, ASoilTypeManager::StaticClass());
SoilTypeManagerActor->ClearTerrainPropertiesMap();
// Set General Settings
SoilTypeManagerActor->SetGeneralTerrainProperties(MetaInfo.GeneralSoilType);
for(TPair<FRoiTile, FSoilTypeROI> SoilROIPair : MetaInfo.SoilTypeRoisMap)
{
FRoiTile SoilROITile = SoilROIPair.Key;
FSoilTypeROI SoilROI = SoilROIPair.Value;
SoilTypeManagerActor->AddTerrainPropertiesToTile(SoilROITile.X, SoilROITile.Y, SoilROI.SoilProperties);
}
}
void UMapGeneratorWidget::CookMiscSpreadedInformationToTiles(const FMapGeneratorMetaInfo& MetaInfo)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Starting Cooking Miscellaneous Info to Tiles in %s %s"),
*CUR_CLASS_FUNC_LINE, *MetaInfo.DestinationPath, *MetaInfo.MapName);
// Spreaded actors (ROIs)
bool bSpreadedSuccess = CookMiscSpreadedActors(MetaInfo);
if(!bSpreadedSuccess)
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: Miscellaneous Spreaded Actor cooking was not successful..."),
*CUR_CLASS_FUNC_LINE);
}
}
void UMapGeneratorWidget::CookMiscSpecificLocationInformationToTiles(const FMapGeneratorMetaInfo& MetaInfo)
{
UWorld* World = GEditor->GetEditorWorldContext().World();
// Only one ROI at a time supported
TArray<FMiscSpecificLocationActorsROI> ActorROIArray;
MetaInfo.MiscSpecificLocationActorsRoisMap.GenerateValueArray(ActorROIArray);
if(ActorROIArray.Num() > 0)
{
FMiscSpecificLocationActorsROI ActorROI = ActorROIArray[0];
FRotator Rotation(0, FMath::RandRange(ActorROI.MinRotationRange, ActorROI.MaxRotationRange), 0);
FActorSpawnParameters SpawnInfo;
AActor* SpawnedActor = World->SpawnActor<AActor>(
ActorROI.ActorClass,
ActorROI.ActorLocation,
Rotation,
SpawnInfo);
SpawnedActor->Tags.Add(TAG_SPECIFIC_LOCATION);
SaveWorld(World);
}
else
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: Miscellaneous Specific Location Actor cooking was not successful..."),
*CUR_CLASS_FUNC_LINE);
}
}
void UMapGeneratorWidget::DeleteAllSpreadedActors(const FMapGeneratorMetaInfo& MetaInfo)
{
TArray<FAssetData> AssetsData;
const FString TilesPath = MetaInfo.DestinationPath;
bool success = LoadWorlds(AssetsData, TilesPath);
for(FAssetData AssetData : AssetsData)
{
UWorld* World = GetWorldFromAssetData(AssetData);
// Check if it is not a tile
if(!World->GetMapName().Contains("_Tile_"))
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: %s Skipped as it is not a tile"),
*CUR_CLASS_FUNC_LINE, *World->GetMapName());
continue;
}
TArray<AActor*> TaggedActors;
UGameplayStatics::GetAllActorsWithTag(World, TAG_SPREADED, TaggedActors);
for(AActor* Actor : TaggedActors)
{
Actor->Destroy();
}
SaveWorld(World);
}
}
void UMapGeneratorWidget::CookVegetationToCurrentTile(const TArray<UProceduralFoliageSpawner*> FoliageSpawners)
{
UE_LOG(LogCarlaToolsMapGenerator, Log,
@ -283,7 +402,7 @@ bool UMapGeneratorWidget::GenerateWaterFromWorld(UWorld* RiversWorld, TSubclassO
SplinePosition.Z = GetLandscapeSurfaceHeight(RiversWorld, SplinePosition.X, SplinePosition.Y, false) + RiverSurfaceDisplacement;
RiverSpline->SetWorldLocationAtSplinePoint(i, SplinePosition);
}
UpdateRiverActorSplinesEvent(RiverActor);
}
return true;
}
@ -376,10 +495,56 @@ TMap<FRoiTile, FTerrainROI> UMapGeneratorWidget::CreateTerrainRoisMap(TArray<FTe
for(FTerrainROI TerrainRoi : TerrainRoisArray)
{
for(FRoiTile TerrainRoiTile : TerrainRoi.TilesList)
{
if(ResultMap.Contains(TerrainRoiTile))
{
ResultMap[TerrainRoiTile] = TerrainRoi;
}
else
{
ResultMap.Add(TerrainRoiTile, TerrainRoi);
}
}
}
return ResultMap;
}
TMap<FRoiTile, FMiscSpreadedActorsROI> UMapGeneratorWidget::CreateMiscSpreadedActorsRoisMap(TArray<FMiscSpreadedActorsROI> SpreadedActorsRoisArray)
{
TMap<FRoiTile, FMiscSpreadedActorsROI> ResultMap;
for(FMiscSpreadedActorsROI SpreadedRoi : SpreadedActorsRoisArray)
{
for(FRoiTile SpreadedRoiTile : SpreadedRoi.TilesList)
{
ResultMap.Add(SpreadedRoiTile, SpreadedRoi);
}
}
return ResultMap;
}
TMap<FRoiTile, FMiscSpecificLocationActorsROI> UMapGeneratorWidget::CreateMiscSpecificLocationActorsRoisMap(TArray<FMiscSpecificLocationActorsROI> SpecificLocationActorsRoisArray)
{
TMap<FRoiTile, FMiscSpecificLocationActorsROI> ResultMap;
for(FMiscSpecificLocationActorsROI SpecificLocationRoi : SpecificLocationActorsRoisArray)
{
for(FRoiTile SpecificLocationRoiTile : SpecificLocationRoi.TilesList)
{
ResultMap.Add(SpecificLocationRoiTile, SpecificLocationRoi);
}
}
return ResultMap;
}
TMap<FRoiTile, FSoilTypeROI> UMapGeneratorWidget::CreateSoilTypeRoisMap(TArray<FSoilTypeROI> SoilTypeRoisArray)
{
TMap<FRoiTile, FSoilTypeROI> ResultMap;
for(FSoilTypeROI SoilTypeRoi : SoilTypeRoisArray)
{
for(FRoiTile SoilTypeRoiTile : SoilTypeRoi.TilesList)
{
ResultMap.Add(SoilTypeRoiTile, SoilTypeRoi);
}
}
return ResultMap;
}
@ -449,6 +614,76 @@ FMapGeneratorWidgetState UMapGeneratorWidget::LoadWidgetStateStructFromFile(cons
return WidgetState;
}
bool UMapGeneratorWidget::GenerateMiscStateFileFromStruct(FMiscWidgetState MiscState, const FString JsonPath)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Creating Miscellaneous State JSON"),
*CUR_CLASS_FUNC_LINE);
TSharedRef<FJsonObject> OutJsonObject = MakeShareable(new FJsonObject());
FJsonObjectConverter::UStructToJsonObject(FMiscWidgetState::StaticStruct(), &MiscState, OutJsonObject, 0, 0);
FString OutputJsonString;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&OutputJsonString);
FJsonSerializer::Serialize(OutJsonObject, JsonWriter);
FFileHelper::SaveStringToFile(OutputJsonString, *JsonPath);
return true;
}
FMiscWidgetState UMapGeneratorWidget::LoadMiscStateStructFromFile(const FString JsonPath)
{
FMiscWidgetState MiscState;
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(), FMiscWidgetState::StaticStruct(), &MiscState, 1, 0);
}
return MiscState;
}
bool UMapGeneratorWidget::GenerateTerrainPresetFileFromStruct(FMapGeneratorPreset Preset, const FString JsonPath)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Creating Terrain Preset State JSON"),
*CUR_CLASS_FUNC_LINE);
TSharedRef<FJsonObject> OutJsonObject = MakeShareable(new FJsonObject());
FJsonObjectConverter::UStructToJsonObject(FMapGeneratorPreset::StaticStruct(), &Preset, OutJsonObject, 0, 0);
FString OutputJsonString;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&OutputJsonString);
FJsonSerializer::Serialize(OutJsonObject, JsonWriter);
FFileHelper::SaveStringToFile(OutputJsonString, *JsonPath);
return true;
}
FMapGeneratorPreset UMapGeneratorWidget::LoadTerrainPresetStructFromFile(const FString JsonPath)
{
FMapGeneratorPreset Preset;
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(), FMapGeneratorPreset::StaticStruct(), &Preset, 1, 0);
}
return Preset;
}
bool UMapGeneratorWidget::LoadWorlds(TArray<FAssetData>& WorldAssetsData, const FString& BaseMapPath, bool bRecursive)
{
UE_LOG(LogCarlaToolsMapGenerator, Log, TEXT("%s: Loading Worlds from %s"),
@ -543,8 +778,15 @@ bool UMapGeneratorWidget::CreateMainLargeMap(const FMapGeneratorMetaInfo& MetaIn
LargeMapManager->LargeMapTilePath = MetaInfo.DestinationPath;
LargeMapManager->LargeMapName = MetaInfo.MapName;
// Set Tile0Offset to 0 to cook tiles info
FVector OriginalTile0Offset = LargeMapManager->GetTile0Offset();
LargeMapManager->SetTile0Offset(FVector(0.0f, 0.0f, 0.0f));
LargeMapManager->GenerateMap_Editor();
// Reset Tile0Offset to original mid-tile position for runtime operations
LargeMapManager->SetTile0Offset(OriginalTile0Offset);
UPackage::SavePackage(BaseMapPackage, World, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
*PackageFileName, GError, nullptr, true, true, SAVE_NoError);
@ -674,36 +916,47 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
}
int FlateningMargin = 30; // Not flatened
//int FlateningMargin = 30; // Not flatened
int FlateningMargin = 0; // Not flatened
int FlateningFalloff = 100; // Transition from actual and flat value
int TileSize = 1009; // Should be calculated by sqrt(HeightData.Num())
int TileSize = TILESIZE; // Should be calculated by sqrt(HeightData.Num())
for(int X = FlateningMargin; X < (TileSize - FlateningMargin); X++)
bool IsThereTileUp, IsThereTileDown, IsThereTileLeft, IsThereTileRight;
/* Blending the height data of the ROI with the height data of the tile. */
if(FTerrainROI::IsTileInRoiBoundary(ThisTileIndex, MetaInfo.TerrainRoisMap, IsThereTileUp, IsThereTileRight, IsThereTileDown, IsThereTileLeft))
{
for(int Y = FlateningMargin; Y < (TileSize - FlateningMargin); Y++)
/* Blending the height data of the ROI with the height data of the tile. */
for(int X = FlateningMargin; X < (TileSize); X++)
{
for(int Y = FlateningMargin; Y < (TileSize); Y++)
{
float TransitionFactor = 1.0f;
if(X < (FlateningMargin + FlateningFalloff))
if(!IsThereTileLeft && X < (FlateningMargin + FlateningFalloff))
{
TransitionFactor *= (X - FlateningMargin) / (float) FlateningFalloff;
}
if(Y < (FlateningMargin + FlateningFalloff))
if(!IsThereTileUp && Y < (FlateningMargin + FlateningFalloff))
{
TransitionFactor *= (Y - FlateningMargin) / (float) FlateningFalloff;
}
if(X > (TileSize - FlateningMargin - FlateningFalloff))
if(!IsThereTileRight && X > (TileSize - FlateningMargin - FlateningFalloff))
{
TransitionFactor *= 1 - ((X - (TileSize - FlateningMargin - FlateningFalloff)) / (float) FlateningFalloff);
}
if(Y > (TileSize - FlateningMargin - FlateningFalloff))
if(!IsThereTileDown && 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);
}
}
}
else
{
HeightData = RoiHeightData;
}
}
// Flatening if contains river
@ -711,9 +964,9 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
// TODO: Check and fix flatening algorithm
if(TileMetaInfo.ContainsRiver)
{
int TileSize = TILESIZE; // Should be calculated by sqrt(HeightData.Num())
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++)
{
@ -750,6 +1003,32 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
SmoothHeightmap(HeightData, SmoothedData);
HeightData = SmoothedData;
// Sew Upper and Left boundaries
if(BoundariesInfo.Num() != 0) // To avoid crashing on the first Tile
{
TArray<uint16> SewedData;
SewUpperAndLeftTiles(HeightData, SewedData, ThisTileIndex.X, ThisTileIndex.Y);
HeightData = SewedData;
}
// Store Boundaries Info
FTileBoundariesInfo ThisTileBoundariesInfo;
TArray<uint16> RightHeightData;
TArray<uint16> BottomHeightData;
for(int DataIndex = 0; DataIndex < TILESIZE - 1; DataIndex++)
{
// Right
RightHeightData.Add(HeightData[ Convert2DTo1DCoord(0, DataIndex, TILESIZE) ]);
// Bottom
BottomHeightData.Add(HeightData[ Convert2DTo1DCoord(DataIndex, TILESIZE - 1, TILESIZE) ]);
}
ThisTileBoundariesInfo.RightHeightData = RightHeightData;
ThisTileBoundariesInfo.BottomHeightData = BottomHeightData;
BoundariesInfo.Add(ThisTileIndex, ThisTileBoundariesInfo);
FVector LandscapeScaleVector(100.0f, 100.0f, 100.0f);
Landscape->CreateLandscapeInfo();
Landscape->SetActorTransform(FTransform(FQuat::Identity, FVector(), LandscapeScaleVector));
@ -764,7 +1043,8 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
Landscape->ReregisterAllComponents();
Landscape->CreateLandscapeInfo();
Landscape->SetActorLabel("Landscape");
Landscape->SetActorLabel(TEXT("Landscape_"+FString::FromInt(i) + "_" + FString::FromInt(j)));
// Landscape->SetActorLabel("Landscape");
// Apply material
AssignLandscapeMaterial(Landscape);
@ -785,10 +1065,11 @@ bool UMapGeneratorWidget::CreateTilesMaps(const FMapGeneratorMetaInfo& MetaInfo)
*CUR_CLASS_FUNC_LINE, *ErrorUnloadingStr.ToString());
return false;
}
}
}
// TODO: Instantiate water if needed
}
}
// Clear Boundaries Data Array
BoundariesInfo.Empty();
CookTilesCollisions(MetaInfo);
@ -923,6 +1204,88 @@ bool UMapGeneratorWidget::CookVegetationToWorld(
return true;
}
bool UMapGeneratorWidget::CookMiscSpreadedActors(const FMapGeneratorMetaInfo& MetaInfo)
{
bool bSuccess = true;
TArray<FRoiTile> ListOfTiles;
MetaInfo.MiscSpreadedActorsRoisMap.GetKeys(ListOfTiles);
for(FRoiTile ThisTile : ListOfTiles)
{
// Check if map is valid
const FString MapCompletePath = MetaInfo.DestinationPath + "/" + MetaInfo.MapName +
"_Tile_" + FString::FromInt(ThisTile.X) + "_" + FString::FromInt(ThisTile.Y);
// Instantiate Weather Actor in main map
const FString WorldToLoadPath = MapCompletePath + "." + MetaInfo.MapName +
"_Tile_" + FString::FromInt(ThisTile.X) + "_" + FString::FromInt(ThisTile.Y);
// UWorld* World = LoadObject<UWorld>(nullptr, *WorldToLoadPath);
bool bLoadedSuccess = FEditorFileUtils::LoadMap(*WorldToLoadPath, false, true);
if(!bLoadedSuccess){
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: Error Loading %s"),
*CUR_CLASS_FUNC_LINE, *WorldToLoadPath);
return false;
}
UWorld* World = GEditor->GetEditorWorldContext().World();
if(!IsValid(World))
{
UE_LOG(LogCarlaToolsMapGenerator, Error, TEXT("%s: Error loading world %s"),
*CUR_CLASS_FUNC_LINE, *WorldToLoadPath);
return false;
}
FMiscSpreadedActorsROI ActorROI = MetaInfo.MiscSpreadedActorsRoisMap[ThisTile];
int NumOfTilesForSpreadedActorsGrid;
switch(ActorROI.ActorsDensity)
{
case ESpreadedActorsDensity::LOW:
NumOfTilesForSpreadedActorsGrid = 50;
break;
case ESpreadedActorsDensity::MEDIUM:
NumOfTilesForSpreadedActorsGrid = 100;
break;
case ESpreadedActorsDensity::HIGH:
NumOfTilesForSpreadedActorsGrid = 200;
break;
}
float TotalMapTileSize = 100800.0f; // In cm
float MaxTileDisplacement = 0.5f * TotalMapTileSize / NumOfTilesForSpreadedActorsGrid;
for(int i = 1; i < NumOfTilesForSpreadedActorsGrid - 1; i++)
{
for(int j = 1; j < NumOfTilesForSpreadedActorsGrid - 1; j++)
{
bool bIsGridTileEligible = FMath::RandRange(0.0f,100.0f) <= ActorROI.Probability;
float ActorXCoord = (i * TotalMapTileSize / NumOfTilesForSpreadedActorsGrid) + FMath::RandRange(-MaxTileDisplacement,MaxTileDisplacement);
float ActorYCoord = (j * TotalMapTileSize / NumOfTilesForSpreadedActorsGrid) + FMath::RandRange(-MaxTileDisplacement,MaxTileDisplacement);
if(bIsGridTileEligible)
{
float ActorZCoord = GetLandscapeSurfaceHeight(World, ActorXCoord, ActorYCoord, false);
FVector Location(ActorXCoord, ActorYCoord, ActorZCoord);
// TODO: Add rotation randomly?
FRotator Rotation(0, 0, 0);
FActorSpawnParameters SpawnInfo;
AActor* SpreadedActor = World->SpawnActor<AActor>(
ActorROI.ActorClass,
Location,
Rotation,
SpawnInfo);
SpreadedActor->Tags.Add(TAG_SPREADED);
}
}
}
// Save map
SaveWorld(World);
}
return bSuccess;
}
UWorld* UMapGeneratorWidget::GetWorldFromAssetData(FAssetData& WorldAssetData)
{
UWorld* World;
@ -947,7 +1310,50 @@ float UMapGeneratorWidget::GetLandscapeSurfaceHeight(UWorld* World, float x, flo
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 Height.IsSet() ? Height.GetValue() : GetLandscapeSurfaceHeightFromRayCast(World, x, y, bDrawDebugLines);
}
return 0.0f;
}
float UMapGeneratorWidget::GetLandscapeSurfaceHeightFromRayCast(UWorld* World, float x, float y, bool bDrawDebugLines)
{
if(World)
{
FVector RayStartingPoint(x, y, 9999999);
FVector RayEndPoint(x, y, -9999999);
// 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;
}
return 0.0f;
}
@ -985,11 +1391,11 @@ void UMapGeneratorWidget::SmoothHeightmap(TArray<uint16> HeightData, TArray<uint
// 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++)
/* Applying a smoothing kernel to the height data */
for(int X = 0; X < (TILESIZE); X++)
{
for(int Y = TileMargin; Y < (TileSize - TileMargin); Y++)
for(int Y = 0; Y < (TILESIZE); Y++)
{
int Value = 0;
@ -998,15 +1404,56 @@ void UMapGeneratorWidget::SmoothHeightmap(TArray<uint16> HeightData, TArray<uint
for(int j = -2; j <=2; j++)
{
float KernelValue = SmoothKernel[(i+2)*2 + (j+2)];
int HeightValue = HeightData[ (X+i) * TileSize + (Y+j) ];
int IndexX = X+i;
int IndexY = Y+j;
/* Checking if the index is out of bounds. If it is, it sets the index to the current X or Y. */
if(IndexX < 0 || IndexX >= TILESIZE)
IndexX = X;
if(IndexY < 0 || IndexY >= TILESIZE)
IndexY = Y;
int HeightValue = HeightData[ Convert2DTo1DCoord(IndexX, IndexY, TILESIZE) ];
Value += (int) ( KernelValue * HeightValue );
}
}
SmoothedData[(X * TileSize) + Y] = Value;
SmoothedData[ Convert2DTo1DCoord(X, Y, TILESIZE) ] = Value;
}
}
OutHeightData = SmoothedData;
}
void UMapGeneratorWidget::SewUpperAndLeftTiles(TArray<uint16> HeightData, TArray<uint16>& OutHeightData, int IndexX, int IndexY)
{
TArray<uint16> SewedData(HeightData);
FRoiTile ThisTile(IndexX, IndexY);
FRoiTile UpTile = ThisTile.Up();
FRoiTile LeftTile = ThisTile.Right(); // Right because of the coord system used
// Up
if(BoundariesInfo.Contains(UpTile))
{
for(int DataIndex = 0; DataIndex < TILESIZE - 1; DataIndex++)
{
TArray<uint16> BottomInfo = BoundariesInfo[UpTile].BottomHeightData;
SewedData[ Convert2DTo1DCoord(DataIndex, 0, TILESIZE) ] = BottomInfo[DataIndex];
}
}
// Left
if(BoundariesInfo.Contains(LeftTile))
{
for(int DataIndex = 0; DataIndex < TILESIZE - 1; DataIndex++)
{
TArray<uint16> RightInfo = BoundariesInfo[LeftTile].RightHeightData;
SewedData[ Convert2DTo1DCoord(TILESIZE - 1, DataIndex, TILESIZE) ] = RightInfo[DataIndex];
}
}
OutHeightData = SewedData;
}

View File

@ -290,3 +290,10 @@ float UProceduralWaterManager::GetLandscapeSurfaceHeight(float x, float y, bool
}
return 0.0f;
}
bool UProceduralWaterManager::CreateRiverPresetFiles(TSubclassOf<AActor> RiverParentClass)
{
// TODO
return true;
}

View File

@ -18,6 +18,8 @@
DECLARE_LOG_CATEGORY_EXTERN(LogCarlaToolsMapGenerator, Log, All);
struct FSoilTerramechanicsProperties;
/// Struct used as container of basic map information
USTRUCT(BlueprintType)
struct CARLATOOLS_API FMapGeneratorMetaInfo
@ -40,10 +42,10 @@ struct CARLATOOLS_API FMapGeneratorMetaInfo
TArray<UProceduralFoliageSpawner*> FoliageSpawners;
UPROPERTY(BlueprintReadWrite)
UTextureRenderTarget2D* GlobalHeightmap;
FSoilTerramechanicsProperties GeneralSoilType;
// UPROPERTY(BlueprintReadWrite)
// UTextureRenderTarget2D* PROVISIONALROIHEIGHTMAP;
UPROPERTY(BlueprintReadWrite)
UTextureRenderTarget2D* GlobalHeightmap;
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FTerrainROI> TerrainRoisMap;
@ -51,6 +53,15 @@ struct CARLATOOLS_API FMapGeneratorMetaInfo
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FVegetationROI> VegetationRoisMap;
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FSoilTypeROI> SoilTypeRoisMap;
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FMiscSpecificLocationActorsROI> MiscSpecificLocationActorsRoisMap;
UPROPERTY(BlueprintReadWrite)
TMap<FRoiTile, FMiscSpreadedActorsROI> MiscSpreadedActorsRoisMap;
UPROPERTY(BlueprintReadWrite)
float RiverChanceFactor;
@ -163,6 +174,30 @@ struct CARLATOOLS_API FMapGeneratorWidgetState
float TerrainDetailedInvert;
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FMapGeneratorPreset
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|TerrainPresets")
FString PresetName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|TerrainPresets")
FMapGeneratorWidgetState WidgetState;
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FTileBoundariesInfo
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
TArray<uint16> RightHeightData;
UPROPERTY()
TArray<uint16> BottomHeightData;
};
/// Class UMapGeneratorWidget extends the functionality of UEditorUtilityWidget
/// to be able to generate and manage maps and largemaps tiles for procedural
/// map generation
@ -186,6 +221,9 @@ public:
UFUNCTION(BlueprintImplementableEvent)
void UpdateTileRoiRT(const FMapGeneratorTileMetaInfo& TileMetaInfo, UMaterialInstanceDynamic* RoiMeterialInstance);
UFUNCTION(BlueprintImplementableEvent)
void UpdateRiverActorSplinesEvent(AActor* RiverActor);
/// Function called by Widget Blueprint which generates all tiles of map
/// @a mapName, and saves them in @a destinationPath
/// Returns a void string is success and an error message if the process failed
@ -194,9 +232,21 @@ public:
/// Function called by Widget Blueprint used to start the whole vegetation
/// process for map defined in @a MetaInfo
UFUNCTION(Category="Map Generator",BlueprintCallable)
UFUNCTION(Category="Map Generator|Vegetation",BlueprintCallable)
void CookVegetation(const FMapGeneratorMetaInfo& MetaInfo);
UFUNCTION(Category="Map Generator|Soil Terramechanics", BlueprintCallable)
void CookSoilTypeToMaps(const FMapGeneratorMetaInfo& MetaInfo);
UFUNCTION(Category="Map Generator|Miscellaneous", BlueprintCallable)
void CookMiscSpreadedInformationToTiles(const FMapGeneratorMetaInfo& MetaInfo);
UFUNCTION(Category="Map Generator|Miscellaneous", BlueprintCallable)
void CookMiscSpecificLocationInformationToTiles(const FMapGeneratorMetaInfo& MetaInfo);
UFUNCTION(Category="Map Generator|Miscellaneous", BlueprintCallable)
void DeleteAllSpreadedActors(const FMapGeneratorMetaInfo& MetaInfo);
/// Function invoked by the widget that cooks the vegetation defined in
/// @a FoliageSpawners only in the world opened in the editor
UFUNCTION(Category="Map Generator", BlueprintCallable)
@ -223,10 +273,10 @@ public:
bool LoadMapInfoFromPath(FString InDirectory, int& OutMapSize, FString& OutFoundMapName);
/// Spawns rivers of types @a RiverClass
UFUNCTION(Category="MapGenerator", BlueprintCallable)
UFUNCTION(Category="MapGenerator|Water", BlueprintCallable)
AActor* GenerateWater(TSubclassOf<class AActor> RiverClass);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
UFUNCTION(Category="MapGenerator|Water", BlueprintCallable)
bool GenerateWaterFromWorld(UWorld* RiversWorld, TSubclassOf<class AActor> RiverClass);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
@ -235,17 +285,26 @@ public:
/// Adds weather actor of type @a WeatherActorClass and sets the @a SelectedWeather
/// 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)
UFUNCTION(Category="MapGenerator|Weather", BlueprintCallable)
AActor* AddWeatherToExistingMap(TSubclassOf<class AActor> WeatherActorClass,
const FMapGeneratorMetaInfo& MetaInfo, const FString SelectedWeather);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
UFUNCTION(Category="MapGenerator|ROIs", BlueprintCallable)
TMap<FRoiTile, FVegetationROI> CreateVegetationRoisMap(TArray<FVegetationROI> VegetationRoisArray);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
UFUNCTION(Category="MapGenerator|ROIs", BlueprintCallable)
TMap<FRoiTile, FTerrainROI> CreateTerrainRoisMap(TArray<FTerrainROI> TerrainRoisArray);
UFUNCTION(Category="MapGenerator", BlueprintCallable)
UFUNCTION(Category="MapGenerator|ROIs", BlueprintCallable)
TMap<FRoiTile, FMiscSpreadedActorsROI> CreateMiscSpreadedActorsRoisMap(TArray<FMiscSpreadedActorsROI> SpreadedActorsRoisArray);
UFUNCTION(Category="MapGenerator|ROIs", BlueprintCallable)
TMap<FRoiTile, FMiscSpecificLocationActorsROI> CreateMiscSpecificLocationActorsRoisMap(TArray<FMiscSpecificLocationActorsROI> SpecificLocationActorsRoisArray);
UFUNCTION(Category="MapGenerator|ROIs", BlueprintCallable)
TMap<FRoiTile, FSoilTypeROI> CreateSoilTypeRoisMap(TArray<FSoilTypeROI> SoilTypeRoisArray);
UFUNCTION(Category="MapGenerator|Vegetation", BlueprintCallable)
bool DeleteAllVegetationInMap(const FString Path, const FString MapName);
UFUNCTION(Category="MapGenerator|JsonLibrary", BlueprintCallable)
@ -254,7 +313,22 @@ public:
UFUNCTION(Category="MapGenerator|JsonLibrary", BlueprintCallable)
FMapGeneratorWidgetState LoadWidgetStateStructFromFile(const FString JsonPath);
UFUNCTION(Category="MapGenerator|JsonLibrary|Misc", BlueprintCallable)
bool GenerateMiscStateFileFromStruct(FMiscWidgetState MiscState, const FString JsonPath);
UFUNCTION(Category="MapGenerator|JsonLibrary|Misc", BlueprintCallable)
FMiscWidgetState LoadMiscStateStructFromFile(const FString JsonPath);
UFUNCTION(Category="MapGenerator|TerrainPresets", BlueprintCallable)
bool GenerateTerrainPresetFileFromStruct(FMapGeneratorPreset Preset, const FString JsonPath);
UFUNCTION(Category="MapGenerator|TerrainPresets", BlueprintCallable)
FMapGeneratorPreset LoadTerrainPresetStructFromFile(const FString JsonPath);
private:
UPROPERTY()
TMap<FRoiTile, FTileBoundariesInfo> BoundariesInfo;
/// Loads a bunch of world objects located in @a BaseMapPath and
/// returns them in @a WorldAssetsData.
/// The function returns true if success, otherwise false
@ -299,6 +373,9 @@ private:
UFUNCTION()
bool CookVegetationToWorld(UWorld* World, const TArray<UProceduralFoliageSpawner*> FoliageSpawners);
UFUNCTION()
bool CookMiscSpreadedActors(const FMapGeneratorMetaInfo& MetaInfo);
/// Returns the world object in @a WorldAssetData
UFUNCTION()
UWorld* GetWorldFromAssetData(FAssetData& WorldAssetData);
@ -308,9 +385,22 @@ private:
UFUNCTION()
float GetLandscapeSurfaceHeight(UWorld* World, float x, float y, bool bDrawDebugLines);
UFUNCTION()
float GetLandscapeSurfaceHeightFromRayCast(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);
UFUNCTION()
void SewUpperAndLeftTiles(TArray<uint16> HeightData, TArray<uint16>& OutHeightData, int IndexX, int IndexY);
// Converting a 2D coordinate to a 1D coordinate.
UFUNCTION()
FORCEINLINE int Convert2DTo1DCoord(int IndexX, int IndexY, int TileSize)
{
return (IndexX * TileSize) + IndexY;
}
};

View File

@ -116,4 +116,10 @@ private:
/// Return the Z value
UFUNCTION()
float GetLandscapeSurfaceHeight(float x, float y, bool bDrawDebugLines = false);
/************ RIVER PRESETS GENERATOR ************/
public:
UFUNCTION(BlueprintCallable)
bool CreateRiverPresetFiles(TSubclassOf<AActor> RiverParentClass);
};

View File

@ -4,9 +4,11 @@
#include "CoreMinimal.h"
#include "Carla/MapGen/SoilTypeManager.h"
#include "Containers/Array.h"
#include "Containers/EnumAsByte.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Math/IntPoint.h"
#include "ProceduralFoliageSpawner.h"
#include "Templates/UnrealTypeTraits.h"
#include "UObject/NoExportTypes.h"
@ -17,10 +19,21 @@
UENUM(BlueprintType)
enum ERegionOfInterestType
{
NONE,
NONE_REGION,
TERRAIN_REGION,
WATERBODIES_REGION, // Not Supported yet
VEGETATION_REGION
VEGETATION_REGION,
MISC_SPREADED_ACTORS_REGION,
MISC_SPECIFIC_LOCATION_ACTORS_REGION,
SOIL_TYPE_REGION
};
UENUM(BlueprintType)
enum ESpreadedActorsDensity
{
LOW,
MEDIUM,
HIGH
};
USTRUCT(BlueprintType)
@ -56,8 +69,38 @@ public:
{
return (this->X == Other.X) && (this->Y == Other.Y);
}
/// A function that returns the tile that is above the current tile.
FORCEINLINE FRoiTile Up()
{
// return FRoiTile(X, Y-1);
return FRoiTile(X-1, Y);
}
/// A function that returns the tile that is below the current tile.
FORCEINLINE FRoiTile Down()
{
// return FRoiTile(X, Y+1);
return FRoiTile(X+1, Y);
}
/// A function that returns the tile that is to the left of the current tile.
FORCEINLINE FRoiTile Left()
{
// return FRoiTile(X-1, Y);
return FRoiTile(X, Y+1);
}
/// A function that returns the tile that is to the right of the current tile.
FORCEINLINE FRoiTile Right()
{
// return FRoiTile(X+1, Y);
return FRoiTile(X, Y-1);
}
};
/// A function that is used to hash the FRoiTile struct.
/// It is used to hash the struct so that it can be used as a key in a TMap.
FORCEINLINE uint32 GetTypeHash(const FRoiTile& Thing)
{
uint32 Hash = FCrc::MemCrc32(&Thing, sizeof(FRoiTile));
@ -76,7 +119,7 @@ struct CARLATOOLS_API FRegionOfInterest
TArray<FRoiTile> TilesList;
UPROPERTY(BlueprintReadWrite)
TEnumAsByte<ERegionOfInterestType> RegionType = ERegionOfInterestType::NONE;
TEnumAsByte<ERegionOfInterestType> RegionType = ERegionOfInterestType::NONE_REGION;
FRegionOfInterest()
{
@ -94,6 +137,7 @@ struct CARLATOOLS_API FRegionOfInterest
return this->RegionType;
}
// A template function that checks if a tile is in a map of regions.
template <typename R>
static FORCEINLINE bool IsTileInRegionsSet(FRoiTile RoiTile, TMap<FRoiTile, R> RoisMap)
{
@ -102,6 +146,42 @@ struct CARLATOOLS_API FRegionOfInterest
return RoisMap.Contains(RoiTile);
}
/// Checking if two regions of interest are equal.
bool Equals(const FRegionOfInterest& Other)
{
// Checking if the number of tiles in the two regions is the same.
if(this->TilesList.Num() != Other.TilesList.Num())
{
return false;
}
// Checking if the two regions have the same tiles.
TMap<FRoiTile, int> TileCount;
for(FRoiTile Tile : Other.TilesList)
{
if(TileCount.Contains(Tile))
TileCount[Tile]++;
else
TileCount.Add(Tile, 1);
}
for(FRoiTile Tile : TilesList)
{
if(!TileCount.Contains(Tile))
return false;
TileCount[Tile]--;
if(TileCount[Tile] == 0)
TileCount.Remove(Tile);
}
if(TileCount.Num() == 0)
return true;
else
return false;
}
};
USTRUCT(BlueprintType)
@ -122,6 +202,7 @@ struct CARLATOOLS_API FVegetationROI : public FRegionOfInterest
FoliageSpawners.Add(Spawner);
}
// A function that adds a list of spawners to the list of spawners of the ROI.
void AddFoliageSpawners(TArray<UProceduralFoliageSpawner*> Spawners)
{
for(UProceduralFoliageSpawner* Spawner : Spawners)
@ -141,14 +222,103 @@ struct CARLATOOLS_API FTerrainROI : public FRegionOfInterest
{
GENERATED_BODY()
// A pointer to a material instance that is used to change the heightmap material of the ROI.
UPROPERTY(BlueprintReadWrite)
UMaterialInstanceDynamic* RoiMaterialInstance;
// A render target that is used to store the heightmap of the ROI.
UPROPERTY(BlueprintReadWrite)
UTextureRenderTarget2D* RoiHeightmapRenderTarget;
FTerrainROI() : FRegionOfInterest(), RoiMaterialInstance()
{}
// TODO: IsEdge() funtion to avoid transition between tiles that belongs to the same ROI
/**
* This function checks if a tile is on the boundary of a region of interest
*
* @param RoiTile The tile we're checking
* @param RoisMap The map of RoiTiles to Rois.
* @param OutUp Is there a tile above this one?
* @param OutRight Is there a ROI to the right of this tile?
* @param OutDown Is there a ROI tile below this one?
* @param OutLeft Is the tile to the left of the current tile in the RoiMap?
*
* return true if the tile is in a boundary
*/
template <typename R>
static bool IsTileInRoiBoundary(FRoiTile RoiTile, TMap<FRoiTile, R> RoisMap, bool& OutUp, bool& OutRight, bool& OutDown, bool& OutLeft)
{
FTerrainROI ThisRoi = RoisMap[RoiTile];
OutUp = RoisMap.Contains(RoiTile.Up()) && ThisRoi.Equals(RoisMap[RoiTile.Up()]);
OutDown = RoisMap.Contains(RoiTile.Down()) && ThisRoi.Equals(RoisMap[RoiTile.Down()]);
OutLeft = RoisMap.Contains(RoiTile.Left()) && ThisRoi.Equals(RoisMap[RoiTile.Left()]);
OutRight = RoisMap.Contains(RoiTile.Right()) && ThisRoi.Equals(RoisMap[RoiTile.Right()]);
return !OutUp || !OutDown || !OutLeft || !OutRight;
}
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FMiscSpreadedActorsROI : public FRegionOfInterest
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
TSubclassOf<AActor> ActorClass;
UPROPERTY(BlueprintReadWrite)
float Probability;
UPROPERTY(BlueprintReadWrite)
TEnumAsByte<ESpreadedActorsDensity> ActorsDensity;
FMiscSpreadedActorsROI() : FRegionOfInterest(), ActorClass(), Probability(0.0f), ActorsDensity(ESpreadedActorsDensity::LOW)
{}
};
/// A struct that is used to store the information of a region of interest that is used to
/// spawn actors in specific locations.
USTRUCT(BlueprintType)
struct CARLATOOLS_API FMiscSpecificLocationActorsROI : public FRegionOfInterest
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
TSubclassOf<AActor> ActorClass;
UPROPERTY(BlueprintReadWrite)
FVector ActorLocation;
UPROPERTY(BlueprintReadWrite)
float MinRotationRange;
UPROPERTY(BlueprintReadWrite)
float MaxRotationRange;
FMiscSpecificLocationActorsROI() : FRegionOfInterest(), ActorClass(), ActorLocation(0.0f)
{}
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FSoilTypeROI : public FRegionOfInterest
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
FSoilTerramechanicsProperties SoilProperties;
FSoilTypeROI() : SoilProperties()
{}
};
USTRUCT(BlueprintType)
struct CARLATOOLS_API FMiscWidgetState
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary|Misc")
bool IsPersistentState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MapGenerator|JsonLibrary|Misc")
FIntPoint InTileCoordinates;
};