Skip to content

Commit 3644dc1

Browse files
committed
Rework Algorithm
Now we get proper elevation by depth, getting closer to a valid procedural operation. But the tiles still fail to connect properly. Water tiles are also done differently so it places water edges wrong. And most annoyingly, the algorithm wrongly picks cliffs always and Ramps are non-existent.
1 parent 8cb20c9 commit 3644dc1

File tree

4 files changed

+154
-123
lines changed

4 files changed

+154
-123
lines changed

demo/Test.tscn

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,3 @@ metadata/_editor_floor_ = Vector3(0, 0, 0)
2828
[node name="Camera3D" type="Camera3D" parent="."]
2929
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 22.3218, 36.7455)
3030
script = ExtResource("3_2au82")
31-
32-
[node name="TextureRect" type="TextureRect" parent="."]
33-
offset_right = 40.0
34-
offset_bottom = 40.0

demo/terrain_gen_script.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func _ready():
1616
grid_map.cell_size = Vector3(1, 0.25, 1);
1717

1818
# Value Noise Based
19-
#generate(grid_map, 128, 128, 4, seed_value, NoiseType.VALUE, 2, 0.79, 0.1);
19+
#generate(grid_map, 128, 128, 4, seed_value, NoiseType.VALUE, 0.79, 0.1);
2020

2121
# Simplex Noise Based
22-
generate(grid_map, 128, 128, 4, seed_value, NoiseType.SIMPLEX, 2, 0.79, 0.12);
22+
generate(grid_map, 128, 128, 4, seed_value, NoiseType.SIMPLEX, 0.3, 0.02);

src/TerrainGen/Terrain_Gen.cpp

Lines changed: 151 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ TerrainGen::TerrainGen() {
99
TerrainGen::~TerrainGen() {
1010
}
1111

12-
void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth, int seed, int noiseType, int noiseOctaves, float jitter, float noiseFreq) {
12+
void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth, int seed, int noiseType, double waterRemoval, float noiseFreq) {
1313
/*****************************************************
1414
1515
Setup the Noise Function
@@ -18,11 +18,6 @@ void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth,
1818
noise->set_noise_type(static_cast<FastNoiseLite::NoiseType>(noiseType));
1919
noise->set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
2020
noise->set_seed(seed);
21-
noise->set_fractal_octaves(noiseOctaves);
22-
noise->set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
23-
noise->set_cellular_jitter(jitter);
24-
noise->set_fractal_lacunarity(2.0);
25-
noise->set_fractal_gain(0.5);
2621
noise->set_frequency(noiseFreq);
2722

2823
/*****************************************************
@@ -46,7 +41,7 @@ void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth,
4641

4742
/*****************************************************
4843
49-
Noise Map Generation & Smoothing
44+
Noise Elevation Generation & Modification
5045
5146
*****************************************************/
5247

@@ -55,181 +50,221 @@ void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth,
5550
for (int y = 0; y < height; y++) {
5651
float currentNoise = noise->get_noise_2d((float)x, (float)y);
5752

58-
float bias = 0.1, stepSize = 4;
59-
int rawNoise = round(pow(((currentNoise + 1.0f) / 2.0f) + bias, 1.5) * depth);
60-
elevationMap[x][y] = round(rawNoise / stepSize) * stepSize;
53+
// Normalize and scale noise to [0, depth]
54+
float normalizedNoise = (currentNoise + 1.0f) / 2.0f;
55+
elevationMap[x][y] = round(normalizedNoise * depth);
6156
}
6257
}
6358

64-
// Downsample by sampling one pixel per block.
59+
// Reduce Resolution by sampling one pixel per block
6560
for (int x = 0; x < reducedX; x++) {
6661
for (int y = 0; y < reducedY; y++) {
6762
int sampleX = x * blockSize;
6863
int sampleY = y * blockSize;
6964

70-
lowResMap[y][x] = (elevationMap[x][y] + 1.0f) / 2.0f;
65+
lowResMap[x][y] = elevationMap[sampleX][sampleY];
7166
}
7267
}
7368

74-
//Posterize
69+
// Posterize
7570
for (int x = 0; x < reducedX; x++) {
7671
for (int y = 0; y < reducedY; y++) {
77-
int quantizedLevel = min(depth - 1, static_cast<int>(lowResMap[x][y] * depth));
78-
lowResMap[x][x] = quantizedLevel / static_cast<float>(depth - 1);
72+
int quantizedLevel = round(lowResMap[x][y]);
73+
lowResMap[x][y] = max(0, min(depth, quantizedLevel));
7974
}
8075
}
8176

