载入中...
搜索中...
未找到
LSystem.cpp
浏览该文件的文档.
2#include "procgen/PointSet.h"
3
4#include <algorithm>
5#include <cmath>
6#include <random>
7
8namespace eve::procgen {
9
10namespace {
11
12constexpr float kPi = 3.14159265358979323846f;
13
14float radians(float degrees) { return degrees * (kPi / 180.f); }
15
16float clampUnit(float value) { return std::max(0.f, std::min(1.f, value)); }
17
18} // namespace
19
21 std::fill(std::begin(leafChars_), std::end(leafChars_), false);
22}
23
24void LSystem::setAxiom(const std::string& axiom) { axiom_ = axiom; }
25
26void LSystem::addRule(char symbol, const std::string& production) {
27 rules_[static_cast<unsigned char>(symbol)] = {Rule{production, 1.f}};
28}
29
30void LSystem::addRules(char symbol, const std::vector<std::string>& productions,
31 const std::vector<float>& weights) {
32 auto& target = rules_[static_cast<unsigned char>(symbol)];
33 const size_t count = std::min(productions.size(), weights.size());
34 target.clear();
35 for (size_t i = 0; i < count; ++i) {
36 const float w = i < weights.size() ? std::max(0.f, weights[i]) : 1.f;
37 target.emplace_back(productions[i], w);
38 }
39}
40
42 for (auto& slot : rules_) slot.clear();
43}
44
45void LSystem::setAngle(float degrees) { angleDeg_ = degrees; }
46void LSystem::setStep(float step) { step_ = std::max(0.f, step); }
47void LSystem::setIterations(int iterations) { iterations_ = std::max(0, iterations); }
48void LSystem::setSeed(uint32_t seed) { seed_ = seed; }
49
50void LSystem::setInitialHeading(float x, float y, float z) {
51 const float n = std::sqrt(x * x + y * y + z * z);
52 if (n <= 1e-9f) return;
53 heading0_ = {x / n, y / n, z / n};
54}
55
56void LSystem::setBranchRadius(float radius) { branchRadius_ = std::max(0.f, radius); }
57void LSystem::setBranchRadiusFalloff(float factor) { radiusFalloff_ = clampUnit(factor); }
58void LSystem::setLeafSize(float size) { leafSize_ = std::max(0.f, size); }
59
60void LSystem::setLeafSymbols(const std::string& symbols) {
61 std::fill(std::begin(leafChars_), std::end(leafChars_), false);
62 for (char c : symbols) leafChars_[static_cast<unsigned char>(c)] = true;
63}
64
65void LSystem::setTropism(float x, float y, float z) { tropism_ = {x, y, z}; }
66
67uint32_t LSystem::getSeed() const { return seed_; }
68int LSystem::getIterations() const { return iterations_; }
69
70LSystem::Vec3 LSystem::rotate(const Vec3& v, const Vec3& axis, float angle) const {
71 const float c = std::cos(angle);
72 const float s = std::sin(angle);
73 const float dot = v.x * axis.x + v.y * axis.y + v.z * axis.z;
74 Vec3 cross{axis.y * v.z - axis.z * v.y, axis.z * v.x - axis.x * v.z,
75 axis.x * v.y - axis.y * v.x};
76 return {v.x * c + cross.x * s + axis.x * dot * (1.f - c),
77 v.y * c + cross.y * s + axis.y * dot * (1.f - c),
78 v.z * c + cross.z * s + axis.z * dot * (1.f - c)};
79}
80
81void LSystem::orthonormalize(Vec3& heading, Vec3& up) const {
82 // right = normalize(cross(up, heading)); then re-derive up to stay exact.
83 Vec3 right{up.y * heading.z - up.z * heading.y, up.z * heading.x - up.x * heading.z,
84 up.x * heading.y - up.y * heading.x};
85 float n = std::sqrt(right.x * right.x + right.y * right.y + right.z * right.z);
86 if (n <= 1e-9f) return;
87 right = {right.x / n, right.y / n, right.z / n};
88 // up = normalize(cross(heading, right))
89 Vec3 nextUp{heading.y * right.z - heading.z * right.y,
90 heading.z * right.x - heading.x * right.z,
91 heading.x * right.y - heading.y * right.x};
92 n = std::sqrt(nextUp.x * nextUp.x + nextUp.y * nextUp.y + nextUp.z * nextUp.z);
93 if (n > 1e-9f) up = {nextUp.x / n, nextUp.y / n, nextUp.z / n};
94}
95
96std::string LSystem::derive() const {
97 std::string word = axiom_;
98 std::mt19937 rng(seed_);
99 std::uniform_real_distribution<float> unit(0.f, 1.f);
100 for (int iter = 0; iter < iterations_; ++iter) {
101 std::string next;
102 next.reserve(word.size() * 2 + 16);
103 for (char ch : word) {
104 const std::vector<Rule>& ruleSet = rules_[static_cast<unsigned char>(ch)];
105 if (ruleSet.empty()) { // no productions: keep the terminal symbol
106 next.push_back(ch);
107 continue;
108 }
109 if (ruleSet.size() == 1) {
110 next += ruleSet[0].first;
111 continue;
112 }
113 float total = 0.f;
114 for (const Rule& r : ruleSet) total += r.second;
115 if (total <= 0.f) {
116 next += ruleSet[0].first;
117 continue;
118 }
119 const float pick = total * unit(rng);
120 float acc = 0.f;
121 const std::string* chosen = &ruleSet[0].first;
122 for (const Rule& r : ruleSet) {
123 acc += r.second;
124 if (pick < acc) {
125 chosen = &r.first;
126 break;
127 }
128 }
129 next += *chosen;
130 }
131 word.swap(next);
132 }
133 return word;
134}
135
136void LSystem::interpret(const std::string& word, LSystemResult& out) const {
137 const float angle = radians(angleDeg_);
138 TurtleState t;
139 t.pos = {0.f, 0.f, 0.f};
140 t.heading = heading0_;
141 t.up = {0.f, 0.f, 1.f};
142 t.depth = 0;
143 orthonormalize(t.heading, t.up);
144
145 std::vector<TurtleState> stack;
146
147 for (char ch : word) {
148 switch (ch) {
149 case 'F': {
150 // Slight phototropism bias toward the tropism direction.
151 if (tropism_.x != 0.f || tropism_.y != 0.f || tropism_.z != 0.f) {
152 t.heading = {t.heading.x + tropism_.x * 0.02f,
153 t.heading.y + tropism_.y * 0.02f,
154 t.heading.z + tropism_.z * 0.02f};
155 const float n = std::sqrt(t.heading.x * t.heading.x +
156 t.heading.y * t.heading.y + t.heading.z * t.heading.z);
157 if (n > 1e-9f) t.heading = {t.heading.x / n, t.heading.y / n, t.heading.z / n};
158 orthonormalize(t.heading, t.up);
159 }
160 const Vec3 start = t.pos;
161 const float scale = std::pow(radiusFalloff_, float(t.depth));
162 const float r0 = branchRadius_ * scale;
163 t.pos = {t.pos.x + t.heading.x * step_, t.pos.y + t.heading.y * step_,
164 t.pos.z + t.heading.z * step_};
165 const float r1 = branchRadius_ * std::pow(radiusFalloff_, float(t.depth + 1));
166 LSystemSegment seg;
167 seg.sx = start.x; seg.sy = start.y; seg.sz = start.z;
168 seg.ex = t.pos.x; seg.ey = t.pos.y; seg.ez = t.pos.z;
169 seg.r0 = r0; seg.r1 = r1; seg.depth = t.depth;
170 out.segments.push_back(seg);
171 break;
172 }
173 case 'f':
174 t.pos = {t.pos.x + t.heading.x * step_, t.pos.y + t.heading.y * step_,
175 t.pos.z + t.heading.z * step_};
176 break;
177 case '+': t.heading = rotate(t.heading, t.up, angle); orthonormalize(t.heading, t.up); break;
178 case '-': t.heading = rotate(t.heading, t.up, -angle); orthonormalize(t.heading, t.up); break;
179 case '&': {
180 Vec3 right{0.f, 0.f, 0.f};
181 orthonormalize(t.heading, t.up);
182 right = {t.up.y * t.heading.z - t.up.z * t.heading.y,
183 t.up.z * t.heading.x - t.up.x * t.heading.z,
184 t.up.x * t.heading.y - t.up.y * t.heading.x};
185 const float n = std::sqrt(right.x * right.x + right.y * right.y + right.z * right.z);
186 if (n > 1e-9f) right = {right.x / n, right.y / n, right.z / n};
187 t.heading = rotate(t.heading, right, angle);
188 orthonormalize(t.heading, t.up);
189 break;
190 }
191 case '^': {
192 Vec3 right{0.f, 0.f, 0.f};
193 orthonormalize(t.heading, t.up);
194 right = {t.up.y * t.heading.z - t.up.z * t.heading.y,
195 t.up.z * t.heading.x - t.up.x * t.heading.z,
196 t.up.x * t.heading.y - t.up.y * t.heading.x};
197 const float n = std::sqrt(right.x * right.x + right.y * right.y + right.z * right.z);
198 if (n > 1e-9f) right = {right.x / n, right.y / n, right.z / n};
199 t.heading = rotate(t.heading, right, -angle);
200 orthonormalize(t.heading, t.up);
201 break;
202 }
203 case '\\': t.up = rotate(t.up, t.heading, angle); orthonormalize(t.heading, t.up); break;
204 case '/': t.up = rotate(t.up, t.heading, -angle); orthonormalize(t.heading, t.up); break;
205 case '[':
206 stack.push_back(t);
207 t.depth += 1;
208 break;
209 case ']':
210 if (!stack.empty()) {
211 t = stack.back();
212 stack.pop_back();
213 }
214 break;
215 default:
216 if (leafChars_[static_cast<unsigned char>(ch)]) {
217 LSystemSegment seg;
218 seg.sx = t.pos.x; seg.sy = t.pos.y; seg.sz = t.pos.z;
219 seg.ex = t.pos.x; seg.ey = t.pos.y; seg.ez = t.pos.z;
220 seg.r0 = 0.f; seg.r1 = 0.f; seg.depth = t.depth;
221 seg.leaf = true; seg.leafSize = leafSize_;
222 seg.dx = t.heading.x; seg.dy = t.heading.y; seg.dz = t.heading.z;
223 out.segments.push_back(seg);
224 out.leafCount += 1;
225 }
226 break;
227 }
228 }
229}
230
232 out.segments.clear();
233 out.leafCount = 0;
234 out.derivation = derive();
235 interpret(out.derivation, out);
236}
237
239 LSystemResult result;
240 generate(result);
241 for (const LSystemSegment& seg : result.segments) {
242 out.add(seg.sx, seg.sy, seg.sz);
243 out.add(seg.ex, seg.ey, seg.ez);
244 }
245}
246
247} // namespace eve::procgen
LogicalId target
double value
Duration start
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
float degrees
Definition CardTypes.cpp:35
glm::vec3 n
Definition Grass.cpp:63
double r
float v
HexVec3 up
HexVec3 right
std::int32_t c
std::array< float, 3 > scale
float radius
std::uint32_t seed
Definition PointSet.cpp:807
float t
std::uint32_t count
TacticalUnit * unit
float weights[3]
int iterations
Definition TreeMesh.cpp:311
float step
Definition TreeMesh.cpp:314
V3 heading
Definition TreeMesh.cpp:292
float size
Definition TreeMesh.cpp:156
float angle
void setAxiom(const std::string &axiom)
Set the starting string.
Definition LSystem.cpp:24
void toPointSet(PointSet &out) const
Generate and pack segment endpoints into a PointSet.
Definition LSystem.cpp:238
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 setStep(float step)
Set the forward distance per F.
Definition LSystem.cpp:46
std::string derive() const
Expand the grammar iterations times.
Definition LSystem.cpp:96
void setLeafSymbols(const std::string &symbols)
Set the set of symbols that emit a leaf marker.
Definition LSystem.cpp:60
void setInitialHeading(float x, float y, float z)
Set the initial heading (normalized).
Definition LSystem.cpp:50
int getIterations() const
Current iteration count.
Definition LSystem.cpp:68
uint32_t getSeed() const
Current seed.
Definition LSystem.cpp:67
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 addRules(char symbol, const std::vector< std::string > &productions, const std::vector< float > &weights)
Add weighted (stochastic) productions for one symbol.
Definition LSystem.cpp:30
void clearRules()
Remove all production rules (the axiom and alphabet remain).
Definition LSystem.cpp:41
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 addRule(char symbol, const std::string &production)
Add a deterministic production for one symbol.
Definition LSystem.cpp:26
void setSeed(uint32_t seed)
Set the deterministic seed.
Definition LSystem.cpp:48
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
int add(float x, float y, float z)
Definition PointSet.cpp:97
std::string word
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