载入中...
搜索中...
未找到
LSystemMesh.cpp
浏览该文件的文档.
5
6#include <algorithm>
7#include <cmath>
8#include <string>
9
10namespace eve::procgen {
11
12namespace {
13
14constexpr float kPi = 3.14159265358979323846f;
15
16struct V3 {
17 float x = 0.f, y = 0.f, z = 0.f;
18};
19
20V3 add(V3 a, V3 b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
21V3 sub(V3 a, V3 b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
22V3 mul(V3 a, float s) { return {a.x * s, a.y * s, a.z * s}; }
23float dot(V3 a, V3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
24V3 cross(V3 a, V3 b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; }
25V3 norm(V3 a) {
26 const float n = std::sqrt(std::max(1e-12f, dot(a, a)));
27 return mul(a, 1.f / n);
28}
29
30void basisFor(V3 axis, V3& right, V3& forward) {
31 axis = norm(axis);
32 const V3 helper = std::fabs(axis.y) < 0.92f ? V3{0.f, 1.f, 0.f} : V3{1.f, 0.f, 0.f};
33 right = norm(cross(helper, axis));
34 forward = norm(cross(axis, right));
35}
36
37void addTaperedCylinder(MeshBuild& out, V3 a, V3 b, float r0, float r1, int sides) {
38 const V3 axis = norm(sub(b, a));
39 V3 right, forward;
40 basisFor(axis, right, forward);
41 const uint32_t base = uint32_t(out.getVertexCount());
42 for (int ring = 0; ring < 2; ++ring) {
43 const V3 center = ring ? b : a;
44 const float radius = ring ? r1 : r0;
45 for (int i = 0; i < sides; ++i) {
46 const float t = float(i) / float(sides);
47 const float angle = t * 2.f * kPi;
48 const V3 radial = add(mul(right, std::cos(angle)), mul(forward, std::sin(angle)));
49 const V3 p = add(center, mul(radial, radius));
50 // Bark occupies the left side of the texture atlas; foliage the right.
51 out.addVertex(p.x, p.y, p.z, radial.x, radial.y, radial.z, t * 0.45f, float(ring));
52 }
53 }
54 for (int i = 0; i < sides; ++i) {
55 const uint32_t n = uint32_t((i + 1) % sides);
56 const uint32_t i0 = base + uint32_t(i), i1 = base + n;
57 const uint32_t i2 = base + uint32_t(sides) + uint32_t(i);
58 const uint32_t i3 = base + uint32_t(sides) + n;
59 out.addTriangle(i0, i2, i1);
60 out.addTriangle(i1, i2, i3);
61 }
62}
63
64void addLeafCard(MeshBuild& out, V3 c, V3 direction, float size, float twist) {
65 V3 right, up;
66 basisFor(norm(direction), right, up);
67 right = add(mul(right, std::cos(twist)), mul(up, std::sin(twist)));
68 up = norm(cross(norm(direction), right));
69 const V3 normal = norm(cross(right, up));
70 const float card = size * 1.25f;
71 const float halfW = card * 0.62f;
72 const float halfH = card * 0.62f;
73 const V3 r = mul(right, halfW), h = mul(up, halfH);
74 const V3 center = add(c, mul(normal, size * 0.04f));
75 const V3 points[4] = {sub(sub(center, r), h), add(sub(center, h), r), add(add(center, r), h),
76 add(sub(center, r), h)};
77
78 // Deterministic panel from twist among the six leaf-card variants.
79 constexpr float kCardU0 = 0.52f;
80 constexpr float kCardU1 = 1.00f;
81 constexpr int kCols = 2;
82 constexpr int kRows = 3;
83 const float phase = twist * 0.318309886f;
84 const int panel = int(std::floor(std::fabs(phase) * float(kCols * kRows))) % (kCols * kRows);
85 const int col = panel % kCols;
86 const int row = panel / kCols;
87 const float inset = 0.04f;
88 const float cellWU = (kCardU1 - kCardU0) / float(kCols);
89 const float cellWV = 1.f / float(kRows);
90 const float u0 = kCardU0 + (float(col) + inset) * cellWU;
91 const float u1 = kCardU0 + (float(col) + 1.f - inset) * cellWU;
92 const float v0 = (float(row) + inset) * cellWV;
93 const float v1 = (float(row) + 1.f - inset) * cellWV;
94 const float uv[4][2] = {{u0, v0}, {u1, v0}, {u1, v1}, {u0, v1}};
95 const uint32_t base = uint32_t(out.getVertexCount());
96 for (int i = 0; i < 4; ++i)
97 out.addVertex(points[i].x, points[i].y, points[i].z, normal.x, normal.y, normal.z, uv[i][0], uv[i][1]);
98 out.addTriangle(base, base + 1, base + 2);
99 out.addTriangle(base, base + 2, base + 3);
100 out.addTriangle(base + 2, base + 1, base);
101 out.addTriangle(base + 3, base + 2, base);
102}
103
104void configurePreset(LSystem& ls, const std::string& style) {
105 ls.setLeafSymbols("L");
106 if (style == "fern") {
107 ls.setAxiom("X");
108 ls.clearRules();
109 ls.addRule('X', "F-[[X]+X]+F[+FX]-X");
110 ls.addRule('F', "FF");
111 ls.setAngle(25.f);
112 ls.setInitialHeading(0.f, 1.f, 0.f);
113 ls.setBranchRadius(0.05f);
114 ls.setBranchRadiusFalloff(0.8f);
115 } else if (style == "plant") {
116 ls.setAxiom("F");
117 ls.clearRules();
118 ls.addRule('F', "F[+F]F[-F]F");
119 ls.setAngle(26.f);
120 ls.setInitialHeading(0.f, 1.f, 0.f);
121 ls.setBranchRadius(0.09f);
122 ls.setBranchRadiusFalloff(0.72f);
123 } else if (style == "weed") {
124 ls.setAxiom("X");
125 ls.clearRules();
126 ls.addRule('X', "F[-X][+X]FX");
127 ls.addRule('X', "F[-X]FX");
128 ls.addRule('X', "F[&X]F^X");
129 ls.setAngle(28.f);
130 ls.setInitialHeading(0.f, 1.f, 0.f);
131 ls.setBranchRadius(0.06f);
132 ls.setBranchRadiusFalloff(0.7f);
133 } else { // tree (default)
134 ls.setAxiom("A");
135 ls.clearRules();
136 ls.addRules('A', {"F[&L A]F[&L A]^F[&L A]F L A", "F[&L A]F L A", "F L A"},
137 {2.f, 1.f, 1.f});
138 ls.setAngle(22.f);
139 ls.setInitialHeading(0.f, 1.f, 0.f);
140 ls.setBranchRadius(0.16f);
141 ls.setBranchRadiusFalloff(0.62f);
142 }
143}
144
145} // namespace
146
147bool generateLSystemMesh(const Params& params, MeshBuild& out, std::string& error) {
148 const std::string style = params.getString("style", "tree");
149 if (style != "tree" && style != "fern" && style != "plant" && style != "weed") {
150 error = "mesh.lsystem: unknown style '" + style + "'";
151 return false;
152 }
153 const std::string leafMode = params.getString("leafMode", "cards");
154 if (leafMode != "cards" && leafMode != "none") {
155 error = "mesh.lsystem: unknown leafMode '" + leafMode + "'";
156 return false;
157 }
158
159 LSystem ls;
160 configurePreset(ls, style);
161 ls.setSeed(params.getSeed());
162 ls.setIterations(params.getInt("iterations", 5));
163 ls.setAngle(params.getFloat("angle", 22.f));
164 ls.setBranchRadius(params.getFloat("branchRadius", 0.16f));
165 ls.setBranchRadiusFalloff(params.getFloat("radiusFalloff", 0.62f));
166 ls.setLeafSize(params.getFloat("leafSize", 0.4f));
167 ls.setTropism(0.f, params.getFloat("tropism", 0.f), 0.f);
168 const int branchSegments = std::clamp(params.getInt("branchSegments", 6), 3, 24);
169 const int leavesPerMarker = std::max(1, params.getInt("leavesPerMarker", 1));
170
171 LSystemResult result;
172 ls.generate(result);
173 if (result.segments.empty()) {
174 error = "mesh.lsystem: generated no geometry";
175 return false;
176 }
177
178 out.reserve(int(result.segments.size() * 12), int(result.segments.size() * 24));
179 for (const LSystemSegment& seg : result.segments) {
180 if (seg.leaf) {
181 if (leafMode == "none") continue;
182 const V3 c = {seg.ex, seg.ey, seg.ez};
183 const V3 dir = {seg.dx, seg.dy, seg.dz};
184 for (int i = 0; i < leavesPerMarker; ++i) {
185 const float twist = float(i) * 2.39996323f; // golden angle
186 addLeafCard(out, c, dir, seg.leafSize, twist);
187 }
188 continue;
189 }
190 const V3 a = {seg.sx, seg.sy, seg.sz};
191 const V3 b = {seg.ex, seg.ey, seg.ez};
192 addTaperedCylinder(out, a, b, std::max(1e-4f, seg.r0), std::max(1e-4f, seg.r1), branchSegments);
193 }
194
195 out.setMeta("recipe", "mesh.lsystem");
196 out.setMeta("style", style);
197 out.setMeta("leafMode", leafMode);
198 out.setMeta("seed", std::to_string(params.getSeed()));
199 if (out.empty()) {
200 error = "mesh.lsystem: generated an empty mesh";
201 return false;
202 }
203 return true;
204}
205
207 RecipeDescriptor schema{std::string("mesh.lsystem"), "L-System Plant", "Mesh", {}};
208 schema.params.push_back(ParamDescriptor::integer("seed", "Seed", 1, 0, 2147483647));
209 schema.params.push_back(
210 ParamDescriptor::choice("style", "Style", "tree", {"tree", "fern", "plant", "weed"}));
211 schema.params.push_back(ParamDescriptor::choice("leafMode", "Leaf Mode", "cards", {"cards", "none"}));
212 schema.params.push_back(ParamDescriptor::integer("iterations", "Iterations", 5, 1, 8));
213 schema.params.push_back(ParamDescriptor::floating("angle", "Turn Angle", 22.f, 5.f, 90.f, 1.f));
214 schema.params.push_back(ParamDescriptor::floating("branchRadius", "Branch Radius", 0.16f, 0.005f, 5.f, 0.005f));
215 schema.params.push_back(ParamDescriptor::floating("radiusFalloff", "Radius Falloff", 0.62f, 0.05f, 1.f, 0.01f));
216 schema.params.push_back(ParamDescriptor::floating("leafSize", "Leaf Size", 0.4f, 0.02f, 10.f, 0.01f));
217 schema.params.push_back(ParamDescriptor::floating("tropism", "Tropism", 0.f, 0.f, 1.f, 0.01f));
218 schema.params.push_back(ParamDescriptor::integer("branchSegments", "Branch Segments", 6, 3, 24));
219 schema.params.push_back(ParamDescriptor::integer("leavesPerMarker", "Leaves Per Marker", 1, 1, 8));
220 registry.registerRecipe(std::move(schema), generateLSystemMesh);
221}
222
223} // namespace eve::procgen
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
float halfW
float halfH
float twist
float uv
float phase
Definition CaveMesh.cpp:58
glm::vec4 p[6]
uint32_t i1
Definition Grass.cpp:61
uint32_t i2
Definition Grass.cpp:61
uint32_t i0
Definition Grass.cpp:61
glm::vec3 n
Definition Grass.cpp:63
double r
HexVec3 up
HexVec3 right
std::int32_t c
int h
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
Texture * normal
std::string error
Definition Package.cpp:60
float radius
std::shared_ptr< const std::vector< glm::vec2 > > points
float t
RoadLaneDirection direction
int sides
Definition TreeMesh.cpp:322
float size
Definition TreeMesh.cpp:156
V3 dir
Definition TreeMesh.cpp:150
float angle
General stochastic bracketed L-system engine.
Definition LSystem.h:55
void setIterations(int iterations)
Set the grammar expansion count.
Definition LSystem.cpp:47
void setBranchRadiusFalloff(float factor)
Set the per-depth radius multiplier.
Definition LSystem.cpp:57
void generate(LSystemResult &out) const
Expand then interpret with the turtle.
Definition LSystem.cpp:231
void setTropism(float x, float y, float z)
Bias growth toward a direction (phototropism).
Definition LSystem.cpp:65
void setLeafSize(float size)
Set the foliage card size.
Definition LSystem.cpp:58
void setBranchRadius(float radius)
Set the trunk radius at depth 0.
Definition LSystem.cpp:56
void setAngle(float degrees)
Set the turtle turn angle.
Definition LSystem.cpp:45
void setSeed(uint32_t seed)
Set the deterministic seed.
Definition LSystem.cpp:48
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
void reserve(int vertexCount, int indexCount)
Reserve.
Definition MeshBuild.cpp:20
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
bool empty() const
Empty.
EVENGINE_API_DOMAINS public API.
void registerRecipe(const std::string &id, MeshRecipeFn fn)
Register a recipe without metadata.
Owning, typed generation parameters.
Definition Params.h:27
std::vector< ParamSpec > params
std::vector< double > forward(const Policy &p, const Observation &o)
Forward.
Definition Learning.h:65
double dot(const Vec2 &a, const Vec2 &b)
Dot.
Definition UrbanTypes.h:38
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
bool generateLSystemMesh(const Params &params, MeshBuild &out, std::string &error)
Build a grammar-based tree/plant mesh from Params.
void registerLSystemRecipes(MeshRecipeRegistry &registry)
Register the mesh.lsystem grammar-based plant recipe.
int axis(int64_t a, size_t rank)
Axis.
WidgetDesc card(std::vector< WidgetDesc > children, std::string id)
Bordered surface container with editor-friendly padding.
Definition Widget.cpp:502
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:679
Full result of expanding an L-system grammar and tracing the turtle.
Definition LSystem.h:31
std::vector< LSystemSegment > segments
Definition LSystem.h:32
One drawn segment produced by an L-system turtle.
Definition LSystem.h:20
static ParamDescriptor choice(std::string key, std::string label, std::string defaultValue, std::vector< std::string > choices)
Construct a finite-choice string descriptor.
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.
Complete metadata and parameter schema shared by every procgen recipe family.
Definition ParamSchema.h:52
std::vector< ParamDescriptor > params
Definition ParamSchema.h:56