Hello community,
I’ve try to generate a terrain with DS algorithm, but the resul is… is really bad, it work fine with small sizes, mx 20x20 points, any higher value brake it into square with cones.
Please help to solve I’ve already spen a full day for it with no result ( I finish it yesterday at 11 am and fixing this untill know ).
Here is the code which I use:
TerrainGen.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "TerrainGen.generated.h"
UCLASS()
class HILL_LAND_GEN_V1_API ATerrainGen : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATerrainGen();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
virtual void OnConstruction(const FTransform& Transform) override;
UPROPERTY(EditAnywhere)
uint16 Size = 2;
UPROPERTY(EditAnywhere)
float roughness = 0.0f;
UPROPERTY(EditAnywhere)
TArray<float> map;
UPROPERTY(EditAnywhere)
uint16 max_offset; // offset max
UPROPERTY(EditAnywhere)
UMaterialInterface* Material;
private:
UProceduralMeshComponent* mesh;
void divide(uint16 _size_);
float get(uint16 x, uint16 y);
void set(uint16 x, uint16 y, float val);
float average( TArray<float> value );
void square(uint16 x, uint16 y, uint16 half, float rand_offset);
void diamond(uint16 x, uint16 y, uint16 half, float rand_offset);
};
TerrainGen.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "hill_land_gen_v1.h"
#include "TerrainGen.h"
// Sets default values
ATerrainGen::ATerrainGen()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
RootComponent = SphereComponent;
mesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("TerrainMesh"));
}
// Called when the game starts or when spawned
void ATerrainGen::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ATerrainGen::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
}
void ATerrainGen::OnConstruction(const FTransform& Transform)
{
if (Size > 1) {
max_offset = Size - 1;
map.SetNumZeroed(Size*Size);
set(0, 0, max_offset);
set(max_offset, 0, max_offset / 2);
set(max_offset, max_offset, max_offset / 2);
set(0, max_offset, 0);
divide(max_offset);
TArray<FVector> vertices;
uint16 offset = 50;
for (uint16 x = 0; x < Size; x++) {
for (uint16 y = 0; y < Size; y++) {
vertices.Add(FVector(x, y, get(x, y)) * offset);
}
}
TArray<int32> Triangles;
uint16 step = 1;
for (uint16 i = 0; i + Size * step < map.Num(); i += step) {
if (i / Size == (i + step) / Size && (i / Size) % step == 0) {
Triangles.Add(i);
Triangles.Add(i + step);
Triangles.Add(i + Size * step);
Triangles.Add(i + step);
Triangles.Add(i + Size * step + step);
Triangles.Add(i + Size * step);
}
}
//mesh->CreateMeshSection(1, vertices, Triangles, normals, UV0, vertexColors, tangents, false);
// With default options
mesh->CreateMeshSection(1, vertices, Triangles, TArray<FVector>(), TArray<FVector2D>(), TArray<FColor>(), TArray<FProcMeshTangent>(), true);
mesh->SetMaterial(1, Material);
mesh->AttachTo(RootComponent);
}
}
void ATerrainGen::divide(uint16 _size_)
{
uint16 x, y, half = _size_ / 2;
float scale = roughness * _size_;
if (half < 1.0f) return;
for (y = half; y < max_offset; y += _size_) {
for (x = half; x < max_offset; x += _size_) {
square(y,x, half, FMath::RandRange(0.0f, 1.0f) * scale * 2 - scale);
}
}
for (y = 0; y <= max_offset; y += half) {
for (x = (y + half) % _size_; x <= max_offset; x += _size_) {
diamond(x, y, half, FMath::RandRange(0.0f, 1.0f) * scale * 2 - scale);
}
}
divide(half);
}
float ATerrainGen::get(uint16 x, uint16 y)
{
if (x < 0 || x > max_offset || y < 0 || y > max_offset) return -1.0f;
return map[x + Size * y];
}
void ATerrainGen::set(uint16 x, uint16 y, float val)
{
checkf(x > -1 && x < Size && y > -1 && y < Size, TEXT("ATerrainGen::set(): x|y out of bounds."));
map[x + Size * y] = val;
}
float ATerrainGen::average(TArray<float> value)
{
float total = 0;
//value = value.FilterByPredicate(](const float& val) {
// return val != -1.0f;
//});
TArray<float> valid;
for (float& val : value) {
if (val != -1.0f)
valid.Add(val);
}
for (float& val : valid) {
total += val;
}
return total / valid.Num();
}
void ATerrainGen::square(uint16 x, uint16 y, uint16 half, float rand_offset)
{
float ave = average(TArray<float>({
get(x - half, y - half), // upper left
get(x + half, y - half), // upper right
get(x + half, y + half), // lower right
get(x - half, y + half) // lower left
}));
set(x, y, ave + rand_offset);
}
void ATerrainGen::diamond(uint16 x, uint16 y, uint16 half, float rand_offset)
{
float ave = average(TArray<float>({
get(x, y - half), // top
get(x + half, y), // right
get(x, y + half), // bottom
get(x - half, y) // left
}));
set(x, y, ave + rand_offset);
}
Hope on your help. Thanks.
P.S. - I am not attaching the result because it really bad, so I am not see reason for it.