Skip to content

Commit 1e77ad5

Browse files
committed
Fix the silly mistakes & add smoothing
1 parent 9dfac4b commit 1e77ad5

File tree

3 files changed

+150
-121
lines changed

3 files changed

+150
-121
lines changed

demo/terrain_gen_script.gd

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
extends TerrainGen
22

3+
enum NoiseType {
4+
TYPE_VALUE = 5,
5+
TYPE_VALUE_CUBIC = 4,
6+
TYPE_PERLIN = 3,
7+
TYPE_CELLULAR = 2,
8+
TYPE_SIMPLEX = 0,
9+
TYPE_SIMPLEX_SMOOTH = 1,
10+
};
11+
312

413
func _ready():
514
var grid_map = $"../GridMap"
615
var seed_value = int(Time.get_unix_time_from_system()) % 1000000;
716
grid_map.cell_size = Vector3(1, 0.25, 1);
8-
generate(grid_map, 100, 100, 3, seed_value, false, 2, 0.03);
17+
generate(grid_map, 100, 100, 3, seed_value, NoiseType.TYPE_SIMPLEX, 2, 0.79, 0.03);

src/TerrainGen/Terrain_Gen.cpp

Lines changed: 138 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,20 @@ TerrainGen::TerrainGen() {
99
TerrainGen::~TerrainGen() {
1010
}
1111

12-
void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth, int seed, bool useFractal, int noiseOctaves, float noiseFreq) {
12+
void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth, int seed, int noiseType, int noiseOctaves, float jitter, float noiseFreq) {
1313
UtilityFunctions::print("Begin Terrain Generation!");
1414

1515
/*****************************************************
1616
1717
Setup the Noise Function
1818
1919
*****************************************************/
20-
21-
noise->set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
22-
noise->set_fractal_type(useFractal ? FastNoiseLite::FractalType::FRACTAL_FBM : FastNoiseLite::FractalType::FRACTAL_NONE);
20+
noise->set_noise_type(static_cast<FastNoiseLite::NoiseType>(noiseType));
21+
noise->set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
2322
noise->set_seed(seed);
2423
noise->set_fractal_octaves(noiseOctaves);
24+
noise->set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
25+
noise->set_cellular_jitter(jitter);
2526
noise->set_fractal_lacunarity(2.0);
2627
noise->set_fractal_gain(0.5);
2728
noise->set_frequency(noiseFreq);
@@ -33,33 +34,64 @@ void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth,
3334
*****************************************************/
3435

3536
vector<vector<vector<int>>> gridMap(
36-
depth, vector<vector<int>>(height, vector<int>(width, 0)));
37+
depth, vector<vector<int>>(width, vector<int>(height, 0)));
38+
39+
// Noise Map may be used later, not sure yet
40+
// vector<vector<float>> noiseMap(width, vector<float>(height, 0.0f));
3741

38-
vector<vector<float>> heightMap(height, vector<float>(width, 0.0f));
42+
vector<vector<int>> elevationMap(width, vector<int>(height, 0));
43+
vector<vector<int>> elevationMapSmooth(width, vector<int>(height, 0));
3944

4045
/*****************************************************
4146
42-
Height Map Generation
47+
Noise Map Generation & Smoothing
4348
4449
*****************************************************/
4550

