载入中...
搜索中...
未找到
BuiltinAlgorithms.cpp
浏览该文件的文档.
2#include "procgen/Semantic.h"
7
8#include <algorithm>
9#include <cmath>
10#include <cstdint>
11#include <random>
12#include <string>
13#include <vector>
14
15namespace eve::procgen {
16namespace {
17
18bool genDungeonBsp(const Params &params, Grid2D &out, std::string &error) {
19 const int w = params.getWidth();
20 const int h = params.getHeight();
21 if (w < 8 || h < 8) {
22 error = "dungeon.bsp: size must be at least 8x8";
23 return false;
24 }
25
26 const int divisionMin = params.getInt("divisionMin", 3);
27 const int divisionRandMax = params.getInt("divisionRandMax", 4);
28 const int roomMinX = params.getInt("roomMinX", params.getInt("roomMin", 5));
29 const int roomRandMaxX = params.getInt("roomRandMaxX", params.getInt("roomRand", 2));
30 const int roomMinY = params.getInt("roomMinY", params.getInt("roomMin", 5));
31 const int roomRandMaxY = params.getInt("roomRandMaxY", params.getInt("roomRand", 2));
32
33 auto matrix = dtlutil::makeMatrix(w, h, 0);
34 // room=1, road=2; wall remains 0
35 dtl::shape::SimpleRogueLike<std::uint_fast8_t> shape(
36 1, 2, size_t(std::max(1, divisionMin)), size_t(std::max(0, divisionRandMax)),
37 size_t(std::max(1, roomMinX)), size_t(std::max(0, roomRandMaxX)),
38 size_t(std::max(1, roomMinY)), size_t(std::max(0, roomRandMaxY)));
39
40 if (!shape.drawSEED(matrix, params.getSeed())) {
41 error = "dungeon.bsp: DTL SimpleRogueLike draw failed";
42 return false;
43 }
44
45 dtlutil::copyMatrixToGrid(matrix, out, [](std::uint_fast8_t v) -> uint32_t {
46 if (v == 1) return Semantic::Floor;
47 if (v == 2) return Semantic::Corridor;
48 return Semantic::Wall;
49 });
50 out.setMeta("algorithm", "dungeon.bsp");
52 return true;
53}
54
55bool genCaveCellular(const Params &params, Grid2D &out, std::string &error) {
56 const int w = params.getWidth();
57 const int h = params.getHeight();
58 if (w < 4 || h < 4) {
59 error = "cave.cellular: size must be at least 4x4";
60 return false;
61 }
62 const int loops = params.getInt("loops", 5);
63 const double fill = double(params.getFloat("fill", 0.45f));
64
65 auto matrix = dtlutil::makeMatrix(w, h, 0);
66 dtl::random::DefaultRandom rng(params.getSeed());
67 // DTL construction consumes one sample; reseed to preserve the legacy stream.
68 rng.seed(params.getSeed());
69 rng.clear();
70 for (int y = 0; y < h; ++y)
71 for (int x = 0; x < w; ++x) matrix[size_t(y)][size_t(x)] = rng.probability(fill) ? 1 : 0;
72 for (int x = 0; x < w; ++x) {
73 matrix.front()[size_t(x)] = 0;
74 matrix.back()[size_t(x)] = 0;
75 }
76 for (int y = 0; y < h; ++y) {
77 matrix[size_t(y)].front() = 0;
78 matrix[size_t(y)].back() = 0;
79 }
80 for (int pass = 0; pass < std::max(1, loops); ++pass) {
81 for (int y = 1; y < h - 1; ++y) {
82 for (int x = 1; x < w - 1; ++x) {
83 const auto left = matrix[size_t(y)][size_t(x - 1)];
84 const auto right = matrix[size_t(y)][size_t(x + 1)];
85 const auto up = matrix[size_t(y - 1)][size_t(x)];
86 const auto down = matrix[size_t(y + 1)][size_t(x)];
87 if (left == right && right == up && up == down) {
88 matrix[size_t(y)][size_t(x)] = left;
89 } else {
90 const std::uint_fast8_t neighbors[] = {left, right, up, down};
91 matrix[size_t(y)][size_t(x)] = neighbors[rng.getBit2()];
92 }
93 }
94 }
95 }
96
97 dtlutil::copyMatrixToGrid(matrix, out, [](std::uint_fast8_t v) -> uint32_t {
98 return v == 1 ? Semantic::Floor : Semantic::Wall;
99 });
100 out.setMeta("algorithm", "cave.cellular");
101 dtlutil::placeSpawnAndStairs(out, params.getSeed());
102 return true;
103}
104
105bool genMazeBacktrack(const Params &params, Grid2D &out, std::string &error) {
106 const int w = params.getWidth();
107 const int h = params.getHeight();
108 // MazeDig works best on odd dimensions.
109 const int mw = w < 5 ? 5 : (w % 2 == 0 ? w - 1 : w);
110 const int mh = h < 5 ? 5 : (h % 2 == 0 ? h - 1 : h);
111
112 auto matrix = dtlutil::makeMatrix(mw, mh, 0);
113 // empty(floor)=1, wall=0
114 dtl::shape::MazeDig<std::uint_fast8_t> shape(1, 0);
115 if (!shape.drawSEED(matrix, params.getSeed())) {
116 error = "maze.backtrack: DTL MazeDig draw failed";
117 return false;
118 }
119
120 out.resize(w, h);
121 out.fill(Semantic::Wall);
122 for (int y = 0; y < mh && y < h; ++y) {
123 for (int x = 0; x < mw && x < w; ++x) {
124 out.setCell(x, y, matrix[size_t(y)][size_t(x)] == 1 ? Semantic::Floor : Semantic::Wall);
125 }
126 }
127 out.setMeta("algorithm", "maze.backtrack");
128 dtlutil::placeSpawnAndStairs(out, params.getSeed());
129 return true;
130}
131
132bool genNoiseTerrain(const Params &params, Grid2D &out, std::string &error) {
133 const int w = params.getWidth();
134 const int h = params.getHeight();
135 if (w < 2 || h < 2) {
136 error = "noise.terrain: size must be at least 2x2";
137 return false;
138 }
139 const float frequency = params.getFloat("frequency", 6.f);
140 const int octaves = params.getInt("octaves", 4);
141 // Height bands in [0, maxHeight]
142 const int maxHeight = params.getInt("maxHeight", 9);
143
144 auto matrix = dtlutil::makeMatrix(w, h, 0);
145 dtl::shape::PerlinIsland<std::uint_fast8_t> shape(double(std::max(0.1f, frequency)),
146 size_t(std::max(1, octaves)),
147 std::uint_fast8_t(std::max(1, maxHeight)), 0);
148 if (!shape.drawSEED(matrix, params.getSeed())) {
149 error = "noise.terrain: DTL PerlinIsland draw failed";
150 return false;
151 }
152
153 const float waterMax = params.getFloat("waterMax", 0.25f);
154 const float sandMax = params.getFloat("sandMax", 0.35f);
155 const float grassMax = params.getFloat("grassMax", 0.65f);
156 const float dirtMax = params.getFloat("dirtMax", 0.80f);
157 const float stoneMax = params.getFloat("stoneMax", 0.92f);
158
159 dtlutil::copyMatrixToGrid(matrix, out, [&](std::uint_fast8_t v) -> uint32_t {
160 const float t = maxHeight <= 0 ? 0.f : float(v) / float(maxHeight);
161 if (t < waterMax) return Semantic::Water;
162 if (t < sandMax) return Semantic::Sand;
163 if (t < grassMax) return Semantic::Grass;
164 if (t < dirtMax) return Semantic::Dirt;
165 if (t < stoneMax) return Semantic::Stone;
166 return Semantic::Snow;
167 });
168 out.setMeta("algorithm", "noise.terrain");
169 dtlutil::placeSpawnAndStairs(out, params.getSeed());
170 return true;
171}
172
174bool genCaveDrunkard(const Params &params, Grid2D &out, std::string &error) {
175 const int w = params.getWidth();
176 const int h = params.getHeight();
177 if (w < 4 || h < 4) {
178 error = "cave.drunkard: size must be at least 4x4";
179 return false;
180 }
181 const float floorPct = std::clamp(params.getFloat("floorPct", 0.45f), 0.05f, 0.95f);
182 const int target = int(float(w * h) * floorPct);
183
184 out.resize(w, h);
185 out.fill(Semantic::Wall);
186
187 std::mt19937 rng(params.getSeed());
188 std::uniform_int_distribution<int> dirDist(0, 3);
189 int x = w / 2;
190 int y = h / 2;
191 int carved = 0;
192 int guard = w * h * 20;
193 while (carved < target && guard-- > 0) {
194 if (out.getCell(x, y) != Semantic::Floor) {
195 out.setCell(x, y, Semantic::Floor);
196 ++carved;
197 }
198 switch (dirDist(rng)) {
199 case 0: if (x > 1) --x; break;
200 case 1: if (x < w - 2) ++x; break;
201 case 2: if (y > 1) --y; break;
202 default: if (y < h - 2) ++y; break;
203 }
204 }
205 // Outer wall frame
206 for (int i = 0; i < w; ++i) {
207 out.setCell(i, 0, Semantic::Wall);
208 out.setCell(i, h - 1, Semantic::Wall);
209 }
210 for (int j = 0; j < h; ++j) {
211 out.setCell(0, j, Semantic::Wall);
212 out.setCell(w - 1, j, Semantic::Wall);
213 }
214 out.setMeta("algorithm", "cave.drunkard");
215 dtlutil::placeSpawnAndStairs(out, params.getSeed());
216 return true;
217}
218
219} // namespace
220
221// Defined in WfcSimple.cpp
222void registerWfcSimple(GeneratorRegistry &registry);
223// Defined in heightmap/BuiltinHeightmap.cpp
224void registerTerrainHeightmapAlgorithm(GeneratorRegistry &registry);
225
227 if (builtinsRegistered_) return;
228 auto dungeon = GeneratorDescriptor::grid("dungeon.bsp", "BSP Dungeon", "Dungeon", 8, 8);
229 dungeon.params.push_back(ParamDescriptor::integer("divisionMin", "Minimum Divisions", 3, 1, 32));
230 dungeon.params.push_back(ParamDescriptor::integer("divisionRandMax", "Division Variation", 4, 0, 32));
231 dungeon.params.push_back(ParamDescriptor::integer("roomMinX", "Minimum Room Width", 5, 1, 128));
232 dungeon.params.push_back(ParamDescriptor::integer("roomRandMaxX", "Room Width Variation", 2, 0, 128));
233 dungeon.params.push_back(ParamDescriptor::integer("roomMinY", "Minimum Room Height", 5, 1, 128));
234 dungeon.params.push_back(ParamDescriptor::integer("roomRandMaxY", "Room Height Variation", 2, 0, 128));
235 registerAlgorithm(std::move(dungeon), genDungeonBsp);
236
237 auto cellular = GeneratorDescriptor::grid("cave.cellular", "Cellular Cave", "Cave", 4, 4);
238 cellular.params.push_back(ParamDescriptor::integer("loops", "Smoothing Passes", 5, 1, 64));
239 cellular.params.push_back(ParamDescriptor::floating("fill", "Initial Fill", 0.45f, 0.f, 1.f, 0.01f));
240 registerAlgorithm(std::move(cellular), genCaveCellular);
241
242 auto drunkard = GeneratorDescriptor::grid("cave.drunkard", "Drunkard Cave", "Cave", 4, 4);
243 drunkard.params.push_back(ParamDescriptor::floating("floorPct", "Floor Coverage", 0.45f, 0.05f, 0.95f,
244 0.01f));
245 registerAlgorithm(std::move(drunkard), genCaveDrunkard);
246
247 auto maze = GeneratorDescriptor::grid("maze.backtrack", "Backtracking Maze", "Maze", 5, 5);
248 registerAlgorithm(std::move(maze), genMazeBacktrack);
249
250 auto noise = GeneratorDescriptor::grid("noise.terrain", "Noise Terrain", "Terrain", 2, 2);
251 noise.params.push_back(ParamDescriptor::floating("frequency", "Frequency", 6.f, 0.1f, 64.f, 0.1f));
252 noise.params.push_back(ParamDescriptor::integer("octaves", "Octaves", 4, 1, 16));
253 noise.params.push_back(ParamDescriptor::integer("maxHeight", "Height Bands", 9, 1, 255));
254 noise.params.push_back(ParamDescriptor::floating("waterMax", "Water Threshold", 0.25f, 0.f, 1.f, 0.01f));
255 noise.params.push_back(ParamDescriptor::floating("sandMax", "Sand Threshold", 0.35f, 0.f, 1.f, 0.01f));
256 noise.params.push_back(ParamDescriptor::floating("grassMax", "Grass Threshold", 0.65f, 0.f, 1.f, 0.01f));
257 noise.params.push_back(ParamDescriptor::floating("dirtMax", "Dirt Threshold", 0.80f, 0.f, 1.f, 0.01f));
258 noise.params.push_back(ParamDescriptor::floating("stoneMax", "Stone Threshold", 0.92f, 0.f, 1.f, 0.01f));
259 registerAlgorithm(std::move(noise), genNoiseTerrain);
260 registerWfcSimple(*this);
265 builtinsRegistered_ = true;
266}
267
268} // namespace eve::procgen
LogicalId target
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
ShaderImageInput shape
float v
HexVec3 up
HexVec3 left
HexVec3 right
Optional hexmap terrain bake bindings for Procgen.
int h
std::string error
Definition Package.cpp:60
float t
void registerAlgorithm(const std::string &id, GeneratorFn fn)
Register an algorithm without metadata for backward compatibility.
void registerBuiltins()
Register built-in DTL / custom map algorithms (idempotent).
std::vector< ParamSpec > params
constexpr uint32_t Floor
Definition Semantic.h:14
constexpr uint32_t Grass
Definition Semantic.h:18
constexpr uint32_t Stone
Definition Semantic.h:20
constexpr uint32_t Wall
Definition Semantic.h:13
constexpr uint32_t Sand
Definition Semantic.h:17
constexpr uint32_t Dirt
Definition Semantic.h:19
constexpr uint32_t Corridor
Definition Semantic.h:15
constexpr uint32_t Water
Definition Semantic.h:16
constexpr uint32_t Snow
Definition Semantic.h:21
void copyMatrixToGrid(const std::vector< std::vector< std::uint_fast8_t > > &matrix, Grid2D &grid, const std::function< uint32_t(std::uint_fast8_t)> &mapValue)
Copies matrix to grid.
Definition DtlHelpers.h:32
void placeSpawnAndStairs(Grid2D &grid, uint32_t seed)
Place spawn + stairs on walkable cells (floor/corridor/grass/dirt/sand).
Definition DtlHelpers.h:47
std::vector< std::vector< std::uint_fast8_t > > makeMatrix(int width, int height, std::uint_fast8_t fill=0)
Make matrix.
Definition DtlHelpers.h:24
void registerUrbanGenerators(GeneratorRegistry &registry)
Register "urban.parcels" (Grid2D) and "mesh.urban" (MeshBuild) builtins.
void registerHexTerrainAlgorithms(GeneratorRegistry &registry)
Register hex.terrain / hex.sphere schemas when hexmap is present.
void registerWfcSimple(GeneratorRegistry &registry)
void registerTerrainHeightmapAlgorithm(GeneratorRegistry &registry)
void registerRoguelikeGenerator(GeneratorRegistry &registry)
Register the "level.roguelike" algorithm (idempotent).
static ParamDescriptor floating(std::string key, std::string label, float defaultValue, float minimum, float maximum, float step)
Construct a bounded floating-point descriptor.
static ParamDescriptor integer(std::string key, std::string label, int defaultValue, int minimum, int maximum, int step=1)
Construct a bounded integer descriptor.
static RecipeDescriptor grid(std::string id, std::string displayName, std::string category, int minimumWidth, int minimumHeight, int maximumWidth=4096, int maximumHeight=4096)
Create a schema with common seed, width and height parameters.