77+
// Upscale back to Original Size
8278
for (int x = 0; x < width; x++) {
8379
for (int y = 0; y < height; y++) {
8480
int srcX = x / blockSize;
8581
int srcY = y / blockSize;
86-
elevationMap[x][y] = lowResMap[srcX][srcY];
82+
83+
int elevationValue = lowResMap[srcX][srcY];
84+
85+
float randomFactor = (float)rand() / RAND_MAX;
86+
if (elevationValue == 0 && randomFactor < waterRemoval) { // Chance to turn Water to Ground
87+
elevationMap[x][y] = 1;
88+
} else {
89+
elevationMap[x][y] = elevationValue;
90+
}
8791
}
8892
}
93+
/*****************************************************
94+
95+
Tile Placement
96+
97+
*****************************************************/
98+
99+
bool forceCliff = false; // Track cliff sections
89100

90101
for (int x = 0; x < width; ++x) {
91102
for (int y = 0; y < height; ++y) {
92103
int elevation = elevationMap[x][y];
93-
/*****************************************************
94104

95-
Edge & Water Setter
96-
Encountering the edge of the Map, set the Tile to ground
105+
TileType tileType = GROUND;
97106

98-
*****************************************************/
107+
bool needsRamp = false, needsCliff = false, adjacentToWater = false;
99108

100-
bool isEdge = false;
109+
int rotationOrientation = 0;
110+
int numElevationChanges = 0;
111+
int numDirectionChanges = 0;
112+
int maxElevationChange = 0;
113+
int waterEdgeNeighbors = 0;
114+
int waterNeighbors[4] = { 0, 0, 0, 0 };
101115

102-
if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
103-
isEdge = true;
104-
}
105-
// Water assignment
106-
if (elevation == 0 && !isEdge) {
116+
if (elevation == 0) {
107117
myGridMap->set_cell_item(Vector3i(x, elevation, y), WATER, 0);
108118
continue;
109-
} else {
110-
/*****************************************************
111-
112-
Elevation Change & Rotation Detection
119+
}
113120

114-
*****************************************************/
121+
int neighborElevations[4] = { elevation, elevation, elevation, elevation };
115122

116-
int neighborElevations[4] = { elevation, elevation, elevation, elevation };
123+
for (int d = 0; d < 4; ++d) {
124+
int nx = x + dx[d];
125+
int ny = y + dy[d];
117126

118-
for (int d = 0; d < 4; ++d) {
119-
int nx = x + dx[d];
120-
int ny = y + dy[d];
127+
if (nx >= 0 && ny >= 0 && nx < width && ny < height) {
128+
neighborElevations[d] = elevationMap[nx][ny];
129+
}
121130

122-
if (nx >= 0 && ny >= 0 && nx < width && ny < height) {
123-
neighborElevations[d] = elevationMap[nx][ny];
131+
if (nx >= 0 && ny >= 0 && nx < width && ny < height) {
132+
neighborElevations[d] = elevationMap[nx][ny];
133+
if (neighborElevations[d] == 0) {
134+
adjacentToWater = true;
135+
waterNeighbors[d] = 1;
124136
}
125137
}
138+
}
126139

127-
TileType tileType = GROUND;
128-
int rotationOrientation = 0;
129-
bool needsRamp = false, needsCliff = false, needsCorner = false;
130-
int numDirectionChanges = 0;
131-
int dominantDirection = -1;
132-
int maxElevationChange = 0;
140+
for (int d = 0; d < 4; ++d) {
141+
int diff = neighborElevations[d] - elevation;
133142

134-
for (int d = 0; d < 4; ++d) {
135-
int diff = neighborElevations[d] - elevation;
136-
if (diff != 0)
137-
numDirectionChanges++;
138-
if (diff == 1 || diff == -1)
139-
needsRamp = true;
140-
if (abs(diff) > 1)
141-
needsCliff = true;
142-
if (abs(diff) > maxElevationChange) {
143-
maxElevationChange = abs(diff);
144-
dominantDirection = d;
143+
if (diff != 0)
144+
numDirectionChanges++;
145+
146+
if (diff == 1 || diff == -1) {
147+
numElevationChanges++;
148+
maxElevationChange = max(maxElevationChange, abs(diff));
149+
150+
float randomFactor = (float)rand() / RAND_MAX;
151+
152+
if (forceCliff || randomFactor < 0.2) {
153+
tileType = CLIFF;
154+
forceCliff = true;
155+
} else {
156+
tileType = RAMP;
157+
forceCliff = false;
145158
}
146159
}
160+
}
147161

148-
if (numDirectionChanges == 0) {
149-
tileType = GROUND;
150-
} else if (needsCliff) {
151-
tileType = CLIFF;
152-
} else if (needsRamp) {
153-
tileType = RAMP;
162+
if (adjacentToWater) {
163+
waterEdgeNeighbors = waterNeighbors[0] + waterNeighbors[1] + waterNeighbors[2] + waterNeighbors[3];
164+
165+
if (waterEdgeNeighbors == 1) {
166+
tileType = WATER_EDGE;
167+
} else if (
168+
(waterNeighbors[0] && waterNeighbors[1]) ||
169+
(waterNeighbors[1] && waterNeighbors[2]) ||
170+
(waterNeighbors[2] && waterNeighbors[3]) ||
171+
(waterNeighbors[3] && waterNeighbors[0])) {
172+
tileType = WATER_CORNER; // Two adjacent water edges form a corner
154173
}
174+
}
155175

156-
if (numDirectionChanges >= 2) {
157-
needsCorner = true;
158-
if (tileType == RAMP)
159-
tileType = RAMP_CORNER;
160-
if (tileType == CLIFF)
161-
tileType = CLIFF_CORNER;
162-
}
176+
if (tileType == CLIFF && forceCliff) {
177+
forceCliff = true;
178+
} else if (tileType == RAMP) {
179+
forceCliff = false;
180+
}
163181

164-
if (tileType == WATER) {
165-
if (numDirectionChanges == 1) {
166-
tileType = WATER_EDGE;
167-
} else if (numDirectionChanges >= 2) {
168-
tileType = WATER_CORNER;
169-
}
182+
if (numElevationChanges >= 2) {
183+
if (tileType == RAMP)
184+
tileType = RAMP_CORNER;
185+
if (tileType == CLIFF)
186+
tileType = CLIFF_CORNER;
187+
}
188+
189+
if (tileType == WATER) {
190+
if (numDirectionChanges == 1) {
191+
tileType = WATER_EDGE;
192+
} else if (numDirectionChanges >= 2) {
193+
tileType = WATER_CORNER;
170194
}
195+
}
171196

172-
if (tileType == RAMP || tileType == CLIFF || tileType == RAMP_CORNER || tileType == CLIFF_CORNER) {
173-
int highestNeighborIndex = -1;
174-
int highestNeighborElevation = elevation;
175-
int secondaryNeighborIndex = -1;
197+
if (tileType == RAMP || tileType == CLIFF || tileType == RAMP_CORNER || tileType == CLIFF_CORNER) {
198+
int highestNeighborIndex = -1;
199+
int secondaryNeighborIndex = -1;
200+
int highestNeighborElevation = elevation;
201+
int secondaryNeighborElevation = elevation;
176202

177-
for (int d = 0; d < 4; ++d) {
178-
if (neighborElevations[d] > highestNeighborElevation) {
203+
for (int d = 0; d < 4; ++d) {
204+
int diff = neighborElevations[d] - elevation;
205+
if (diff > 0) {
206+
if (diff > highestNeighborElevation - elevation) {
179207
secondaryNeighborIndex = highestNeighborIndex;
208+
secondaryNeighborElevation = highestNeighborElevation;
180209
highestNeighborElevation = neighborElevations[d];
181210
highestNeighborIndex = d;
211+
} else if (diff > secondaryNeighborElevation - elevation) {
212+
secondaryNeighborElevation = neighborElevations[d];
213+
secondaryNeighborIndex = d;
182214
}
183215
}
216+
}
217+
218+
// Determine if we have a corner case (two adjacent neighbors at higher elevation)
219+
bool isCorner = (secondaryNeighborIndex != -1 && abs(neighborElevations[secondaryNeighborIndex] - elevation) > 0);
184220

185-
bool isCorner = (secondaryNeighborIndex != -1 && abs(neighborElevations[secondaryNeighborIndex] - elevation) > 0);
186-
187-
if (highestNeighborIndex != -1) {
188-
if (!isCorner) {
189-
switch (highestNeighborIndex) {
190-
case 0:
191-
rotationOrientation = 2;
192-
break; // North
193-
case 1:
194-
rotationOrientation = 3;
195-
break; // East
196-
case 2:
197-
rotationOrientation = 0;
198-
break; // South
199-
case 3:
200-
rotationOrientation = 1;
201-
break; // West
202-
}
203-
} else {
204-
if ((highestNeighborIndex == 0 && secondaryNeighborIndex == 1) ||
205-
(highestNeighborIndex == 1 && secondaryNeighborIndex == 0)) {
221+
// Apply rotation based on elevation direction
222+
if (highestNeighborIndex != -1) {
223+
if (!isCorner) {
224+
switch (highestNeighborIndex) {
225+
case 0:
206226
rotationOrientation = 0;
207-
} else if ((highestNeighborIndex == 2 && secondaryNeighborIndex == 3) ||
208-
(highestNeighborIndex == 3 && secondaryNeighborIndex == 2)) {
209-
rotationOrientation = 3;
210-
} else if ((highestNeighborIndex == 0 && secondaryNeighborIndex == 3) ||
211-
(highestNeighborIndex == 3 && secondaryNeighborIndex == 0)) {
212-
rotationOrientation = 1;
213-
} else if ((highestNeighborIndex == 1 && secondaryNeighborIndex == 2) ||
214-
(highestNeighborIndex == 2 && secondaryNeighborIndex == 1)) {
215-
rotationOrientation = 2;
216-
}
227+
break; // North
228+
case 1:
229+
rotationOrientation = 10;
230+
break; // East
231+
case 2:
232+
rotationOrientation = 16;
233+
break; // South
234+
case 3:
235+
rotationOrientation = 22;
236+
break; // West
237+
}
238+
} else {
239+
// Handle corner rotation logic
240+
if ((highestNeighborIndex == 0 && secondaryNeighborIndex == 1) ||
241+
(highestNeighborIndex == 1 && secondaryNeighborIndex == 0)) {
242+
rotationOrientation = 0; // Top-right corner
243+
} else if ((highestNeighborIndex == 2 && secondaryNeighborIndex == 3) ||
244+
(highestNeighborIndex == 3 && secondaryNeighborIndex == 2)) {
245+
rotationOrientation = 10; // Bottom-left corner
246+
} else if ((highestNeighborIndex == 0 && secondaryNeighborIndex == 3) ||
247+
(highestNeighborIndex == 3 && secondaryNeighborIndex == 0)) {
248+
rotationOrientation = 16; // Top-left corner
249+
} else if ((highestNeighborIndex == 1 && secondaryNeighborIndex == 2) ||
250+
(highestNeighborIndex == 2 && secondaryNeighborIndex == 1)) {
251+
rotationOrientation = 22; // Bottom-right corner
217252
}
218253
}
219254
}
255+
}
220256

221-
/*****************************************************
257+
/*****************************************************
222258
223-
Grid Map Cell Setter
259+
Grid Map Cell Setter
224260
225-
*****************************************************/
261+
*****************************************************/
226262

227-
myGridMap->set_cell_item(Vector3i(x, elevation, y), tileType, rotationOrientation);
228-
}
263+
myGridMap->set_cell_item(Vector3i(x, elevation, y), tileType, rotationOrientation);
229264
}
230265
}
231266
}
232267

233268
void TerrainGen::_bind_methods() {
234-
ClassDB::bind_method(D_METHOD("generate", "GridMap", "height", "width", "depth", "seed", "noiseType", "noiseOctaves", "jitter", "noiseFreq"), &TerrainGen::generate);
269+
ClassDB::bind_method(D_METHOD("generate", "GridMap", "height", "width", "depth", "seed", "noiseType", "waterRemoval", "noiseFreq"), &TerrainGen::generate);
235270
}

src/TerrainGen/Terrain_Gen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class TerrainGen : public Node {
4242

4343
//Generate Terrain
4444
//Takes in a height & width for size of map on the X & Z axis
45-
void generate(GridMap *myGridMap, int height, int width, int depth, int seed, int noiseType, int noiseOctaves = 2, float jitter = 0.0, float noiseFreq = 0.005);
45+
void generate(GridMap *myGridMap, int height, int width, int depth, int seed, int noiseType, double waterRemoval, float noiseFreq = 0.005);
4646
};
4747

4848
#endif

0 commit comments

Comments
 (0)