46-
for (int y = 0; y < height; y++) {
47-
for (int x = 0; x < width; x++) {
48-
heightMap[y][x] = noise->get_noise_2d((float)x, (float)y) / 2.0f * 0.5f;
51+
for (int x = 0; x < width; x++) {
52+
for (int y = 0; y < height; y++) {
53+
float currentNoise = noise->get_noise_2d((float)x, (float)y);
54+
// noiseMap[x][y] = currentNoise;
55+
elevationMap[x][y] = round(((currentNoise + 1.0f) / 2.0f) * depth);
4956
}
5057
}
5158

59+
// Apply a Median Filter
60+
for (int x = 0; x < width; x++) {
61+
for (int y = 0; y < height; y++) {
62+
vector<int> neighbors;
63+
64+
for (int dx = -1; dx <= 1; dx++) {
65+
for (int dy = -1; dy <= 1; dy++) {
66+
int nx = x + dx;
67+
int ny = y + dy;
68+
69+
// Ensure indices are valid before adding to the list
70+
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
71+
neighbors.push_back(elevationMap[nx][ny]);
72+
}
73+
}
74+
}
75+
76+
if (!neighbors.empty()) {
77+
sort(neighbors.begin(), neighbors.end());
78+
elevationMapSmooth[x][y] = max(0, static_cast<int>(neighbors[neighbors.size() / 2]));
79+
}
80+
}
81+
}
82+
83+
// Apply the smoothed elevation map
84+
elevationMap = elevationMapSmooth;
85+
5286
/*****************************************************
5387
5488
Tile Placement
5589
5690
*****************************************************/
5791

58-
for (int y = 0; y < height; ++y) {
59-
for (int x = 0; x < width; ++x) {
60-
float noiseValue = heightMap[x][y];
61-
int elevation = round(((noiseValue + 1.0f) / 2.0f) * depth); // Normalize to depth
62-
92+
for (int x = 0; x < width; ++x) {
93+
for (int y = 0; y < height; ++y) {
94+
int elevation = elevationMap[x][y];
6395
/*****************************************************
6496
6597
Edge & Water Setter
@@ -73,134 +105,121 @@ void TerrainGen::generate(GridMap *myGridMap, int height, int width, int depth,
73105
isEdge = true;
74106
}
75107
// Water assignment
76-
if (elevation == 1 && !isEdge) {
108+
if (elevation == 0 && !isEdge) {
77109
myGridMap->set_cell_item(Vector3i(x, elevation, y), WATER, 0);
78110
continue;
79-
}
111+
} else {
112+
/*****************************************************
80113
81-
/*****************************************************
114+
Elevation Change & Rotation Detection
82115
83-
Elevation Change & Rotation Detection
116+
*****************************************************/
84117

85-
*****************************************************/
118+
int neighborElevations[4] = { elevation, elevation, elevation, elevation };
119+
int dx[4] = { 1, -1, 0, 0 };
120+
int dy[4] = { 0, 0, 1, -1 };
86121

87-
int neighborElevations[4] = { elevation, elevation, elevation, elevation };
88-
int dx[4] = { 1, -1, 0, 0 };
89-
int dy[4] = { 0, 0, 1, -1 };
122+
for (int d = 0; d < 4; ++d) {
123+
int nx = x + dx[d];
124+
int ny = y + dy[d];
90125

91-
for (int d = 0; d < 4; ++d) {
92-
int nx = x + dx[d];
93-
int ny = y + dy[d];
94-
95-
if (nx >= 0 && ny >= 0 && nx < width && ny < height) {
96-
neighborElevations[d] = heightMap[ny][nx];
126+
if (nx >= 0 && ny >= 0 && nx < width && ny < height) {
127+
neighborElevations[d] = elevationMap[nx][ny];
128+
}
97129
}
98-
}
99130

100-
// Elevation analysis
101-
TileType tileType = GROUND;
102-
int rotationOrientation = 0;
103-
bool needsRamp = false, needsCliff = false, needsCorner = false;
104-
int numDirectionChanges = 0;
105-
int dominantDirection = -1;
106-
int maxElevationChange = 0;
107-
108-
for (int d = 0; d < 4; ++d) {
109-
int diff = neighborElevations[d] - elevation;
110-
if (diff != 0)
111-
numDirectionChanges++;
112-
if (diff == 1 || diff == -1)
113-
needsRamp = true;
114-
if (abs(diff) > 1)
115-
needsCliff = true;
116-
if (abs(diff) > maxElevationChange) {
117-
maxElevationChange = abs(diff);
118-
dominantDirection = d;
131+
// Elevation analysis
132+
TileType tileType = GROUND;
133+
int rotationOrientation = 0;
134+
bool needsRamp = false, needsCliff = false, needsCorner = false;
135+
int numDirectionChanges = 0;
136+
int dominantDirection = -1;
137+
int maxElevationChange = 0;
138+
139+
for (int d = 0; d < 4; ++d) {
140+
int diff = neighborElevations[d] - elevation;
141+
if (diff != 0)
142+
numDirectionChanges++;
143+
if (diff == 1 || diff == -1)
144+
needsRamp = true;
145+
if (abs(diff) > 1)
146+
needsCliff = true;
147+
if (abs(diff) > maxElevationChange) {
148+
maxElevationChange = abs(diff);
149+
dominantDirection = d;
150+
}
119151
}
120-
}
121-
122-
// Assign terrain type
123-
if (numDirectionChanges == 0) {
124-
tileType = GROUND;
125-
} else if (needsCliff) {
126-
tileType = CLIFF;
127-
} else if (needsRamp) {
128-
tileType = RAMP;
129-
}
130152

131-
// Corner detection: Two direction elevation change
132-
if (numDirectionChanges >= 2) {
133-
needsCorner = true;
134-
if (tileType == RAMP)
135-
tileType = RAMP_CORNER;
136-
if (tileType == CLIFF)
137-
tileType = CLIFF_CORNER;
138-
}
153+
// Assign terrain type
154+
if (numDirectionChanges == 0) {
155+
tileType = GROUND;
156+
} else if (needsCliff) {
157+
tileType = CLIFF;
158+
} else if (needsRamp) {
159+
tileType = RAMP;
160+
}
139161

140-
// Water Edge & Corner detection
141-
if (tileType == WATER) {
142-
if (numDirectionChanges == 1) {
143-
tileType = WATER_EDGE;
144-
} else if (numDirectionChanges >= 2) {
145-
tileType = WATER_CORNER;
162+
// Corner detection: Two direction elevation change
163+
if (numDirectionChanges >= 2) {
164+
needsCorner = true;
165+
if (tileType == RAMP)
166+
tileType = RAMP_CORNER;
167+
if (tileType == CLIFF)
168+
tileType = CLIFF_CORNER;
146169
}
147-
}
148170

149-
/*
150-
The key rotations (Orientation) in Godot's Grid Map around the Y-axis are:
151-
- 0 → No rotation
152-
- 1 → 90° clockwise around Y
153-
- 2 → 180° around Y
154-
- 3 → 90° counterclockwise around Y
155-
- 10 → 180° around Y (alternative basis)
156-
- 11 → 90° counterclockwise around Y (alternative basis)
157-
- 9 → 90° clockwise around Y (alternative basis)
158-
159-
Which can be set with : `myGridMap->set_cell_item(Vector3i(x, y, z), meshID, orientation);`
160-
*/
161-
162-
// Assign rotation using **0, 90, 180, 270** only
163-
if (tileType == RAMP || tileType == CLIFF || tileType == RAMP_CORNER || tileType == CLIFF_CORNER) {
164-
if (!needsCorner) {
165-
switch (dominantDirection) {
166-
case 0:
167-
rotationOrientation = 0; // No Rotation
168-
break; // North
169-
case 1:
170-
rotationOrientation = 1; // 90 Degrees Clockwise
171-
break; // East
172-
case 2:
173-
rotationOrientation = 2; // 180 Degrees Clockwise
174-
break; // South
175-
case 3:
176-
rotationOrientation = 3; // 270 Degrees Clockwise
177-
break; // West
171+
// Water Edge & Corner detection
172+
if (tileType == WATER) {
173+
if (numDirectionChanges == 1) {
174+
tileType = WATER_EDGE;
175+
} else if (numDirectionChanges >= 2) {
176+
tileType = WATER_CORNER;
178177
}
179-
} else {
180-
// Corner cases use the same **0, 90, 180, 270** logic.
181-
if (neighborElevations[0] != elevation && neighborElevations[2] != elevation) {
182-
rotationOrientation = 1; // Vertical corner (North-South elevation change)
183-
} else if (neighborElevations[1] != elevation && neighborElevations[3] != elevation) {
184-
rotationOrientation = 2; // Horizontal corner (East-West elevation change)
185-
} else if (neighborElevations[0] != elevation && neighborElevations[1] != elevation) {
186-
rotationOrientation = 0; // Corner facing North-East
187-
} else if (neighborElevations[2] != elevation && neighborElevations[3] != elevation) {
188-
rotationOrientation = 3; // Corner facing South-West
178+
}
179+
180+
// Assign rotation using **0, 90, 180, 270** only
181+
if (tileType == RAMP || tileType == CLIFF || tileType == RAMP_CORNER || tileType == CLIFF_CORNER) {
182+
if (!needsCorner) {
183+
switch (dominantDirection) {
184+
case 0:
185+
rotationOrientation = 0; // No Rotation
186+
break; // North
187+
case 1:
188+
rotationOrientation = 1; // 90 Degrees Clockwise
189+
break; // East
190+
case 2:
191+
rotationOrientation = 2; // 180 Degrees Clockwise
192+
break; // South
193+
case 3:
194+
rotationOrientation = 3; // 270 Degrees Clockwise
195+
break; // West
196+
}
197+
} else {
198+
// Corner cases use the same **0, 90, 180, 270** logic.
199+
if (neighborElevations[0] != elevation && neighborElevations[2] != elevation) {
200+
rotationOrientation = 1; // Vertical corner (North-South elevation change)
201+
} else if (neighborElevations[1] != elevation && neighborElevations[3] != elevation) {
202+
rotationOrientation = 2; // Horizontal corner (East-West elevation change)
203+
} else if (neighborElevations[0] != elevation && neighborElevations[1] != elevation) {
204+
rotationOrientation = 0; // Corner facing North-East
205+
} else if (neighborElevations[2] != elevation && neighborElevations[3] != elevation) {
206+
rotationOrientation = 3; // Corner facing South-West
207+
}
189208
}
190209
}
191-
}
192210

193-
/*****************************************************
211+
/*****************************************************
194212
195-
Grid Cell Setter
213+
Grid Cell Setter
196214
197-
*****************************************************/
215+
*****************************************************/
198216

199-
myGridMap->set_cell_item(Vector3i(x, elevation, y), tileType, rotationOrientation);
217+
myGridMap->set_cell_item(Vector3i(x, elevation, y), tileType, rotationOrientation);
218+
}
200219
}
201220
}
202221
}
203222

204223
void TerrainGen::_bind_methods() {
205-
ClassDB::bind_method(D_METHOD("generate", "GridMap", "height", "width", "depth", "seed", "useFBM", "noiseOctaves", "noiseFreq"), &TerrainGen::generate);
224+
ClassDB::bind_method(D_METHOD("generate", "GridMap", "height", "width", "depth", "seed", "noiseType", "noiseOctaves", "jitter", "noiseFreq"), &TerrainGen::generate);
206225
}

src/TerrainGen/Terrain_Gen.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <godot_cpp/classes/node.hpp>
77
#include <godot_cpp/variant/utility_functions.hpp>
88

9+
#include <algorithm>
910
#include <vector>
1011

1112
using namespace godot;
@@ -36,7 +37,7 @@ class TerrainGen : public Node {
3637

3738
//Generate Terrain
3839
//Takes in a height & width for size of map on the X & Z axis
39-
void generate(GridMap *myGridMap, int height, int width, int depth, int seed, bool useFractal = false, int noiseOctaves = 2, float noiseFreq = 0.005);
40+
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);
4041
};
4142

4243
#endif

0 commit comments

Comments
 (0)