载入中...
搜索中...
未找到
RockMesh.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6#include <unordered_map>
7#include <vector>
8
9namespace eve::procgen {
10namespace {
11
12struct Vec3 {
13 float x = 0.f, y = 0.f, z = 0.f;
14};
15
16struct Tri {
17 uint32_t a = 0, b = 0, c = 0;
18};
19
20struct ShapeProfile {
21 const char *name;
22 float axisX, axisY, axisZ;
23 float power;
24 float skewX, skewZ;
25 int cuts;
26 float cutDepth;
27};
28
29Vec3 normalize(Vec3 v) {
30 const float len = std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
31 if (len <= 1e-8f) return {0.f, 1.f, 0.f};
32 return {v.x / len, v.y / len, v.z / len};
33}
34
35Vec3 cross(Vec3 a, Vec3 b) {
36 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
37 a.x * b.y - a.y * b.x};
38}
39
40inline float fade(float t) { return t * t * t * (t * (t * 6.f - 15.f) + 10.f); }
41inline float mix(float a, float b, float t) { return a + (b - a) * t; }
42
43float grad3(uint32_t h, float x, float y, float z) {
44 const uint32_t g = h & 15u;
45 const float u = g < 8 ? x : y;
46 const float v = g < 4 ? y : (g == 12 || g == 14 ? x : z);
47 return ((g & 1u) ? -u : u) + ((g & 2u) ? -v : v);
48}
49
50float noise3(float x, float y, float z, uint32_t seed) {
51 const int xi = int(std::floor(x)), yi = int(std::floor(y)), zi = int(std::floor(z));
52 const float xf = x - float(xi), yf = y - float(yi), zf = z - float(zi);
53 const float u = fade(xf), v = fade(yf), w = fade(zf);
54 auto hash = [&](int ix, int iy, int iz) {
55 return uint32_t(ix) * 374761393u + uint32_t(iy) * 668265263u +
56 uint32_t(iz) * 1274126177u + seed * 2246822519u;
57 };
58 const float n000 = grad3(hash(xi, yi, zi), xf, yf, zf);
59 const float n100 = grad3(hash(xi + 1, yi, zi), xf - 1.f, yf, zf);
60 const float n010 = grad3(hash(xi, yi + 1, zi), xf, yf - 1.f, zf);
61 const float n110 = grad3(hash(xi + 1, yi + 1, zi), xf - 1.f, yf - 1.f, zf);
62 const float n001 = grad3(hash(xi, yi, zi + 1), xf, yf, zf - 1.f);
63 const float n101 = grad3(hash(xi + 1, yi, zi + 1), xf - 1.f, yf, zf - 1.f);
64 const float n011 = grad3(hash(xi, yi + 1, zi + 1), xf, yf - 1.f, zf - 1.f);
65 const float n111 = grad3(hash(xi + 1, yi + 1, zi + 1), xf - 1.f, yf - 1.f, zf - 1.f);
66 return mix(mix(mix(n000, n100, u), mix(n010, n110, u), v),
67 mix(mix(n001, n101, u), mix(n011, n111, u), v), w);
68}
69
70float fbm3(float x, float y, float z, uint32_t seed, int octaves) {
71 float sum = 0.f, amplitude = 0.5f, frequency = 1.f, norm = 0.f;
72 for (int i = 0; i < octaves; ++i) {
73 sum += noise3(x * frequency, y * frequency, z * frequency,
74 seed + uint32_t(i) * 1013u) * amplitude;
75 norm += amplitude;
76 amplitude *= 0.5f;
77 frequency *= 2.f;
78 }
79 return norm > 0.f ? sum / norm : 0.f;
80}
81
82uint32_t nextRandom(uint32_t &state) {
83 state ^= state << 13u;
84 state ^= state >> 17u;
85 state ^= state << 5u;
86 return state;
87}
88
89float random01(uint32_t &state) {
90 return float(nextRandom(state) & 0x00ffffffu) / float(0x01000000u);
91}
92
93float randomSigned(uint32_t &state) { return random01(state) * 2.f - 1.f; }
94
95bool resolveShape(const std::string &requested, uint32_t seed, ShapeProfile &shape) {
96 static constexpr ShapeProfile profiles[] = {
97 {"boulder", 1.f, 1.f, 0.94f, 0.92f, 0.02f, -0.01f, 3, 0.13f},
98 {"slab", 1.28f, 0.62f, 1.02f, 0.74f, 0.08f, -0.03f, 5, 0.18f},
99 {"block", 1.04f, 0.90f, 0.96f, 0.46f, 0.03f, 0.02f, 6, 0.20f},
100 {"shard", 0.70f, 1.34f, 0.56f, 0.62f, 0.22f, -0.12f, 7, 0.24f},
101 // Tall fractured outcrop for cliff / ridge props (ARPG top-down sets).
102 {"cliff", 1.18f, 1.72f, 0.78f, 0.48f, 0.06f, -0.10f, 9, 0.30f},
103 };
104 std::string name = requested;
105 if (name == "mixed") name = profiles[seed % 5u].name;
106 for (const ShapeProfile &candidate : profiles) {
107 if (name == candidate.name) {
108 shape = candidate;
109 return true;
110 }
111 }
112 return false;
113}
114
115float shapedCoordinate(float value, float power) {
116 return std::copysign(std::pow(std::abs(value), power), value);
117}
118
119uint64_t edgeKey(uint32_t a, uint32_t b) {
120 if (a > b) std::swap(a, b);
121 return (uint64_t(a) << 32u) | uint64_t(b);
122}
123
124uint32_t midpoint(uint32_t a, uint32_t b, std::vector<Vec3> &vertices,
125 std::unordered_map<uint64_t, uint32_t> &cache) {
126 const uint64_t key = edgeKey(a, b);
127 const auto found = cache.find(key);
128 if (found != cache.end()) return found->second;
129 const Vec3 &va = vertices[a], &vb = vertices[b];
130 const uint32_t index = uint32_t(vertices.size());
131 vertices.push_back(normalize({(va.x + vb.x) * 0.5f, (va.y + vb.y) * 0.5f,
132 (va.z + vb.z) * 0.5f}));
133 cache.emplace(key, index);
134 return index;
135}
136
137void createIcosphere(int subdivisions, std::vector<Vec3> &vertices, std::vector<Tri> &triangles) {
138 constexpr float phi = 1.6180339887498948482f;
139 vertices = {{-1, phi, 0}, {1, phi, 0}, {-1, -phi, 0}, {1, -phi, 0},
140 {0, -1, phi}, {0, 1, phi}, {0, -1, -phi}, {0, 1, -phi},
141 {phi, 0, -1}, {phi, 0, 1}, {-phi, 0, -1}, {-phi, 0, 1}};
142 for (Vec3 &v : vertices) v = normalize(v);
143 triangles = {{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
144 {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
145 {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
146 {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}};
147
148 for (int level = 0; level < subdivisions; ++level) {
149 std::unordered_map<uint64_t, uint32_t> cache;
150 std::vector<Tri> next;
151 next.reserve(triangles.size() * 4);
152 for (const Tri &t : triangles) {
153 const uint32_t ab = midpoint(t.a, t.b, vertices, cache);
154 const uint32_t bc = midpoint(t.b, t.c, vertices, cache);
155 const uint32_t ca = midpoint(t.c, t.a, vertices, cache);
156 next.push_back({t.a, ab, ca});
157 next.push_back({t.b, bc, ab});
158 next.push_back({t.c, ca, bc});
159 next.push_back({ab, bc, ca});
160 }
161 triangles.swap(next);
162 }
163}
164
165} // namespace
166
167bool generateRockMesh(const Params &params, MeshBuild &out, std::string &error) {
168 const int subdivisions = params.getInt("subdivisions", 3);
169 if (subdivisions < 0 || subdivisions > 5) {
170 error = "mesh.rock: subdivisions must be in [0,5]";
171 return false;
172 }
173 const float radius = std::max(0.05f, params.getFloat("radius", 0.72f));
174 const uint32_t seed = params.getSeed();
175 const std::string requestedShape = params.getString("baseShape", "mixed");
176 ShapeProfile shape{};
177 if (!resolveShape(requestedShape, seed, shape)) {
178 error = "mesh.rock: unknown baseShape '" + requestedShape +
179 "' (use mixed|boulder|slab|block|shard|cliff)";
180 return false;
181 }
182 const bool cliffShape = requestedShape == "cliff" || std::string(shape.name) == "cliff";
183 const float flattening =
184 std::clamp(params.getFloat("flattening", cliffShape ? 0.08f : 0.22f), 0.f, 0.7f);
185 const float angularity =
186 std::clamp(params.getFloat("angularity", cliffShape ? 0.72f : 0.38f), 0.f, 1.f);
187 const float erosion =
188 std::clamp(params.getFloat("erosion", cliffShape ? 0.22f : 0.16f), 0.f, 0.45f);
189 const float scale = std::max(0.25f, params.getFloat("scale", cliffShape ? 3.1f : 2.4f));
190 const int octaves = std::clamp(params.getInt("octaves", cliffShape ? 5 : 4), 1, 8);
191
192 uint32_t randomState = seed ^ 0x9e3779b9u;
193 const float variation = std::clamp(params.getFloat("variation", 0.42f), 0.f, 1.f);
194 shape.axisX *= 1.f + randomSigned(randomState) * 0.18f * variation;
195 shape.axisY *= 1.f + randomSigned(randomState) * 0.14f * variation;
196 shape.axisZ *= 1.f + randomSigned(randomState) * 0.18f * variation;
197 shape.power = std::clamp(shape.power + randomSigned(randomState) * 0.12f * variation,
198 0.32f, 1.1f);
199 shape.skewX += randomSigned(randomState) * 0.12f * variation;
200 shape.skewZ += randomSigned(randomState) * 0.12f * variation;
201 if (params.has("axisX")) shape.axisX = std::max(0.2f, params.getFloat("axisX", shape.axisX));
202 if (params.has("axisY")) shape.axisY = std::max(0.2f, params.getFloat("axisY", shape.axisY));
203 if (params.has("axisZ")) shape.axisZ = std::max(0.2f, params.getFloat("axisZ", shape.axisZ));
204 if (params.has("shapePower"))
205 shape.power = std::clamp(params.getFloat("shapePower", shape.power), 0.25f, 1.25f);
206 if (params.has("skewX")) shape.skewX = params.getFloat("skewX", shape.skewX);
207 if (params.has("skewZ")) shape.skewZ = params.getFloat("skewZ", shape.skewZ);
208 const int cutCount = std::clamp(params.getInt("cutCount", shape.cuts), 0, 12);
209 const float cutDepth = std::clamp(params.getFloat("cutDepth", shape.cutDepth), 0.f, 0.42f);
210
211 std::vector<Vec3> directions;
212 std::vector<Tri> triangles;
213 createIcosphere(subdivisions, directions, triangles);
214 std::vector<Vec3> positions(directions.size());
215 std::vector<Vec3> normals(directions.size());
216 const float steps = 3.f + angularity * 9.f;
217 for (size_t i = 0; i < directions.size(); ++i) {
218 const Vec3 d = directions[i];
219 const float qx = std::round(d.x * steps) / steps;
220 const float qy = std::round(d.y * steps) / steps;
221 const float qz = std::round(d.z * steps) / steps;
222 const float strata = fbm3((qx + 2.3f) * scale, (qy + 4.7f) * scale,
223 (qz + 8.1f) * scale, seed, octaves);
224 const float pits = fbm3((d.x + 7.2f) * scale * 2.7f,
225 (d.y + 1.9f) * scale * 2.7f,
226 (d.z + 5.4f) * scale * 2.7f,
227 seed + 7919u, std::max(2, octaves - 1));
228 const float displacement = strata * (0.08f + angularity * 0.16f) -
229 std::max(0.f, pits) * erosion;
230 const float r = std::max(radius * 0.45f, radius + displacement);
231 Vec3 p = {shapedCoordinate(d.x, shape.power) * r * shape.axisX,
232 shapedCoordinate(d.y, shape.power) * r * shape.axisY * (1.f - flattening),
233 shapedCoordinate(d.z, shape.power) * r * shape.axisZ};
234 p.x += p.y * shape.skewX;
235 p.z += p.y * shape.skewZ;
236 positions[i] = p;
237 }
238
239 // Clamp the deformed surface against deterministic planes. This preserves the icosphere's
240 // economical topology while creating broad fracture faces and non-round silhouettes.
241 for (int cut = 0; cut < cutCount; ++cut) {
242 Vec3 plane = normalize({randomSigned(randomState), randomSigned(randomState),
243 randomSigned(randomState)});
244 if (cut == 0) {
245 // Cliffs keep a steep face toward +X; other shapes keep a stable base.
246 plane = cliffShape ? normalize({1.f, -0.15f, 0.08f})
247 : normalize({0.2f, -1.f, 0.1f});
248 }
249 const float support = radius * std::sqrt(
250 plane.x * plane.x * shape.axisX * shape.axisX +
251 plane.y * plane.y * shape.axisY * shape.axisY * (1.f - flattening) *
252 (1.f - flattening) +
253 plane.z * plane.z * shape.axisZ * shape.axisZ);
254 const float depth = cutDepth * (0.72f + random01(randomState) * 0.56f);
255 const float limit = support * (1.f - depth);
256 for (Vec3 &p : positions) {
257 const float excess = p.x * plane.x + p.y * plane.y + p.z * plane.z - limit;
258 if (excess > 0.f) {
259 p.x -= plane.x * excess;
260 p.y -= plane.y * excess;
261 p.z -= plane.z * excess;
262 }
263 }
264 }
265
266 for (const Tri &t : triangles) {
267 const Vec3 &a = positions[t.a], &b = positions[t.b], &c = positions[t.c];
268 const Vec3 face = cross({b.x - a.x, b.y - a.y, b.z - a.z},
269 {c.x - a.x, c.y - a.y, c.z - a.z});
270 for (uint32_t index : {t.a, t.b, t.c}) {
271 normals[index].x += face.x;
272 normals[index].y += face.y;
273 normals[index].z += face.z;
274 }
275 }
276 for (Vec3 &n : normals) n = normalize(n);
277
278 out.clear();
279 out.reserve(int(positions.size()), int(triangles.size() * 3));
280 for (size_t i = 0; i < positions.size(); ++i) {
281 const Vec3 &p = positions[i], &n = normals[i];
282 // Dominant-axis planar UVs reduce stretching vs world-XY projection and
283 // let procedural rock/moss albedos read as surface detail instead of
284 // smeared bands across facets.
285 const float ax = std::fabs(n.x), ay = std::fabs(n.y), az = std::fabs(n.z);
286 float u = 0.f, v = 0.f;
287 if (ax >= ay && ax >= az) {
288 u = p.z;
289 v = p.y;
290 } else if (ay >= az) {
291 u = p.x;
292 v = p.z;
293 } else {
294 u = p.x;
295 v = p.y;
296 }
297 const float uvScale = 0.55f / std::max(0.05f, radius);
298 out.addVertex(p.x, p.y, p.z, n.x, n.y, n.z, u * uvScale + 0.5f, v * uvScale + 0.5f);
299 }
300 for (const Tri &t : triangles) out.addTriangle(t.a, t.b, t.c);
301 out.setMeta("algorithm", "mesh.rock");
302 out.setMeta("topology", "icosphere");
303 out.setMeta("baseShape", shape.name);
304 out.setMeta("cutCount", std::to_string(cutCount));
305 out.setMeta("subdivisions", std::to_string(subdivisions));
306 return true;
307}
308
309} // namespace eve::procgen
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int az
Definition CaveMesh.cpp:113
glm::vec4 p[6]
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
tensor::Graph g
Definition GpuGraph.cpp:7
std::uint32_t key
ShaderImageInput shape
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
std::uint32_t ab
double r
std::vector< float > normals
std::vector< float > positions
float v
std::int32_t c
int h
std::array< float, 3 > scale
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::vector< TriangleRef > triangles
int level
size_t directions
Definition OnnxLstm.cpp:29
std::string error
Definition Package.cpp:60
std::vector< Point > vertices
float radius
std::uint32_t seed
Definition PointSet.cpp:807
float d
int steps
float t
float axisZ
Definition RockMesh.cpp:22
float axisY
Definition RockMesh.cpp:22
float skewZ
Definition RockMesh.cpp:24
float skewX
Definition RockMesh.cpp:24
float cutDepth
Definition RockMesh.cpp:26
float axisX
Definition RockMesh.cpp:22
int cuts
Definition RockMesh.cpp:25
float power
Definition RockMesh.cpp:23
bool found
int limit
Definition TreeMesh.cpp:164
uint32_t index
std::uint32_t depth
std::vector< double > phi
float qy
float qx
float qz
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
void addVertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
Adds vertex.
Definition MeshBuild.cpp:33
void addTriangle(uint32_t i0, uint32_t i1, uint32_t i2)
Adds triangle.
Definition MeshBuild.cpp:46
void reserve(int vertexCount, int indexCount)
Reserve.
Definition MeshBuild.cpp:20
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
void clear()
Clears .
Definition MeshBuild.cpp:8
Owning, typed generation parameters.
Definition Params.h:27
std::vector< ParamSpec > params
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
Vec2 normalize(const Vec2 &a)
Normalize.
Definition UrbanTypes.h:44
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
bool generateRockMesh(const Params &params, MeshBuild &out, std::string &error)
Build a shared-vertex, deformed icosphere rock for economical game props.
Definition RockMesh.cpp:167