Hello! I am trying to create a UTextureRenderTarget2D that will display what the in-game world would look like. This is to speed up iterations and make it easier to see and make changes to the procedural world generation. The problem I am encountering is that over about 400px for the width or height, the operation takes approximately 5 minutes to complete. I am using
WorldCanvas->K2_DrawPolygon(nullptr, FVector2D(DrawX, DrawY), FVector2D(1), 4, Paint);
to draw to the target, since I only need one pixel for each world tile. If comment out the portion that draws to the render target, the operation can complete in less than a second. I don’t know where or how to begin, so here is the code that generates the map and draws to the target. Any suggestions are appreciated. Thanks!
EDIT: Forgot to mention that I am using version 4.27 because it fit the project better
void ALandscapeHelper::DrawWorld(int32 RenderLayer)
{
// Create our noises
CreateWrappers(true);
// Make sure our render target reflects the world size
PrimeDrawingBoardForDisplay();
// Start drawing to our target
UCanvas* WorldCanvas;
FVector2D CanvasSize;
FDrawToRenderTargetContext CanvasContext;
UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(GWorld, DrawingBoard, WorldCanvas, CanvasSize, CanvasContext);
UE_LOG(LogTemp, Warning, TEXT("Canvas Draw Size %s, World size is %s"), *CanvasSize.ToString(), *WorldXY.ToString());
WorldCanvas->bNoSmooth = true;
// Our variables for looping
// If our world ref is centered center ours as well
int32 FirstX = bIsWorldCentered ? (WorldXY.X / 2) * -1 : 1;
int32 LastX = bIsWorldCentered ? WorldXY.X - (WorldXY.X / 2) : WorldXY.X;
int32 FirstY = bIsWorldCentered ? (WorldXY.Y / 2) * -1 : 1;
int32 LastY = bIsWorldCentered ? WorldXY.Y - (WorldXY.Y / 2) : WorldXY.Y;
// For shifting our hex tiles
bool bShifted = false;
// Positions to draw on our target
// For it to mirror the world generator
// we need to start x at the begining and
// y at the end
int32 DrawX = 1;
int32 DrawY = WorldXY.Y;
// If the render layer is greater than the wrappers length
// set it to the last wrapper
if (RenderLayer > Wrappers.Num() - 1)
{
RenderLayer = Wrappers.Num() - 1;
}
// select the start and end of the loop
// NoiseCount will either be the length of the array or the render layer
// Start will either be 0 or render layer
int32 NoiseCount = RenderLayer <= -1 ? Wrappers.Num() : RenderLayer;
int32 Start = RenderLayer <= -1 ? 0 : RenderLayer;
// Get the settings of the noise
TArray<FName> NoiseKeys;
NoiseInfo.GetKeys(NoiseKeys);
// This is the loop to create the map
// A full column is created for every row
for (int32 xi = FirstX; xi < LastX; xi++) // X loop for world generation. For every X make the whole row of Y
{
for (int32 yi = FirstY; yi < LastY; yi++) // Y loop
{
// Height of the tile
float TileZScale = 0.f;
// This will be used for determining the color to draw
int32 LandType = 0;
FVector2D TileLocation;
// Get the location of the tiles
TileLocation.X = (TileSpacing * yi) + (yi * 2 * MeshWidth) + (bShifted ? MeshWidth * -1 : 0);
TileLocation.Y = (HexOffset * xi * 1.5f) + (xi * TileSpacing); // this math is for the hexagon
float WholeNoiseVal = 0.f;
float RawHeight; // Unclamped height
float NoiseVal;
// Loop through the noises
for (int32 i = Start; i < NoiseCount; i++)
{
UFastNoiseWrapper* CurrentNoise = Wrappers[i];
FNoiseSettings CurrentSetting = *NoiseInfo.Find(NoiseKeys[i]);
// Choose the gather mode for this map
// Afterwards it is just getting the noise and respecting the polish mode
switch (HeightMode)
{
case Group:
NoiseVal = CurrentNoise->GetNoise2D(TileLocation.X * CurrentSetting.NoiseScale, TileLocation.Y * CurrentSetting.NoiseScale);
switch (CurrentSetting.PolishMode)
{
case Original:
// Keep noise untouched
break;
case Truncate:
NoiseVal = (float)FMath::TruncToInt(NoiseVal);
break;
case Round:
NoiseVal = FMath::RoundHalfToEven(NoiseVal);
break;
}
WholeNoiseVal += NoiseVal;
break;
case Individual:
NoiseVal = CurrentNoise->GetNoise2D(TileLocation.X * CurrentSetting.NoiseScale, TileLocation.Y * CurrentSetting.NoiseScale);
switch (CurrentSetting.PolishMode)
{
case Original:
// Keep noise untouched
break;
case Truncate:
NoiseVal = (float)FMath::TruncToInt(NoiseVal);
break;
case Round:
NoiseVal = FMath::RoundHalfToEven(NoiseVal);
break;
}
RawHeight = (((NoiseVal * CurrentSetting.HeightStrength)) / CurrentSetting.Damping);
if (CurrentSetting.bIsSubtractive)
{
WholeNoiseVal -= FMath::Clamp(RawHeight, 0.f, RawHeight + 1.f);
//TileZScale -= FMath::Clamp(RawHeight, 1.f, 1500.f);
}
else
{
WholeNoiseVal += RawHeight;
//TileZScale += FMath::Clamp(RawHeight, 1.f, 1500.f);
}
break;
default:
NoiseVal = CurrentNoise->GetNoise2D(TileLocation.X * CurrentSetting.NoiseScale, TileLocation.Y * CurrentSetting.NoiseScale);
switch (CurrentSetting.PolishMode)
{
case Original:
// Keep noise untouched
break;
case Truncate:
NoiseVal = (float)FMath::TruncToInt(NoiseVal);
break;
case Round:
NoiseVal = FMath::RoundHalfToEven(NoiseVal);
break;
}
WholeNoiseVal += NoiseVal;
break;
}
}
if (HeightMode == Group)
{
// Group the noises and use global settings
RawHeight = (((WholeNoiseVal * WorldGlobalHeightStrength)) / WorldGlobalHeightDamping);
TileZScale = FMath::Clamp(RawHeight, 1.f, 1500.f);
}
else
{
TileZScale = FMath::Clamp(WholeNoiseVal, 1.f, 1500.f);
}
// Set water level to 1 not matter what
TileZScale = TileZScale <= Water ? 1.f : TileZScale;
FLinearColor Paint;
if (bShowRawValue)
{
// Black and white color
Paint = FMath::Lerp<FLinearColor>(FLinearColor::Black, FLinearColor::White, TileZScale / 750);
WorldCanvas->K2_DrawBox(FVector2D(xi, yi), CanvasSize, 1.f, Paint);
}
else
{
// Set draw color
if (TileZScale <= Water)
{
Paint = WaterColor;
}
else
{
if (TileZScale >= Snow) // Tile is same or higher than snow
{
Paint = SnowColor;
}
else if (TileZScale >= Rock) // Tile is same or higher than rock
{
Paint = RockColor;
}
else if (TileZScale >= Grass) // Tile is same or higher than grass
{
Paint = GrassColor;
}
else if (TileZScale < Grass) // Tile is lower than grass, but not water
{
Paint = SandColor;
}
}
//WorldCanvas->K2_DrawLine(WorldXY, WorldXY + FIntPoint(0, 1), 1.f, Paint);
//WorldCanvas->DrawTile(nullptr, WorldXY.X, WorldXY.Y, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, EBlendMode::BLEND_Opaque);
WorldCanvas->K2_DrawPolygon(nullptr, FVector2D(DrawX, DrawY), FVector2D(1), 4, Paint);
//UE_LOG(LogTemp, Warning, TEXT("World X %d | World Y %d"), xi, yi);
}
DrawY--;
//CurrentInstance++;
}
DrawY = WorldXY.Y;
DrawX++;// --;
bShifted = !bShifted;
}
UE_LOG(LogTemp, Warning, TEXT("Finished drawing"));
UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(this, CanvasContext);
UE_LOG(LogTemp, Warning, TEXT("Finilized drawing"));
}