载入中...
搜索中...
未找到
ShapeGrammar.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cctype>
5#include <cmath>
6#include <cstdlib>
7#include <sstream>
8
9namespace eve::procgen {
10namespace {
11
12uint32_t mixGrammar(uint32_t value) {
13 value += 0x9e3779b9u;
14 value = (value ^ (value >> 16u)) * 0x21f0aaadu;
15 value = (value ^ (value >> 15u)) * 0x735a2d97u;
16 return value ^ (value >> 15u);
17}
18float grammarUnit(uint32_t seed) { return float(mixGrammar(seed) >> 8u) * (1.f / 16777216.f); }
19
20float polylineLength(const PointSet& points) {
21 float result = 0.f;
22 for (size_t i = 1; i < points.points().size(); ++i) {
23 const auto& a = points.points()[i - 1];
24 const auto& b = points.points()[i];
25 const float dx = b.x - a.x;
26 const float dy = b.y - a.y;
27 const float dz = b.z - a.z;
28 result += std::sqrt(dx * dx + dy * dy + dz * dz);
29 }
30 return result;
31}
32
33bool samplePolyline(const PointSet& points, float distance, ProcgenPoint& output) {
34 float cursor = 0.f;
35 for (size_t i = 1; i < points.points().size(); ++i) {
36 const auto& a = points.points()[i - 1];
37 const auto& b = points.points()[i];
38 const float dx = b.x - a.x;
39 const float dy = b.y - a.y;
40 const float dz = b.z - a.z;
41 const float length = std::sqrt(dx * dx + dy * dy + dz * dz);
42 if (length <= 0.f) continue;
43 if (distance <= cursor + length || i + 1 == points.points().size()) {
44 const float t = std::clamp((distance - cursor) / length, 0.f, 1.f);
45 output.x = a.x + dx * t;
46 output.y = a.y + dy * t;
47 output.z = a.z + dz * t;
48 output.yaw = std::atan2(dz, dx) * 57.29577951308232f;
49 return true;
50 }
51 cursor += length;
52 }
53 return false;
54}
55
56} // namespace
57
59 modules_.clear();
60 moduleOrder_.clear();
61 error_.clear();
62 lastSymbolCount_ = 0;
63 lastUsedLength_ = 0.f;
64 lastSplineLength_ = 0.f;
65}
66
67bool ShapeGrammar::addModule(const std::string& symbol, const std::string& asset, float length, float weight) {
68 if (symbol.empty() || asset.empty() || length <= 0.f || weight <= 0.f) return false;
69 const auto existing = modules_.find(symbol);
70 if (existing != modules_.end() && !existing->second.empty() &&
71 std::abs(existing->second.front().length - length) > 0.0001f)
72 return false;
73 if (modules_.find(symbol) == modules_.end()) moduleOrder_.push_back(symbol);
74 modules_[symbol].push_back({asset, length, weight});
75 return true;
76}
77
78bool ShapeGrammar::removeModule(const std::string& symbol) {
79 if (modules_.erase(symbol) == 0) return false;
80 moduleOrder_.erase(std::remove(moduleOrder_.begin(), moduleOrder_.end(), symbol), moduleOrder_.end());
81 return true;
82}
83bool ShapeGrammar::hasModule(const std::string& symbol) const { return modules_.find(symbol) != modules_.end(); }
84int ShapeGrammar::getModuleCount() const { return int(moduleOrder_.size()); }
85std::string ShapeGrammar::getModuleSymbol(int index) const {
86 return index >= 0 && index < int(moduleOrder_.size()) ? moduleOrder_[size_t(index)] : std::string();
87}
88int ShapeGrammar::getVariantCount(const std::string& symbol) const {
89 const auto found = modules_.find(symbol);
90 return found == modules_.end() ? 0 : int(found->second.size());
91}
92std::string ShapeGrammar::getVariantAsset(const std::string& symbol, int index) const {
93 const auto found = modules_.find(symbol);
94 return found != modules_.end() && index >= 0 && index < int(found->second.size())
95 ? found->second[size_t(index)].asset
96 : std::string();
97}
98float ShapeGrammar::getVariantLength(const std::string& symbol, int index) const {
99 const auto found = modules_.find(symbol);
100 return found != modules_.end() && index >= 0 && index < int(found->second.size())
101 ? found->second[size_t(index)].length
102 : 0.f;
103}
104
105bool ShapeGrammar::validate(const std::string& grammar) {
106 error_.clear();
107 Parser parser{grammar};
108 const auto parsed = parser.sequence();
109 if (!parser.error.empty()) {
110 error_ = parser.error;
111 return false;
112 }
113 if (parsed.empty()) {
114 error_ = "grammar is empty";
115 return false;
116 }
117 std::vector<const Element*> stack;
118 for (const auto& element : parsed) stack.push_back(&element);
119 while (!stack.empty()) {
120 const Element* element = stack.back();
121 stack.pop_back();
122 if (!element->symbol.empty() && !hasModule(element->symbol)) {
123 error_ = "unknown module: " + element->symbol;
124 return false;
125 }
126 for (const auto& child : element->children) stack.push_back(&child);
127 }
128 return true;
129}
130
131PointSet* ShapeGrammar::generate(const std::string& grammar, PointSet* controlPoints, uint32_t seed,
132 bool acceptIncomplete) {
133 error_.clear();
134 lastSymbolCount_ = 0;
135 lastUsedLength_ = 0.f;
136 lastSplineLength_ = 0.f;
137 if (!controlPoints || controlPoints->getCount() < 2) {
138 error_ = "generate: requires at least two control points";
139 return nullptr;
140 }
141 if (!validate(grammar)) return nullptr;
142 Parser parser{grammar};
143 const auto parsed = parser.sequence();
144 lastSplineLength_ = polylineLength(*controlPoints);
145 if (!acceptIncomplete && sequenceMinLength(parsed) > lastSplineLength_ + 0.0001f) {
146 error_ = "mandatory grammar does not fit spline";
147 return nullptr;
148 }
149 std::vector<std::string> symbols;
150 float used = 0.f;
151 if (!expandSequence(parsed, lastSplineLength_, symbols, used)) {
152 error_ = "grammar expansion failed";
153 return nullptr;
154 }
155
157 float cursor = 0.f;
158 for (size_t i = 0; i < symbols.size(); ++i) {
159 const auto* variant = chooseVariant(symbols[i], mixGrammar(seed ^ uint32_t(i)));
160 if (!variant || cursor + variant->length > lastSplineLength_ + 0.0001f) {
161 if (!acceptIncomplete) {
162 error_ = "expanded module does not fit spline: " + symbols[i];
163 return nullptr;
164 }
165 break;
166 }
168 if (!samplePolyline(*controlPoints, cursor + variant->length * 0.5f, point)) break;
169 point.seed = mixGrammar(seed ^ uint32_t(i));
170 const int pointIndex = output.appendPoint(std::move(point));
171 output.trySetStringAttribute(pointIndex, "module", symbols[i]).expect("shape grammar module metadata schema");
172 output.trySetStringAttribute(pointIndex, "asset", variant->asset).expect("shape grammar asset metadata schema");
173 output.trySetFloatAttribute(pointIndex, "length", variant->length)
174 .expect("shape grammar length metadata schema");
175 cursor += variant->length;
176 }
177 lastSymbolCount_ = output.getCount();
178 lastUsedLength_ = cursor;
179 return new PointSet(std::move(output));
180}
181
182std::string ShapeGrammar::getError() const { return error_; }
183std::string ShapeGrammar::debugReport() const {
184 std::ostringstream out;
185 out << "modules=" << modules_.size() << " symbols=" << lastSymbolCount_ << " used=" << lastUsedLength_
186 << " spline=" << lastSplineLength_;
187 if (!error_.empty()) out << " error=" << error_;
188 return out.str();
189}
190
191void ShapeGrammar::Parser::skipWhitespace() {
192 while (position < text.size() && std::isspace(static_cast<unsigned char>(text[position]))) ++position;
193}
194
195std::vector<ShapeGrammar::Element> ShapeGrammar::Parser::sequence(char terminator) {
196 std::vector<Element> result;
197 while (position < text.size()) {
198 skipWhitespace();
199 if (position >= text.size()) break;
200 if (terminator != '\0' && text[position] == terminator) {
201 ++position;
202 return result;
203 }
204 Element element;
205 if (text[position] == '[') {
206 ++position;
207 element.children = sequence(']');
208 if (!error.empty()) return {};
209 if (element.children.empty()) {
210 error = "empty grammar group";
211 return {};
212 }
213 } else {
214 const size_t start = position;
215 while (position < text.size()) {
216 const unsigned char ch = static_cast<unsigned char>(text[position]);
217 // Decimal digits are reserved for the exact-repeat suffix: A3 means
218 // three A modules. Keeping module tokens digit-free removes the
219 // otherwise unresolvable ambiguity between a symbol and its repeat.
220 if (!std::isalpha(ch) && ch != '_' && ch != '-' && ch != '.') break;
221 ++position;
222 }
223 if (start == position) {
224 error = "expected module at character " + std::to_string(position);
225 return {};
226 }
227 element.symbol = text.substr(start, position - start);
228 }
229 skipWhitespace();
230 if (position < text.size() && text[position] == '*') {
231 element.repeatMin = 0;
232 element.repeatMax = -1;
233 ++position;
234 } else if (position < text.size() && text[position] == '+') {
235 element.repeatMin = 1;
236 element.repeatMax = -1;
237 ++position;
238 } else if (position < text.size() && std::isdigit(static_cast<unsigned char>(text[position]))) {
239 const size_t start = position;
240 while (position < text.size() && std::isdigit(static_cast<unsigned char>(text[position]))) ++position;
241 element.repeatMin = element.repeatMax =
242 std::max(0, std::atoi(text.substr(start, position - start).c_str()));
243 }
244 result.push_back(std::move(element));
245 skipWhitespace();
246 if (position < text.size() && text[position] == ',') {
247 ++position;
248 continue;
249 }
250 if (terminator != '\0' && position < text.size() && text[position] == terminator) continue;
251 if (position < text.size()) {
252 error = "expected comma at character " + std::to_string(position);
253 return {};
254 }
255 }
256 if (terminator != '\0') error = "unterminated grammar group";
257 return result;
258}
259
260float ShapeGrammar::elementMinLength(const Element& element) const {
261 float unit = 0.f;
262 if (!element.symbol.empty()) {
263 const auto found = modules_.find(element.symbol);
264 if (found != modules_.end() && !found->second.empty()) {
265 unit = found->second.front().length;
266 for (const auto& variant : found->second) unit = std::min(unit, variant.length);
267 }
268 } else {
269 unit = sequenceMinLength(element.children);
270 }
271 return unit * float(element.repeatMin);
272}
273
274float ShapeGrammar::sequenceMinLength(const std::vector<Element>& sequence, size_t from) const {
275 float result = 0.f;
276 for (size_t i = from; i < sequence.size(); ++i) result += elementMinLength(sequence[i]);
277 return result;
278}
279
280bool ShapeGrammar::expandSequence(const std::vector<Element>& sequence, float available,
281 std::vector<std::string>& symbols, float& used) const {
282 for (size_t i = 0; i < sequence.size(); ++i) {
283 const auto& element = sequence[i];
284 float unitLength = !element.symbol.empty() ? elementMinLength(Element{element.symbol, {}, 1, 1})
285 : sequenceMinLength(element.children);
286 if (unitLength <= 0.f) return false;
287 const float reservedAfter = sequenceMinLength(sequence, i + 1);
288 int repeats = element.repeatMin;
289 if (element.repeatMax < 0) {
290 const float room = std::max(0.f, available - used - reservedAfter);
291 repeats = std::max(element.repeatMin, int(std::floor(room / unitLength)));
292 } else {
293 repeats = element.repeatMax;
294 }
295 for (int repeat = 0; repeat < repeats; ++repeat) {
296 if (used + unitLength + reservedAfter > available + 0.0001f) return true;
297 if (!element.symbol.empty()) {
298 symbols.push_back(element.symbol);
299 used += unitLength;
300 } else {
301 float groupUsed = 0.f;
302 if (!expandSequence(element.children, available - used - reservedAfter, symbols, groupUsed))
303 return false;
304 used += groupUsed;
305 }
306 }
307 }
308 return true;
309}
310
311const ShapeModuleVariant* ShapeGrammar::chooseVariant(const std::string& symbol, uint32_t seed) const {
312 const auto found = modules_.find(symbol);
313 if (found == modules_.end() || found->second.empty()) return nullptr;
314 float total = 0.f;
315 for (const auto& variant : found->second) total += variant.weight;
316 float choice = grammarUnit(seed) * total;
317 for (const auto& variant : found->second) {
318 choice -= variant.weight;
319 if (choice <= 0.f) return &variant;
320 }
321 return &found->second.back();
322}
323
324} // namespace eve::procgen
double value
Duration start
std::string output
std::string from
std::string variant
float length
Definition CaveMesh.cpp:94
std::int32_t second
std::string text
std::array< float, 3 > position
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
std::string error
Definition Package.cpp:60
std::uint32_t seed
Definition PointSet.cpp:807
std::shared_ptr< const std::vector< glm::vec2 > > points
float t
bool found
float dz
float dy
float dx
std::string element
TacticalUnit * unit
std::size_t cursor
int children
Definition TreeMesh.cpp:295
float size
Definition TreeMesh.cpp:156
uint32_t index
glm::vec3 point
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
int getCount() const
Returns the count.
Definition PointSet.cpp:48
std::string getModuleSymbol(int index) const
Returns the module symbol.
bool removeModule(const std::string &symbol)
Removes module.
void clear()
Remove every registered symbol and diagnostic.
int getModuleCount() const
Returns the module count.
float getVariantLength(const std::string &symbol, int index) const
Returns the variant length.
std::string getError() const
Returns the error.
bool validate(const std::string &grammar)
Parse and validate grammar without generating output.
bool hasModule(const std::string &symbol) const
True when module.
int getVariantCount(const std::string &symbol) const
Returns the variant count.
bool addModule(const std::string &symbol, const std::string &asset, float length, float weight=1.f)
Register a weighted asset variant for a symbol.
std::string getVariantAsset(const std::string &symbol, int index) const
Returns the variant asset.
PointSet * generate(const std::string &grammar, PointSet *controlPoints, uint32_t seed, bool acceptIncomplete)
Expand grammar along control points.
std::string debugReport() const
Debug report.
double polylineLength(const Polyline &pl)
Total length of an open polyline.
One deterministic sample used by script-first procedural pipelines.
Definition PointSet.h:17